用的是全志的R528 SDK,Linux內核是5.4,新增加一個250000的非標準波特率 參考網路大神文檔,實踐並記錄寶貴的經驗。 方法: 1、修改內核的/include/uapi/asm-generic/termbits.h文件 這個CBAUD原來是0010017改為0030017,是用來做掩碼 ...
用的是全志的R528 SDK,Linux內核是5.4,新增加一個250000的非標準波特率
參考網路大神文檔,實踐並記錄寶貴的經驗。
方法:
1、修改內核的/include/uapi/asm-generic/termbits.h文件
這個CBAUD原來是0010017改為0030017,是用來做掩碼計算的。
圖1
這兩個是新加的
圖2
這個頭文件一共更改這三個地方。先說為什麼增加波特率使用0020001而不在B4000000後面遞增使用0010020,這是因為這個低位的20已經被占用了,
如下圖3,所以找了沒被占用的位置,0020000的中2這個bit位置在c_cflag中沒被占用(c_cflag是用於設置波特率和其他一些信息的)。
第一處的0030017也是這個原因,就是將波特率使用的這些巨集定義包含進去。
圖3
2、/drivers/tty/tty_baudrate.c文件
這個文件就是獲取波特率具體數值的文件,應用端的數據傳入到內核,內核解析並獲得250k波特率這個數值就是在這個文件,
先在文件
頭部的波特率列表中增加所需數值,如圖4,其中的250000和B250000為新增加。
圖4
修改函數speed_t tty_termios_baud_rate(struct ktermios *termios),圖5
圖5
其中圈起來的地方是新加的,這就是根據剛纔新加的部分進行波特率修改,新的0020001,與CBAUDEX2進行運算判斷高位位置,
之所以cbaud+=30是因為前面已經有了30個波特率了,見圖5。這樣內核就修改完了。
最後重新編譯內核, 重新燒錄系統鏡像。
3、應用程式測試驗證
應用端的配置,應用端通常使用tcsetattr這個函數進行配置,在使能之前,對齊c_cflag進行賦值就可
struct termios , termios_new;
termios_new.c_cflag |= 0020001;
(其餘配置省略)
tcsetattr(fdcom, TCSANOW, &termios_new);
這裡說一下為什麼不能使用cfsetispeed、cfsetospeed 函數。
因為這兩個函數只能指定原來標準的波特率,設置我們非標準的0020001的時候就會設置失敗
//ret = cfsetispeed(&newttys1, 0020001);
//printf("reti = %d\n",ret);
//ret = cfsetospeed(&newttys1, 0020001);
//printf("reto = %d\n",ret);
應用層測試代碼
baud_test.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "uart_oper.h"
#define UART1_DEV_NAME "/dev/ttyS1" /*需根據實際埠修改*/
#define BUF_LEN 100
int main(int argc,char const * argv[])
{
int fd =-1,ret =-1;
char buff[BUF_LEN]={0};
int i =0;
int n =0;
int len = BUF_LEN;
int baud = 0;
if(argc !=2)
{
printf("arg is not 2\n");
return -1;
}
baud = atoi(argv[1]);
printf("baud =%d\n",baud);
fd = open(UART1_DEV_NAME, O_RDWR | O_NOCTTY | O_NDELAY);
if(fd < 0)
{
perror("Can't open uart1 port");
return(void *)"uart1 dev error";
}
ret = set_serial(fd,baud, 8, 'N', 1); /*可能需要根據情況調整*/
// ret = set_serial(fd, 115200, 8, 'N', 1); /*可能需要根據情況調整*/
if(ret < 0)
{
printf("set_serial error\n");
return -1;
}
for(i =0 ;i<100;i++)
{
buff[i] =0x55;
}
while(1)
{
n = write(fd, buff, len);
printf("n =%d\n",n);
if(n < 0)
{
printf("send write error\n");
sleep(1);
return -1;
}
sleep(1);
}
return 0;
}
uart_oper.c
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "uart_oper.h"
/**
*@brief 配置串口
*@param fd:串口文件描述符.
nSpeed:波特率,
nBits:數據位 7 or 8,
nEvent:奇偶校驗位,
nStop:停止位
*@return 失敗返回-1;成功返回0;
*/
int set_serial(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newttys1, oldttys1;
/*保存原有串口配置*/
if(tcgetattr(fd, &oldttys1) != 0)
{
perror("Setupserial 1");
return - 1;
}
memset(&newttys1, 0, sizeof(newttys1));
/*CREAD 開啟串列數據接收,CLOCAL並打開本地連接模式*/
newttys1.c_cflag |= (CLOCAL | CREAD);
newttys1.c_cflag &=~CSIZE; /*設置數據位*/
switch(nBits) /*數據位選擇*/
{
case 7:
newttys1.c_cflag |= CS7;
break;
case 8:
newttys1.c_cflag |= CS8;
break;
default:break;
}
switch(nEvent) /*奇偶校驗位*/
{
case '0':
newttys1.c_cflag |= PARENB; /*開啟奇偶校驗*/
newttys1.c_iflag |= (INPCK | ISTRIP); /*INPCK打開輸入奇偶校驗,ISTRIP 去除字元的第八個比特*/
newttys1.c_cflag |= PARODD; /*啟動奇校驗(預設為偶校驗)*/
break;
case 'E':
newttys1.c_cflag |= PARENB; /*開啟奇偶校驗*/
newttys1.c_iflag |= (INPCK | ISTRIP); /*INPCK打開輸入奇偶校驗,ISTRIP 去除字元的第八個比特*/
newttys1.c_cflag &= ~PARODD; /*啟動偶校驗*/
break;
case 'N':
newttys1.c_cflag &= ~PARENB; /*無奇偶校驗*/
break;
default:break;
}
switch(nSpeed) /*設置波特率*/
{
case 2400:
cfsetispeed(&newttys1, B2400);
cfsetospeed(&newttys1, B2400);
break;
case 4800:
cfsetispeed(&newttys1, B4800);
cfsetospeed(&newttys1, B4800);
break;
case 9600:
cfsetispeed(&newttys1, B9600);
cfsetospeed(&newttys1, B9600);
break;
case 115200:
cfsetispeed(&newttys1, B115200);
cfsetospeed(&newttys1, B115200);
break;
case 250000:
//ret = cfsetispeed(&newttys1, 0020001);
//printf("reti = %d\n",ret);
//ret = cfsetospeed(&newttys1, 0020001);
//printf("reto = %d\n",ret);
newttys1.c_cflag |= 0020001;
break;
default :
cfsetispeed(&newttys1, B9600);
cfsetospeed(&newttys1, B9600);
break;
}
/*設置停止位*/
/*停止位為1,則清除CSTOPB,如停止位為2,則激活CSTOPB*/
if(nStop == 1)
{
newttys1.c_cflag &= ~CSTOPB; /*預設為停止位1*/
}
else if(nStop == 2)
{
newttys1.c_cflag |= CSTOPB;
}
/*設置最少字元和等待時間,對於接收字元和等待時間沒有特別的要求時*/
newttys1.c_cc[VTIME] = 0; /*非規範模式讀取時的超時時間*/
newttys1.c_cc[VMIN] = 0; /*非規範模式讀取時的最小字元數*/
/*tcflush 清空終端未完成的輸入、輸出請求及數據
TCIFLUSH表示清空正接收到的數據,且不讀取出來*/
tcflush(fd, TCIFLUSH);
/*激活配置使其生效*/
if((tcsetattr(fd, TCSANOW, &newttys1)) != 0)
{
perror("usart set error");
return - 1;
}
return 0;
}
uart_oper.h
#ifndef __UART_OPER_H__
#define __UART_OPER_H__
int set_serial(int fd, int nSpeed, int nBits, char nEvent, int nStop);
#endif
編譯
arm-openwrt-linux-gcc baud_test.c 生成 baud_test.o
arm-openwrt-linux-gcc uart_oper.c 生成 uart_oper.o
arm-openwrt-linux-gcc tt baud_test.o uart_oper.o 連接到一起生成tt測試程式
備註當直接執行arm-openwrt-linux-gcc baud_test.c -o tt 的時候報錯,找不到uart_oper.h中的函數。
將tt 拷貝到系統中。
終端執行tt 250000
之後用示波器測串口發出的波形。
波特率傳送速率計算:
一、波特率為9600表示的是串口每秒鐘可以傳輸9600bit,每傳輸1bit所需時間:
1 s / 9600 b i t = 1000000 ( u s ) / 9600 ( b i t ) = 1000 / 9.6 = 104.1667 u s 1s/9600bit = 1000000(us)/9600(bit) =1000/9.6 =104.1667us1s/9600bit=1000000(us)/9600(bit)=1000/9.6=104.1667us
那麼8bit就是 104.1667 ∗ 8 = 833.3336 u s 104.1667*8 =833.3336us104.1667∗8=833.3336us
實際項目中,串口通信時數據格式是:起始位+8位數據+奇偶校驗位+停止位 ,一般都沒有奇偶校驗位,所以是10位
也就是一個位元組的時間為 104.1667 ∗ 10 = 1041.667 u s 104.1667*10 =1041.667us104.1667∗10=1041.667us
二、波特率為19200每傳輸1bit所需時間:
1 s / 19200 b i t = 1000000 ( u s ) / 19200 ( b i t ) = 1000 / 19.2 = 52.0833 u s 1s/19200bit=1000000(us)/19200(bit) =1000/19.2 =52.0833us1s/19200bit=1000000(us)/19200(bit)=1000/19.2=52.0833us
三、波特率115200每傳輸1bit所需時間:
1 s / 115200 b i t = 1000000 ( u s ) / 115200 ( b i t ) = 1000 / 115.2 = 8.6806 u s ; 1s/ 115200bit = 1000000 (us)/ 115200(bit)= 1000/115.2 = 8.6806us;1s/115200bit=1000000(us)/115200(bit)=1000/115.2=8.6806us;
四、波特率為250000每傳輸1bit所需時間:
1 s / 250000 b i t = 1000000 ( u s ) / 250000( b i t ) = 1000 / 250 = 4 u s ;
從測試結果看是成功的。
4、終端命令行執行設置串口的命令
查詢串口配置命令
uart0:
cat /sys/bus/platform/drivers/uart/2500000.uart/ctrl_info
uart 1:
cat /sys/bus/platform/drivers/uart/2500400.uart/ctrl_info
查詢
root@TinaLinux:/# stty -a -F /dev/ttyS1
speed 9600 baud;stty: /dev/ttyS1
設置
stty -F /dev/ttyS1 ispeed 115200 ospeed 115200 cs8
參考資料
LINUX的串口非標準波特率更改 - 知乎 (zhihu.com)