2018-07-19 21:26:42 weixin_42218802 阅读数 723
  • 嵌入式Linux文件与串口编程

    本课程介绍Linux环境下shell编程,普通文件与设备文件的编程方法,串口介绍与应用编程。 学习条件: 1.C语言编程基础 2.嵌入式Linux开发基础

    7346 人正在学习 去看看 沈寒

    之前编写了一个读取GPS内容的代码,现将之贴出来。此代码中涉及到串口初始化、串口操作的打开、关闭、读写,还涉及到GPS相关协议的解析,如接收到的GPS数据如何处理,如何发送数据到GPS模块等。协议采用的是SIM68VB   NMEA协议。

  1、 串口初始化代码如下:

   此函数为设置串口 属性如波特率、数据位、校验位、停止位,此函数将串口设置为非阻塞,在读串口时可采用I/O多路复用的机制。

例:configs ="115200,8,1,N" 115200代表波特率,8数据位,1停止位,N无校验

int SetNolockComCfg(int fd, char *configs)
{
    int uBaudRate, databits, stopbits;
    char parity;
    char temp[32];
    char *p1 = NULL, *p2 = NULL;
    int n = 0;
    unsigned int flag = 0;
    unsigned short Speed;
    int stats;
    struct termios options;

    /* clear struct for new port settings */
    bzero(&options, sizeof (struct termios));

    //波特率
    p1 = configs;
    p2 = strchr(p1, ',');
    n = p2 - p1;
    if (n) {
        strncpy(temp, p1, n);
        temp[n] = '\0';
        uBaudRate = atoi(temp);
    }
    else
        uBaudRate = COM_BAUDDEF;

    //数据位
    p1 = strchr(p2 + 1, ',');
    n = p1 - (p2 + 1);
    if (n) {
        strncpy(temp, p2 + 1, n);
        temp[n] = '\0';
        databits = atoi(temp);
    }
    else
        databits = COM_DABITSDEF;

    // 停止位
    p2 = strchr(p1 + 1, ',');
    n = p2 - (p1 + 1);
    if (n) {
        strncpy(temp, p1 + 1, n);
        temp[n] = '\0';
        stopbits = atoi(temp);
    }
    else
        stopbits = COM_STBITSDEF;

    //校验位
    if (p2 + 1 != NULL) {
        strcpy(temp, p2 + 1);
        parity = temp[0];
    }
    else {
        parity = COM_PARITYDEF;
    }

    //设置波特率
    switch (uBaudRate) {
    case 110:
        Speed = B110;
        break;

    case 300:
        Speed = B300;
        break;

    case 600:
        Speed = B600;
        break;

    case 1200:
        Speed = B1200;
        break;

    case 2400:
        Speed = B2400;
        break;

    case 4800:
        Speed = B4800;
        break;

    case 9600:
        Speed = B9600;
        break;

    case 19200:
        Speed = B19200;
        break;

    case 38400:
        Speed = B38400;
        break;

    case 57600:
        Speed = B57600;
        break;

    case 115200:
        Speed = B115200;
        break;

    case 230400:
        Speed = B230400;
        break;

    case 460800:
        Speed = B460800;
        break;

    case 921600:
        Speed = B921600;
        break;

    default:
        fprintf(stderr, "Unsupported data size\n");
        return -1;
    }

    flag |= Speed;

    //设置数据位
    switch (databits) {
    case 5:
        flag |= CS5;
        break;

    case 6:
        flag |= CS6;
        break;

    case 7:
        flag |= CS7;
        break;

    case 8:
        flag |= CS8;
        break;

    default:
        fprintf(stderr, "Unsupported data size\n");
        return -1;

    }

    //设置停止位
    switch (stopbits) {
    case 1:
        break;

    case 2:
        flag |= CSTOPB;
        break;

    default:
        fprintf(stderr, "Unsupported stop bits\n");
        return -1;

    }

    //设置奇偶校验位
    switch (parity) {
    case 'n':
    case 'N':
        options.c_iflag = IGNPAR;
        break;

    case 'e':
    case 'E':
        flag |= PARENB;
        options.c_iflag = 0;
        break;

    case 'o':
    case 'O':
        flag |= PARENB | PARODD;
        options.c_iflag = 0;
        break;

    case 's':
    case 'S':
        flag |= PARENB | CMSPAR;
        options.c_iflag = 0;
        break;

    case 'm':
    case 'M':
        flag |= PARENB | PARODD | CMSPAR;
        options.c_iflag = 0;
        break;

    default:
        fprintf(stderr, "Unsupported parity\n");
        return -1;

    }

    options.c_cflag = flag | CREAD | CLOCAL;
    options.c_oflag = 0;
    options.c_lflag = 0;
    options.c_cc[VINTR] = 0;
    options.c_cc[VQUIT] = 0;
    options.c_cc[VERASE] = 0;
    options.c_cc[VKILL] = 0;
    options.c_cc[VEOF] = 4;
    // 100MS 串口等待接收(2S)
    options.c_cc[VTIME] = 0; 
    options.c_cc[VMIN] = 0;
    options.c_cc[VSWTC] = 0;
    options.c_cc[VSTART] = 0;
    options.c_cc[VSTOP] = 0;
    options.c_cc[VSUSP] = 0;
    options.c_cc[VEOL] = 0;
    options.c_cc[VREPRINT] = 0;
    options.c_cc[VDISCARD] = 0;
    options.c_cc[VWERASE] = 0;
    options.c_cc[VLNEXT] = 0;
    options.c_cc[VEOL2] = 0;

    //刷新输入输出缓冲
    tcflush(fd, TCIOFLUSH);

    //把设置真正写到串口中去
    stats = tcsetattr(fd, TCSANOW, &options);

    if (stats != 0) {
        perror("tcsetattr fd1");
        return -1;

    }

    return 0;
}
2、GPS相关的数据定义在一个结构体中,如下


