实验目的
基于龙芯SOC平台进行串口通信测试,顺便使用串口驱动GNSS模块,显示经纬度信息等
原理图
默认的串口是uart0,uart1和uart2均是空闲、现成可用的串口,如下图黑色圆框所示
但从设备树上看,貌似有9路串口
cpu_uart0: serial@0x16100000 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16100000 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <0>;
no-loopback-test;
};
cpu_uart1: serial@0x16100400 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16100400 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <1>;
no-loopback-test;
};
cpu_uart2: serial@0x16100800 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16100800 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <2>;
no-loopback-test;
};
cpu_uart3: serial@0x16100c00 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16100c00 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <2>;
no-loopback-test;
};
cpu_uart4: serial@0x16101000 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16101000 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <2>;
no-loopback-test;
};
cpu_uart5: serial@0x16101400 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16101400 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <2>;
no-loopback-test;
};
cpu_uart6: serial@0x16101800 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16101800 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <3>;
no-loopback-test;
};
cpu_uart7: serial@0x16101c00 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16101c00 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <3>;
no-loopback-test;
};
cpu_uart8: serial@0x16102000 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16102000 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <3>;
no-loopback-test;
};
cpu_uart9: serial@0x16102400 {
#device_type = "serial";
status = "disabled";
compatible = "ns16550,loongson,frac";
reg = <0 0x16102400 0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&icu>;
interrupts = <3>;
no-loopback-test;
};
compatible为"ns16550,loongson,frac",控制器驱动文件如下
./drivers/ts/serial/8250/8250_port.c
./drivers/ts/serial/8250/8250_early.o
./drivers/ts/serial/8250/8250_of.o
./drivers/ts/serial/8250/8250_early.c
./drivers/ts/serial/8250/8250.h
./drivers/ts/serial/8250/8250_core.c
./drivers/ts/serial/8250/8250_of.c
可以看到内核.config文件中有一些跟8250驱动相关的开关,8250是一个非常常见的串口驱动模型
自发自收
将串口2的txd和rxd连起来,在两个不同的Linux ssh终端输入以下分别发送、接收数据
发送端
while true;
do
echo `date` >> /dev/ttyS2;
sleep 1;
done
接收端
cat /dev/ttyS2
PC通信
C语言配置串口2,交叉编译以下程序并部署到开发板上面
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int main(int argc, char const *argv[])
{
// open ttyS2
int fd = open("/dev/ttyS2", O_RDONLY);
if (fd == -1) {
printf("Unable to open dev!\n");
return -1;
}
// uart config
struct termios ts;
memset(&ts, 0, sizeof ts);
if(tcgetattr(fd, &ts) != 0) {
perror("Error from tcgetattr");
return -1;
}
// cfsetospeed(&ts, B9600);
cfsetispeed(&ts, B9600);
ts.c_cflag &= ~PARENB; // 清除校验位
ts.c_cflag &= ~CSTOPB; // 停止位为1
ts.c_cflag &= ~CSIZE;
ts.c_cflag |= CS8; // 8位数据位
ts.c_cflag &= ~CRTSCTS; // 无硬件流控
ts.c_cflag |= CREAD | CLOCAL; // 打开接收器,忽略modem控制线
ts.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始输入
ts.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用软件流控
ts.c_oflag &= ~OPOST; // 原始输出
ts.c_cc[VMIN] = 0; // 读取时不等待
ts.c_cc[VTIME] = 5; // 0.5秒超时
if (tcsetattr(fd, TCSANOW, &ts) != 0) {
perror("Error from tcsetattr");
return -1;
}
// receive data from PC
while(1) {
char buf[256];
int ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
printf("Reading error!\n");
return -1;
}
buf[ret] = '\0';
printf("Receive %d bytes : %s\n", ret, buf);
sleep(1);
}
close(fd);
return 0;
}
利用串口工具在PC端发送数据,看开发板端能否收到数据,串口配置波特率9600、数据位8、停止位1、奇偶校验位None
GNSS驱动
模块连接,这里用的是中科微的GNSS模块:ATGM332D
GNSS报文解析,程序根据中科微提供的SDK移植而来
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <unistd.h>
#define false 0
#define true 1
#define bool char
#define GPS_Buffer_Length 80
#define UTCTime_Length 11
#define latitude_Length 11
#define N_S_Length 2
#define longitude_Length 12
#define E_W_Length 2
#define gpsRxBufferLength 76
typedef struct
{
char GPS_Buffer[GPS_Buffer_Length];
char UTCTime[UTCTime_Length]; // UTC时间
char latitude[latitude_Length]; // 纬度
char N_S[N_S_Length]; // N/S
char longitude[longitude_Length]; // 经度
char E_W[E_W_Length]; // E/W
}GnssPacket;
static unsigned char gpsRxBuffer[gpsRxBufferLength];
static unsigned char RX_Count;
static GnssPacket gnss;
static bool isGetData = false;
static bool isParseData = false;
static bool isUsefull = false;
void Delay_ms(unsigned int n)
{
usleep(n*1000);
}
void errorLog(int num)
{
while (1)
{
printf("ERROR : %c\r\n", num+0x30);
}
}
void clrStruct()
{
isGetData = false;
isParseData = false;
isUsefull = false;
memset(gnss.GPS_Buffer, 0, GPS_Buffer_Length);
memset(gnss.UTCTime, 0, UTCTime_Length);
memset(gnss.latitude, 0, latitude_Length);
memset(gnss.N_S, 0, N_S_Length);
memset(gnss.longitude, 0, longitude_Length);
memset(gnss.E_W, 0, E_W_Length);
}
void parseGpsBuffer()
{
char *subString;
char *subStringNext;
char i = 0;
if (isGetData)
{
isGetData = false;
printf("**************************************************************************************************\r\n");
printf(gnss.GPS_Buffer);
for (i = 0 ; i <= 6 ; i++)
{
if (i == 0)
{
if ((subString = strstr(gnss.GPS_Buffer, ",")) == NULL)
errorLog(1);
}
else
{
subString++;
if ((subStringNext = strstr(subString, ",")) != NULL)
{
char usefullBuffer[2];
switch(i)
{
case 1:memcpy(gnss.UTCTime, subString, subStringNext - subString);break;
case 2:memcpy(usefullBuffer, subString, subStringNext - subString);break;
case 3:memcpy(gnss.latitude, subString, subStringNext - subString);break;
case 4:memcpy(gnss.N_S, subString, subStringNext - subString);break;
case 5:memcpy(gnss.longitude, subString, subStringNext - subString);break;
case 6:memcpy(gnss.E_W, subString, subStringNext - subString);break;
default:break;
}
subString = subStringNext;
isParseData = true;
if(usefullBuffer[0] == 'A')
isUsefull = true;
else if(usefullBuffer[0] == 'V')
isUsefull = false;
}
else
{
errorLog(2);
}
}
}
}
}
void printGpsBuffer()
{
if (isParseData)
{
isParseData = false;
printf("gnss.UTCTime = ");
printf(gnss.UTCTime);
printf("\r\n");
if(isUsefull)
{
isUsefull = false;
printf("gnss.latitude = ");
printf(gnss.latitude);
printf("\r\n");
printf("gnss.N_S = ");
printf(gnss.N_S);
printf("\r\n");
printf("gnss.longitude = ");
printf(gnss.longitude);
printf("\r\n");
printf("gnss.E_W = ");
printf(gnss.E_W);
printf("\r\n");
}
else
{
printf("GNSS DATA is not usefull!\r\n");
}
}
}
void getGNSSData(unsigned char SBUF)
{
unsigned char temp = 0;
char i = 0;
temp = SBUF;
if(temp == '$')
{
RX_Count = 0;
}
if(RX_Count <= 5)
{
gpsRxBuffer[RX_Count++] = temp;
}
else if(gpsRxBuffer[0] == '$' &gpsRxBuffer[4] == 'M' && gpsRxBuffer[5] == 'C') //确定是否收到"GPRMC/GNRMC"这一帧数据
{
gpsRxBuffer[RX_Count++] = temp;
if(temp == '\n')
{
memset(gnss.GPS_Buffer, 0, GPS_Buffer_Length); //清空
memcpy(gnss.GPS_Buffer, gpsRxBuffer, RX_Count); //保存数据
isGetData = true;
RX_Count = 0;
memset(gpsRxBuffer, 0, gpsRxBufferLength); //清空
}
if(RX_Count >= 75)
{
RX_Count = 75;
gpsRxBuffer[RX_Count] = '\0';//添加结束符
}
}
}
int main(int argc, char const *argv[])
{
// open ttyS1
int fd = open("/dev/ttyS1", O_RDONLY);
if (fd == -1) {
printf("Unable to open dev!\n");
return -1;
}
// uart config
struct termios ts;
memset(&ts, 0, sizeof ts);
if(tcgetattr(fd, &ts) != 0) {
perror("Error from tcgetattr");
return -1;
}
// cfsetospeed(&ts, B9600);
cfsetispeed(&ts, B9600);
ts.c_cflag &= ~PARENB; // 清除校验位
ts.c_cflag &= ~CSTOPB; // 停止位为1
ts.c_cflag &= ~CSIZE;
ts.c_cflag |= CS8; // 8位数据位
ts.c_cflag &= ~CRTSCTS; // 无硬件流控
ts.c_cflag |= CREAD | CLOCAL; // 打开接收器,忽略modem控制线
ts.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始输入
ts.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用软件流控
ts.c_oflag &= ~OPOST; // 原始输出
ts.c_cc[VMIN] = 0; // 读取时不等待
ts.c_cc[VTIME] = 5; // 0.5秒超时
if (tcsetattr(fd, TCSANOW, &ts) != 0) {
perror("Error from tcsetattr");
return -1;
}
while(1) {
// 读取数据
char buf[512];
int ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
perror("Error reading from serial port");
return -1;
}
buf[ret] = '\0'; // 添加字符串结束符
size_t len = strlen(buf);
for(int i = 0; i < len; i++) {
getGNSSData(buf[i]);
}
parseGpsBuffer();
printGpsBuffer();
sleep(1);
}
close(fd);
return 0;
}
经度、纬度、东西、北半球南半球、UTC时间信息解析如下
参考
GNSS模块资料:GPS模块 北斗模块 双模 卫星定位 BD ATGM332D 支持STM32 UNO 51-tmall.com天猫