转:http://www.360doc.com/content/12/0222/15/1317564_188649565.shtml

在Linux下对设备的操作方法与对文件的操作方法是一样的,因此对串口的读写就可以使用简单的read()、write()函数来完成,所不同的是只是需要对串口的其他参数进行其他配置,本文实现的是宿主机实现写功能,目标机实现读功能,采用单工方式的串口通信,下面针对我个人的mini2440开发板简单介绍下串口应用开发的步骤。

  笔者的操作系统 Ubuntu 10.10; 使用USB转串口:/dev/ttyUSB0; 开发板mini2440,开发板串口:/dev/ttySAC0  (电脑中串口名称不一定一样)

  1.打开串口,这里使用函数open_port.c实现。

  2.设置串口参数,这里使用函数set_com_config.c实现。

  3.读写串口,宿主机上是com_writer.c;目标机上是com_reader_arm9.c

  以下再分别展开介绍上面步骤的详细子步骤:

  1.打开串口函数open_port.c实现步骤:

(1)利用open()函数打开:

1)针对宿主机的串口打开方法:
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY);

2)针对目标机的串口打开方法:

fd = open("/dev/ttySAC0",O_RDWR | O_NOCTTY | O_NDELAY);

(2)利用fcntl()函数恢复串口的为阻塞状态,用于等待串口数据的读入:

fcntl(fd, F_SETFL, 0)

(3)利用isatty()函数测试打开的文件描述符是否链接到一个终端设备,以进一步确认串口是否正确打开:

isatty(STDIN_FILENO);

(4)打开宿主机的串口程序open_port.c如下所示:

 
[cpp] view plaincopy
  1. /*打开宿主机串口函数*/
  2. #define MAX_COM_NUM 3
  3. int open_port(int com_port)
  4. {
  5. int fd;
  6. //#if (COM_TYPE == GNR_COM)    /*use general Serial port*/
  7. //      char *dev[] = {"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};
  8. //#else /*use USB turn Serial port*/
  9. char *dev[] = {"/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2"};               //使用USB转串口。ps:用预处理没能成功选择,望指教??
  10. //#endif
  11. if((com_port < 0) || (com_port > MAX_COM_NUM))
  12. return -1;
  13. /*打开串口*/
  14. fd = open(dev[com_port - 1], O_RDWR | O_NOCTTY | O_NDELAY);
  15. if(fd < 0)
  16. {
  17. perror("open serial port");
  18. return -1;
  19. }
  20. /*恢复串口为阻塞状态*/
  21. if(fcntl(fd, F_SETFL, 0) < 0)
  22. {
  23. perror("fcntl F_SETFL\n");
  24. return -1;
  25. }
  26. /*测试是否为终端设备*/
  27. if(isatty(STDIN_FILENO) == 0)
  28. {
  29. perror("standard input is not a terminal device");
  30. return -1;
  31. }
  32. return fd;
  33. }

(5)打开目标机(mini2440开发板)串口程序open_port_arm9.c(不同的就只是在char *dev[]那里):

 
[cpp] view plaincopy
  1. /*打开开发板串口程序open_port_arm9.c (不同的就只是在char *dev[]那里)*/
  2. #define MAX_COM_NUM 3
  3. int open_port(int com_port)
  4. {
  5. int fd;
  6. //#if (COM_TYPE == GNR_COM)    /*use general Serial port*/
  7. //      char *dev[] = {"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};
  8. //#else /*use USB turn Serial port*/
  9. //      char *dev[] = {"/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2"};
  10. char *dev[] = {"/dev/ttySAC0","/dev/SAC1","/dev/SAC2"};
  11. //#endif
  12. if((com_port < 0) || (com_port > MAX_COM_NUM))
  13. return -1;
  14. /*打开串口*/
  15. fd = open(dev[com_port - 1], O_RDWR | O_NOCTTY | O_NDELAY);
  16. if(fd < 0)
  17. {
  18. perror("open serial port");
  19. return -1;
  20. }
  21. /*恢复串口为阻塞状态*/
  22. if(fcntl(fd, F_SETFL, 0) < 0)
  23. {
  24. perror("fcntl F_SETFL\n");
  25. return -1;
  26. }
  27. /*测试是否为终端设备*/
  28. if(isatty(STDIN_FILENO) == 0)
  29. {
  30. perror("standard input is not a terminal device");
  31. return -1;
  32. }
  33. return fd;
  34. }

  2.设置串口参数,这里使用函数set_com_config.c实现。

