• Linux 串口应用编程


    1 串口 API

    Linux串口通信:

    Linux 系统中,操作设备的统一接口就是: open/ioctl/read/write
    对于 UART ,又在 ioctl 之上封装了很多函数,主要是用来设置行规程。所以对于 UART ,编程的套路就是:
    open
    设置行规程,比如波特率、数据位、停止位、检验位、 RAW 模式、一有数据就返回;
    read/write
    怎么设置行规程?行规程的参数用结构体 termios 来表示,可以参考 Linux串口—struct termios 结构体:

    这些函数在名称上有一些惯例:
    tc terminal contorl
    cf: control flag

    下面列出一些函数:

    函数名                                 作用

    tcgetattr                 get terminal attributes, 获得终端的属性
    tcsetattr                 set terminal attributes, 修改终端参数
    tcflush                    清空终端未完成的输入/输出请求及数据
    cfsetispeed           sets the input baud rate, 设置输入波特率
    cfsetospeed          sets the output baud rate, 设置输出波特率
    cfsetspeed             同时设置输入、输出波特率
            函数不多,主要是需要设置好 termios 中的参数,这些参数很复杂,可以参考 Linux 串口— struct termios 结构体。

    2 串口收发实验

            本实验通过把串口的发送、接收引脚短接,实现自发自收:使用 write 函数发出字符,使用 read 函数读取字符。

    2.1 上机实验

    2.1.1 设置工具链

    1. vim ~/.bashrc
    2. export ARCH=arm
    3. export CROSS_COMPILE=arm-buildroot-linux-gnueabihfexport PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin

    2.1.2 编译、执行程序

    1. 1. Ubuntu 上
    2. arm-buildroot-linux-gnueabihf-gcc -o serial_send_recv serial_send_recv.c
    3. 2. 板子上
    4. /mnt/serial_send_recv /dev/ttymxc5

    2.2 GPS 模块实验

    2.2.1 GPS 简介

            全球定位系统(Global Positioning System GPS) 是一种以空中卫星为基础的高精度无线电导航的定位系统,它在全球任何地方以及近地空间都能够提供准确的地理位置、车行速度及精确的时间信息。GPS 主要由三大组成部分:空间部分、地面监控部分和用户设备部分。GPS 系统具有高精度、全天候、用广泛等特点。
            太空卫星部分由多颗卫星组成,分成多个轨道,绕行地球一周约 12 小时。每个卫星均持续发射载有卫星轨道数据及时间的无线电波,提供地球上的各种接收机来应用。
            地面管制部分,这是为了追踪及控制太空卫星运行所设置的地面管制站,主要工作为负责修正与维护每个卫星能够正常运转的各项参数数据,以确保每个卫星都能够提供正确的讯息给使用者接收机来接收
            使用者接收机(即用户设备),追踪所有的 GPS 卫星,并实时的计算出接收机所在位置的坐标、移动速度及时间。我们日常接触到的是用户设备部分,这里使用到的 GPS 模块即为用户设备接收机部分。

    2.2.2 GPS 模块硬件

            GPS 模块与外部控制器的通讯接口有多种方式,这里我们使用串口进行通讯,波特率为 9600bps,1bit 停止位,无校验位,无流控,默认每秒输出一次标准格式数据。
            GPS 模块外观如下图所示,通过排线与控制器进行供电和通讯。该模块为集成模块,没有相关原理图。

    2.2.3 GPS 模块数据格式

            GPS 使用多种标准数据格式,目前最通用的 GNSS 格式是 NMEA0183 格式。NMEA0183 是最终定位格式,即将二进制定位格式转为统一标准定位格式,与卫星类型无关。这是一套定义接收机输出的标准信息,有几种不同的格式,每种都是独立相关的 ASCII 格式,逗点隔开数据流,数据流长度从 30-100 字符不等,通常以每秒间隔持续输出。
            NVMEA0183 格式主要针对民用定位导航,与专业 RTCM2.3/3.0 CMR+ 的GNSS 数据格式不同。通过 NMEA0183 格式,可以实现 GNSS 接收机与 PC PDA 之间的数据交换,可以通过 USB COM 口等通用数据接口进行数据传输,其兼容性高,数据传输稳定。这里我们使用串口进行是通讯,通信框图如下图所示。

            我们使用串口接收数据,收到的数据包含:$GPGGA GPS 定位数据)、 $GPGLL(地理定位信息)、$GPGSA (当前卫星信息)、 $GPGSV (可见卫星状态信息)、$GPRMC(推荐最小定位信息)、 $GPVTG (地面速度信息)。
            这里我们只分析$GPGGA (Global Positioning System Fix Data) 即可,它包含了 GPS 定位经纬度、质量因子、 HDOP 、高程、参考站号等字段。其标准格式如下:
    $GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*hh
            $XXGGA 语句各字段的含义和取值范围各字段的含义和取值范围见下表所示,XX 取值有:
    GPGGA :单 GPS
    BDGGA :单北斗
    GLGGA :单 GLONASS
    GNGGA :多星联合定位

    $XXGGA 字段

    <1>             UTC 时间 hhmmss.ss                 000000.00~235959.99
    <2>             纬度,格式:ddmm.mmmm       000.00000~8959.9999
    <3>             南北半球                                     N 北纬 S 南纬
    <4>             经度格式 dddmm.mmmm           00000.0000~17959.9999
    <5>             东西半球                                     E 表示东经 W 表示西经
    <6>             GPS 状态                                    0=未定位
                                                                           1=GPS 单点定位固定解
                                                                           2=差分定位
                                                                           3=PPS 解
                                                                           4=RTK 固定解
                                                                           5=RTK 浮点解
                                                                           6=估计值
                                                                           7=手工输入模式
                                                                           8=模拟模式
    <7>             应用解算位置的卫星数                00~12
    <8>             HDOP 水平图形强度因子            0.500~99.000(大于 6 不可用 )
    <9>             海拔高度                                     -9999.9~99999.9
               地球椭球面相对大地水准面的高度(高程异常) -9999.9~99999.9
    <11>           差分时间              从最近一次接收到差分信号开始的秒数,如果不是差分定位将为空
    <12>           参考站号                                     0000~1023;不使用 DGPS 时为空
    例子: $GPGGA 074529.82 2429.6717 N 11804.6973 E 1 8 1.098 , 42.110,,, M ,, *76

    2.3 编程

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. /* set_opt(fd,115200,8,'N',1) */
    11. int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
    12. {
    13. struct termios newtio,oldtio;
    14. if ( tcgetattr( fd,&oldtio) != 0) {
    15. perror("SetupSerial 1");
    16. return -1;
    17. }
    18. bzero( &newtio, sizeof( newtio ) );
    19. newtio.c_cflag |= CLOCAL | CREAD;
    20. newtio.c_cflag &= ~CSIZE;
    21. newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
    22. newtio.c_oflag &= ~OPOST; /*Output*/
    23. switch( nBits )
    24. {
    25. case 7:
    26. newtio.c_cflag |= CS7;
    27. break;
    28. case 8:
    29. newtio.c_cflag |= CS8;
    30. break;
    31. }
    32. switch( nEvent )
    33. {
    34. case 'O':
    35. newtio.c_cflag |= PARENB;
    36. newtio.c_cflag |= PARODD;
    37. newtio.c_iflag |= (INPCK | ISTRIP);
    38. break;
    39. case 'E':
    40. newtio.c_iflag |= (INPCK | ISTRIP);
    41. newtio.c_cflag |= PARENB;
    42. newtio.c_cflag &= ~PARODD;
    43. break;
    44. case 'N':
    45. newtio.c_cflag &= ~PARENB;
    46. break;
    47. }
    48. switch( nSpeed )
    49. {
    50. case 2400:
    51. cfsetispeed(&newtio, B2400);
    52. cfsetospeed(&newtio, B2400);
    53. break;
    54. case 4800:
    55. cfsetispeed(&newtio, B4800);
    56. cfsetospeed(&newtio, B4800);
    57. break;
    58. case 9600:
    59. cfsetispeed(&newtio, B9600);
    60. cfsetospeed(&newtio, B9600);
    61. break;
    62. case 115200:
    63. cfsetispeed(&newtio, B115200);
    64. cfsetospeed(&newtio, B115200);
    65. break;
    66. default:
    67. cfsetispeed(&newtio, B9600);
    68. cfsetospeed(&newtio, B9600);
    69. break;
    70. }
    71. if( nStop == 1 )
    72. newtio.c_cflag &= ~CSTOPB;
    73. else if ( nStop == 2 )
    74. newtio.c_cflag |= CSTOPB;
    75. newtio.c_cc[VMIN] = 1; /* 读数据时的最小字节数: 没读到这些数据我就不返回! */
    76. newtio.c_cc[VTIME] = 0; /* 等待第1个数据的时间:
    77. * 比如VMIN设为10表示至少读到10个数据才返回,
    78. * 但是没有数据总不能一直等吧? 可以设置VTIME(单位是10秒)
    79. * 假设VTIME=1,表示:
    80. * 10秒内一个数据都没有的话就返回
    81. * 如果10秒内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回
    82. */
    83. tcflush(fd,TCIFLUSH);
    84. if((tcsetattr(fd,TCSANOW,&newtio))!=0)
    85. {
    86. perror("com set error");
    87. return -1;
    88. }
    89. //printf("set done!\n");
    90. return 0;
    91. }
    92. int open_port(char *com)
    93. {
    94. int fd;
    95. //fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);
    96. fd = open(com, O_RDWR|O_NOCTTY);
    97. if (-1 == fd){
    98. return(-1);
    99. }
    100. if(fcntl(fd, F_SETFL, 0)<0) /* 设置串口为阻塞状态*/
    101. {
    102. printf("fcntl failed!\n");
    103. return -1;
    104. }
    105. return fd;
    106. }
    107. int read_gps_raw_data(int fd, char *buf)
    108. {
    109. int i = 0;
    110. int iRet;
    111. char c;
    112. int start = 0;
    113. while (1)
    114. {
    115. iRet = read(fd, &c, 1);
    116. if (iRet == 1)
    117. {
    118. if (c == '$')
    119. start = 1;
    120. if (start)
    121. {
    122. buf[i++] = c;
    123. }
    124. if (c == '\n' || c == '\r')
    125. return 0;
    126. }
    127. else
    128. {
    129. return -1;
    130. }
    131. }
    132. }
    133. /* eg. $GPGGA,082559.00,4005.22599,N,11632.58234,E,1,04,3.08,14.6,M,-5.6,M,,*76" */
    134. int parse_gps_raw_data(char *buf, char *time, char *lat, char *ns, char *lng, char *ew)
    135. {
    136. char tmp[10];
    137. if (buf[0] != '$')
    138. return -1;
    139. else if (strncmp(buf+3, "GGA", 3) != 0)
    140. return -1;
    141. else if (strstr(buf, ",,,,,"))
    142. {
    143. printf("Place the GPS to open area\n");
    144. return -1;
    145. }
    146. else {
    147. //printf("raw data: %s\n", buf);
    148. sscanf(buf, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]", tmp, time, lat, ns, lng, ew);
    149. return 0;
    150. }
    151. }
    152. /*
    153. * ./serial_send_recv
    154. */
    155. int main(int argc, char **argv)
    156. {
    157. int fd;
    158. int iRet;
    159. char c;
    160. char buf[1000];
    161. char time[100];
    162. char Lat[100];
    163. char ns[100];
    164. char Lng[100];
    165. char ew[100];
    166. float fLat, fLng;
    167. /* 1. open */
    168. /* 2. setup
    169. * 115200,8N1
    170. * RAW mode
    171. * return data immediately
    172. */
    173. /* 3. write and read */
    174. if (argc != 2)
    175. {
    176. printf("Usage: \n");
    177. printf("%s \n", argv[0]);
    178. return -1;
    179. }
    180. fd = open_port(argv[1]);
    181. if (fd < 0)
    182. {
    183. printf("open %s err!\n", argv[1]);
    184. return -1;
    185. }
    186. iRet = set_opt(fd, 9600, 8, 'N', 1);
    187. if (iRet)
    188. {
    189. printf("set port err!\n");
    190. return -1;
    191. }
    192. while (1)
    193. {
    194. /* eg. $GPGGA,082559.00,4005.22599,N,11632.58234,E,1,04,3.08,14.6,M,-5.6,M,,*76"*/
    195. /* read line */
    196. iRet = read_gps_raw_data(fd, buf);
    197. /* parse line */
    198. if (iRet == 0)
    199. {
    200. iRet = parse_gps_raw_data(buf, time, Lat, ns, Lng, ew);
    201. }
    202. /* printf */
    203. if (iRet == 0)
    204. {
    205. printf("Time : %s\n", time);
    206. printf("ns : %s\n", ns);
    207. printf("ew : %s\n", ew);
    208. printf("Lat : %s\n", Lat);
    209. printf("Lng : %s\n", Lng);
    210. /* 纬度格式: ddmm.mmmm */
    211. sscanf(Lat+2, "%f", &fLat);
    212. fLat = fLat / 60;
    213. fLat += (Lat[0] - '0')*10 + (Lat[1] - '0');
    214. /* 经度格式: dddmm.mmmm */
    215. sscanf(Lng+3, "%f", &fLng);
    216. fLng = fLng / 60;
    217. fLng += (Lng[0] - '0')*100 + (Lng[1] - '0')*10 + (Lng[2] - '0');
    218. printf("Lng,Lat: %.06f,%.06f\n", fLng, fLat);
    219. }
    220. }
    221. return 0;
    222. }

    2.4 接线

  • 相关阅读:
    Tomcat突然停止运行/Server Tomcat v8.5 Server at localhost fail
    ubuntu部署k8s
    C#9.0记录类型
    亚商投资顾问 早餐FM/1028华为海外推广5.5G
    一台机器下,多个Java版本的粗放与精细管理
    基于 Hexo 从零开始搭建个人博客(五)
    高一女孩成 AI 赛事奖项收割机,05 后新星如何踏上 CV 之路?
    数字档案一体化解决方案
    进程与线程(四)
    Leetcode算法入门与数组丨5. 数组二分查找
  • 原文地址:https://blog.csdn.net/weixin_48856218/article/details/134520002