安信可A7模块GPS-编程获取GPS定位信息

来源:互联网 发布:访客网络设置多少 编辑:程序博客网 时间:2024/05/15 14:38
主机操作系统Centos 6.7 
交叉编译器环境:arm-Linux-gcc-4.5.4 (可通过命令/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc -v查询) 
开发板平台: fl2440 
Linux内核版本: linux-3.0 .54 
模块:安信可A7模块   
  上一篇我们用开发板在开发板通过命令microcom -s 9600 /dev/ttyUSB0监听到GPS发出的信息,紧接着我们通过程序获取GPS定位信息。
1.串口编程
由于A7模块获取到的GPS定位信息是通过串口发送到终端设备,我们在编程实现获取GPS定位信息中自然涉及到串口编程。
关于串口如何设置波特率,停止位。可以参考以下博客:
http://www.cnblogs.com/jason-lu/articles/3173988.html
http://www.cnblogs.com/dartagnan/archive/2013/04/25/3042417.html
串口配置程序如下:
在该程序中,实现了对串口的波特率,数据位,停止位,奇偶校验位的设定
/********************************************************************************* *      Copyright:  (C) 2017 Huang Weiming<710564672@qq.com> *                  All rights reserved. * *       Filename:  set_ttyUSB0.c *    Description:  This file  *                  *        Version:  1.0.0(2017年06月12日) *         Author:  Huang Weiming <710564672@qq.com> *      ChangeLog:  1, Release initial version on "2017年06月12日 12时54分36秒" *                  ********************************************************************************/#include #include #include #include #include #include #include #include #include int set_serial(int fd,int nSpeed,int nBits,char nEvent,int nStop){    struct termios newttys1,oldttys1;/*注释1*/     /*保存原有串口配置*/     if(tcgetattr(fd,&oldttys1)!=0) /*注释2*/     {          perror("Setupserial 1");          return -1;     }     bzero(&newttys1,sizeof(newttys1));     newttys1.c_cflag|=(CLOCAL|CREAD );/*CREAD 开启串行数据接收,CLOCAL并打开本地连接模*/     newttys1.c_cflag &=~CSIZE;/*为了设置字符长度,首先用字符长度屏蔽标志CSIZE将表示字符长度的位清0*/     /*数据位选择*/        switch(nBits)     {         case 7:             newttys1.c_cflag |=CS7;             break;         case 8:             newttys1.c_cflag |=CS8;             break;     }     /*设置奇偶校验位*/     switch( nEvent )     {         case '0':  /*奇校验*/             newttys1.c_cflag |= PARENB;/*开启奇偶校验*/             newttys1.c_iflag |= (INPCK | ISTRIP);/*INPCK打开输入奇偶校验;ISTRIP去除字符的第八个比特  */             newttys1.c_cflag |= PARODD;/*启用奇校验(默认为偶校验)*/             break;         case 'E':/*偶校验*/             newttys1.c_cflag |= PARENB; /*开启奇偶校验  */             newttys1.c_iflag |= ( INPCK | ISTRIP);/*打开输入奇偶校验并去除字符第八个比特*/             newttys1.c_cflag &= ~PARODD;/*启用偶校验*/             break;         case 'N': /*无奇偶校验*/             newttys1.c_cflag &= ~PARENB;             break;     }     /*设置波特率*/    switch( nSpeed )      {        case 2400:            cfsetispeed(&newttys1, B2400);            cfsetospeed(&newttys1, B2400);            break;        case 4800:            cfsetispeed(&newttys1, B4800);            cfsetospeed(&newttys1, B4800);            break;        case 9600:            cfsetispeed(&newttys1, B9600);            cfsetospeed(&newttys1, B9600);            break;        case 115200:            cfsetispeed(&newttys1, B115200);            cfsetospeed(&newttys1, B115200);            break;        default:            cfsetispeed(&newttys1, B9600);            cfsetospeed(&newttys1, B9600);            break;    }     /*设置停止位*/    if( nStop == 1)/*设置停止位;若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB*/    {        newttys1.c_cflag &= ~CSTOPB;/*默认为一位停止位; */    }    else if( nStop == 2)    {        newttys1.c_cflag |= CSTOPB;/*CSTOPB表示送两位停止位*/    }    /*设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时*/    newttys1.c_cc[VTIME] = 0;/*非规范模式读取时的超时时间;*/    newttys1.c_cc[VMIN]  = 0; /*非规范模式读取时的最小字符数*/    tcflush(fd ,TCIFLUSH);/*tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不读取出来 */     /*激活配置使其生效*/    if((tcsetattr( fd, TCSANOW,&newttys1))!=0)    {        perror("com set error");        return -1;    }    return 0;} /*  ----- End of set_serial()  -----  */
(1)termios结构
struct termio{       unsigned short  c_iflag;       /* 输入模式标志 */        unsigned short  c_oflag;       /* 输出模式标志 */        unsigned short  c_cflag;       /* 控制模式标志*/        unsigned short  c_lflag;        /*区域模式标志或本地模式标志或局部模式*/        unsigned char   c_line;         /* line discipline行控制*/        unsigned char   c_cc[NCC];      /* control characters */};
该结构体中c_cflag最为重要,可设置波特率、数据位、校验位、停止位。在设置波特率时需要在数字前加上'B',
如B9600,B15200.使用其需通过“与”“或”操作方式:
c_cflag的参数可设置为:
CBAUD:(不属于 POSIX)波特率掩码 (4+1 位)。
CBAUDEX:(不属于 POSIX) 扩展的波特率掩码 (1 位),包含在 CBAUD 中。
CSIZE:字符长度掩码。取值为 CS5, CS6, CS7, 或 CS8。
CSTOPB:设置两个停止位,而不是一个。
CREAD:打开接受者。
PARENB:允许输出产生奇偶信息以及输入的奇偶校验。
PARODD:输入和输出是奇校验。
CLOCAL:忽略 modem 控制线。
(2)tcgetattr和tcsetattr函数
为了便于通过程序来获得和修改终端参数,Linux还提供了tcgetattr函数和tcsetattr函数。tcgetattr用于获取终端的相关参数,而tcsetattr函数用于设置终端参数。