(1)串口的设置主要是设置struct termios结构体的各成员值,如下所示:

 
[cpp] view plaincopy
  1. #include<termios.h>
  2. struct termios
  3. {
  4. unsigned short c_iflag;           /*输入模式标志*/
  5. unsigned short c_oflag;           /*输出模式标志*/
  6. unsigned short c_cflag;           /*控制模式标志*/
  7. unsigned short c_lflag;           /*本地模式标志*/
  8. unsigned char c_line;             /*线路规程*/
  9. unsigned char c_cc[NCC];   /*控制特性*/
  10. speed_t c_ispeed;          /*输入速度*/
  11. speed_t c_ospeed;          /*输出速度*/
  12. };

(2)set_com_config.c程序如下:

 
[cpp] view plaincopy
  1. int set_com_config(int fd, int baud_rate, \
  2. int data_bits, char parity, int stop_bits)
  3. {
  4. struct termios new_cfg,old_cfg;
  5. int speed;
  6. /*步骤一:保存原先串口配置*/
  7. if(tcgetattr(fd, &old_cfg) != 0)
  8. {
  9. perror("tcgetattr");
  10. return -1;
  11. }
  12. new_cfg = old_cfg;
  13. /*步骤二:激活选项*/
  14. cfmakeraw(&new_cfg);   //config to raw mode
  15. /*步骤三:设置字符大小*/
  16. new_cfg.c_cflag &= ~CSIZE;     //set mask
  17. /*步骤四:设置波特率*/
  18. switch(baud_rate)
  19. {
  20. case 2400:
  21. speed = B2400;
  22. break;
  23. case 4800:
  24. speed = B4800;
  25. break;
  26. case 9600:
  27. speed = B9600;
  28. break;
  29. case 19200:
  30. speed = B19200;
  31. break;
  32. case 38400:
  33. speed = B38400;
  34. break;
  35. default:
  36. case 115200:
  37. speed = B115200;
  38. break;
  39. }
  40. cfsetispeed(&new_cfg, speed);
  41. cfsetospeed(&new_cfg, speed);
  42. /*步骤五:设置数据位*/
  43. switch(data_bits)
  44. {
  45. case 7:
  46. new_cfg.c_cflag |= CS7;
  47. break;
  48. default:
  49. case 8:
  50. new_cfg.c_cflag |= CS8;
  51. break;
  52. }
  53. /*步骤六:设置奇偶校验位*/
  54. switch(parity)
  55. {
  56. default:
  57. case 'n':
  58. case 'N':
  59. new_cfg.c_cflag &= ~PARENB;
  60. new_cfg.c_cflag &= ~INPCK;
  61. break;
  62. case 'o':
  63. case 'O':
  64. new_cfg.c_cflag |= (PARODD | PARENB);
  65. new_cfg.c_cflag |= INPCK;
  66. break;
  67. case 'e':
  68. case 'E':
  69. new_cfg.c_cflag |= PARENB;
  70. new_cfg.c_cflag &= ~PARODD;
  71. new_cfg.c_cflag |= INPCK;
  72. break;
  73. case 's':      //as no parity
  74. case 'S':
  75. new_cfg.c_cflag &= ~PARENB;
  76. new_cfg.c_cflag &= ~CSTOPB;
  77. break;
  78. }//end of 'switch parity'
  79. /*步骤七:设置停止位*/
  80. switch (stop_bits)
  81. {
  82. default:
  83. case 1:
  84. new_cfg.c_cflag &= ~CSTOPB;
  85. break;
  86. case 2:
  87. new_cfg.c_cflag |= CSTOPB;
  88. break;
  89. }//end of 'switch stop_bits'
  90. /*步骤八:设置最少字符和等待时间*/
  91. new_cfg.c_cc[VTIME] = 0;
  92. new_cfg.c_cc[VMIN] = 1;
  93. /*步骤九:处理未接收的字符*/
  94. tcflush(fd,TCIFLUSH);
  95. /*步骤十:激活新配置*/
  96. if((tcsetattr(fd, TCSANOW, &new_cfg)) != 0)
  97. {
  98. perror("tcsetattr");
  99. return -1;
  100. }
  101. return 0;
  102. }

  3.读写串口,宿主机上是com_writer.c;目标机上是com_reader_arm9.c

