使用C对串行AT命令响应缓慢,但使用minicom快速响应

时间:2016-07-20 21:01:00

标签: c++ serial-port termios

我正在使用我编写的C ++脚本从Beaglebone Black的串口读取问题。该脚本向Adafruit FONA GSM / GPS设备发送命令并从其接收响应并工作,除了在发送命令和实际从设备接收任何字节之间存在长时间延迟(我必须在设备之间放置1秒的延迟)写入和读取命令以获取设备的响应)。但是,当我使用minicom串行终端仿真器时,发送命令和接收响应之间没有明显的延迟。我猜这与我打开串口的方式有关,但我不知道还能改变什么。现在我将设置设置为原始输入和输出模式,没有行控制或回显,但仍无法缩短响应时间。任何帮助或想法都非常欢迎和赞赏!谢谢!

CPP文件:

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <linux/serial.h>
#include <termios.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include "Fona_control.h"


Fona_control::Fona_control(void)
{
    begin();
}

void Fona_control::get_gps(void)
{
    printf("Reading GPS\n");
    unsigned char gpsbuff[250];
    memset(gpsbuff,'\0',250);
    sleep(0.1);

    int bytes_a = 0;
    int n_write = write(fona_fd,GPS_GET_DATA,sizeof(GPS_GET_DATA)-1);
    sleep(1);
    ioctl(fona_fd,FIONREAD,&bytes_a);
    printf("Bytes avail: %i\n",bytes_a);
    int n_read = read(fona_fd,gpsbuff,bytes_a);
    printf("Buffer: %s\n",gpsbuff);
    printf("Bytes read: %i\n",n_read);

    return;
}