struct    stGPS {
    //位置信息有效性
    char            loca_state;   
    //东西经
    char            we;            
    //经度位置
    unsigned int    Longitude;
    //南北纬
    char            ns;    
    //纬度位置
    unsigned int    Latitude;        
    //海拔高度
    unsigned int    Altitude;    
    //距1970-1-1 0:0:0 的秒数
    unsigned int    Time;            
};

3、获取GPS信息的函数如下

相关宏:
#define        GPGGA        "$GPGGA"    //GPS定位系统输出字符串的头部
#define        GPRMC        "$GPRMC"
#define        BDGGA        "$BDGGA"    //北斗定位系统输出字符串的头部
#define        BDRMC        "$BDRMC"
 

int GetBDGPSData(int    bdgps_fd, struct  stGPS    *bdgps, int mode)
{
    int     ret;
    char    read_buf[512] = {0};
    char     *pGGA = NULL, *pRMC = NULL;
    char     gpstime[64] = {0};
    char    altitude[64] = {0};
    char    ns;       //纬度标识
    char    we;        //经度标识
    char    state;    //状态标识
    char    ns_data[16] = {0};  //纬度
    char    we_data[16] = {0};    //经度
    char     date[16] = {0};
    struct    tm     utc_time = {0};    
    int     year;
    int     daytime;
    int     err_num;
    int        flag = 0;
    char     str1[10] = {0};
    char     str2[10] = {0};
    
    switch (mode){
        case    BD_MODE:
            memcpy(str1, BDGGA, sizeof(BDGGA));
            memcpy(str2, BDRMC, sizeof(BDRMC));
            break;
        case    GPS_MODE:
        default:
            memcpy(str1, GPGGA, sizeof(GPGGA));
            memcpy(str2, GPRMC, sizeof(GPRMC));
            break;
    }
    
    for(err_num=0; err_num<MAX_ERROR; err_num++){
        delay(0, 100);
        ret = read(bdgps_fd, read_buf, sizeof(read_buf));
        pGGA = strstr(read_buf,str1);  //在字符串中查找字符串
        pRMC = strstr(read_buf,str2);
        if((ret>150) && pGGA && pRMC){
            flag = 1;
            break;
        }
    }
    if(!flag)
        return -1;
    
    printf("read num = %d \nread_buf: %s \n", ret, read_buf);

   //根据需求我们需要采集到两次信息

    sscanf(pGGA, "%[^\r]", pGGA);     //解析字符串
    sscanf(pRMC, "%[^\r]", pRMC);
    

    //根据协议解析字符串
    sscanf(pGGA, "%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%[^,]",altitude); 
    //                      时间  状态   纬度  ns    经度  we    
    sscanf(pRMC, "%*[^,],%[^,],%c, %[^,],%c,%[^,],%c,%*[^,],%*[^,],%[^,]",
                    gpstime, &state , ns_data, &ns, we_data, &we,  date);
    year = atoi(date);
    daytime = atoi(gpstime);
    
    utc_time.tm_sec   = daytime%100;
    utc_time.tm_min   = daytime%10000/100 ;
    utc_time.tm_hour  = daytime/10000;
    utc_time.tm_mday  = year/10000;
    utc_time.tm_mon   = year%10000/100;
    utc_time.tm_year  = year%100 + 2000;
    
    bdgps->Longitude  = atof(we_data) * 10000;
    bdgps->Latitude   = atof(ns_data) * 10000;
    bdgps->loca_state = state;
    bdgps->Altitude      = atof(altitude) * 10000;
    bdgps->ns          = ns;
    bdgps->we          = we;
    bdgps->Time       = mktime(&utc_time);
    
    return 0;
}

 