(1)宿主机的写程序com_writer.c程序如下:

 
[cpp] view plaincopy
  1. /*com_writer.c*/
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<sys/types.h>
  6. #include<sys/stat.h>
  7. #include<fcntl.h>
  8. #include<unistd.h>
  9. #include<errno.h>
  10. #include<termios.h>
  11. #include"open_port.c"
  12. #include"set_com_config.c"
  13. #define HOST_COM_PORT 1        /*使用1表示PC机的串口1*/
  14. #define BUFFER_SIZE 30 /*最大缓存大小*/
  15. int main(void)
  16. {
  17. int fd;
  18. char buff[BUFFER_SIZE];
  19. if((fd = open_port(HOST_COM_PORT)) < 0)       /*打开串口*/
  20. {
  21. perror("open_port");
  22. return 1;
  23. }
  24. if(set_com_config(fd, 115200, 8, 'N', 1) < 0)
  25. {
  26. perror("set_com_config");
  27. return 1;
  28. }
  29. do
  30. {
  31. printf("Input some words(enter 'quit' to exit):");
  32. memset(buff, 0, BUFFER_SIZE);
  33. if(fgets(buff, BUFFER_SIZE, stdin) == NULL)
  34. {
  35. perror("fgets");
  36. break;
  37. }
  38. write(fd,buff,strlen(buff));
  39. } while(strncmp(buff,"quit",4));
  40. close(fd);
  41. return 0;
  42. }

(2)目标机读串口程序com_reader_arm9.c:

 
[cpp] view plaincopy
  1. /*com_reader.c*/
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<sys/types.h>
  6. #include<sys/stat.h>
  7. #include<errno.h>
  8. #include<unistd.h>
  9. #include<termios.h>
  10. #include<fcntl.h>
  11. #include"open_port_arm9.c"
  12. #include"set_com_config.c"
  13. #define BUFFER_SIZE 30 /*最大缓存区*/
  14. #define TARGET_COM_PORT 1      /*用1来表示目标机上的串口1*/
  15. int main(void)
  16. {
  17. int fd;
  18. char buff[BUFFER_SIZE];
  19. if((fd = open_port(TARGET_COM_PORT)) < 0)     /*打开串口*/
  20. {
  21. perror("open_port");
  22. return 1;
  23. }
  24. if(set_com_config(fd, 115200, 8, 'N', 1) < 0) /*配置串口*/
  25. {
  26. perror("set_com_config");
  27. return 1;
  28. }
  29. do
  30. {
  31. memset(buff, 0, BUFFER_SIZE);
  32. if(read(fd, buff, BUFFER_SIZE) > 0)
  33. printf("The received words are : %s", buff);
  34. } while(strncmp(buff, "quit", 4));
  35. close(fd);
  36. return 0;
  37. }

  最后,进行将com_writer.c用gcc编译: gcc -g com_writer.c -o com_writer

将com_reader_arm9.c用arm-linux-gcc交叉编译:arm-linux-gcc -g com_reader_arm9.c -o com_reader_arm9     并且将其NFS或者其他方法放在开发板上,然后打开。

