一.串口介绍
串口设备是嵌入式开发中最常用的外设之一,通过串口打印信息可以调试程序的运行,通 过串口也可以链接很多种外设,比如串口打印机,蓝牙,wifi,GPS,GPRS 等等。
数据传输方式:
串口通信使用的是串行传输方式,即将数据按照顺序一个一个地发送或接收。每个数据位 都在一定的时间间隔内发送或接收。串口使用两条数据线(TXD,RXD)和一条地线(GND) 进行传输。
数据格式:
串口通信使用的是异步传输方式,数据被分成固定大小的数据帧,每个数据帧由起始位、 数据位、校验位和停止位组成,通常称为“帧”。其中起始位和停止位用于标识数据帧的开始和 结束,校验位用于检验数据传输的准确性。数据位的数量一般为 7 位或 8 位。
波特率:
波特率是指数据传输的速度,也称为比特率,表示每秒钟可以传输多少个二进制数据位。 波特率越高,数据传输的速度就越快。常见的波特率有 9600bps、19200bps、38400bps、115200bps 等。
二.串口节点介绍
在嵌入式设备中,串口设备通常以字符设备节点的形式出现在 Linux 系统中。在 Linux 系 统中,每个设备都由一个设备节点(device node)来表示,设备节点是与设备相关联的一个文 件,以/dev 目录下的文件形式存在。 串口设备节点通常以 tty 开头,具体命名方式根据串口的类型和数量不同而不同。开发板 系统启动之后,使用以下命令打印终端设备节点。
查看串口ttyS2是否有数据输出:
cat /dev/ttyS2
三.使用uart读取模块串口数据
下图为uart读取一帧数据,数据位所代表的解析:
串口编程:解析串口数据
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
unsigned char *PM25_data;
unsigned char *VOC_data;
unsigned char *CO2_data;
unsigned char *Temp_Humi_data;
static void unpackMcuData(unsigned char *buf, int size)
{
if (size < 5) {
return;
}
if (buf[0] != 0x55 && buf[1] != 0xaa) {
return;
}
int dataSize = (int)buf[2];
if ((buf[dataSize+ 2] != 0x0d) && (buf[dataSize + 2] != 0x0a)) {
return;
}
unsigned char crc = 0;
for (int i = 2; i < dataSize + 1; i++) {
crc ^= buf[i];
}
if (crc != buf[dataSize + 1]) {
return;
}
switch (buf[3]) {
case 0x02 : {
PM25_data = 0;
PM25_data |= (((int)buf[9] & 0xff) << 8); //PM2.5 H
PM25_data |= ((int)buf[10] & 0xff); //PM2.5 L
PM25_data |= (((int)buf[11] & 0xff) << 24); //PM10 H
PM25_data |= (((int)buf[12] & 0xff) << 16); //PM10 L
break;
}
case 0x03 : {
VOC_data = 0;
VOC_data |= (((int)buf[4] & 0xff) << 8); //VOC H
VOC_data |= ((int)buf[5] & 0xff); //VOC L
break;
}
case 0x04 : {
CO2_data = ((int)buf[8] & 0xff) * 256 + ((int)buf[9] & 0xff);
break;
}
case 0x05 : {
Temp_Humi_data = 0;
Temp_Humi_data |= (((int)buf[4] & 0xff) << 24); //humi H
Temp_Humi_data |= (((int)buf[5] & 0xff) << 16); //humi L
Temp_Humi_data |= (((int)buf[6] & 0xff) << 8); //Temp H
Temp_Humi_data |= ((int)buf[7] & 0xff); //humi L
break;
}
default : break;
}
}
void *getMcuData(void *ptr)
{
#if 1
struct termios new_options, old_options;
fd_set r_fds;
struct timeval timeout;
#define SET_IO_SPEED B115200
static unsigned char _cmdLine[64];
static unsigned int _u8ReceivedBytes = 0;
int ret = 0;
input_fd = open(MCU_TTY_PORT, O_RDWR| O_NOCTTY | O_NDELAY );
if(input_fd == -1)
{
printf(" error open %s\n", MCU_TTY_PORT);
return 0;
}
//fcntl(input_fd, F_SETFL, FNDELAY);
tcgetattr(input_fd, &old_options); // 获取串口的设置选项
// config ttyS0 not do local ECHO, turn off flow control
tcgetattr(input_fd, &new_options);
new_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | ISIG);
new_options.c_iflag &= ~(INPCK | INLCR | ICRNL | IUCLC | IXON | IXOFF);
new_options.c_oflag &= ~OPOST; // raw output
speed_t setiospeed=115200;
cfsetispeed(&new_options, setiospeed); // 设置波特率
cfsetospeed(&new_options, setiospeed);
tcsetattr(input_fd, TCSANOW, &new_options); // 把新的设置应用到串口上
while(1){
timeout.tv_sec = 0;
timeout.tv_usec = 10*1000; // 10 ms
FD_ZERO(&r_fds);
FD_SET(input_fd, &r_fds);
ret = ::select(input_fd + 1, &r_fds, NULL, NULL, &timeout); //阻塞
if(ret == 0)
{
continue;
}
else if(ret > 0 && FD_ISSET(input_fd, &r_fds))//else if(ret > 0)
{
ret = read(input_fd, _cmdLine, sizeof(_cmdLine)); //读取串口数据
if(ret <= 0)
{
continue;
}
char str[128] = {0};
sprintf(str, "recv: %02x %02x %02x %02x ", _cmdLine[0], _cmdLine[1], _cmdLine[2], _cmdLine[3]);
printf(" 6 data %s\n", str);
unpackMcuData(_cmdLine, ret);
}
}
#endif
}
int main(void)
{
pthread_t tid;
pthread_attr_t thr_attr;
pthread_attr_init(&thr_attr);
if ((pthread_create(&tid, &thr_attr, getMcuData, (void *)NULL)) == -1){
printf("getMcuData create error!\n");
return -1;
}
return 0;
}