树莓派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的描述,具體不多講,看說明文檔即可,以下僅羅列部分:
NAMEtermios, tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed, cfsetspeed - getand set terminal attributes, line control, get and set baud rateSYNOPSIS#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_SOURCEDESCRIPTIONThe termios functions describe a general terminal interface that is provided to control asynchronous communications ports.The termios structureMany 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 theassociated 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 inPOSIX.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 ;}/* 有O_NONBLOCK 即為非阻塞方式,默認阻塞方式,等待vim vtime*/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 bitswitch (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 parityswitch(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 stopbitsswitch(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) ; // 10mSreturn gsfd ; }2.2 接收函數修改
提一下,Linux串口接收主要由阻塞和非阻塞接收兩種方式,關于如何配置Linux串口驅動可以參考這篇博文:Linux 使用fcntl c_cc[VMIN] c_cc[CTIME]設置串口阻塞與非阻塞讀取數據
實際測試時發現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,進行編譯并運行如下:
可以看到,數據以及拼包成功,說明驅動修改有效。
總結
以上是生活随笔為你收集整理的树莓派UART串口编程--使用wiringPi库-C开发【2-修改驱动】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Airtest网易自动化测试工具初探(一
- 下一篇: 程序员「在知乎装逼被怼」,决定用『面试』