4、 main函数,采用select机制读取GPS串口的数据
int main()
{
    int        ret = 0;
    fd_set    readfs;
    int     bdgps_fd, maxfd;
    int     err_num = 0;
    
    struct    stGPS    bdgps = {0};
    struct  timeval timeout={1, 0}; 
    
    bdgps_fd = open("/dev/ttyS1", O_RDWR);
    if(bdgps_fd < 0){
        perror("open ttyS1");
        return -1;
    }
    
    //设置为非阻塞
    ret = SetNolockComCfg(bdgps_fd, CONFIG);
    if(ret)
        return -1;
    
    while(1){
        FD_ZERO(&readfs);
        FD_SET(bdgps_fd, &readfs);
        maxfd = bdgps_fd + 1;
        ret = select(maxfd, &readfs, NULL, NULL, &timeout);
        if(ret < 0){
            perror("select");   //允许出错三次
            err_num++;
            if(err_num < MAX_ERROR)
                continue;
            else
                break;
        }
        err_num = 0;
        
        if(FD_ISSET(bdgps_fd, &readfs)){            
            ret = GetBDGPSData(bdgps_fd, &bdgps, GPS_MODE);
            if(ret)
                continue;
            
            printf("bdgps->state:    %c \n",bdgps.loca_state);
            printf("bdgps->we:       %c \n",bdgps.we);
            printf("bdgps->Longitude:%d \n",bdgps.Longitude);
            printf("bdgps->ns:       %c \n",bdgps.ns);
            printf("bdgps->Latitude: %d \n",bdgps.Latitude);
            printf("bdgps->Altitude: %d \n",bdgps.Altitude);
            printf("bdgps->Time:      %d \n",bdgps.Time);    
        }
        usleep(50*1000);
    }
    close(bdgps_fd);
    
    return 0;
    
}

 

至此,一个完整的GPS读取数据的程序已经完成,已经能够读取到相关的位置信息。

 

2011-09-02 13:43:19 jiangjiankang 阅读数 6135
  • 嵌入式Linux文件与串口编程

    本课程介绍Linux环境下shell编程,普通文件与设备文件的编程方法,串口介绍与应用编程。 学习条件: 1.C语言编程基础 2.嵌入式Linux开发基础

    7346 人正在学习 去看看 沈寒
 

考察C程序员是否合格的一个重要标准就是看他操作字符串的能力,一个合格的C程序员应该可以熟练的对字符串进行拆分、组合、格式转换以及搜索定位,从一堆数据中提取出有效信息。

比如说我们要做一个GPS导航的项目,需要读取GPS模块以ASCII码的形式发送过来的数据,然后对这些数据进行处理,提取我们需要的信息。这就涉及到很多操作字符串的问题。下面就以此为例,利用strstr函数和sscanf函数解析GPS数据。

GPS输出的数据格式如下:
$GPGGA,121252.000,3937.3032,N,11611.6046,E,1,05,2.0,45.9,M,-5.7,M,,0000*77
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
$GPVTG,359.95,T,,M,15.15,N,28.0,K,A*04
$GPGGA,121253.000,3937.3090,N,11611.6057,E,1,06,1.2,44.6,M,-5.7,M,,0000*72
$GPGSA,A,3,14,15,05,22,18,26,,,,,,,2.1,1.2,1.7*3D
$GPGSV,3,1,10,18,84,067,23,09,67,067,27,22,49,312,28,15,47,231,30*70
$GPGSV,3,2,10,21,32,199,23,14,25,272,24,05,21,140,32,26,14,070,20*7E
$GPGSV,3,3,10,29,07,074,,30,07,163,28*7D

