樹莓派UART串口編程--使用wiringPi庫-C開發【2-修改驅動】

一、前言

上一篇博文記錄了使用wiringPi提供的串口驅動wiringSerial.c wiringSerial.h,並基於該驅動對串口進行簡單的通信,測試中發現該串口的驅動比較簡單,接收數據會存在分包的現象,另外一點是串口配置只提供了波特率參數配置,未提供其他如校驗、數據位和停止位驅動。這一片博文主要是對驅動進行修改。

二、修改驅動

wiringPi的驅動源碼可以在https://projects.drogon.net/raspberry-pi/wiringpi/中下載。找到wiringSerial.c 和 wiringSerial.h兩個文件,複製一份並重名命爲wiringSerial_Driver.c wiringSerial_Driver.h。以便於我們修改。

具體地,修改包括初始化函數、串口接收函數和串口發送函數。

2.1 初始化函數修改

可以同時初始化波特率、停止位、校驗位和數據位。參數gsfd爲全局定義的串口打開設備ID號,static int gsfd;

linux串口編程主要是對termios進行初始化,需要包含頭文件:#include <termios.h>

在Rpi3B中輸入:

man termios

可以查看termios的描述,具體不多講,看說明文檔即可,以下僅羅列部分:


NAME
       termios,  tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed, cfsetspeed - get
       and set terminal attributes, line control, get and set baud rate

SYNOPSIS
       #include <termios.h>
       #include <unistd.h>
       int tcgetattr(int fd, struct termios *termios_p);
       int tcsetattr(int fd, int optional_actions,
                     const struct termios *termios_p);
       int tcsendbreak(int fd, int duration);
       int tcdrain(int fd);
       int tcflush(int fd, int queue_selector);
       int tcflow(int fd, int action);
       void cfmakeraw(struct termios *termios_p);
       speed_t cfgetispeed(const struct termios *termios_p);
       speed_t cfgetospeed(const struct termios *termios_p);
       int cfsetispeed(struct termios *termios_p, speed_t speed);
       int cfsetospeed(struct termios *termios_p, speed_t speed);
       int cfsetspeed(struct termios *termios_p, speed_t speed);
   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
       cfsetspeed(), cfmakeraw(): _BSD_SOURCE

DESCRIPTION
       The termios functions describe a general terminal interface that is provided to control asynchronous communications ports.
   The termios structure
       Many of the functions described here have a termios_p argument that is a pointer to a termios structure.  This structure contains at least the follow‐
       ing members:
           tcflag_t c_iflag;      /* input modes */
           tcflag_t c_oflag;      /* output modes */
           tcflag_t c_cflag;      /* control modes */
           tcflag_t c_lflag;      /* local modes */
           cc_t     c_cc[NCCS];   /* special characters */
       The  values  that  may be assigned to these fields are described below.  In the case of the first four bit-mask fields, the definitions of some of the
       associated flags that may be set are exposed only if a specific feature test macro (see feature_test_macros(7))  is  defined,  as  noted  in  brackets
       ("[]").
       In  the  descriptions  below,  "not  in  POSIX"  means that the value is not specified in POSIX.1-2001, and "XSI" means that the value is specified in
       POSIX.1-2001 as part of the XSI extension.

那麼具體的驅動可以改寫成一下內容:

int myserialOpen (const char *device, const int baud, const int nbit, const char parity, const int nstop)
{
  struct termios options ;
  speed_t myBaud ;
  int     status;

  switch (baud)
  {
    case      50:	myBaud =      B50 ; break ;
    case      75:	myBaud =      B75 ; break ;
    case     110:	myBaud =     B110 ; break ;
    case     134:	myBaud =     B134 ; break ;
    case     150:	myBaud =     B150 ; break ;
    case     200:	myBaud =     B200 ; break ;
    case     300:	myBaud =     B300 ; break ;
    case     600:	myBaud =     B600 ; break ;
    case    1200:	myBaud =    B1200 ; break ;
    case    1800:	myBaud =    B1800 ; break ;
    case    2400:	myBaud =    B2400 ; break ;
    case    4800:	myBaud =    B4800 ; break ;
    case    9600:	myBaud =    B9600 ; break ;
    case   19200:	myBaud =   B19200 ; break ;
    case   38400:	myBaud =   B38400 ; break ;
    case   57600:	myBaud =   B57600 ; break ;
    case  115200:	myBaud =  B115200 ; break ;
    case  230400:	myBaud =  B230400 ; break ;
    case  460800:	myBaud =  B460800 ; break ;
    case  500000:	myBaud =  B500000 ; break ;
    case  576000:	myBaud =  B576000 ; break ;
    case  921600:	myBaud =  B921600 ; break ;
    case 1000000:	myBaud = B1000000 ; break ;
    case 1152000:	myBaud = B1152000 ; break ;
    case 1500000:	myBaud = B1500000 ; break ;
    case 2000000:	myBaud = B2000000 ; break ;
    case 2500000:	myBaud = B2500000 ; break ;
    case 3000000:	myBaud = B3000000 ; break ;
    case 3500000:	myBaud = B3500000 ; break ;
    case 4000000:	myBaud = B4000000 ; break ;

    default:
      return -2 ;
  }

  if ((gsfd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
    return -1 ;

  fcntl (gsfd, F_SETFL, O_RDWR) ;

// Get and modify current options:

  tcgetattr (gsfd, &options) ;

    cfmakeraw   (&options) ;
    cfsetispeed (&options, myBaud) ;
    cfsetospeed (&options, myBaud) ;
    
    //data bit
    switch (nbit)
    {
        case 7: options.c_cflag &= ~CSIZE ; options.c_cflag |= CS7; break;
        case 8: options.c_cflag &= ~CSIZE ; options.c_cflag |= CS8; break;
        default:  options.c_cflag &= ~CSIZE ; options.c_cflag |= CS8; break;
    }
    //data parity
    switch(parity)  
    {
      case 'n':
      case 'N':
	      options.c_cflag &= ~PARENB ;
	      options.c_cflag &= ~INPCK;
	      break;
      case 'o':
      case 'O': 
	      options.c_cflag |= PARENB ;
	      options.c_cflag |= PARODD ;
	      options.c_cflag |= INPCK  ;
	      options.c_cflag |= ISTRIP	; 
	      break;
      case 'e':
      case 'E': 
	      options.c_cflag |= PARENB ;
	      options.c_cflag &= ~PARODD;
	      options.c_cflag |= INPCK 	;
	      options.c_cflag |= ISTRIP	;
	      break;
      default:
	      options.c_cflag &= ~PARENB ;
	      options.c_cflag &= ~INPCK;
	      break;
   }
   //data stopbits
    switch(nstop)
    {
      case 1: options.c_cflag &= ~CSTOPB ; break;
      case 2: options.c_cflag |= CSTOPB ;  break; 
      default: options.c_cflag &= ~CSTOPB ; break;    
    }

    options.c_cflag |= (CLOCAL | CREAD) ;
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
    options.c_oflag &= ~OPOST ;

    options.c_cc [VMIN]  =   0 ;
    options.c_cc [VTIME] = 10 ;	// 0.5 seconds (100 deciseconds)

  tcsetattr (gsfd, TCSANOW, &options) ;

  ioctl (gsfd, TIOCMGET, &status);

  status |= TIOCM_DTR ;
  status |= TIOCM_RTS ;

  ioctl (gsfd, TIOCMSET, &status);

  usleep (10000) ;	// 10mS

  return gsfd ;
}

2.2 接收函數修改

實際測試時發現wiringSerial的接收函數serialDataAvail緩存大小隻有8字符,因此,超過8字符的數據將會分包,接收函數修改主要是對數據進行拼包,具體實現如下:

int myserialDataRead( char *buf,  int * size)
{
  int size_i,i;
  i = 0;
  size_i = 0;
  while(1)
  {
  	i = read(gsfd, buf+size_i ,1024);
  	size_i += i;
  	if(i == 8)
  	{
  		
  	}
  	else if(i>0 && i <= 8)
  	{
  		*size = size_i;
  		return 0;
  	}
  	else
  	{
  		return -1;
  	}
  }
}

2.3 發送函數修改

發送函數基本和wiringSerial提供的驅動一致,只是將傳入的設備ID用全局變量替代,這樣在使用該函數時無需輸入串口的ID。具體修改如下:

void myserialDataSend(const char * ptr,int size)
{
  while(size--)
  {
    serialPutchar(gsfd,*(ptr++));
  } 
}

三、編譯測試

對應的頭文件增加函數的聲明,編寫main.c測試文件:

#include<wiringPi.h>
#include"wiringSerial_Driver.h"
#include<stdio.h>
#include<string.h>

int main()
{
    int filedevid;
    int recbytenum;
    int rxflag;
    int i;
    char buf[1024];
    char bufprintf[1024];
    memset(buf,0,1024);
    memset(bufprintf,0,1024);
    wiringPiSetup();
    if((filedevid=myserialOpen("/dev/ttyAMA0",115200,8,'E',1))<0)
    {
        printf("/dev/ttyAMA0 Open Faild\r\n");
        return -1;
    }
    else
    {
        printf("/dev/ttyAMA0 Open with 115200,115200,8,'E',1, success\r\n");
        while(1)
        {
            rxflag = myserialDataRead(buf,&recbytenum);
            if(rxflag != -1)
            {
            	printf("Rpi3 uart rx %d byte: %s\r\n",recbytenum,buf);
                sprintf(bufprintf,"Rpi3 uart Tx %d byte: %s\r\n",recbytenum,buf);
                myserialDataSend(bufprintf, recbytenum);
            }
        }
    }
}



編寫makefile,進行編譯並運行如下:

可以看到,數據以及拼包成功,說明驅動修改有效。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章