当前位置: 首页 > news >正文

作网站青岛市疾病预防控制中心紧急提示

作网站,青岛市疾病预防控制中心紧急提示,分类信息网站怎么做,住房城乡建设部门门户网站目录Demo串口应用编程介绍终端Terminal串口应用编程(配置、读取、写入)struct termios 结构体配置输入模式: c_iflag输出模式: c_oflag控制模式: c_cflag(波特率、数据位、校验位、停止位)本地模式: c_lflag特殊控制字符: c_cc注意事项三种工作模式(原始模式read是否阻塞)什么时… 目录Demo串口应用编程介绍终端Terminal串口应用编程(配置、读取、写入)struct termios 结构体配置输入模式: c_iflag输出模式: c_oflag控制模式: c_cflag(波特率、数据位、校验位、停止位)本地模式: c_lflag特殊控制字符: c_cc注意事项三种工作模式(原始模式read是否阻塞)什么时候会使用原始模式打开串口设备获取终端当前的配置参数:tcgetattr()函数串口终端配置(原始模式为例)1) 配置原始模式2) 接收使能3) 设置串口的波特率4) 设置数据位大小5) 设置奇偶校验位6) 设置停止位7) 设置MIN 和TIME 的值(read是否阻塞)缓冲区的处理(清空、阻塞、暂停)使配置生效tcsetattr()函数读写数据read()、write()串口应用编程实战读取串口数据写操作在开发板上进行测试读测试写测试Demo 参考【嵌入式LINUX系统编程】嵌入式LINUX串口编程 #include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include termios.h #include errno.hint set_opt(int,int,int,char,int);void main(){int fd,nByte;char *uart3 /dev/ttyS2;char bufferR[2];char bufferW[2]; memset(bufferR, \0, 2);memset(bufferW, \0, 2);if((fdopen(uart3,O_RDWR,0777))0){printf(failed\n);}else{printf(success\n);set_opt(fd, 115200, 8, N, 1); }while(1){ nByte 0;memset(bufferR, \0, 2);memset(bufferW, \0, 2);// printf(hello\n);if((nByte read(fd, bufferR, 1)) 1){ //MCU串口发送接收都是单字节(单字符)函数printf(receive:%c\n,bufferR[0]);bufferW[0] A;write(fd,bufferW,1); //串口发送单字节单字符 buffer[0] datamemset(bufferR, \0, 2);memset(bufferW, \0, 2);nByte 0;}}close(fd);}//串口通用初始化函数 int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop) {struct termios newtio,oldtio;//定义结构体newtio和oldtio//将原串口的数据取到oldtioif ( tcgetattr( fd,oldtio) ! 0) { perror(SetupSerial 1);return -1;}//将newio清零和设置c_cflagbzero( newtio, sizeof( newtio ) );newtio.c_cflag | CLOCAL | CREAD;//使能接收和忽略控制线newtio.c_cflag ~CSIZE;//设置数据位switch( nBits ){case 7:newtio.c_cflag | CS7;break;case 8:newtio.c_cflag | CS8;break;}//设置校验位switch( nEvent ){//偶校验case O:newtio.c_cflag | PARENB;//使能奇偶校验newtio.c_cflag | PARODD;//偶校验newtio.c_iflag | (INPCK | ISTRIP);//输入校验并忽略第八位break;case E: newtio.c_iflag | (INPCK | ISTRIP);newtio.c_cflag | PARENB;newtio.c_cflag ~PARODD;//取消偶校验置零偶校验位开启奇校验break;case N: newtio.c_cflag ~PARENB;//不进行奇偶校验break;}//设置波特率switch( nSpeed ){case 2400:cfsetispeed(newtio, B2400);cfsetospeed(newtio, B2400);break;case 4800:cfsetispeed(newtio, B4800);cfsetospeed(newtio, B4800);break;case 9600:cfsetispeed(newtio, B9600);cfsetospeed(newtio, B9600);break;case 115200:cfsetispeed(newtio, B115200);cfsetospeed(newtio, B115200);break;case 460800:cfsetispeed(newtio, B460800);cfsetospeed(newtio, B460800);break;default:cfsetispeed(newtio, B9600);cfsetospeed(newtio, B9600);break;}//设置停止位if( nStop 1 )newtio.c_cflag ~CSTOPB;//一位停止位else if ( nStop 2 )newtio.c_cflag | CSTOPB;//两位停止位newtio.c_cc[VTIME] 0;//不设置读取超时newtio.c_cc[VMIN] 0;//读取最小字符数为0tcflush(fd,TCIFLUSH);//清空缓冲区//使配置生效if((tcsetattr(fd,TCSANOW,newtio))!0){perror(com set error);return -1;}// printf(set done!\n\r);return 0; }串口应用编程介绍 本小节我们来学习Linux 下串口应用编程串口UART是一种非常常见的外设串口在嵌入式开发领域当中一般作为一种调试手段通过串口输出调试打印信息或者发送指令给主机端进行处理还可以通过串口与其他设备或传感器进行通信譬如有些sensor 就使用了串口通信的方式与主机端进行数据交互。 串口全称叫做串行接口使用两条线即可实现双向通信一条用于发送一条用于接收。串口通信距离远但是速度相对会低。 串口UART在嵌入式Linux 系统中常作为系统的标准输入、输出设备系统运行过程产生的打印信息通过串口输出同理串口也作为系统的标准输入设备用户通过串口与Linux 系统进行交互。 所以串口在Linux 系统就是一个终端提到串口就不得不引出“终端Terminal”这个概念了。 终端Terminal 终端就是处理主机输入、输出的一套设备它用来显示主机运算的输出并且接受主机要求的输入。典型的终端包括显示器键盘套件、打印机打字机套件等。只要能提供给计算机输入和输出功能它就是终端。 终端的分类 本地终端PC 机连接了显示器、键盘以及鼠标等设备这样的一个显示器/键盘组合就是一个本地终端同样对于开发板来说一个LCD 显示器、键盘和鼠标等构成本地终端。用串口连接的远程终端开发板通过串口线连接到一个带有显示器和键盘的PC 机在PC 机通过运行一个终端模拟程序譬如SecureCRT 等来获取并显示开发板通过串口发出的数据、同样还可以通过这些终端模拟程序将用户数据通过串口发送给开发板Linux 系统。基于网络的远程终端譬如我们可以通过ssh、Telnet 这些协议登录到一个远程主机。 前面两个属于物理终端远程终端又叫做伪终端。 终端对应的设备节点 在Linux 当中一切皆是文件。当然终端也不例外每一个终端在/dev 目录下都有一个对应的设备节点。 ⚫ /dev/ttyXX 是一个数字编号譬如0、1、2、3 等设备节点在Linux 中/dev/ttyX 代表的都是上述提到的本地终端包括/dev/tty1~/dev/tty63 共63 个这是Linux 内核在初始化时所生成的63 个本地终端。如下所示 ⚫ /dev/pts/XX 是一个数字编号譬如0、1、2、3 等设备节点这类设备节点是伪终端对应的设备节点。譬如我们通过ssh 或Telnet 这些远程登录协议登录到开发板主机那么开发板Linux 系统会在/dev/pts 目录下生成一个设备节点如下所示 ⚫ 串口终端设备节点/dev/ttymxcX对于ALPHA/Mini I.MX6U 开发板来说有两个串口也就是有两个串口终端对应两个设备节点如下所示 这里为什么是0 和2、而不是0 和1I.MX6U SoC 支持8 个串口外设出厂系统只注册了2 个串口外设分别是UART1 和UART3所以对应这个数字就是0 和2。 mxc 这个名字不是一定的命名与驱动有关系与硬件平台有关但是名字前缀都是以“tty”开头以表明它是一个终端。 在Linux 系统下我们可以使用who 命令来查看计算机系统当前连接了哪些终端一个终端就表示有一个用户使用该计算机如下所示 可以看到开发板系统当前有两个终端连接到它一个是串口终端即开发板的USB 调试串口对应/dev/ttymxc0另一个是伪终端是笔者通过ssh 连接的。 串口应用编程(配置、读取、写入) 我们知道了串口在Linux 系统中是一种终端设备并且在我们的开发板上其设备节点为/dev/ttymxc0UART1和/dev/ttymxc2UART3。 串口的应用编程很简单通过ioctl()对串口进行配置调用read()读取串口的数据、调用write()向串口写入数据但是我们不这么做因为Linux 为上层用户做了一层封装将这些ioctl()操作封装成了一套标准的API我们直接使用这一套标准API 即可。 这些API 是C 库函数可以使用man 手册查看到它们的帮助信息这一套接口并不是仅针对串口开发的而是针对所有的终端设备通过ssh 远程登录连接的伪终端也是终端设备。 要使用这个API需要包含termios.h 头文件。 struct termios 结构体配置 struct termios 结构体描述了终端的配置信息struct termios 结构体定义如下 struct termios {tcflag_t c_iflag; /* input mode flags */ 输入模式tcflag_t c_oflag; /* output mode flags */输出模式tcflag_t c_cflag; /* control mode flags */控制模式tcflag_t c_lflag; /* local mode flags */本地模式cc_t c_line; /* line discipline */ 线路规程cc_t c_cc[NCCS]; /* control characters */特殊控制字符speed_t c_ispeed; /* input speed */输入速率speed_t c_ospeed; /* output speed */输出速率 };输入模式: c_iflag 输入模式控制输入数据终端驱动程序从串口或键盘接收到的字符数据在被传递给应用程序之前的处理方式。所有的标志都被定义为宏c_oflag、c_cflag 以及c_lflag 成员也都采用这种方式进行配置。 c_iflag 成员的宏如下所示 宏描述IGNBRK忽略输入终止条件BRKINT当检测到输入终止条件时发送SIGINT 信号IGNPAR忽略帧错误和奇偶校验错误PARMRK对奇偶校验错误做出标记INPCK对接收到的数据执行奇偶校验ISTRIP将所有接收到的数据裁剪为7 比特位、也就是去除第八位INLCR将接收到的NL换行符转换为CR回车符IGNCR忽略接收到的CR回车符ICRNL将接收到的CR回车符转换为NL换行符IUCLC将接收到的大写字符映射为小写字符IXON启动输出软件流控IXOFF启动输入软件流控 以上所列举出的这些宏我们可以通过man 手册查询到它们的详细描述信息执行命令 man 3 termios如下图所示 输出模式: c_oflag 输出模式控制输出字符的处理方式即由应用程序发送出去的字符数据在传递到串口或屏幕之前是如何处理的。可用于c_oflag 成员的宏如下所示 宏描述OPOST启用输出处理功能如果不设置该标志则其他标志都被忽略OLCUC将输出字符中的大写字符转换成小写字符ONLCR将输出中的换行符NL ‘\n’转换成回车符CR ‘\r’OCRNL将输出中的回车符CR ‘\r’转换成换行符NL ‘\n’ONOCR在第0 列不输出回车符CRONLRET不输出回车符OFILL发送填充字符以提供延时OFDEL如果设置该标志则表示填充字符为DEL 字符否则为NULL字符 控制模式: c_cflag(波特率、数据位、校验位、停止位) 控制模式控制终端设备的硬件特性譬如对于串口来说该字段比较重要可设置串口波特率、数据位、校验位、停止位等硬件特性。可用于c_cflag 成员的标志如下所示 CBAUD波特率的位掩码B0波特率为0…………B12001200 波特率B18001800 波特率B24002400 波特率B48004800 波特率B96009600 波特率B1920019200 波特率B3840038400 波特率B5760057600 波特率B115200115200 波特率B230400230400 波特率B460800460800 波特率B500000500000 波特率B576000576000 波特率B921600921600 波特率B10000001000000 波特率B11520001152000 波特率B15000001500000 波特率B20000002000000 波特率B25000002500000 波特率B30000003000000 波特率………… CSIZE数据位的位掩码CS55 个数据位CS66 个数据位CS77 个数据位CS88 个数据位CSTOPB2 个停止位如果不设置该标志则默认是一个停止位CREAD接收使能PARENB使能奇偶校验PARODD使用奇校验、而不是偶校验HUPCL关闭时挂断调制解调器CLOCAL忽略调制解调器控制线CRTSCTS使能硬件流控 在其它一些系统中可能会使用c_ispeed 成员变量和c_ospeed 成员变量来指定串口的波特率 在Linux 系统下则是使用CBAUD 位掩码所选择的几个bit 位来指定串口波特率。 事实上termios API 中提供了cfgetispeed()和cfsetispeed()函数分别用于获取和设置串口的波特率。 本地模式: c_lflag 本地模式用于控制终端的本地数据处理和工作模式。可用于c_lflag 成员的标志如下所示 项目ValueISIG若收到信号字符INTR、QUIT 等则会产生相应的信号ICANON启用规范模式ECHO启用输入字符的本地回显功能。当我们在终端输入字符的时候字符会显示出来这就是回显功能ECHOE若设置ICANON则允许退格操作ECHOK若设置ICANON则KILL 字符会删除当前行ECHONL若设置ICANON则允许回显换行符ECHOCTL若设置ECHO则控制字符制表符、换行符等会显示成“^X”其中X 的ASCII 码等于给相应控制字符的ASCII 码加上0x40。例如退格字符0x08会显示为“^H”H’的ASCII 码为0x48ECHOPRT若设置ICANON 和IECHO则删除字符退格符等和被删除的字符都会被显示ECHOKE若设置ICANON则允许回显在ECHOE 和ECHOPRT 中设定的KILL字符NOFLSH在通常情况下当接收到INTR、QUIT 和SUSP 控制字符时会清空输入和输出队列。如果设置该标志则所有的队列不会被清空TOSTOP若一个后台进程试图向它的控制终端进行写操作则系统向该后台进程的进程组发送SIGTTOU 信号。该信号通常终止进程的执行IEXTEN启用输入处理功能 特殊控制字符: c_cc 特殊控制字符是一些字符组合如CtrlC、CtrlZ 等当用户键入这样的组合键终端会采取特殊处理方式。struct termios 结构体中c_cc 数组将各种特殊字符映射到对应的支持函数。每个字符位置数组下标由对应的宏定义的如下所示 ⚫ VEOF文件结尾符EOF对应键为CtrlD该字符使终端驱动程序将输入行中的全部字符传递给正在读取输入的应用程序。如果文件结尾符是该行的第一个字符则用户程序中的read 返回0表示文件结束。 ⚫ VEOL附加行结尾符EOL对应键为Carriage returnCR作用类似于行结束符。 ⚫ VEOL2第二行结尾符EOL2对应键为Line feedLF ⚫ VERASE删除操作符ERASE对应键为BackspaceBS该字符使终端驱动程序删除输入行中的最后一个字符 ⚫ VINTR中断控制字符INTR对应键为CtrlC该字符使终端驱动程序向与终端相连的进程发送SIGINT 信号 ⚫ VKILL删除行符KILL对应键为CtrlU该字符使终端驱动程序删除整个输入行 ⚫ VMIN在非规范模式下指定最少读取的字符数MIN ⚫ VQUIT退出操作符QUIT对应键为CtrlZ该字符使终端驱动程序向与终端相连的进程发送SIGQUIT 信号。 ⚫ VSTART开始字符START对应键为CtrlQ重新启动被STOP 暂停的输出。 ⚫ VSTOP停止字符STOP对应键为CtrlS字符作用“截流”即阻止向终端的进一步输出。用于支持XON/XOFF 流控。 ⚫ VSUSP挂起字符SUSP对应键为CtrlZ该字符使终端驱动程序向与终端相连的进程发送SIGSUSP 信号用于挂起当前应用程序。 ⚫ VTIME非规范模式下指定读取的每个字符之间的超时时间以分秒为单位TIME。 在以上所列举的这些宏定义中TIME 和MIN 值只能用于非规范模式可用于控制非规范模式下read()调用的一些行为特性后面再向大家介绍。 注意事项 对于这些变量尽量不要直接对其初始化而要将其通过“按位与”、“按位或”等操作添加标志或清除某个标志。譬如通常不会这样对变量进行初始化 struct termios ter;ter.c_iflag IGNBRK | BRKINT | PARMRK;而是要像下面这样 ter.c_iflag | (IGNBRK | BRKINT | PARMRK | ISTRIP);并非所有标志对于实际的终端设备来说都是有效的例如串口可以配置波特率、数据位、停止位等参数但其它终端是不一定支持这些配置的譬如本地终端键盘、显示器只不过这些终端设备都使用了这一套API 来编程 三种工作模式(原始模式read是否阻塞) 当ICANON 标志被设置时表示启用终端的规范模式默认情况为规范模式。 规范模式下所有的输入是基于行进行处理的。在用户输入一个行结束符回车符、EOF 等之前系统调用read()函数是读不到用户输入的任何字符的。除了EOF 之外的行结束符回车符等与普通字符一样会被read()函数读取到缓冲区中。在规范模式中行编辑是可行的而且一次read()调用最多只能读取一行数据。如果在read()函数中被请求读取的数据字节数小于当前行可读取的字节数则read()函数只会读取被请求的字节数剩下的字节下次再被读取。 非规范模式下所有的输入是即时有效的不需要用户另外输入行结束符而且不可进行行编辑。在非规范模式下对参数MIN(c_cc[VMIN])和TIME(c_cc[VTIME])的设置决定read()函数的调用方式。 上一小节给大家提到过TIME 和MIN 的值只能用于非规范模式两者结合起来可以控制对输入数据的读取方式。根据TIME 和MIN 的取值不同会有以下4 种不同情况 ⚫ MIN 0 和TIME 0在这种情况下read()调用总是会立即返回。若有可读数据则读取数据并返回被读取的字节数否则读取不到任何数据并返回0。 ⚫ MIN 0 和TIME 0在这种情况下read()函数会被阻塞直到有MIN 个字符可以读取时才返回返回值是读取的字符数量。到达文件尾时返回0。 ⚫ MIN 0 和TIME 0在这种情况下只要有数据可读或者经过TIME 个十分之一秒的时间read()函数则立即返回返回值为被读取的字节数。如果超时并且未读到数据则read()函数返回0。 ⚫ MIN 0 和TIME 0在这种情况下当有MIN 个字节可读或者两个输入字符之间的时间间隔超过TIME 个十分之一秒时read()函数才返回。因为在输入第一个字符后系统才会启动定时器所以在这种情况下read()函数至少读取一个字节后才返回。 原始模式Raw mode 按照严格意义来讲原始模式是一种特殊的非规范模式。在原始模式下所有的输入数据以字节为单位被处理。在这个模式下终端是不可回显的并且禁用终端输入和输出字符的所有特殊处理。在我们的应用程序中可以通过调用cfmakeraw()函数将终端设置为原始模式。 cfmakeraw()函数内部其实就是对struct termios 结构体进行了如下配置 termios_p-c_iflag ~(IGNBRK | BRKINT | PARMRK | ISTRIP| INLCR | IGNCR | ICRNL | IXON); termios_p-c_oflag ~OPOST; termios_p-c_lflag ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); termios_p-c_cflag ~(CSIZE | PARENB); termios_p-c_cflag | CS8;什么时候会使用原始模式 串口在Linux 系统下是作为一种终端设备存在终端通常会对用户的输入、输出数据进行相应的处理如前所述。 但是串口并不仅仅只扮演着人机交互的角色数据以字符的形式传输、也就数说传输的数据其实字符对应的ASCII 编码值串口本就是一种数据串行传输接口通过串口可以与其他设备或传感器进行数据传输、通信譬如很多sensor 就使用了串口方式与主机端进行数据交互。那么在这种情况下我们就得使用原始模式意味着通过串口传输的数据不应进行任何特殊处理、不应将其解析成ASCII 字符。 打开串口设备 接下来编写串口应用程序。第一步便是打开串口设备使用open()函数打开串口的设备节点文件得到文件描述符 int fd; fd open(/dev/ttymxc2, O_RDWR | O_NOCTTY); if (0 fd) {perror(open error);return -1; }调用open()函数时使用了O_NOCTTY 标志该标志用于告知系统/dev/ttymxc2 它不会成为进程的控制终端。 获取终端当前的配置参数:tcgetattr()函数 通常在配置终端之前我们会先获取到终端当前的配置参数将其保存到一个struct termios 结构体对象中以后可很方便地将终端恢复到原来的状态这也是为了安全起见以及后续的调试方便。 #include termios.h #include unistd.hint tcgetattr(int fd, struct termios *termios_p);第一个参数对应串口终端设备的文件描述符fd。 调用tcgetattr 函数之前我们需要定义一个struct termios 结构体变量tcgetattr()调用成功后会将终端当前的配置参数保存到termios_p 指针所指的对象中。 函数调用成功返回0失败将返回-1并且会设置errno以告知错误原因。 使用示例如下 struct termios old_cfg;if (0 tcgetattr(fd, old_cfg)) {/* 出错处理*/do_something(); }串口终端配置(原始模式为例) 假设我们需要采用原始模式进行串口数据通信。 1) 配置原始模式 调用termios.h头文件中申明的cfmakeraw()函数可以将终端配置为原始模式 struct termios new_cfg;memset(new_cfg, 0x0, sizeof(struct termios));//配置为原始模式 cfmakeraw(new_cfg);这个函数没有返回值。 2) 接收使能 使能接收功能只需在struct termios 结构体的c_cflag 成员中添加CREAD 标志即可如下所示 new_cfg.c_cflag | CREAD; //接收使能3) 设置串口的波特率 不能直接通过位掩码来操作使用函数 cfsetispeed(new_cfg, B115200);//输入波特率 cfsetospeed(new_cfg, B115200);//输出波特率B115200 是一个宏表示波特率为115200。 一般输入和输出波特率设置成一样的。 我们还可以直接使用cfsetspeed()函数一次性设置输入和输出波特率该函数也是在termios.h头文件中申明使用方式如下 cfsetspeed(new_cfg, B115200);这几个函数在成功时返回0失败时返回-1。 4) 设置数据位大小 通过位掩码来设置数据位大小先将c_cflag 成员中CSIZE 位掩码所选择的几个bit 位清零然后再设置数据位大小如下所示 new_cfg.c_cflag ~CSIZE; new_cfg.c_cflag | CS8; //设置为8 位数据位5) 设置奇偶校验位 奇偶校验位配置涉及到struct termios 结构体中的两个成员变量c_cflag 和c_iflag。对于c_cflag 成员添加PARENB 标志使能串口的奇偶校验功能对于c_iflag需要添加INPCK 标志这样才能对接收到的数据执行奇偶校验代码如下所示 //奇校验使能 new_cfg.c_cflag | (PARODD | PARENB); new_cfg.c_iflag | INPCK;//偶校验使能 new_cfg.c_cflag | PARENB; new_cfg.c_cflag ~PARODD; /* 清除PARODD 标志配置为偶校验*/ new_cfg.c_iflag | INPCK;//无校验 new_cfg.c_cflag ~PARENB; new_cfg.c_iflag ~INPCK;6) 设置停止位 停止位则是通过设置c_cflag 成员的CSTOPB 标志而实现的。若停止位为一个比特则清除CSTOPB 标志若停止位为两个则添加CSTOPB 标志即可。以下分别是停止位为一个和两个比特时的代码 // 将停止位设置为一个比特 new_cfg.c_cflag ~CSTOPB;// 将停止位设置为2 个比特 new_cfg.c_cflag | CSTOPB;7) 设置MIN 和TIME 的值(read是否阻塞) 前面介绍MIN 和TIME 的取值会影响非规范模式下read()调用的行为特征原始模式是一种特殊的非规范模式所以MIN 和TIME 在原始模式下也是有效的。 在对接收字符和等待时间没有特别要求的情况下可以将MIN 和TIME 设置为0这样则在任何情况下read()调用都会立即返回此时对串口的read 操作会设置为非阻塞方式如下所示 new_cfg.c_cc[VTIME] 0; new_cfg.c_cc[VMIN] 0;缓冲区的处理(清空、阻塞、暂停) 串口使用之前缓冲区中可能已经存在一些数据等待处理或者当前正在进行数据传输、接收。调用termios.h中声明的函数来处理目前串口缓冲中的数据函数原型 #include termios.h #include unistd.h//这三个函数调用成功时返回0失败将返回-1、并且会设置errno 以指示错误类型。 int tcdrain(int fd); int tcflush(int fd, int queue_selector); int tcflow(int fd, int action);1、调用tcdrain()函数后会使得应用程序阻塞直到串口输出缓冲区中的数据全部发送完毕为止 2、调用tcflow()函数会暂停串口上的数据传输或接收工作具体情况取决于参数action取值如下 ⚫ TCOOFF暂停数据输出输出传输 ⚫ TCOON重新启动暂停的输出 ⚫ TCIOFF发送STOP 字符停止终端设备向系统发送数据 ⚫ TCION发送一个START 字符启动终端设备向系统发送数据 3、调用tcflush()函数会清空输入/输出缓冲区中的数据具体情况取决于参数queue_selector如下 ⚫ TCIFLUSH对接收到而未被读取的数据进行清空处理 ⚫ TCOFLUSH对尚未传输成功的输出数据进行清空处理 ⚫ TCIOFLUSH包括前两种功能即对尚未处理的输入/输出数据进行清空处理。 通常我们会选择tcdrain()或tcflush()函数来对串口缓冲区进行处理。 譬如调用tcdrain()阻塞 tcdrain(fd);调用tcflush()清空缓冲区 tcflush(fd, TCIOFLUSH);使配置生效tcsetattr()函数 前面结构体成员配置还未生效通过tcsetattr()函数将配置参数写到硬件设备其函数原型如下所示 #include termios.h #include unistd.hint tcsetattr(int fd, int optional_actions, const struct termios *termios_p);调用该函数会将参数termios_p 所指struct termios 对象中的配置参数写入到终端设备中使配置生效 而参数optional_actions 可以指定更改何时生效其取值如下 ⚫ TCSANOW配置立即生效。 ⚫ TCSADRAIN配置在所有写入fd 的输出都传输完毕之后生效。 ⚫ TCSAFLUSH所有已接收但未读取的输入都将在配置生效之前被丢弃。 该函数调用成功时返回0失败将返回-1,、并设置errno 以指示错误类型。 譬如调用tcsetattr()将配置参数写入设备使其立即生效 tcsetattr(fd, TCSANOW, new_cfg);读写数据read()、write() 调用read()、write()函数即可。 串口应用编程实战 编程实战在串口终端的原始模式下使用串口进行数据传输包括通过串口发送数据、以及读取串口接收到的数据并将其打印出来。 本例程源码对应的路径为开发板光盘-11、Linux C 应用编程例程源码-26_uart-uart_test.c。 /***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.文件名 : uart_test.c作者 : 邓涛版本 : V1.0描述 : 串口在原始模式下进行数据传输--应用程序示例代码其他 : 无论坛 : www.openedv.com日志 : 初版 V1.0 2021/7/20 邓涛创建***************************************************************/#define _GNU_SOURCE //在源文件开头定义_GNU_SOURCE宏 #include stdio.h #include stdlib.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #include sys/ioctl.h #include errno.h #include string.h #include signal.h #include termios.htypedef struct uart_hardware_cfg {unsigned int baudrate; /* 波特率 */unsigned char dbit; /* 数据位 */char parity; /* 奇偶校验 */unsigned char sbit; /* 停止位 */ } uart_cfg_t;static struct termios old_cfg; //用于保存终端的配置参数 static int fd; //串口终端对应的文件描述符/**** 串口初始化操作** 参数device表示串口终端的设备节点**/ static int uart_init(const char *device) {/* 打开串口终端 */fd open(device, O_RDWR | O_NOCTTY);if (0 fd) {fprintf(stderr, open error: %s: %s\n, device, strerror(errno));return -1;}/* 获取串口当前的配置参数 */if (0 tcgetattr(fd, old_cfg)) {fprintf(stderr, tcgetattr error: %s\n, strerror(errno));close(fd);return -1;}return 0; }/**** 串口配置** 参数cfg指向一个uart_cfg_t结构体对象**/ static int uart_cfg(const uart_cfg_t *cfg) {struct termios new_cfg {0}; //将new_cfg对象清零speed_t speed;/* 设置为原始模式 */cfmakeraw(new_cfg);/* 使能接收 */new_cfg.c_cflag | CREAD;/* 设置波特率 */switch (cfg-baudrate) {case 1200: speed B1200;break;case 1800: speed B1800;break;case 2400: speed B2400;break;case 4800: speed B4800;break;case 9600: speed B9600;break;case 19200: speed B19200;break;case 38400: speed B38400;break;case 57600: speed B57600;break;case 115200: speed B115200;break;case 230400: speed B230400;break;case 460800: speed B460800;break;case 500000: speed B500000;break;default: //默认配置为115200speed B115200;printf(default baud rate: 115200\n);break;}if (0 cfsetspeed(new_cfg, speed)) {fprintf(stderr, cfsetspeed error: %s\n, strerror(errno));return -1;}/* 设置数据位大小 */new_cfg.c_cflag ~CSIZE; //将数据位相关的比特位清零switch (cfg-dbit) {case 5:new_cfg.c_cflag | CS5;break;case 6:new_cfg.c_cflag | CS6;break;case 7:new_cfg.c_cflag | CS7;break;case 8:new_cfg.c_cflag | CS8;break;default: //默认数据位大小为8new_cfg.c_cflag | CS8;printf(default data bit size: 8\n);break;}/* 设置奇偶校验 */switch (cfg-parity) {case N: //无校验new_cfg.c_cflag ~PARENB;new_cfg.c_iflag ~INPCK;break;case O: //奇校验new_cfg.c_cflag | (PARODD | PARENB);new_cfg.c_iflag | INPCK;break;case E: //偶校验new_cfg.c_cflag | PARENB;new_cfg.c_cflag ~PARODD; /* 清除PARODD标志配置为偶校验 */new_cfg.c_iflag | INPCK;break;default: //默认配置为无校验new_cfg.c_cflag ~PARENB;new_cfg.c_iflag ~INPCK;printf(default parity: N\n);break;}/* 设置停止位 */switch (cfg-sbit) {case 1: //1个停止位new_cfg.c_cflag ~CSTOPB;break;case 2: //2个停止位new_cfg.c_cflag | CSTOPB;break;default: //默认配置为1个停止位new_cfg.c_cflag ~CSTOPB;printf(default stop bit size: 1\n);break;}/* 将MIN和TIME设置为0 */new_cfg.c_cc[VTIME] 0;new_cfg.c_cc[VMIN] 0;/* 清空缓冲区 */if (0 tcflush(fd, TCIOFLUSH)) {fprintf(stderr, tcflush error: %s\n, strerror(errno));return -1;}/* 写入配置、使配置生效 */if (0 tcsetattr(fd, TCSANOW, new_cfg)) {fprintf(stderr, tcsetattr error: %s\n, strerror(errno));return -1;}/* 配置OK 退出 */return 0; }/*** --dev/dev/ttymxc2 --brate115200 --dbit8 --parityN --sbit1 --typeread ***/ /**** 打印帮助信息**/ static void show_help(const char *app) {printf(Usage: %s [选项]\n\n必选选项:\n --devDEVICE 指定串口终端设备名称, 譬如--dev/dev/ttymxc2\n --typeTYPE 指定操作类型, 读串口还是写串口, 譬如--typeread(read表示读、write表示写、其它值无效)\n\n可选选项:\n --brateSPEED 指定串口波特率, 譬如--brate115200\n --dbitSIZE 指定串口数据位个数, 譬如--dbit8(可取值为: 5/6/7/8)\n --parityPARITY 指定串口奇偶校验方式, 譬如--parityN(N表示无校验、O表示奇校验、E表示偶校验)\n --sbitSIZE 指定串口停止位个数, 譬如--sbit1(可取值为: 1/2)\n --help 查看本程序使用帮助信息\n\n, app); }/**** 信号处理函数当串口有数据可读时会跳转到该函数执行**/ static void io_handler(int sig, siginfo_t *info, void *context) {unsigned char buf[10] {0};int ret;int n;if(SIGRTMIN ! sig)return;/* 判断串口是否有数据可读 */if (POLL_IN info-si_code) {ret read(fd, buf, 8); //一次最多读8个字节数据printf([ );for (n 0; n ret; n)printf(0x%hhx , buf[n]);printf(]\n);} }/**** 异步I/O初始化函数**/ static void async_io_init(void) {struct sigaction sigatn;int flag;/* 使能异步I/O */flag fcntl(fd, F_GETFL); //使能串口的异步I/O功能flag | O_ASYNC;fcntl(fd, F_SETFL, flag);/* 设置异步I/O的所有者 */fcntl(fd, F_SETOWN, getpid());/* 指定实时信号SIGRTMIN作为异步I/O通知信号 */fcntl(fd, F_SETSIG, SIGRTMIN);/* 为实时信号SIGRTMIN注册信号处理函数 */sigatn.sa_sigaction io_handler; //当串口有数据可读时会跳转到io_handler函数sigatn.sa_flags SA_SIGINFO;sigemptyset(sigatn.sa_mask);sigaction(SIGRTMIN, sigatn, NULL); }int main(int argc, char *argv[]) {uart_cfg_t cfg {0};char *device NULL;int rw_flag -1;unsigned char w_buf[10] {0x11, 0x22, 0x33, 0x44,0x55, 0x66, 0x77, 0x88}; //通过串口发送出去的数据int n;/* 解析出参数 */for (n 1; n argc; n) {if (!strncmp(--dev, argv[n], 6))device argv[n][6];else if (!strncmp(--brate, argv[n], 8))cfg.baudrate atoi(argv[n][8]);else if (!strncmp(--dbit, argv[n], 7))cfg.dbit atoi(argv[n][7]);else if (!strncmp(--parity, argv[n], 9))cfg.parity argv[n][9];else if (!strncmp(--sbit, argv[n], 7))cfg.sbit atoi(argv[n][7]);else if (!strncmp(--type, argv[n], 7)) {if (!strcmp(read, argv[n][7]))rw_flag 0; //读else if (!strcmp(write, argv[n][7]))rw_flag 1; //写}else if (!strcmp(--help, argv[n])) {show_help(argv[0]); //打印帮助信息exit(EXIT_SUCCESS);}}if (NULL device || -1 rw_flag) {fprintf(stderr, Error: the device and read|write type must be set!\n);show_help(argv[0]);exit(EXIT_FAILURE);}/* 串口初始化 */if (uart_init(device))exit(EXIT_FAILURE);/* 串口配置 */if (uart_cfg(cfg)) {tcsetattr(fd, TCSANOW, old_cfg); //恢复到之前的配置close(fd);exit(EXIT_FAILURE);}/* 读|写串口 */switch (rw_flag) {case 0: //读串口数据async_io_init(); //我们使用异步I/O方式读取串口的数据调用该函数去初始化串口的异步I/Ofor ( ; ; )sleep(1); //进入休眠、等待有数据可读有数据可读之后就会跳转到io_handler()函数break;case 1: //向串口写入数据for ( ; ; ) { //循环向串口写入数据write(fd, w_buf, 8); //一次向串口写入8个字节sleep(1); //间隔1秒钟}break;}/* 退出 */tcsetattr(fd, TCSANOW, old_cfg); //恢复到之前的配置close(fd);exit(EXIT_SUCCESS); }代码有点长不过与串口相关的代码并不是很多。 首先来看下main()函数进入到main()函数之后有一个for()循环这是对用户传参进行解析譬如用户可以指定串口终端的设备节点、串口波特率、数据位个数、停止位个数、奇偶校验等具体的使用方法参照show_help()函数。 接着调用uart_init()函数:打开串口终端设备、获取串口终端当前的配置参数将其保存到old_cfg 变量中。 接着调用uart_cfg()函数:将串口配置为原始模式、使能串口接收、设置串口波特率、数据位个数、停止位个数、奇偶校验以及MIN 和TIME 值的设置最后清空缓冲区将配置参数写入串口设备使其生效。 最后根据用户传参中–type 选项所指定类型进行读串口或写串口操作 –typeread 表示读操作–typewrite 表示串口写入操作。 读取串口数据 程序使用了异步I/O 的方式读取数据首先调用async_io_init()函数对异步I/O 进行初始化注册信号处理函数。当检测到有数据可读时会跳转到信号处理函数io_handler()执行在这个函数中读取串口的数据并将其打印出来这里需要注意的是本例程一次最多读取8 个字节数据如果可读数据大于8 个字节多余的数据会在下一次read()调用时被读取出来。 写操作 我们直接调用write()函数每隔一秒钟向串口写入8 个字节数据[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]。 编译示例代码 在开发板上进行测试 将上小节编译得到的可执行文件拷贝到开发板Linux 系统/home/root 目录下如下所示 ALPHA I.MX6U 开发板上一共预留出了两个串口一个USB 串口对应I.MX6U 的UART1、一个RS232/RS485 串口对应I.MX6U 的UART3如图26.3.2 和图26.3.3 所示。 注意板子上的485 和232 接口是共用了I.MX6U 的UART3这两个接口无法同时使用可通过配置底板上的JP1 端子来使能RS232 或RS485 接口使用跳线帽将每一列上面的两个针脚连接起来此时RS232接口被使能、而RS485 接口不能使用如果使用跳线帽将下面两个针脚连接起来如图26.3.2 中所示则此时RS485 接口被使能、RS232 接口不能使用。 本次测试笔者使用RS232 串口注意不能使用USB 串口进行测试它是系统的控制台终端。由于Mini开发板只有一个USB 串口没有RS232 或RS485 接口所以不太好测试当然并不是说没有办法进行测试虽然Mini 板上没有232 或485 接口但是串口用到的I/O 都已经通过扩展口引出了你使用一个USB转TTL 模块也是可以测试的。 将板上的RS232 接口通过USB 转RS232串口线连接到PC 机。 接下来进行测试首先执行如下命令查看测试程序的帮助信息 ./testApp --help可选选项表示是可选的如果没有指定则会使用默认值 读测试 ./testApp --dev/dev/ttymxc2 --typeread执行测试程序时笔者没有指定波特率、数据位个数、停止位个数以及奇偶校验等程序将使用默认的配置波特率115200、数据位个数为8、停止位个数为1、无校验 程序执行之后在Windows 下打开串口调试助手上位机软件譬如正点原子的XCOM 串口调试助手 打开XCOM 之后对其进行配置、并打开串口如下所示 点击发送按钮向开发板RS232 串口发送8 个字节数据[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]此时我们的应用程序便会读取到串口的数据这些数据就是PC 机串口调试助手发送过来的数据如下所示 写测试 测试完读串口后我们再来测试向串口写数据按CtrlC 结束测试程序再次执行测试程序本次测试写串口如下所示 ./testApp --dev/dev/ttymxc2 --typewrite执行测试程序后测试程序会每隔1 秒中将8 个字节数据[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]写入到RS232 串口此时PC 端串口调试助手便会接收到这些数据如下所示
http://www.pierceye.com/news/59509/