void Fona_control::begin(void)
{
    printf("FONA Beginning\n");

    struct termios oldtio, newtio;
    struct serial_struct serinfo;
    // Load the pin configuration
    /* Open modem device for reading and writing and not as controlling tty
    because we don't want to get killed if linenoise sends CTRL-C. */
    fona_fd = open(FONA_DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fona_fd < 0) { perror(FONA_DEVICE); exit(-1); }

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

    /*  BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
    CRTSCTS : output hardware flow control (only used if the cable has
              all necessary lines. See sect. 7 of Serial-HOWTO)
    CS8     : 8n1 (8bit,no parity,1 stopbit)
    CLOCAL  : local connection, no modem contol
    CREAD   : enable receiving characters */
    cfsetspeed(&newtio,BAUDRATE_Fona);
    newtio.c_cflag |= ( CLOCAL | CREAD );
    newtio.c_cflag &= ~CSIZE;
    newtio.c_cflag |= CS8;
    newtio.c_cflag &= ~PARENB;   
    newtio.c_cflag &= ~CRTSCTS;
    newtio.c_cflag &= ~CSTOPB;
    newtio.c_cc[VMIN]  = 1;
    newtio.c_cc[VTIME] = 1;

ioctl (fona_fd, TIOCGSERIAL, &serinfo);
serinfo.flags |= 0x4000;
ioctl (fona_fd, TIOCSSERIAL, &serinfo);

    /* setup for non-canonical mode */
    //newtio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | ISTRIP | INLCR | INPCK | ICRNL | IXON | IGNCR);  
    newtio.c_iflag = 0; 

    /* Set line flags */ 
    //newtio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    newtio.c_lflag = 0; 

    /*  Raw output
    newtio.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | ONOEOT| OFILL | OLCUC | OPOST); */
    newtio.c_oflag = 0;


    /* now clean the modem line and activate the settings for the port */
    tcflush(fona_fd, TCIOFLUSH);
    if(tcsetattr(fona_fd,TCSANOW,&newtio) < 0)
    {
        printf("Error Setting FONA Serial Port Attributes!");
        exit(0);
    };

    /* terminal settings done, now send FONA initialization commands*/

    sleep(1);
    unsigned char buffer[50];
    int bytes_avail = 0;
    int n_write     = 0;
    int n_read      = 0;
    int cnt         = 0;
    memset(buffer,'\0',50);
    tcflush(fona_fd, TCIOFLUSH);
    while(strstr((char *)buffer,"OK") == NULL && cnt < 5)
    {
        memset(buffer,'\0',50);
        n_write = write(fona_fd,FONA_AT,sizeof(FONA_AT)-1);
        sleep(1);
        ioctl(fona_fd,FIONREAD,&bytes_avail);
        printf("BA: %i\n",bytes_avail);
        if(bytes_avail > 0)
        {
            n_read = read(fona_fd,buffer,bytes_avail);
            printf("%s\n",buffer);
        }
        sleep(1);
        cnt++;
    }

    sleep(1);
    n_write = write(fona_fd,"+++",3);
    bytes_avail = 0;
    sleep(1);
    ioctl(fona_fd,FIONREAD,&bytes_avail);
    printf("BA2: %i\n",bytes_avail);
    n_read = read(fona_fd,buffer,bytes_avail);
    printf("%s",buffer);

    printf("AT Accepted\n");
    sleep(1);
    tcflush(fona_fd, TCIOFLUSH);
    unsigned char buffer1[50];
    memset(buffer1,'\0',50);

    int n = write(fona_fd,FONA_ECHO_OFF,sizeof(FONA_ECHO_OFF)-1);
    printf("Writ: %i\n",n);
    bytes_avail = 0;
    sleep(1);
    ioctl(fona_fd,FIONREAD,&bytes_avail);
    printf("BA2: %i\n",bytes_avail);
    n = read(fona_fd,buffer1,bytes_avail);
    printf("%s",buffer1);
    memset(buffer1,'\0',50);
    sleep(1);


    n = write(fona_fd,GPS_POWER_ON,sizeof(GPS_POWER_ON)-1);
    printf("Writ: %i\n",n);
    bytes_avail = 0;
    sleep(1);
    ioctl(fona_fd,FIONREAD,&bytes_avail);
    printf("BA2: %i\n",bytes_avail);
    n = read(fona_fd,buffer1,bytes_avail);
    printf("%s\n",buffer1);
    memset(buffer1,'\0',50);
    sleep(1);

    n = write(fona_fd,FONA_SMS_TYPE,sizeof(FONA_SMS_TYPE)-1);
    printf("Writ: %i\n",n);
    bytes_avail = 0;
    sleep(1);
    ioctl(fona_fd,FIONREAD,&bytes_avail);
    printf("BA2: %i\n",bytes_avail);
    n = read(fona_fd,buffer1,bytes_avail);
    printf("%s\n",buffer1);
    sleep(1);

}

H档案:

#ifndef _fona_control_H
#define _fona_control_H

#include <stdint.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>

#define FONA_DEVICE "/dev/ttyO5" //Beaglebone Black serial port
//#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define BAUDRATE_Fona B115200   // Change as needed, keep B

/* Define FONA AT Commands */
#define FONA_AT         "AT\r\n"
#define FONA_ECHO_OFF       "ATE0\r\n"
#define FONA_CMD_REPEAT     "A/\r\n"
#define FONA_NO_ECHO        "ATE0\r\n"
#define FONA_PIN_CHECK      "AT+CPIN?\r\n"
#define FONA_PIN_SEND       "AT+CPIN=1234\r\n"
#define FONA_SMS_TYPE       "AT+CMGF=1\r\n"


/* Define FONA GPS AT Commands */
#define GPS_POWER_ON    "AT+CGNSPWR=1\r\n"
#define GPS_POWER_OFF   "AT+CGNSPWR=0\r\n"
#define GPS_GET_DATA    "AT+CGNSINF\r\n"