tcgetattr函数用于获取与终端相关的参数。参数fd为终端的文件描述符,返回的结果保存在termios结构体中。
tcsetattr的参数opt使我们可以指定在什么时候新的终端属性才起作用。opt可以指定为下列常数中的一个:
  • TCSANOW:更改立即发生;
  • TCSADRAIN:发送了所有输出后更改才发生。若更改输出参数则应使用此选择项。
  • TCSAFLUSH:发送了所有输出后更改才发生。更进一步,在更改发生时未读的所有输入数据都被删除(刷清)。
tcsetattr函数的返回值易于产生混淆。如果它执行了任意一种所要求的动作,即使未能执行所有要求的动作,它也返回0(表示成功)。如果该函数返回0,则我们有责任检查该函数是否执行了所有要求的动作。这就意味着,在调用tcsetattr设置所希望的属性后,需调用tcgetattr,然后将实际终端属性与所希望的属性相比较,以检测两者是否有区别。
(3)波特率函数
波特率(baud rate)是一个历史沿用的术语,现在它指的是“位/每秒”。虽然大多数终端设备对输入和输出使用同一波特率,但是只要硬件许可,可以将它们设置为两个不同值。
#include <termios.h>speed_t cfgetispeed(const struct termios *termptr);speed_t cfgetospeed(const struct termios *termptr);                                                        两个函数返回:波特率值int cfsetispeed(struct termios *termptr, speed_t speed);int cfsetospeed(struct termios *termptr, speed_t speed);                                                        两个函数返回:若成功为0,出错为-1
两个cfget函数的返回值,以及两个cfset函数的speed参数都是下列常数之一:B50、B75、B110、B134、B150、B200、B300、B600、B1200、B1800、B2400、B4800、B9600、B19200或B38400。常数B0表示“挂断”。在调用tcsetattr时将输出波特率指定为B0,则调制解调器的控制线就不再起作用。

