Linux 串口编程记录

电玩女神 2022-03-25 02:39 358阅读 0赞

参考:https://blog.csdn.net/wangzhen209/article/details/52620906

执笔时间:2019.1.18

一、安装串口驱动

在 /dev/目录中 tty* ttys* 的文件为串口设备的驱动文件,现在的笔记本和台式机很多已经不在自带串口,我用了USB-串口的数据线,此时驱动为ttyUSB*,我用的Ubuntu 18.04 内核中已经自带USB-串口的驱动文件,不过没有编译。如果直接使用串口线,而没有用到USB转串口设备,就不需要安装驱动。

1、如果使用了USB转串口,一般情况下也不需要安装驱动了,目前linux系统已经包含了该驱动,可以自动识别,亦可通过以下命令查看以便确认是否支持。

lsmod | grep usbserial

如果输出内容中包含 usbserial 说明能正确识别该设备,否则安装该设备的驱动。

参考:https://blog.csdn.net/hhhlizhao/article/details/53790822

输入 dmesg | tail -f

若出现类似

[ 8697.367818] usb 1-2: Detected FT232RL
[ 8697.368307] usb 1-2: FTDI USB Serial Device converter now attached to ttyUSB0

的信息,说明USB转串口的驱动已经加载,可以直接使用。

注意:默认情况下ubuntu已经安装了USB转串口驱动(pl2303)。

2、插上USB转串口,在终端输入命令#dmesg | grep ttyUSB0,如果出现连接成功信息,则说明ubuntu系统已经识别该设备了。

注意:虚拟机环境下的ubuntu默认情况下是不能自动识别的,需要在虚拟机窗口右下角点击”Prolific USB-Serial Controller”,然后选择”Connect (Disconnect from Host)”,这样才能被ubuntu系统识别。

3、在上面minicom的配置中设置Serial Device: /dev/ttyUSB0,重启开发板,这样基本上就可以正常使用minicom来打印串口信息了。

4、问题
如果经过上面的步骤minicom还是不能正常工作,出现如下错误提示:
# sudo minicom
minicom: cannot open /dev/ttyUSB0: 没有该文件或目录

这时可以尝试换一个USB口,然后再次操作以上流程。如果还是提示这个错误,那么可以使用下面的方法来解决。

这 种方法是在硬件里添加串口设备,将window下的设备添加到虚拟机里。也就是说,要在window获得焦点的时候加入usb转串口,然后再到虚拟机下将 这个设备添加进去,这时就可以在ubuntu下查看添加的这个设备的设备文件,一般是/dev/tty0或者 /dev/ttyS0。

这种方法其实是将window的usb转串口作为虚拟机的串口,所以就是tty0或者ttyS0了,而不是真正在ubuntu下加载的。

具体步骤如下:
(1)打开虚拟机环境,然后选择”VM—>Settings(Ctrl+D)”。
(2)点”Add”,进入添加硬件向导,选择”Serial Port”,点”Next”。
(3)选择第一项”Use physical port on the host”,点”Next”。
(4)选择”Physical serial port”方式为”Auto detect”,勾选”Connect at power on”,点”Finish”完成。

二、串口测试

现在已经有很多linux串口调试工具,https://blog.csdn.net/wilylcyu/article/details/51766283

其中cutecom简单实用,界面友好,适合类似我这种Linux入门的同志使用。

安装 cutecom :sudo apt-get install cutecom

打开程序:sudo cutecom

点开右上角settings,设置合适自己的参数,重点是设备选取一定要正确

我的是 /dev/ttyUSB0

点击Open打开串口,在input栏输入内容按Enter即可令串口发送数据

将串口的第二、第三脚短接,再次发送数据,即可在接受栏中看到发送的数据

证明串口设备正常发、收数据。

三、串口编程

