Linux下串口编程基础
串口知識
串行接口 (SerialInterface) 是指數據一位一位地順序傳送,其特點是通信線路簡單,只要一對傳輸線就可以實現雙向通信(可以直接利用電話線作為傳輸線),從而大大降低了成本,特別適用于遠距離通信,但傳送速度較慢。
1. 波特率
表示每秒傳輸的比特數,串口通信的雙方必須保持一致才能通信數據位,若波特率為115200,它表示什么呢?
對于發送斷,即每秒鐘發送115200bit。
對于接收端,115200波特率意味著串口通信在數據線上的采樣率為115200Hz.
2. 數據位
這是衡量通信中實際數據位的參數。當計算機發送一個信息包,實際的數據不會是8位的,標準的值是5、6、7和8位。如何設置取決于你想傳送的信息。比如,標準的ASCII碼是0~127(7位)。擴展的ASCII碼是0~255(8位)。如果數據使用簡單的文本(標準 ASCII碼),那么每個數據包使用7位數據。每個包是指一個字節,包括開始/停止位,數據位和奇偶校驗位。由于實際數據位取決于通信協議的選取,術語“包”指任何通信的情況。7位或8位數據中不僅僅是數據,還包括開始/停止位,數據位以及奇偶校驗位等
3. 停止位
用于表示單個包的最后一位。典型的值為1,1.5和2位。由于數據是在傳輸線上定時的,并且每一個設備有其自己的時鐘,很可能在通信中兩臺設備間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,并且提供計算機校正時鐘同步的機會。適用于停止位的位數越多,不同時鐘同步的容忍程度越大,但是數據傳輸率同時也越慢。
4. 奇偶校驗
一般奇偶校驗位的應用是由硬件完成的,軟件只需要在初始化的時候對MCU的串口外設設置一下
奇偶校驗的形式一般是在數據位后面跟一個奇偶校驗位,如果數據位是8位的,那么加上校驗位就是9位數據;如果數據位是7位的,那么加上校驗位就是8位數據,校驗位和數據之間沒有任何意義上的關聯,它不是數據的一部分,它只是關心數據中的“1”的個數是否為奇數(奇校驗),或者偶數(偶校驗)。奇校驗,奇校驗就是控制器在發送的一個字節或者一個數據幀里面含有的“1”的個數進行奇數個的修正調整,這里采用8位數據位+1位奇校驗位的形式舉個簡單的例子,A發送數據0x35到B,后面緊跟一個奇校驗位X, 0x35的二進制 = 0011 0101,可以看出8個數據位中一共有4個‘1’,那此時硬件會根據“1”的個數來設置X,這里會將奇校驗位X調整為“1”,為什么呢? 因為數據位的“1”的個數是偶數,而我們用的是奇校驗,所以為了達到有奇數個“1”的目的,調整奇偶校驗位X為“1”,那么這9個位發出去就有奇數個“1”啦,當B接收的時候,B的硬件同樣會對接收到“數據+X”中“1”的個數進行計數判斷,如果是奇數個“1”,則認為數據接收正確,否則認為數據錯誤。而當A發送數據0x25( 0010 0101)到B的時候,則X應該就是“0”了,因為要保證發送出去的數據位+X位一共有奇數個“1”。
串口編程
串口編程的步驟:
a) 打開串口
這里是串口操作需要的一些頭文件
Linux下一切皆文件,所以串口操作也是對文件進行操作的
Linux的串口文件位于 /dev下的
這里我們通過標準的文件打開操作嘗試打開串口 1
在這之前先建立一個 .c 文件 這里是 uatr2.c
編譯 .c 文件
gcc -o uart2.o uart2.c運行
./uart2.o這里輸出success 說明串口能成功打開
調用open()函數來代開串口設備,對于串口的打開操作,必須使用O_NOCTTY參數。
O_NOCTTY:表示打開的是一個終端設備,程序不會成為該端口的控制終端。如果不使用此標志,任務一個輸入(eg:鍵盤中止信號等)都將影響進程。
O_NDELAY:表示不關心DCD信號線所處的狀態(端口的另一端是否激活或者停止)。
1、 串口波特率設置
串口的設置主要是設置 struct termios 結構體的各成員值。
設置這個結構體很復雜,這里就只說說常見的一些設置:
波特率設置
下面是修改波特率的代碼:
設置波特率的例子函數:
/** *@brief 設置串口通信速率 *@param fd 類型 int 打開串口的文件句柄 *@param speed 類型 int 串口速度 *@return void */ int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,B38400, B19200, B9600, B4800, B2400, B1200, B300, }; int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9600, 4800, 2400, 1200, 300, }; void set_speed(int fd, int speed){int i;int status;struct termios Opt;tcgetattr(fd, &Opt);for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {if (speed == name_arr[i]) { tcflush(fd, TCIOFLUSH); cfsetispeed(&Opt, speed_arr[i]); cfsetospeed(&Opt, speed_arr[i]); status = tcsetattr(fd, TCSANOW, &Opt); if (status != 0) { perror("tcsetattr fd1"); return; } tcflush(fd,TCIOFLUSH); } } }3、校驗位和停止位的設置
設置效驗的函數:
4、 讀寫串口
設置好串口之后,讀寫串口就很容易了,把串口當作文件讀寫就是。
發送數據
讀取串口數據
使用文件操作read函數讀取,如果設置為原始模式(Raw Mode)傳輸數據,那么read函數返回的字符數是實際串口收到的字符數。
可以使用操作文件的函數來實現異步讀取,如fcntl,或者select等來操作。
5、 關閉串口
關閉串口就是關閉文件。
close(fd);
下面是串口1的一個簡單的讀取與發送的例子
#include <stdio.h> /*標準輸入輸出定義*/ #include <stdlib.h> /*標準函數庫定義*/ #include <unistd.h> /*Unix標準函數定義*/ #include <sys/types.h> /**/ #include <sys/types.h> /**/ #include <sys/stat.h> /**/ #include <fcntl.h> /*文件控制定義*/ #include <termios.h> /*PPSIX終端控制定義*/ #include <errno.h> /*錯誤號定義*/#define FALSE 0 #define TRUE 1/***@brief 設置串口通信速率 *@param fd 類型 int 打開串口的文件句柄 *@param speed 類型 int 串口速度 *@return void*/int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,B38400, B19200, B9600, B4800, B2400, B1200, B300, }; int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300,38400, 19200, 9600, 4800, 2400, 1200, 300, }; void set_speed(int fd, int speed) {int i;int status;struct termios Opt;tcgetattr(fd, &Opt);for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++){if (speed == name_arr[i]){tcflush(fd, TCIOFLUSH);cfsetispeed(&Opt, speed_arr[i]);cfsetospeed(&Opt, speed_arr[i]);status = tcsetattr(fd, TCSANOW, &Opt);if (status != 0)perror("tcsetattr fd1");return;}tcflush(fd,TCIOFLUSH);} } /** { int fd = open( Dev, O_RDWR | O_NOCTTY | O_NDELAY); //| O_NOCTTY | O_NDELAYif (-1 == fd){ /*設置數據位數*/perror("Can't Open Serial Port");return -1;}elsereturn fd;} /** *@breif main() */ int main(int argc, char **argv) {int fd , n;int nread;char buff[512];char *dev ="/dev/ttyS1";fd = OpenDev(dev);if (fd>0)set_speed(fd,19200);else{printf("Can't Open Serial Port!\n");exit(0);}if (set_Parity(fd,8,1,'N')== FALSE){printf("Set Parity Error\n");exit(1);}while(1){while((nread = read(fd,buff,512))>0){printf("\nLen %d\n",nread);buff[nread+1]='\0';printf("\n%s",buff);n = write(fd, "I get\r", 4) //如果收到數據則向串口發送I Get if (n < 0)fputs("write() of 4 bytes failed!\n", stderr);}}//close(fd);//exit(0); }總結
以上是生活随笔為你收集整理的Linux下串口编程基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 奇葩问题都有解
- 下一篇: Linux 下的tar常用命令及操作