可以看到,GPS模块发送过来的原始数据有很多,但是通常我们只需要其中的一部分信息就够用了,比如对于导航的功能,我们只需要以$GPRMC开头,以换行符结束的一行信息就够了。即:
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54

因此我们需要做的就是从读取的数据中截取以$GPRMC开头的一行信息,然后从中解析出经纬度、日期时间等有效信息即可。

假设从串口读取的数据存放在一个字符串指针char *raw_buf指向的内存单元里,首先我们通过ANSI C提供的strstr()函数找到以$GPRMC开头以换行符’\n’结束的字符串:
/* find "$GPRMC" from raw_buf */
if ((wellhandled_string = strstr(raw_buf, “$GPRMC”)) != NULL)
{
        for (i=0; i
        {
                if (wellhandled_string[i] == '\n')
                {
                        wellhandled_string[i] = '\0'; //replace ‘\n’ with null
                }
        }
}
strstr()函数的原型是这样声明的:
char *strstr(const char *haystack, const char *needle);

strstr()函数可以在字符串haystack中搜索字符串needle第一次出现的位置,并且返回指向字符串needle首地址的指针,如果没有搜索到则返回NULL。因此上面的代码为我们在读取的原始数据raw_buf里搜索$GPRMC第一次出现的位置,并将返回的指针赋给wellhandled_string,这样如果搜索成功,则wellhandled_string就会指向以$GPRMC开始的字符串,接下来通过一个for循环找到换行符’\n’,将其替换为’\0’,即字符串结束符。这样就得到了一个指向有效数据的字符串指针wellhandled_string。

然后要做的工作就是从wellhandled_string中提取出经纬度、日期时间等信息。这个工作就可以交给强大的sscanf函数来实现。sscanf函数的原型如下:
int sscanf(const char *str, const char *format, ...);

我们都比较熟悉scanf这个函数,scanf可以从标准输入流读取与指定格式相符的数据。sscanf则是从const char *str中读取。它的强大之处在于可以方便地从字符串中取出整数、浮点数和字符串等各种类型的数据,而且它还具有类似于正则表达式的匹配功能,sscanf默认是以空格分隔字符串的,如果不是以空格来分割的话,就可以使用%[ ]来指定分割的条件。如%[a-z]表示读取a到z的所有字符,%[^a-z]表示过滤a-z之间的所有字符,即只要遇到a到z之间的任意字符,转换立刻停止。比如:
sscanf(“abcdefABCDEF”, “%[^A-Z]”, str);
printf(“%s\n”, str);
result is: abcdef
%[^A-Z]这样的匹配格式为我们取遇到大写字母为止的字符串。利用这种匹配方式,我们就可以灵活的操作字符串,得到我们想要的结果。

现在我们需要从下面的字符串中提取有效信息:
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54

GPRMC每个字段的含义如下:
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
<1> UTC时间,hhmmss(时分秒)格式
<2> 定位状态,A=有效定位,V=无效定位
<3> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输)
<4> 纬度半球N(北半球)或S(南半球)
<5> 经度dddmm.mmmm(度分)格式(前面的0也将被传输)
<6> 经度半球E(东经)或W(西经)
<7> 地面速率(000.0~999.9节,前面的0也将被传输)
<8> 地面航向(000.0~359.9度,以真北为参考基准,前面的0也将被传输)
<9> UTC日期,ddmmyy(日月年)格式
<10> 磁偏角(000.0~180.0度,前面的0也将被传输)
<11> 磁偏角方向,E(东)或W(西)
<12> 模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)

我们提取1~9九条信息。用一个结构体存放这些信息:
typedef struct gps_info
{
        char utc_time[BUF_SIZE];
        char status;
        float latitude_value;
        char latitude;
        float longtitude_value;
        char longtitude;
        float speed;
        float azimuth_angle;
        char utc_data[BUF_SIZE];
}GPS_INFO;

因为每一个字段之间都是以逗号间隔开的,所以我们可以利用%[^,]来分割字符串,这样用sscanf函数就可以实现对有效信息的提取:
sscanf(wellhandled_string,"$GPRMC,%[^,],%c,%f,%c,%f,%c,%f,%f,%[^,]",
            rmc_info->utc_time,\
            &(rmc_info->status),&(rmc_info->latitude_value),&(rmc_info->latitude),\
            &(rmc_info->longtitude_value),&(rmc_info->longtitude),&(rmc_info->speed),\
            &(rmc_info->azimuth_angle),\
            rmc_info->utc_data );