将串口线直连,这里我是通过终端用telnet登陆到开发板,然后用nfs挂载文件到开发板,留下串口用来通信(之前用kermit利用串口相连通信收到数据的时候会有异常,且不能正常退出,可能是互相占用的问题),再打开com_reader_arm9程序。接着在宿主机上运行com_writer,再里面输入字符,开发板上面就会收到。如下图所示:

 宿主机打开com_writer

目标机打开com_reader_arm9

  最后,我想总结下我弄这个程序所遇到的一些问题,希望在我以后知识提高的时候可以解决掉或者各位现在提供些参考意见。

问题1:能不能只用一个open_port.c函数就直接选择USB转串口、普通PC串口、开发板串口的选择?之前按照书中的条件编译#if  #else  #endif没能实现,默认选择普通PC串口,所以源程序中我注释掉了。

问题2:以上两图中,对于com_writer写入的第一条语句时候com_reader_arm9并不能读出,只能从第二条开始。之后我试着把new_cfg.c_cc[VMIN] = 1改成"=0”后再进行编译连接,反倒在目标板上用com_reader_arm9不能读出任何来自宿主机的写信息了。

问题3:在第一次成功实现串口单工通信后,用quit退出后,想要再次实现串口通信确不行了,得要开发板重启后才能再次进行串口通信。

如果是自己写的驱动,先检查驱动是否支持波特率的修改。  如果支持: stty -F /dev/ttyS0 speed 115200 cs8 -parenb -cstopb -echo 修改 波特率115200 8 N 1 下位机采用相同的配置,并在linux终端输入echo "hello" > /dev/ttyS0 看看是否能通信。

stty -F /dev/ttyUSB0 speed 115200 cs8 -parenb -cstopb -echo

我自己的代码(输出):

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/select.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>struct timeval timeout;/*设置串口参数*/
int init_tty(int fd)
{struct termios termios_rfid;bzero(&termios_rfid, sizeof(termios_rfid));//清空结构体cfmakeraw(&termios_rfid);//设置终端属性,激活选项cfsetispeed(&termios_rfid, B9600);//输入波特率cfsetospeed(&termios_rfid, B9600);//输出波特率termios_rfid.c_cflag |= CLOCAL | CREAD;//本地连接和接收使能termios_rfid.c_cflag &= ~CSIZE;//清空数据位termios_rfid.c_cflag |= CS8;//数据位为8位termios_rfid.c_cflag &= ~PARENB;//无奇偶校验termios_rfid.c_cflag &= ~CSTOPB;//一位停止位tcflush(fd,TCIFLUSH);termios_rfid.c_cc[VTIME] = 10;//设置等待时间termios_rfid.c_cc[VMIN] = 1;tcflush(fd, TCIFLUSH);//清空输入缓冲区if(tcsetattr(fd, TCSANOW, &termios_rfid))//激活串口设置return 0;return 1;
}int main(void)
{int fd;char ch[]="good job!";while(1){fd = open("/dev/ttyUSB0", O_RDWR);if(fd < 0){printf("open s3c2410_serial1 failed!\n");}   if(init_tty(fd) == -1)//初始化串口{printf("init_tty in failed!\n");}timeout.tv_sec = 1;//设置超时时间为1秒timeout.tv_usec = 0;write(fd, ch, sizeof(ch));tcflush(fd, TCIFLUSH);//清空输入缓冲区tcflush(fd, TCOFLUSH);//清空输入缓冲区close(fd);sleep(3);}return 0;
}

本文中的程序参考了书籍《嵌入式Linux应用程序开发标准教程(第2版)》

原创文章,欢迎转载,转载请注明:blog.csdn.net/jjzhoujun2010

ttyUSB 串口编程 Linux下 USB转串口相关推荐

  1. linux下usb转串口驱动分析

    linux下usb转串口驱动分析 分类: linux driver 2012-06-08 15:11 456人阅读 评论(0) 收藏 举报 linux struct interface returni ...

  2. linux+串口+卡,Linux下PCI转串口卡驱动方法

    PCI转串口卡安装  型号NetMos Nm9835CV 1.插入PCI卡到主机 2.启动 Linux,打开终端 3.输入命令:#setserial  /dev/ttyS0 -a   (COM-1) ...

  3. linux下usb设备节点名不固定,解决Linux下USB设备节点ttyUSB名不固定的问题,生成固定USB转串口设备节点...

    解决Linux下USB设备节点ttyUSB名不固定的问题,生成固定USB转串口设备节点 2018-09-19 http://blog.sina.com.cn/s/blog_8b58097f0102wx ...

  4. linux内核 usb转串口,Linux 使用usb转串口作为调试串口

    芯片串口用来做数据通信使用,需要调试串口时则使用usb转串口debug用. 内核需要几个配置的地方: 1) -> Device Drivers |       -> USB support ...

  5. Linux下PCI转串口卡驱动安装方法

    Linux下PCI转串口卡驱动安装方法 ----------------------------------- 由于公司产品要做行业市场,而产品与行业用户间PC的通讯为RS232串口方式.而行业用户那 ...

  6. 嵌入式linux查看usb设备驱动程序,嵌入式Linux下USB驱动程序的设计

    嵌入式Linux下USB驱动程序的设计 usb概念:  USB(Universal Serial Bus)即通用串行总线,是一种全新的双向同步传输的支持热插拔的数据传输总线,其目的是为了提供一种兼容不 ...

  7. Linux下USB小工具usbmanager 1.0测试版发布

    最近因为需要调试一些USB设备驱动,需要频繁的在Linux下查看USB设备信息,发现Linux下USB设备管理起来非常不方便.lsusb 显示连接在系统上的USB设备信息,显示的信息比较乱,查看起来不 ...

  8. Linux下USB suspend/resume源码分析【转】

    转自:http://blog.csdn.net/aaronychen/article/details/3928479 Linux下USB suspend/resume源码分析 Author:aaron ...

  9. usb linux 内核,Linux下USB内核之学习笔记

    Linux下USB子系统软件结构为 USB 内核(USB驱动,USBD )处于系统的中心,对于它进行研究是能够进行USB驱动开发(包括客户驱动和主机驱动)的第一步.它为客户端驱动和主机控制器驱动提供了 ...