使用这些函数时,应当理解输入、输出波特率是存放在termios结构中的。在调用任一cfget函数之前,先要用tcgetattr获得设备的termios结构。与此类似,在调用任一cfset函数后,应将波特率设置到termios结构中。为使这种更改影响到设备,应当调用tcsetattr函数。如果所设置的波特率有错,则在调用tcsetattr之前,不会发现这种错误。
(4)行控制函数
#include <termios.h>int tcdrain(int filedes);int tcflow(int filedes, int action);int tcflush(int filedes, int queue);int tcsendbreak(int filedes, int duration);                                                            四个函数返回:若成功则为0,若出错则为-1
其中,参数filedes引用一个终端设备,否则出错返回,errno设置为ENOTTY。

tcdrain函数等待所有输出都被发送。

tcflow用于对输入和输出流控制进行控制。action参数应当是下列四个值之一:

  • TCOOFF:输出被挂起;
  • TCOON:以前被挂起的输出被重新起动;
  • TCIOFF:系统发送一个STOP字符。这将使终端设备暂停发送数据;
  • TCION:系统发送一个START字符。这将使终端恢复发送数据。
tcflush函数刷清(抛弃)输入缓存(终端驱动程序已接收到,但用户程序尚未读)或输出缓存(用户程序已经写,但尚未发送)。queue参数应当是下列三个常数之一:

  • TCIFLUSH:刷清输入队列;
  • TCOFLUSH:刷清输出队列;
  • TCIOFLUSH:刷清输入、输出队列;
tcsendbreak函数在一个指定的时间区间内发送连续的0位流。若duration参数为0,则此种发送延续0.25~ 0.5秒之间。POSIX.1说明若duration非0,则发送时间依赖于实现。
2.GPS解析程序
在gps_analyse.c文件中有两个函数gps_analyse 和 print_gps ,分别实现对gps返回的数据进行分析处理和打印到标准输出。
/********************************************************************************* *      Copyright:  (C) 2017 Huang Weiming<710564672@qq.com> *                  All rights reserved. * *       Filename:  gps_analyse.c *    Description:  This file  *                  *        Version:  1.0.0(2017年06月12日) *         Author:  Huang Weiming <710564672@qq.com> *      ChangeLog:  1, Release initial version on "2017年06月12日 12时57分45秒" *                  ********************************************************************************/#include #include #include #include #include #include #include #include "gps.h"int gps_analyse (char *buff,GPRMC *gps_data){    char *ptr = NULL;    if(gps_data==NULL)    {        return -1;    }    if(strlen(buff)<10)    {        return -1;    }    /*如果buff字符串中包含字符"$GPRMC"则将$GPRMC的地址赋值给ptr*/    if(NULL==(ptr=strstr(buff,"$GPRMC")))    {        return -1;    }    sscanf(ptr,"$GPRMC,%d.000,%c,%f,N,%f,E,%f,%f,%d,,,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->direction),&(gps_data->date),&(gps_data->mode));/*sscanf函数为从字符串输入,上面这句话的意思是将ptr内存单元的值作为输入分别输入到后面的结构体成员*/    return 0;} /* ----- End of gps_analyis()  ----- */int print_gps (GPRMC *gps_data){    printf("                                                           \n");    printf("                                                           \n");    printf("===========================================================\n");    printf("==                   全球GPS定位导航模块                 ==\n");    printf("==              开发人员:黄伟铭                         ==\n");    printf("==              邮箱:710564672@qq.com                   ==\n");    printf("==              平台:fl2440                             ==\n");    printf("===========================================================\n");    printf("                                                           \n");    printf("===========================================================\n");    printf("==   GPS状态位 : %c  [A:有效状态 V:无效状态]              \n",gps_data->pos_state);    printf("==   GPS模式位 : %c  [A:自主定位 D:差分定位]               \n", gps_data->mode);    printf("==   日期      : 20%02d-%02d-%02d                                  \n",gps_data->date%100,(gps_data->date%10000)/100,gps_data->date/10000);    printf("==   当前时间  : %02d:%02d:%02d                                   \n",(gps_data->time/10000+8)%24,(gps_data->time%10000)/100,gps_data->time%100);    printf("==   纬度      : 北纬:%d度%d分%d秒                              \n", ((int)gps_data->latitude) / 100, (int)(gps_data->latitude - ((int)gps_data->latitude / 100 * 100)), (int)(((gps_data->latitude - ((int)gps_data->latitude / 100 * 100)) - ((int)gps_data->latitude - ((int)gps_data->latitude / 100 * 100))) * 60.0));    printf("==   经度      : 东经:%d度%d分%d秒                              \n", ((int)gps_data->longitude) / 100, (int)(gps_data->longitude - ((int)gps_data->longitude / 100 * 100)), (int)(((gps_data->longitude - ((int)gps_data->longitude / 100 * 100)) - ((int)gps_data->longitude - ((int)gps_data->longitude / 100 * 100))) * 60.0));    printf("==   航速      : %.3f                                        \n",gps_data->speed);    printf("==                                                       \n");    printf("============================================================\n");    return 0;} /* ----- End of printf_gps()  ----- */
这里需要注意的是相关数据的转换,GPS接收机直接返回的数据还不太适合我们的阅读习惯,所以我们需要按照一定的规则转换成方便我们识别的数据。
(1)strstr函数:返回字符串中首次出现子串的地址
包含文件:string.h
函数名: strstr
函数原型:
char strstr( char *str, char substr );
 【参数说明】str为要检索的字符串,substr为要检索的子串。