这个函数执行后,打印出的保存在struct gps_info结构体里的信息如下所示:
utc_time: 024813.640
status: A
latitude: N latitude value: 3158.460693
longtitude: E longtitude value: 11848.374023
speed: 10.050000
azimuth_angle: 324.269989
utc_data: 150706

可见,利用好sscanf函数,可以让我们可以很高效的处理字符串。

2013-06-17 21:08:57 hjc0321 阅读数 1887
  • 嵌入式Linux文件与串口编程

    本课程介绍Linux环境下shell编程,普通文件与设备文件的编程方法,串口介绍与应用编程。 学习条件: 1.C语言编程基础 2.嵌入式Linux开发基础

    7346 人正在学习 去看看 沈寒
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<termios.h>
#include<string.h>

int set_opt(int fd, int bSpeed, int dBits, char parity, int stopBit)
{
	struct termios newtio,oldtio;
	if  ( tcgetattr( fd,&oldtio)  !=  0)
        { 
		perror("tcgetattr");
		exit(1);
      	}
       
        bzero( &newtio, sizeof( newtio ) );
	newtio.c_cflag  |=  CLOCAL | CREAD;//将本地模式(CLOCAL)和串行数据接收(CREAD)设置为有效
        /*这里有两个选项应当一直打开,一个是CLOCAL,另一个是CREAD。这两个选项可以保证你的程序不会
        变成端口的所有者,而端口所有者必须去处理发散性作业控制和挂断信号,同时还保证了串行接口驱动会读取过来的数据字节。*/

        newtio.c_cflag &= ~CSIZE;//屏蔽数据位

	switch( dBits )
	{
	case 7:
		newtio.c_cflag |= CS7;
		break;
	case 8:
		newtio.c_cflag |= CS8;//8 data bits
		break;
	}
        
        //设置奇偶位
	switch(parity)
	{
	case 'O':
		newtio.c_cflag |= PARENB;//使能奇偶校验
    		newtio.c_cflag |= PARODD;//奇
		newtio.c_iflag |= (INPCK | ISTRIP);//将奇偶校验设置为有效同时从接收字串中脱去奇偶校验位
		break;
	case 'E': 
		newtio.c_iflag |= (INPCK | ISTRIP);
		newtio.c_cflag |= PARENB;
		newtio.c_cflag &= ~PARODD;
		break;
	case 'N':  
		newtio.c_cflag &= ~PARENB;
		break;
	}
        
       //设置波特率
	switch( bSpeed )
	{
	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;
	case 460800:
		cfsetispeed(&newtio, B460800);
		cfsetospeed(&newtio, B460800);
		break;
	default:
		cfsetispeed(&newtio, B9600);
		cfsetospeed(&newtio, B9600);
		break;
	}

       //设置停止位
       if( stopBit == 1 )
	    newtio.c_cflag &=  ~CSTOPB;
	else if ( nStop == 2 )
	    newtio.c_cflag |=  CSTOPB;

        newtio.c_cc[VTIME]  = 0;//设置等待数据时间,单位:0.1秒
	newtio.c_cc[VMIN] = 100;//Minimum number of characters to read
      
        tcflush(fd,TCIFLUSH);//刷新缓冲区,让输入输出数据有效:Flush input and output buffers and make the change
        if((tcsetattr(fd,TCSANOW,&newtio))!=0)//TCSANOW标志所有改变必须立刻生效而不用等到数据传输结束
	{
		perror("com set error");
		return -1;
	}

	return 0;
}

int main(void)
{
	int atFd, nmeaFd,nset1, nwrite, nread;
   	char at[20], nmea[1024];
         
        atFd = open("/dev/ttyUSB2", O_RDWR);//打开at串口
	if (atFd == -1)
        {
             perror("atFd:");
             exit(1);
         }

         set_opt(atFd, 4800, 8, 'N', 1);//设置at串口属性         
         memset(at, 0, 20);
         memcpy(at, "AT+CGPS=1\r", sizeof("AT+CGPS=1\r"));
         nwrite = write(atFd, at, strlen(at));
         if(-1 == nwrite)
         {
              perror("at com write");
              exit(1);
         }  

         nmeaFd = open("/dev/ttyUSB1", O_RDWR);//打开nmea串口 
         if (nmeaFd == -1)
         {
             perror("nmeaFd:");
             exit(1);
         }

set_opt(nmeaFd, 4800, 8, 'N', 1);//设置nmea串口属性 while(1) { memset(nmea, 0, 1024); nread = read(nmeaFd, nmea, 1024);//读串口 if (nread > 0) { printf("\n\tGPS dataLen=%d, data:\n",nread); nmea[nread] = '\0'; printf( "%s\n", nmea); //输出所读取的数据 }  sleep(2); } close(atFd);
 close(nmeaFd);  return 0;}




                                
