• 使用香橙派 学习Linux的串口开发


    串口的回顾 & 硬件接线

    关于串口也是之前学习过很多次了,详见:

    认识串口 和 蓝牙模块HC08_hc08蓝牙模块_mjmmm的博客-CSDN博客

    串口的再认识-CSDN博客

    香橙派提供了两路串口第一路就是在刷机时串口连接的引脚(对应驱动ttyS0),第二路就是物理引脚8和10(对应驱动ttyS5

    此处要请出老朋友CH340,这次连接物理引脚8和10的第二路串口: 

    在使用串口连接香橙派的时候,使用的Mobaxterm就可以视为一个串口助手,但Moba更多的是提供一个基于指令交互的平台,所以串口助手的使用还是选择之前用过的AI Thinker:

    在实际应用中,单片机作为比较简单的芯片,可以去负责数据的采集,然后通过串口接到相对高级的香橙派或其他芯片,香橙派读取数据并进行复杂的数据分析或开发,包括人工智能,UI,网络等在单片机中难以实现的功能,同时通过串口给单片机发送各种指令。

    基于wiringPI库的串口开发

    关于串口的代码,wiringPI库同样提供了demo代码:

    cp一份过来:

    (也可以使用SourceInsight来读代码!)

    首先,发现打开默认的demo打开的是串口2的驱动,所以此处要改成串口5的驱动:

    然后编译运行:(显示的就是串口助手中发来的字符的ASCII码形式)

    串口助手中记得勾选HEX显示:(发送的就是16进制的0到256)

     

    基于demo的优化

    可以使用之前学习的线程相关概念来优化这个demo,关于线程的知识之前也学过,详见:

    线程_mjmmm的博客-CSDN博客

    Linux线程 --- 生产者消费者模型(C语言)-CSDN博客

    serial_wiringPI_test.c:
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. void *read_serial(void *arg)
    9. {
    10. char *sendbuf;
    11. sendbuf = (char *)malloc(32*sizeof(char));
    12. char *p = sendbuf;
    13. while(1){
    14. memset(sendbuf,'\0',32*sizeof(char));
    15. fgets(sendbuf,sizeof(sendbuf),stdin);
    16. //scanf("%s",sendbuf);
    17. while(*sendbuf != '\0'){
    18. serialPutchar (*((int *)arg), *sendbuf) ; //串口打印数据的函数 serialPutchar()
    19. sendbuf++;
    20. }
    21. sendbuf = p;
    22. }
    23. pthread_exit(NULL);
    24. }
    25. void *write_serial(void *arg)
    26. {
    27. while(1){
    28. while(serialDataAvail (*((int *)arg))){ //当串口有数据的时候进入while
    29. printf ("%c", serialGetchar (*((int *)arg))) ; //串口接收数据的函数serialGetchar()
    30. fflush (stdout) ;
    31. }
    32. }
    33. pthread_exit(NULL);
    34. }
    35. int main ()
    36. {
    37. int fd ;
    38. int ret;
    39. pthread_t read_thread;
    40. pthread_t write_thread;
    41. if ((fd = serialOpen ("/dev/ttyS5", 115200)) < 0) //打开驱动文件,配置波特率
    42. {
    43. fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
    44. return 1 ;
    45. }
    46. if (wiringPiSetup () == -1)
    47. {
    48. fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
    49. return 1 ;
    50. }
    51. ret = pthread_create(&read_thread,NULL,read_serial,(void *)&fd);
    52. if(ret != 0){
    53. printf("read_serial create error\n");
    54. return 1;
    55. }
    56. ret = pthread_create(&write_thread,NULL,write_serial,(void *)&fd);
    57. if(ret != 0){
    58. printf("write_serial create error\n");
    59. return 1;
    60. }
    61. pthread_join(read_thread,NULL);
    62. pthread_join(write_thread,NULL);
    63. return 0 ;
    64. }
    实现效果:

    发送数据:

    接收数据:

    也可以一边发一边接,因为经过优化,接和发被封装在了不同的线程中! 

    Linux原生串口开发 

    通过sourceinsight查看跳转wiringPI库实现的串口代码,就会发现函数的实现并不困难,所以可以尝试不使用wiringPI库,自己通过Linux封装函数实现串口的通讯。

    首先观察wiringPi库,其对于串口最核心的就是三个函数,serialOpen()serialPutchar()serialGetchar()所以我就自己写一个C文件来实现这三个函数(其实所谓的自己实现就是根据sourceinsight跳转这三个函数,然后删去一些我认为在使用中不必要的代码,与其说是自己实现,更不如说是对这三个函数进行一个删减,精简化),然后创建一个关于它的h文件,最后在串口通讯的函数里添加这个我写的h文件,使用我自己实现的这三个函数来完成串口的通讯。

    步骤为:编写mjm_uart_tool.c -> 编写mjm_uart_tool.h -> 编写serial_mjm_test.c调用mjm_uart_tool.h来实现和刚刚使用wiringPI相同的效果

    mjm_uart_tool.c:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include "wiringSerial.h"
    13. int myserialOpen (const char *device, const int baud)
    14. {
    15. struct termios options ;
    16. speed_t myBaud ;
    17. int status, fd ;
    18. switch (baud){
    19. case 9600: myBaud = B9600 ; break ;
    20. case 115200: myBaud = B115200 ; break ;
    21. }
    22. if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
    23. return -1 ;
    24. fcntl (fd, F_SETFL, O_RDWR) ;
    25. // Get and modify current options:
    26. tcgetattr (fd, &options) ;
    27. cfmakeraw (&options) ;
    28. cfsetispeed (&options, myBaud) ;
    29. cfsetospeed (&options, myBaud) ;
    30. options.c_cflag |= (CLOCAL | CREAD) ;
    31. options.c_cflag &= ~PARENB ;
    32. options.c_cflag &= ~CSTOPB ;
    33. options.c_cflag &= ~CSIZE ;
    34. options.c_cflag |= CS8 ;
    35. options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
    36. options.c_oflag &= ~OPOST ;
    37. options.c_cc [VMIN] = 0 ;
    38. options.c_cc [VTIME] = 100 ; // Ten seconds (100 deciseconds)
    39. tcsetattr (fd, TCSANOW, &options) ;
    40. ioctl (fd, TIOCMGET, &status);
    41. status |= TIOCM_DTR ;
    42. status |= TIOCM_RTS ;
    43. ioctl (fd, TIOCMSET, &status);
    44. usleep (10000) ; // 10mS
    45. return fd ;
    46. }
    47. void serialSendstring (const int fd, const char *s)
    48. {
    49. int ret;
    50. ret = write (fd, s, strlen (s));
    51. if (ret < 0)
    52. printf("Serial Puts Error\n");
    53. }
    54. int serialGetstring (const int fd, char *buffer)
    55. {
    56. int n_read;
    57. n_read = read(fd, buffer,32);
    58. return n_read;
    59. }
    60. int serialDataAvail (const int fd) //用来判断串口有无数据的函数,直接复制黏贴过来的
    61. {
    62. int result ;
    63. if (ioctl (fd, FIONREAD, &result) == -1)
    64. return -1 ;
    65. return result ;
    66. }

    mjm_uart_tool.h:

    1. int myserialOpen (const char *device, const int baud);
    2. void serialSendstring (const int fd, const char *s);
    3. int serialGetstring (const int fd, char *buffer);
    4. int serialDataAvail (const int fd);

    serial_mjm_test.c:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include "mjm_uart_tool.h"
    16. void *read_serial(void *arg)
    17. {
    18. char *sendbuf;
    19. sendbuf = (char *)malloc(32*sizeof(char));
    20. while(1){
    21. memset(sendbuf,'\0',32*sizeof(char));
    22. fgets(sendbuf,sizeof(sendbuf),stdin);
    23. serialSendstring (*((int *)arg), sendbuf) ;
    24. }
    25. pthread_exit(NULL);
    26. }
    27. void *write_serial(void *arg)
    28. {
    29. char readbuf[32] = {'\0'};
    30. while(1){
    31. while(serialDataAvail (*((int *)arg))){
    32. serialGetstring (*((int *)arg),readbuf) ;
    33. printf("-> %s\n",readbuf);
    34. memset(readbuf,'\0',32);
    35. }
    36. }
    37. pthread_exit(NULL);
    38. }
    39. int main ()
    40. {
    41. int fd ;
    42. int ret;
    43. pthread_t read_thread;
    44. pthread_t write_thread;
    45. if ((fd = myserialOpen ("/dev/ttyS5", 115200)) < 0) //打开驱动文件,配置波特率
    46. {
    47. fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
    48. return 1 ;
    49. }
    50. /* if (wiringPiSetup () == -1)
    51. {
    52. fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
    53. return 1 ;
    54. }*/
    55. ret = pthread_create(&read_thread,NULL,read_serial,(void *)&fd);
    56. if(ret != 0){
    57. printf("read_serial create error\n");
    58. return 1;
    59. }
    60. ret = pthread_create(&write_thread,NULL,write_serial,(void *)&fd);
    61. if(ret != 0){
    62. printf("write_serial create error\n");
    63. return 1;
    64. }
    65. pthread_join(read_thread,NULL);
    66. pthread_join(write_thread,NULL);
    67. return 0 ;
    68. }

    注意,由于这里没有使用wiringPI库,所以编译不需要使用之前的build.sh脚本,直接使用gcc就可以,但是要记得链线程的库:

    gcc serial_mjm_test.c mjm_uart_tool.c -lpthread

    实现效果: 

    发送数据:

    接收数据:

    可见,此时,我将serialOpen()serialPutchar()serialGetchar(),替换成了自己的myserialOpen()serialSendstring()serialGetstring();(还原封不动照搬了serialDataAvail函数),然后实现了和刚刚类似的效果,甚至还有所改进,因为我实现的接收函数可以直接介绍一整个字符串,所以可以在之前打印“->”用于区分,但是原来的serialgetchar是一个字符一个字符接收,很难实现这样的效果。

  • 相关阅读:
    Photoshop图片处理
    Apache Doris系列之:数据模型
    慢SQL排查定位
    Java实现从Redis中批量读取数据
    JavaScript之void 0 === undefined
    Spring-SpEL表达式超级详细使用全解
    5.4版本内核ufs设备信息查询方式
    .Net6使用halcon21.05的窗口错误解决方法
    前端(二十六)——常见的HTTP异常状态码以及正反向代理配置
    springboot+websocket+vue聊天室
  • 原文地址:https://blog.csdn.net/mjmmm/article/details/132844178