参考:https://blog.csdn.net/u012010054/article/details/81092579

  1. #include <termios.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <fcntl.h>
  5. #include <linux/kernel.h>
  6. #include <stdio.h>
  7. #include <pthread.h>
  8. #include <string.h>
  9. #include <sys/select.h>
  10. #include <sys/time.h>
  11. char rev_buf[256];
  12. char databuf[] = {1,2,0x33,0x34,0x35,6,7,11,12,89};
  13. int uart_fd;
  14. #define DEV_NAME "/dev/ttyUSB0"
  15. int Set_Com_Config(int fd, int baud_rate, int data_bits, char parity, int stop_bits)
  16. {
  17. struct termios new_cfg, old_cfg;
  18. int speed;
  19. /*保存并测试现有串口参数设置,在这里如果串口号等出错,会有相关出错信息*/
  20. if(tcgetattr(fd, &old_cfg) != 0) /*该函数得到fd指向的终端配置参数,并将它们保存到old_cfg变量中,成功返回0,否则-1*/
  21. {
  22. perror("tcgetttr");
  23. return -1;
  24. }
  25. /*设置字符大小*/
  26. new_cfg = old_cfg;
  27. cfmakeraw(&new_cfg); /*配置为原始模式*/
  28. new_cfg.c_cflag &= ~CSIZE; /*用位掩码清空数据位的设置*/
  29. /*设置波特率*/
  30. switch(baud_rate)
  31. {
  32. case 2400:
  33. speed = B2400;
  34. break;
  35. case 4800:
  36. speed = B4800;
  37. break;
  38. case 9600:
  39. speed = B9600;
  40. break;
  41. case 19200:
  42. speed = B19200;
  43. break;
  44. case 38400:
  45. speed = B38400;
  46. break;
  47. default:
  48. case 115200:
  49. speed = B115200;
  50. break;
  51. }
  52. cfsetispeed(&new_cfg, speed); //设置输入波特率
  53. cfsetospeed(&new_cfg, speed); //设置输出波特率
  54. /*设置数据长度*/
  55. switch(data_bits)
  56. {
  57. case 5:
  58. new_cfg.c_cflag &= ~CSIZE;//屏蔽其它标志位
  59. new_cfg.c_cflag |= CS5;
  60. break;
  61. case 6:
  62. new_cfg.c_cflag &= ~CSIZE;//屏蔽其它标志位
  63. new_cfg.c_cflag |= CS6;
  64. break;
  65. case 7:
  66. new_cfg.c_cflag &= ~CSIZE;//屏蔽其它标志位
  67. new_cfg.c_cflag |= CS7;
  68. break;
  69. default:
  70. case 8:
  71. new_cfg.c_cflag &= ~CSIZE;//屏蔽其它标志位
  72. new_cfg.c_cflag |= CS8;
  73. break;
  74. }
  75. /*设置奇偶校验位*/
  76. switch(parity)
  77. {
  78. default:
  79. case 'n':
  80. case 'N': //无校验
  81. {
  82. new_cfg.c_cflag &= ~PARENB;
  83. new_cfg.c_iflag &= ~INPCK;
  84. }
  85. break;
  86. case 'o': //奇校验
  87. case 'O':
  88. {
  89. new_cfg.c_cflag |= (PARODD | PARENB);
  90. new_cfg.c_iflag |= INPCK;
  91. }
  92. break;
  93. case 'e': //偶校验
  94. case 'E':
  95. {
  96. new_cfg.c_cflag |= PARENB;
  97. new_cfg.c_cflag &= ~PARODD;
  98. new_cfg.c_iflag |= INPCK;
  99. }
  100. break;
  101. }
  102. /*设置停止位*/
  103. switch(stop_bits)
  104. {
  105. default:
  106. case 1:
  107. new_cfg.c_cflag &= ~CSTOPB;
  108. break;
  109. case 2:
  110. new_cfg.c_cflag |= CSTOPB;
  111. break;
  112. }
  113. /*设置等待时间和最小接收字符*/
  114. new_cfg.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */
  115. new_cfg.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */
  116. /*处理未接收字符*/
  117. tcflush(fd, TCIFLUSH); //溢出数据可以接收,但不读
  118. /* 激活配置 (将修改后的termios数据设置到串口中)
  119. * TCSANOW:所有改变立即生效
  120. */
  121. if((tcsetattr(fd, TCSANOW, &new_cfg))!= 0)
  122. {
  123. perror("tcsetattr");
  124. return -1;
  125. }
  126. return 0;
  127. }
  128. int Uart_Send(int fd, char *data, int data_len)
  129. {
  130. int len = 0;
  131. len = write(fd, data, data_len);
  132. if(len == data_len) {
  133. return len;
  134. } else {
  135. tcflush(fd, TCOFLUSH); //TCOFLUSH刷新写入的数据但不传送
  136. return -1;
  137. }
  138. }
  139. int Uart_Recv(int fd, char *rev_buf, int data_len)
  140. {
  141. int len, fs_sel;
  142. fd_set fs_read;
  143. struct timeval tv_timeout;
  144. FD_ZERO(&fs_read); //清空集合
  145. FD_SET(fd,&fs_read); // 将一个给定的文件描述符加入集合之中
  146. tv_timeout.tv_sec = 5;
  147. tv_timeout.tv_usec = 0;
  148. //使用select实现串口的多路通信
  149. fs_sel = select(fd + 1, &fs_read, NULL, NULL, &tv_timeout); //如果select返回值大于0,说明文件描述符发生了变化
  150. //printf("fs_sel = %d\n",fs_sel); //如果返回0,代表在描述符状态改变前已超过timeout时间,错误返回-1
  151. if(fs_sel)
  152. {
  153. len = read(fd, rev_buf, data_len);
  154. printf("len = %d, fs_sel = %d\n", len, fs_sel);
  155. return len;
  156. }
  157. else
  158. {
  159. return 0;
  160. }
  161. }
  162. int Uart_Init(int fd, int speed, int databits, int parity, int stopbits)
  163. {
  164. //设置串口数据帧格式
  165. if (Set_Com_Config(fd, speed, databits, parity, stopbits) == 0)
  166. {
  167. return 0;
  168. }
  169. else
  170. {
  171. return -1;
  172. }
  173. }
  174. int Uart_Receive_thread(void)
  175. {
  176. int r_count;
  177. while(1)
  178. {
  179. r_count = Uart_Recv(uart_fd, rev_buf, sizeof(rev_buf));
  180. if(r_count)
  181. {
  182. for(int i = 0; i < r_count; i++)
  183. {
  184. printf("rev_buf[%d] = 0x%x\n", i, rev_buf[i]);
  185. }
  186. }
  187. }
  188. }
  189. void Uart_Send_thread(void)
  190. {
  191. int s_count;
  192. while(1)
  193. {
  194. sleep(1);
  195. printf("This is Uart_Send_thread.\n");
  196. s_count = Uart_Send(uart_fd, databuf, strlen(databuf));
  197. if(s_count == -1)
  198. printf("Uart_Send Error!\n");
  199. else
  200. printf("Send %d data successfully!\n", s_count);
  201. }
  202. }
  203. int main(int argc, char *argv[])
  204. {
  205. pthread_t id_Uart_Receive, id_Uart_Send;
  206. int ret1, ret2;
  207. uart_fd = open(DEV_NAME, O_RDWR | O_NOCTTY);
  208. if(uart_fd == -1)
  209. {
  210. printf("Uart Open Failed!\n");
  211. exit(EXIT_FAILURE);
  212. }
  213. if(Uart_Init(uart_fd, 115200, 8, 'E', 1) == -1)
  214. {
  215. printf("Uart_Init Failed!\n");
  216. exit(EXIT_FAILURE);
  217. }
  218. ret1 = pthread_create(&id_Uart_Receive, NULL, (void *) Uart_Receive_thread, NULL);
  219. ret2 = pthread_create(&id_Uart_Send, NULL, (void *) Uart_Send_thread, NULL);
  220. if(ret1!=0){
  221. printf ("Create Uart_Receive_thread error!\n");
  222. close(uart_fd);
  223. exit (1);
  224. }
  225. if(ret2!=0){
  226. printf ("Create Uart_Send_thread error!\n");
  227. close(uart_fd);
  228. exit (1);
  229. }
  230. while(1)
  231. {
  232. printf("This is the main process.\n");
  233. usleep(1000000);
  234. }
  235. pthread_join(id_Uart_Receive, NULL);
  236. pthread_join(id_Uart_Send, NULL);
  237. return 0;
  238. }