【返回值】返回字符串str中第一次出现子串substr的地址;如果没有检索到子串,则返回NULL。
if(NULL==(ptr=strstr(buff,”$GPRMC”)))return -1;这里返回的是GPRMC所在位置的地址。
(2)sscanf函数:从一个字符串中读进与指定格式相符的数据.
  函数原型:
  int sscanf( string str, string fmt, mixed var1, mixed var2 ... );
  int scanf( const char *format [,argument]... );
  说明:
  sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源
假设GPRMC为如下信息:$GPRMC,062363.00,A,2236.33923,N,11402.35855,E,0.304,306.80,020411,,,A*62sscanf(ptr,”$GPRMC,%d.000,%c,%f,N,%f,E,%f,%f,%d,,,%c*”,&(gps_date->time),&(gps_date->pos_state),&(gps_date->latitude),&(gps_date->longitude),&(gps_date->speed),&(gps_date->direction),&(gps_date->date),&(gps_date->mode));则该函数将ptr中以GPRMC为起始地址的内容格式化输入到结构体的各成员中去,结构体定义在”gps.h”中。
3.主测试程序
(1)主函数gps_main.c
/********************************************************************************* *      Copyright:  (C) 2017 Huang Weiming<710564672@qq.com> *                  All rights reserved. * *       Filename:  gps_main.c *    Description:  This file  *                  *        Version:  1.0.0(2017年06月12日) *         Author:  Huang Weiming <710564672@qq.com> *      ChangeLog:  1, Release initial version on "2017年06月12日 13时12分36秒" *                  ********************************************************************************/#include #include #include #include #include #include #include #include #include #include "gps.h"#define GPS_LEN 512 int set_serial(int fd,int nSpeed, int nBits, char nEvent, int nStop);int gps_analyse(char *buff,GPRMC *gps_date);int print_gps(GPRMC *gps_date);/******************************************************************************** *  Description: *   Input Args: *  Output Args: * Return Value: ********************************************************************************/int main (int argc, char **argv){    int fd = 0;    int nread = 0;    GPRMC gprmc;    char gps_buff[GPS_LEN];    char *dev_name = "/dev/ttyUSB0";    fd = open(dev_name,O_RDWR|O_NOCTTY|O_NDELAY);    if(fd<0)    {        printf("open ttyS1 failed.\n");        return -1;    }    set_serial( fd,9600,8,'N',1);    while(1)    {        sleep(2);        nread = read(fd,gps_buff,sizeof(gps_buff));        if(nread<0)        {            printf("read GPS date error!!\n");            return -2;        }        printf("gps_buff: %s\n", gps_buff);        memset(&gprmc, 0 , sizeof(gprmc));/* 内存空间初始化  */        gps_analyse(gps_buff,&gprmc);        print_gps(&gprmc);    }    close(fd);    return 0;} /* ----- End of main() ----- */
串口使用详解
打开串口 :
fd = open(“/dev/ttyS0”,O_RDWR | O_NOCTTY | O_NDELAY);
参数–
O_NOCTTY:通知linux系统,这个程序不会成为这个端口的控制终端.
O_NDELAY:通知linux系统不关心DCD信号线所处的状态(端口的另一端是否激活或者停止).
然后恢复串口的状态为阻塞状态,用于等待串口数据的读入,用fcntl函数: fcntl(fd,F_SETFL,0); //F_SETFL:设置文件flag为0,即默认,即阻塞状态
读写串口:
串口的读写与普通文件一样,使用read,write函数
read(fd,buff,8);
write(fd,buff,8);
设置串口参数 :
调用uart.c中的set_op()t函数
set_opt( fd,9600,8,’N’,1);
波特率设为9600;数据选择位设置位8位;N表示关闭奇偶校验位;停止位为1,则清除CSTOPB。
(2)头文件gps_h
/******************************************************************************** *      Copyright:  (C) 2017 Huang Weiming<710564672@qq.com> *                  All rights reserved. * *       Filename:  gps.h *    Description:  This head file  * *        Version:  1.0.0(2017年06月12日) *         Author:  Huang Weiming <710564672@qq.com> *      ChangeLog:  1, Release initial version on "2017年06月12日 13时03分37秒" *                  ********************************************************************************/#ifndef __GPS_H__#define __GPS_H__typedef unsigned int UINT;typedef int BYTE;typedef long int WORD;typedef struct __gprmc__{    UINT time;     char pos_state;    float latitude;    float longitude;    float speed;     float direction;    UINT date;    float declination;    char dd;    char mode;}GPRMC;extern int gps_analyse(char *buff,GPRMC *gps_date);extern int print_gps(GPRMC *gps_date);extern int set_serial(int fd,int nSpeed, int nBits, char nEvent, int nStop);#endif
(3)Makefile编写
CC =/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gccobjs    =set_ttyUSB0.o gps_analyse.o gps_main.osrcs    =set_ttyUSB0.c gps_analyse.c gps_main.cgps_bin: $(objs)    $(CC) -o gps_bin $(objs)    @make cleangps_main.o: $(srcs) gps.h    $(CC) -c $(srcs)set_ttyUSB0.o: set_ttyUSB0.c    $(CC) -c set_ttyUSB0.canalyse_gps.o: analyse_gps.c gps.h    $(CC) -c analyse_gps.cclear:    @rm  gps_bin.PHONY: cleanclean:    @rm *.o
4.开发板测试结果


总结:
分析该程序,我们可以从主函数开始看。我们要操作的是串口,所以具体可以有如下几步总结:

1、打开设备"/dev/ttyUSB0"
2、调用set_serial函数设置串口波特率、数据位等。
3、将该设备打印出的GPS信息读入一个buf中
4、定义一个GPRMC结构体gprmc,并用memset进行初始化。
5、打印gps_buff的内容
6、调用gps_analyse函数和print_gps函数打印出获取到的GPS数据


终端I/O termios属性设置 tcsetattr设置  点击打开链接