最新文章

  1. Python:读取两种Word文件简述及文件未能引发事件错误
  2. 反思响应json字符串的优化写法
  3. 一个注册为输入法的木马分析
  4. canvas笔记-非零环绕原则及剪纸实例
  5. python 串行线程终止后还会执行下一个吗_多线程笔记
  6. 【干货笔记】CS224n-2019 学习笔记 Lecture 01 Introduction and Word Vectors
  7. php获取跨域json数据,PHP使用ajax跨域获取json数据的两种方法
  8. AXure破解授权码
  9. nvivo三天写论文!可视化操作实战
  10. 电脑怎么提取图片中的文字?
  11. 如果计算机电源突然断电 会导致,电脑突然断电问题
  12. 开放共赢 平安云AI生态合作开启
  13. Ecshop 扯淡问题
  14. ​Excel如何转换成Word文档?教你如何实现转换
  15. html页面中常见的特殊符号,收集的web页面html中常用的特殊符号大全分享
  16. 骨传导耳机原理是什么?主要适合什么人群?
  17. java实现用户每日签到功能
  18. 电科矩阵理论CH4特征值的摄动与估计--证明题
  19. 写个css边框样式。。。
  20. publisher是干什么的

热门文章

  1. 超微ipmi遇到问题,求高手指点
  2. Vue 自定义多选组件
  3. Scratch4 2022.12
  4. 禅道----项目经理创建项目
  5. 华为、软银、星巴克、沃博联、BD、金沙等公司高管变动
  6. 极客风---尝试用代码做一份演示文稿吧
  7. SkeyeWebPlayer.js H5播放器是怎么样炼成的
  8. java integer 相等_Java Integer.equals()判断相等
  9. d3.js实现双击删除节点并删除节点上的文字
  10. 题解【洛谷】P1002 过河卒