好久没这么写代码了,总算了做完了。特么的现在一想到自己昨天把代码误删了就还是会很懵逼.....
下面说说这个小项目。刚拿到GPS模块的时候开始查资料,发现代码好多啊,好多东西。确实,陌生的东西比较多。但是无非就是
1.串口编程的首要条件:通过编程配置好串口状态。2.获取GPS数据之后怎么用以及都有什么用。
现在的我们都是站在巨人的肩膀上前进啊,在这互联时代,上网随手一查就是一堆攻略,稍加修改就好了。但是主要的编程解析就是看各自的C功底了。
PS:GPS模块一般是基于NMEA-0183协议;我通过对其中的GPRMC最小定位信息来进行解析,实现定位功能。
这里帮助自己熟悉记忆一下:
串口通信,串口的基本参数配置主要包括:波特率,数据位,停止位,奇偶校验位。
实现设置串口属性的结构体是:
struct termios { tcflag_t c_iflag; //输入模式标志,控制终端输入方式 tcflag_t c_oflag; //输出模式标志,控制终端输出方式 tcflag_t c_cflag; //控制模式标志,指定终端硬件控制信息 tcflag_t c_lflag; //本地模式标志,控制终端编辑功能 cc_t c_cc[NCCS]; //控制字符,用于保存终端驱动程序中的特殊字符 };还有个tcgetattr()函数,这是获取当前串口属性的,它若成功返回值是保存到termios结构体中
用时再取,即用即取。
GPS串口编程基本流程:open函数打开相应串口设备 --> 初始化串口的配置,设置基本参数 --> 读取GPS数据 --> 解析所获取到的GPS数据。
直接上代码好了:
Makefile:
CC = /opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc bins = GPS objs = tty.o analysegps.o GpsMain.o srcs = tty.c analysegps.c GpsMain.c $(bins): $(objs) $(CC) -o GPS $(objs) GpsMain.o: $(srcs) gps.h $(CC) -c $(srcs) tty.o: tty.c $(CC) -c tty.c analysegps.o: analysegps.c gps.h $(CC) -c analysegps.c .PHONY: clean clean: rm *.ogps.h头文件
/********************************************************************************* * Copyright: (C) 2015 songyong<handy_skyoutlook.com> * All rights reserved. * * Filename: gps_data.c * Description: This file * * Version: 1.0.0(2015年08月11日) * Author: sky <[email protected]> * ChangeLog: 1, Release initial version on "2015年08月11日 11时52分24秒" * ********************************************************************************/ #ifndef GPS_H_ #define GPS_H_ #define UTC 512 #define CTC 512 struct gps { char utc_time[UTC]; char gps_status; float gps_lng;//经度 float gps_lat; char lng_hemisphere;//经度半球 char lat_hemisphere; float gps_rate; char ctc_time[CTC]; char gps_mode; }; /* Time */ struct Gtime { int year; int month; int day; int hour; int minute; int second; }; extern int open_port(char *console_name); extern int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop); extern int analyse(char *buf,struct gps *gs,struct Gtime *tm); extern void print(struct gps *gp,struct Gtime *ti); #endif
tty.c 配置串口并打开设备:
/********************************************************************************* * Copyright: (C) 2015 songyong<handy_skyoutlook.com> * All rights reserved. * * Filename: tty.c * Description: This file * * Version: 1.0.0(2015年08月10日) * Author: sky <[email protected]> * ChangeLog: 1, Release initial version on "2015年08月10日 14时55分13秒" * ********************************************************************************/ #include <stdio.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #define GPS_LEN 512 /******************************************************************************** * Description: * Input Args: * Output Args: * Return Value: ********************************************************************************/ int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop) { struct tremios newtio,oldtio; if ( tcgetattr( fd, &oldtio) != 0) { perror("Setupserial 1"); return -1; } bzero( &newtio, siezeof( newtio )); newtio.c_cflag |=( CLOCAL | CREAD );//CREAD开启串行数据接收,CLOCAL并打开本地连接模式 newtio.c_cflag &=~CSIZE; //设置字符长度 switch( nBits ) //选择数据位 { case 7: newtio.c_cflag |=CS7; break; case 8: newtio.c_cflag |=CS8; break; } switch( nEvent ) //设置校验位 { case ‘0’: //奇校验 newtio.c_cflag |= PARENB;//开启奇偶校验 newtio.c_iflag |= (INPCK | ISTRIP);//INPCK打开输入奇偶校验;ISTRIP去除字符的第八个比特 newtio.c_cflag |= PARODD;//启用奇校验(默认为偶校验) break; case ‘E’: //偶校验 newtio.c_cflag |= PARENB; //开启奇偶校验 newtio.c_iflag |= ( INPCK | ISTRIP);//打开输入奇偶校验并去除字符第八个比特 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; default: cfsetispeed(&newtio, B9600); cfsetospeed(&newtio, B9600); break; } if( nStop == 1) //设置停止位; { newtio.c_cflag &= ~CSTOPB; //默认为送一位停止位; } else if ( nStop == 2) { newtio.c_cflag |= CSTOPB; //CSTOPB表示送两位停止位; } newtio.c_cc[VTIME] = 0; //非规范模式读取时的超时时间; newtio.c_cc[VMIN] = 0; //非规范模式读取时的最小字符数; tcflush(fd ,TCIFLUSH); //tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不会读取出来 if( (tcsetattr( fd, TCSANOW,&newtio)) != 0) //TCSANOW不等数据传输完毕就立即改变属性 { perror("com set error"); return -1; } return 0; } int open_port(char *console_name) { int fd; if(0 >(fd = open(console_name,O_RDWR|O_NOCTTY|O_NDELAY))) { printf("fd = %d\n",fd); printf("Fail:"); } printf("Open Comport..."); return fd; }analysegps.c 解析获取到的GPS数据:
这个函数放上来后排版很乱,我就不放上来了。主要实现函数是sscanf。思路很简单:strstr定位 --> sscanf重定位
有兴趣的可以到我开源中国的项目里面查看:http://git.oschina.net/sky_handy/mygps
GpsMain.c 主函数:
/********************************************************************************* * Copyright: (C) 2015 songyong<handy_skyoutlook.com> * All rights reserved. * * Filename: GpsMain.c * Description: This file * * Version: 1.0.0(2015年08月11日) * Author: sky <[email protected]> * ChangeLog: 1, Release initial version on "2015年08月11日 14时59分27秒" * ********************************************************************************/ #include <stdio.h> #include <fcntl.h> #include <termios.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> #include <sys/stat.h> #include "gps.h" #define GPS_LEN 512 int main (int argc, char **argv) { int fd=0; int rread=0,i=0; char buffer[GPS_LEN]; char *console_name="/dev/ttyS1"; struct gps ggps; struct Gtime GGtime; printf("will open comport\n"); fd = open_port(console_name); printf("Open Comport is OK!\n"); if (( i=set_opt( fd,4800,8,'N',1)) < 0) { perror("set_opt error"); return -1; } printf("Begin to Receive GPS data...\n"); while(1) { sleep(1); rread = read( fd, buffer, sizeof(buffer)); if( rread > 0) { analyse(buffer,&ggps,&GGtime); print(&ggps,&GGtime); } sleep(1); } close(fd); return 0; }
项目小结:
知道了串口编程的流程,但是具体细节方面还有待进一步提高。这次实现的是从串口获取数据,也就是读操作。tcgetattr()保存当前串口属性,tcsetattr()设置新串口的属性,cfsetispeed()设置输入的波特率,cfsetospeed()设置输出的波特率。要想利用串口通信还要进行写操作的实验练习。Open函数打开的时候有NOCTTY|NDELAY可选择打开时候的阻塞模式,除此还可以在配置串口时通过fcntl(fd, F_SETFL,FNDELAY)来设置非阻塞模式,通过fcntl(fd,F_SETFL,0)来设置阻塞模式。Linux C编程还要继续加强,起初,是打算使用strstr把buf中的字符串取出来。再strchr获得那一段。就跟以前获取网卡ip地址一样。可后来发现如果是要做GPS数据解析的话。可以使用sscanf函数一次性的把buf中的字符串指定成想要的格式重定位到对应的buf中去。这就大大简化了程序。还再次领教了编译环境与运行环境不是一回事儿。编译好了不等于能运行成功。头文件里面不要对结构体定义变量,否则容易出现多次定义的错误。使用结构体时,指针类型的成员要记得赋初值。结构体指针也是如此,要先malloc或者是指向另外一个结构体变量。因为一个没有初始化的野指针很容易出现段错误。前几天看APUE看到signal知道了有core这么一个存储文件的存在。大致知道出错时系统能为我们创立core文件存储程序崩溃前的状态帮助我们能尽快的找到程序里面的错误或是崩溃点。这次出现段错误,网上查资料说core+gdb是处理段错误的神器。开发板上面也可以产生core文件,但是综合编译环境和依赖的动态库等,不便于放到pc上面来查找原因。不过这次起码知道了如果在pc上linux执行可执行文件出现段错误可以利用gdb+core来快速解决。还有就是自己的C语言功底真的需要提高,当时都没怎么想到通过参数指针传值数据时加上const保护数据。多使用typedef方便多了。看了一段项目代码后目前反而觉得C里面难得的是一些带参宏定义了。不多说了,继续多看多写多想吧。