2012-02-28 22:28:09 lanyang123456 阅读数 6778
  • 嵌入式Linux文件与串口编程

    本课程介绍Linux环境下shell编程,普通文件与设备文件的编程方法,串口介绍与应用编程。 学习条件: 1.C语言编程基础 2.嵌入式Linux开发基础

    7346 人正在学习 去看看 沈寒
 

//----------------------------------------------------

//AUTHOR: lanyang123456

//DATE:2012-02-28

//----------------------------------------------------

 内核版本2.6.18 或2.6.32 或 3.1.10

Linux内核源码中自带USB转串口驱动

目录为drivers/usb/serial,

一般情况下,系统将USB转串口驱动编译成内核模块,需要时自动加载到内核中。

将GPS设备(USB接口)插入到计算机中,系统自动识别设备,将USB转为串口,并输出信息。

命令行下查看,#dmesg

[  909.159038] usb 2-4: new full speed USB device number 5 using ohci_hcd
[  909.338158] usb 2-4: New USB device found, idVendor=067b, idProduct=2303
[  909.338166] usb 2-4: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[  909.558764] USB Serial support registered for pl2303
[  909.559656] pl2303 2-4:1.0: pl2303 converter detected
[  909.594064] usb 2-4: pl2303 converter now attached to ttyUSB0
[  909.594866] usbcore: registered new interface driver pl2303
[  909.594872] pl2303: Prolific PL2303 USB to serial adaptor driver

 

 

#lsmod 可以查看新增加了模块,pl2303(GPS设备是pl2303芯片,大约多于70%的GPS设备是使用Prolific  pl2303 driver这个驱动的。)

 

查看目录 /dev

通过命令ls -l /dev/ttyUSB0

crw-rw---- 1 root dialout 188,0……/dev/ttyUSB0

 

 

内核2.6.18系统下

直接通过ttyUSB0读GPS数据

程序如下

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<termios.h>
#include<string.h>

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
	struct termios newtio,oldtio;
	if  ( tcgetattr( fd,&oldtio)  !=  0) { 
		perror("SetupSerial 1");
		return -1;
	}
	bzero( &newtio, sizeof( newtio ) );
	newtio.c_cflag  |=  CLOCAL | CREAD;
	newtio.c_cflag &= ~CSIZE;

	switch( nBits )
	{
	case 7:
		newtio.c_cflag |= CS7;
		break;
	case 8:
		newtio.c_cflag |= CS8;
		break;
	}

	switch( nEvent )
	{
	case 'O':
		newtio.c_cflag |= PARENB;
		newtio.c_cflag |= PARODD;
		newtio.c_iflag |= (INPCK | ISTRIP);
		break;
	case 'E': 
		newtio.c_iflag |= (INPCK | ISTRIP);
		newtio.c_cflag |= PARENB;
		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;
	case 460800:
		cfsetispeed(&newtio, B460800);
		cfsetospeed(&newtio, B460800);
		break;
	default:
		cfsetispeed(&newtio, B9600);
		cfsetospeed(&newtio, B9600);
		break;
	}
	if( nStop == 1 )
		newtio.c_cflag &=  ~CSTOPB;
	else if ( nStop == 2 )
	newtio.c_cflag |=  CSTOPB;
	newtio.c_cc[VTIME]  = 0;//重要
	newtio.c_cc[VMIN] = 100;//返回的最小值  重要
	tcflush(fd,TCIFLUSH);
	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
	{
		perror("com set error");
		return -1;
	}
//	printf("set done!\n\r");
	return 0;
}

int main(void)
{
	int fd1,nset1,nread;
	char buf[1024];

	fd1 = open("/dev/ttyUSB0", O_RDWR);//打开串口
	if (fd1 == -1)
		exit(1);

	nset1 = set_opt(fd1,4800, 8, 'N', 1);//设置串口属性
	if (nset1 == -1)
		exit(1);

	while	(1)

	{
		memset(buf,0,1024);	
		nread = read(fd1, buf, 1024);//读串口
		if (nread > 0){
			printf("\n GPS DATALen=%d\n",nread); 
			buf[nread] = '\0';
			printf( "GPS %s\n", buf); //输出所读数据
		}
		sleep(2);//睡眠,等待数据多一点
 
	}
	close(fd1);
	return 0;
}


 