/* Define FONA GPS NMEA Commands */
#define PMTK_CMD_HOT_START  "AT+CGNSCMD=0,"$PMTK101*32"\r\n"
#define PMTK_CMD_WARM_START "AT+CGNSCMD=0,"$PMTK102*31"\r\n"
#define PMTK_CMD_COLD_START     "AT+CGNSCMD=0,"$PMTK103*30"\r\n"
#define PMTK_SET_NMEA_5HZ       "AT+CGNSCMD=0,"$PMTK220,200*2C"\r\n"
#define PMTK_SET_BAUD_38400     "AT+CGNSCMD=0,"$PMTK251,38400*27"\r\n"
#define PMTK_SET_WAAS       "AT+CGNSCMD=0,"$PMTK301,2*2E"\r\n"
#define PMTK_SET_SBAS       "AT+CGNSCMD=0,"$PMTK313,1*2E"\r\n"
#define PMTK_NMEA_TYPES         "AT+CGNSCMD=0,"$PMTK314,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0*29"\r\n"
#define PMTK_STANDY_MODE    "AT+CGNSCMD=0,"$PMTK161,0*28"\r\n"    //Send any byte to exit standby mode


class Fona_control {
   public:

      void begin(void);
      void get_gps(void);
      Fona_control(void); // Constructor when using HardwareSerial

      uint8_t fix_status, fix_mode, sats, sats_used, glo_sats, cn0;
      uint8_t month, day, minute;
      uint32_t year;

      double seconds, latitude, longitude, speed, course, hdop, vdop, pdop, hpa, vpa;

      int fona_fd;

   private:


};


#endif

1 个答案:

答案 0 :(得分:1)

1秒的睡眠是杀气腾腾的,我可以向你保证minicom没有这样做。它将等待数据进入,而不是轮询,然后显示它。串行数据很慢。以9600波特率发送的每个字符大约需要一毫秒才能到达。在115.2k波特,你在一个小于85微秒的时间内移动一个角色。另一方面,你的Beaglebone在纳秒内工作,所以如果你在阅读之前没有等待,那么数据还不会存在。也就是说,你应该在不到1毫秒的时间内获得“OK”并且等待一整秒是非常难以理解的。

考虑阻塞并等待“OK”所需的三个字节

while(strstr((char *)buffer,"OK") == NULL && cnt < 5)
{
    memset(buffer,'\0',50);
    n_write = write(fona_fd,FONA_AT,sizeof(FONA_AT)-1);
    n_read = read(fona_fd,buffer,3);
    cnt++;
}

如果设备从不响应,则可以永久阻止,因此您需要超时

我能想到的最简单的全能超时机制就是这样:

int retry= 5;
while (retry)
{
    fd_set readfs;    // file descriptor set used by select
    struct timeval timeout;
    FD_ZERO(&readfs); // clear file descriptor set 
    FD_SET(fona_fd, &readfs); //  set our port as the one item in the set
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    if (select(fona_fd+1, &readfs, NULL, NULL, &timeout) !=0)
    {
        rval = read(fona_fd, buffer, sizeof(buffer)-1); 
        // will stop reading after configurable gap between bytes read
        buffer[rval] = '\0'; // NULL terminate the buffer or it ain't a string
                             // If not a string, results of strstr are undefined
        if (strstr((char *)buffer,"OK") != NULL) 
        {
            break;
        }
    }
    retry--;
}

Documentation on select。这是非常酷的功能。 Not as cool as epoll,但更容易找到教程。如果你做了很多selectepoll好看的话。

在这种情况下选择等待数据1秒钟然后放弃。如果它找到数据,程序将进入if主体并且read将读取,直到自收到最后一个字符或缓冲区已满以来已经过了一些用户可配置的字符时间数。

然后我们将null终止缓冲区中的数据,以便我们可以在其上使用字符串处理例程。这也消除了memset缓冲区的需要。如果缓冲区包含“OK”,我们退出循环。

否则我们递减重试计数器并循环以查看是否有更多重试。