【驱动篇】龙芯LS2K0300之串口设备驱动

实验目的

基于龙芯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天猫

相关推荐

  1. 微RK3588驱动设计DVP并口摄像头2

    2024-07-19 07:46:03       25 阅读
  2. 设备驱动开发_2

    2024-07-19 07:46:03       43 阅读
  3. linux驱动字符设备驱动框架

    2024-07-19 07:46:03       53 阅读
  4. linux 设备驱动tty 线路设置

    2024-07-19 07:46:03       41 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-19 07:46:03       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-19 07:46:03       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-19 07:46:03       58 阅读
  4. Python语言-面向对象

    2024-07-19 07:46:03       69 阅读

热门阅读

  1. vue项目使用iview☞Modal后页面不能滚动的诡异问题

    2024-07-19 07:46:03       21 阅读
  2. STM32 | 看门狗+RTC源码解析

    2024-07-19 07:46:03       21 阅读
  3. 富文本中提取信息并去除其中的HTML或XML标签

    2024-07-19 07:46:03       22 阅读
  4. 2024前端面试真题【手写篇】

    2024-07-19 07:46:03       18 阅读
  5. 聊聊最近在看的一本书-《从极简到极致》

    2024-07-19 07:46:03       20 阅读
  6. Python数据获取(网页视频、音频版)

    2024-07-19 07:46:03       22 阅读
  7. Log4j2原理及应用详解(九)

    2024-07-19 07:46:03       23 阅读
  8. 关于Kafka的17个问题

    2024-07-19 07:46:03       19 阅读
  9. Leetcode 392. 判断子序列

    2024-07-19 07:46:03       20 阅读
  10. 无重复字符的最长子串(leetcode)

    2024-07-19 07:46:03       23 阅读