一次读的结果如下

GPS DATALen=395

GPS $GPGGA,000531.979,0000.0000,N,00000.0000,E,0,00,50.0,0.0,M,0.0,M,0.0,0000*76

$GPGSA,A,1,,,,,,,,,,,,,50.0,50.0,50.0*05

$GPGSV,1,1,01,02,00,000,00*4A

$GPRMC,000531.979,V,0000.0000,N,00000.0000,E,,,270621,,*14

$GPVTG,,T,,M,,N,,K*4E

$GPGGA,000532.979,0000.0000,N,00000.0000,E,0,00,50.0,0.0,M,0.0,M,0.0,0000*75

$GPRMC,000532.979,V,0000.0000,N,00000.0000,E,,,270621,,*17

$GPVTG,,T,,M,,N,,K*4E

 

 

当在内核版本为2.6.32

运行程序,读到的数据长度为0

 

在3.1.10下

运行程序时,程序停在open函数这,不能往下运行

曾经怀疑是USB转串口驱动有问题,自己一琢磨,不是驱动的问题,内核版本越新,驱动更完善。所以考虑应用程序中,对串口操作函数的参数问题。

 

解决办法:

将open 函数改为

open(“/dev/ttyUSB0”,O_RDWR|O_NONBLOCK);

程序运行即可正常。

 

 

参考:

http://hi.baidu.com/%CD%FE%B5%C4%C9%FA%BB%EE/blog/item/5cae0e2749c7c71b8a82a135.html

 

http://linux.chinaunix.net/techdoc/develop/2007/11/15/972357.shtml 

 

 

转载请注明出处。谢谢!
2015-11-02 18:01:46 liushaofang 阅读数 885
  • 嵌入式Linux文件与串口编程

    本课程介绍Linux环境下shell编程,普通文件与设备文件的编程方法,串口介绍与应用编程。 学习条件: 1.C语言编程基础 2.嵌入式Linux开发基础

    7346 人正在学习 去看看 沈寒

摘要

鉴于串口在嵌入式系统中广泛应用于操作GPRS模块、读卡器模块和GPS北斗模块等,串口操作成为嵌入式系统开发中必备工作,但是目前web中并没有提供一个封装良好的串口初始化和读写接口函数,因此本文基于工程实践提供了操作串口的接口函数和测试程序,为嵌入式Linux的串口操作提供了较大的便利。本文中提供的串口操作接口函数并未将常用串口参数如波特率、数据位、停止位和校验方式进行参数化,因此也有待进一步封装。

背景

由于智慧泊车工程项目中开发的硬件设备需要通过串口和AT命令访问GPRS模块,自己的工作环境也从涉密事业单位变成了能够随时能够访问Internet的公司,因此打算逐步将这些操作进行抽象和封装,以备不时之需。受到开发周期的限制,目前还未来得及将常用串口参数如波特率、数据位、停止位和校验方式进行参数化,在后续工作中会逐步完善和提高,也希望从业同行能够对其中不足进行批评指正,实现共同进步。

技术方案

串口操作涉及的文件主要是termios.h头文件中的struct termios结构,该结构的定义如下:

#define NCC 8
struct termio {
 unsigned short c_iflag;
 unsigned short c_oflag;
 unsigned short c_cflag;
 unsigned short c_lflag;
 unsigned char c_line;
 unsigned char c_cc[NCC];
};

详细参数定义内核代码中有较详细的解释,后续再博客修改时会陆续加上。
串口操作接口函数头文件uart.h文件

/*
*@brief:uart operation interface
*@author:
*@email:
*@date:30th Oct 2015
*/
#ifndef __UART_H__
#define __UART_H__

#include <termios.h>
#include <unistd.h>
#include <fcntl.h>

#define DATA_BUFF_SIZE  256

int open_serial(char pchSerialName[], int nSerialNameLen);
int config_serial(int nFdSerial);
void signal_serial_data_prepared_handler(int nFdSerial);
int write_serial(int nFdSerial, char pchDataBuf[], int nDataBufLen);
int close_serial(int nFdSerial);

#endif

串口操作接口函数源文件uart.c文件

/*
*@brief:uart operation interface
*@author:
*@email:
*@date:30th Oct 2015
*/
#include <string.h>
#include "uart.h"