相关文章:

  • 自己做报名网站网站能不能用自己的电脑做服务器
  • 免费企业网站模板源码建设运营平台网站的方法
  • 杭州网站建设慕枫网站建设都需要哪些资质
  • 吐鲁番市建设局网站大连建站免费模板
  • 专业网站设计工作室广告案例的网站
  • 安徽省建设工程八大员报名网站网络推广服务公司外包
  • 手机网站模板素材下载html5软件下载手机版
  • 微信兼职网站开发长安网站建设培训机构
  • 网站不能上传图片wordpress批量替换图片路径
  • 秦皇岛网站wordpress html标签
  • wordpress外网打开慢南通seo网站价格
  • 网站建设与推广方案模板wordpress运费
  • 建设个人网站多少钱学校门户网站建设的好处
  • 公司网站如何备案企业网站如何上存
  • 全球建站做网页游戏网站需要什么
  • 淘宝店铺网站策划书quiz在哪个网站做
  • 协会网站建站建筑网络计划图中tp是什么意思
  • 滁州网站建设梦天堂月子会所网站源码
  • 注册网站需要什么手续网站开发设计图psd
  • 做网站用的主机多少合适江苏10大网站建设公司
  • 深圳建立网站的公司中建建设银行网站
  • 做外贸的网站要多少钱网站提速
  • 科技 网站建设网站icp备案信息如何查询
  • 河间米各庄网站建设制作网站建设后台怎么修改
  • 个人网站可以做自媒体吗黄岛开发区做网站网络公司
  • 无障碍浏览网站怎么做大气个人网站源码
  • 网站开发与制作工资wordpress网站阿里云备案号
  • 惠阳网站建设软件app免费下载
  • 外贸商城 网站建设wordpress用户互通
  • 湖南建设部网站微信小程序定制公司