存为文件Comtest.c,用gcc编译:

gcc -o ComTest ComTest.c

报错:

ComTest.c:(.text+0x6a8):对‘pthread_create’未定义的引用
ComTest.c:(.text+0x6c8):对‘pthread_create’未定义的引用

参考:https://www.cnblogs.com/leechanx/p/3322611.html

这是因为linux标准库没有pthread.h,需要在运行参数中指定需要动态加载的库

在编译命令末尾加上 -lpthread

gcc -o ComTest ComTest.c -lpthread

编译成功。

运行ComTest程序,此处需要使用超级管理员权限:

sudo ./ComTest

串口不断发送数据:

This is the main process.
This is Uart_Send_thread.
Send 10 data successfully!
This is the main process.
This is Uart_Send_thread.
Send 10 data successfully!

短接串口2、3引脚后:

This is the main process.
This is Uart_Send_thread.
Send 10 data successfully!
len = 10, fs_sel = 1
rev_buf[0] = 0x1
rev_buf[1] = 0x2
rev_buf[2] = 0x33
rev_buf[3] = 0x34
rev_buf[4] = 0x35
rev_buf[5] = 0x6
rev_buf[6] = 0x7
rev_buf[7] = 0xb
rev_buf[8] = 0xc
rev_buf[9] = 0x59

成功实现Linux下串口设备C语言编程调试。

发表评论

表情:
评论列表 (有 0 条评论,358人围观)

还没有评论,来说两句吧...

相关阅读