char g_serial_data_buffer[DATA_BUFF_SIZE] = { 0 };

int open_serial(char pchSerialName[], int nSerialNameLen)
{
    int nFdSerial;
    if(pchSerialName == NULL || nSerialNameLen <= 0)
        return -1;
    nFdSerial = open(pchSerialName, O_RDWR|O_NOCTTY);
    if(nFdSerial < 0)
        return -1;
    return nFdSerial;
}

/*
*@brief:configure serial port as 115200bps | 8 databits | 1 stopbit | no parity check
*@param:
    nFdSerial:handle of serial port
*@return:
*@author:
*@date:31th Oct 2015
*@todo:parameterize uart parameters
*/
int config_serial(int nFdSerial)
{
    struct termios tio;
    tcgetattr(nFdSerial, &tio);
    tcflush(nFdSerial, TCIFLUSH);
    cfsetispeed(&tio, B115200);//115200bps
    cfsetospeed(&tio, B115200);

    tio.c_cflag |= CS8;//8 data bits
    tio.c_cflag &= ~PARENB;//no parity check
    tio.c_oflag &= ~OPOST;
    tio.c_cflag &= ~CSTOPB;//1 stop bit
    tio.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN);
    tio.c_iflag &= ~(INPCK | BRKINT | ICRNL | ISTRIP | IXON);  

    tio.c_cc[VMIN] = 64;
    tio.c_cc[VTIME] = 1;

    if(tcsetattr(nFdSerial, TCSANOW, &tio) != 0)
    {
        perror("%s,L%d,in %s, configure serial failed.\n", __FILE__, __LINE__, __func__);
        return -1;
    }

    return 0;
}

void signal_serial_data_prepared_handler(int nFdSerial)
{
    int nReadBufLen = 0;
    memset(g_serial_data_buffer, 0, sizeof(g_serial_data_buffer));
    read(nFdSerial, g_serial_data_buffer, DATA_BUFF_SIZE);
}

int write_serial(int nFdSerial, char pchDataBuf[], int nDataBufLen)
{
    int nWriteBufLen = 0;
    nWriteBufLen = write(nFdSerial, pchDataBuf, nDataBufLen);
    return nWriteBufLen;
}

int close_serial(int nFdSerial)
{
    if(nFdSerial < 0)
    {
        perror("%s,L%d,in %s, close serial failed.\n", __FILE__, __LINE__, __func__);
        return -1;
    }
    close(nFdSerial);
    return 0;
}

串口测试主程序main.c文件如下。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include "uart.h"

#define UART_DEVICE_NAME    "/dev/ttySAC0"

int nFdSerial = -1;

void system_exit(int nExitCode)
{
    if(nFdSerial > 0)
        close(nFdSerial);
    exit(nExitCode);
}

int main(int argc, char * * argv, char * * env)
{
    char chDataTest[16] = { 0 };
    int nIndex = 0;

    nFdSerial = open_serial(UART_DEVICE_NAME, strlen(UART_DEVICE_NAME));
    if(nFdSerial < 0)
    {
        printf("%s,L%d,in %s, open %s failed.\n", __FILE__, __LINE__, __func__, UART_DEVICE_NAME);
        return;
    }   

    config_serial(nFdSerial);

    signal(SIGINT, system_exit);

    for(nIndex = 0; nIndex < sizeof(chDataTest); nIndex++)
    {
        chDataTest[nIndex] = nIndex + 1;
    }   

    while(1)
    {
        write_serial(nFdSerial, chDataTest, sizeof(chDataTest));
        usleep(1000000);    
    }

    return 0;
}

在linux上交叉编译的Makefile文件如下。

#!/bin/sh
CROSS=arm-linux-
CC=$(CROSS)gcc
FLAGS=-lpthread -wall

objects = main.o uart.o 

uart_test:$(objects)
    $(CC) -o uart_test $(objects)  
    cp uart_test /mnt/hgfs/vmware_shared/images
clean:
    rm  -f uart_test *.o

测试结果如下图所示。
串口测试结果图
上文中的源码链接如下:
http://www.pudn.com/downloads711/sourcecode/embedded/detail2854204.html

结语

目前提供的接口简化了串口操作,但是串口的主要参数还有待进一步参数化,另外还需要采用软中断用户自定义signal方式实现串口接收数据处理,在后续的工程开发中会进一步完善。

没有更多推荐了,返回首页