技術文章:基于Linux的tty架構及UART驅動詳解
三. 模塊詳細設計
3.1. 關鍵函數接口3.1.1. uart_register_driver功能: uart_register_driver用于串口驅動uart_driver注冊到內核(串口核心層)中,通常在模塊初始化函數調用該函數。
*參數:drv:要注冊的uart_driver
*返回值:成功,返回0;否則返回錯誤碼
int uart_register_driver(struct uart_driver *drv)
3.1.2. uart_unregister_driver功能:uart_unregister 用于注銷我們已注冊的uart_driver,通常在模塊卸載函數調用該函數,
*參數 : drv:要注銷的uart_driver
*返回值:成功返回0,否則返回錯誤碼
void uart_unregister_driver(struct uart_driver *drv)
3.1.3. uart_add_one_port功能:uart_add_one_port用于為串口驅動添加一個串口端口,通常在探測到設備后(驅動的設備probe方法)調用該函數
*參數:
* drv:串口驅動
* port:要添加的串口端口
*返回值:成功,返回0;否則返回錯誤碼
int uart_add_one_port(struct uart_driver *drv,struct uart_port *port)
3.1.4. uart_remove_one_port功能:uart_remove_one_port用于刪除一個已經添加到串口驅動中的串口端口,通常在驅動卸載時調用該函數
*參數:
* drv:串口驅動
* port:要刪除的串口端口
*返回值:成功,返回0;否則返回錯誤碼
int uart_remove_one_port(struct uart_driver *drv,struct uart_port *port)
3.1.5. uart_write_wakeup功能:uart_write_wakeup喚醒上層因串口端口寫數據而堵塞的進程,通常在串口發送中斷處理函數中調用該函數
*參數:
* port: 需要喚醒寫堵塞進程的串口端口
void uart_write_wakeup(struct uart_port *port)
3.1.6. uart_suspend_port功能:uart_suspend_port用于掛起特定的串口端口
*參數:
* drv:要掛起的串口端口鎖所屬的串口驅動
* port:要掛起的串口端口
*返回值:成功返回0;否則返回錯誤碼
int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)
3.1.7. uart_resume_port功能:uart_resume_port用于恢復某一已掛起的串口
*參數:
* drv:要恢復的串口端口所屬的串口驅動
* port:要恢復的串口端口
*返回值:成功返回0;否則返回錯誤碼
int uart_resume_port(struct uart_driver *drv, struct uart_port *port)
3.1.8. uart_get_baud_rate功能:uart_get_baud_rate通過解碼termios結構體來獲取指定串口的波特率
*參數:
* port:要獲取波特率的串口端口
* termios:當前期望的termios配置(包括串口波特率)
* old:以前的termios配置,可以為NULL
* min:可以接受的最小波特率
* max:可以接受的最大波特率
* 返回值:串口波特率
unsigned int uart_get_baund_rate(struct uart_port *port, struct ktermios *termios, struct ktermios *old,unsigned int min, unsigned int max)
3.1.9. uart_get_divisor功能:uart_get_divisor 用于計算某一波特率的串口時鐘分頻數(串口波特率除數)
*參數:
* port:要計算分頻數的串口端口
* baud:期望的波特率
*返回值:串口時鐘分頻數
unsigned int uart_get_divisor(struct uart_port *port, unsigned int baund)
3.1.10. uart_update_timeout功能:uart_update_timeout用于更新(設置)串口FIFO超出時間
*參數:
* port:要更新超時間的串口端口
* cfalg:termios結構體的cflag值
* baud:串口的波特率
void uart_update_timeout(struct uart_port *port,unsigned int cflag, unsigned int baud)
3.1.11. uart_insert_char功能:uart_insert_char用于向uart層插入一個字符
*參數:
* port:要寫信息的串口端口
* status:RX buffer狀態
* overrun:在status中的overrun bit掩碼
* ch:需要插入的字符
* flag:插入字符的flag:TTY_BREAK,TTY_PSRIYY, TTY_FRAME
void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun,unsigned int ch, unsigned int flag)
3.1.12. uart_console_write功能:uart_console_write用于向串口端口寫一控制臺信息
*參數:
* port:要寫信息的串口端口
* s:要寫的信息
* count:信息的大小
* putchar:用于向串口端口寫字符的函數,該函數有兩個參數:串口端口和要寫的字符
Void uart_console_write(struct uart_port *port,const char *s, unsigned int count,viod(*putchar)(struct uart_port*, int))
4. 模塊使用說明4.1. 串口編程4.1.1. 串口控制函數屬性說明tcgetatrr取屬性(termios結構)tcsetarr設置屬性(termios結構)cfgetispeed得到輸入速度cfsetispeed得到輸出速度cfstospeed設置輸出速度tcdrain等待所有輸出都被傳輸tcflow掛起傳輸或接收tcflush刷請未決輸出和/或輸入tcsendbreak送BREAK字符tcgetpgrp得到前臺進程組IDTcsetpgrp設置前臺進程組ID4.1.2. 串口配置流程(1) 保持原先串口配置,使用tegetatrr(fd, &oldtio);struct termious newtio, oldtio;
tegetattr(fd, &oldtio);
(2) 激活選項有CLOCAL和CREAD,用于本地連接和接收使用newtio.cflag |= CLOCAL|CREAD;
(3) 設置波特率newtio.c_cflag = B115200;
(4) 設置數據位,需使用掩碼設置newtio.c_cflag &= ~CSIZE;
Newtio.c_cflag |= CS8;
(5) 設置停止位,通過激活c_cflag中的CSTOP實現。若停止位為1,則清除CSTOPB,若停止位為2,則激活CSTOPnewtio.c_cflag &= ~CSTOPB; 停止位設置為1
Newtio.c_cflag |= CSTOPB; 停止位設置為2
(6) 設置流控newtio.c_cfag |= CRTSCTS 開啟硬件流控
newtio.c_cfag |= (IXON | IXOFF | IXANY); 開啟軟件流控
(7) 奇偶檢驗位設置,使用c_cflag和c_ifag.設置奇校驗newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
設置偶校驗
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag |= ~PARODD;
(8) 設置最少字符和等待時間,對于接收字符和等待時間沒有什么特別的要求,可設置為0:newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
(9) 處理要寫入的引用對象tcflush函數刷清(拋棄)輸入緩沖(終端程序已經接收到,但用戶程序尚未讀)或輸出緩沖(用戶程序已經寫,但未發送)。int tcflash(int filedes, int quene)
quene數應當是下列三個常數之一:
*TCIFLUSH 刷清輸入隊列
*TCOFLUSH 刷清輸出隊列
*TCIOFLUSH 刷清輸入、輸出隊列
例如:
tcflush(fd, TCIFLUSH);
(10) 激活配置,在完成配置后,需要激活配置使其生效。使用tcsetattr()函數:int tcsetarr(int filedes, const struct termios *termptr);
opt 指定在什么時候新的終端屬性才起作用,
*TCSANOW:更改立即發生
*TCSADRAIN:發送了所有輸出后更改才發生。若更改輸出參數則應使用此選項
*TCSAFLUSH:發送了所有輸出后更改才發生。更進一步,在更改發生時未讀的
所有輸入數據都被刪除(刷清)
例如:tcsetatrr(fd, TCSANOW, &newtio);
4.1.3. 使用流程(1)打開串口,例如"/dev/ttySLB0"fd = open("/dev/ttySLB0",O_RDWR | O_NOCTTY | O_NDELAY);
O_NOCTTY:是為了告訴Linux這個程序不會成為這個端口上的“控制終端”。如果不這樣做的話,所有的輸入,比如鍵盤上過來的Ctrl+C中止信號等等,會影響到你的進程。
O_NDELAY:這個標志則是告訴Linux這個程序并不關心DCD信號線的狀態,也就是不管串口是否有數據到來,都是非阻塞的,程序繼續執行。
(2)恢復串口狀態為阻塞狀態,用于等待串口數據的讀入,用fcntl函數:fcntl(fd,F_SETFL,0); //F_SETFL:設置文件flag為0,即默認,即阻塞狀態
(3)接著測試打開的文件描述符是否應用一個終端設備,以進一步確認串口是否正確打開。isatty(STDIN_FILENO);
(4)讀寫串口串口的讀寫與普通文件一樣,使用read,write函數
read(fd, buf ,8);
write(fd,buff,8);
4.1.4. Demo
以下給出一個測溫模塊收取數據的例子
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <log/log.h>
#include <stdlib.h>
#define UART_DEVICE "/dev/ttySLB1"
struct temp {
float temp_max1;
float temp_max2;
float temp_max3;
float temp_min;
float temp_mean;
float temp_enviromem;
char temp_col[1536];
};
int main(void)
{
int count, i, fd;
struct termios oldtio, newtio;
struct temp *temp;
temp = (struct temp *)malloc(sizeof(struct temp));
if (!temp) {
printf("malloc failed");
return -1;
}
char cmd_buf1[] = { 0xAA, 0x01, 0x04, 0x00, 0x06, 0x10, 0x05, 0x00, 0xBB};
char cmd_buf2[] = { 0xAA, 0x01, 0x04, 0x00, 0x00, 0xA0, 0x00, 0x03, 0xBB};
char cmd_buf3[] = { 0xAA, 0x01, 0x04, 0x00, 0x03, 0x10, 0x01, 0x00, 0xBB};
char read_buf[2000];
//-----------打開uart設備文件------------------
fd = open(UART_DEVICE, O_RDWR | O_NOCTTY);
if (fd < 0) {
printf("Open %s failed", UART_DEVICE);
return -1;
} else {
printf("Open %s successfully", UART_DEVICE);
}
//-----------設置操作參數-----------------------
tcgetattr(fd, &oldtio);//獲取當前操作模式參數
memset(&newtio, 0, sizeof(newtio));
//波特率=230400 數據位=8 使能數據接收
newtio.c_cflag = B230400 | CS8 | CLOCAL | CREAD | CSTOPB;
newtio.c_iflag = IGNPAR;
tcflush(fd, TCIFLUSH);//清空輸入緩沖區和輸出緩沖區
tcsetattr(fd, TCSANOW, &newtio);//設置新的操作參數
//printf("input: %s, len = %d", cmd_buf, strlen(cmd_buf));
//------------向urat發送數據-------------------
for (i = 0; i < 9; i++)
printf("%#X ", cmd_buf1[i]);
count = write(fd, cmd_buf1, 9);
if (count != 9) {
printf("send failed");
return -1;
}
usleep(500000);
memset(read_buf, 0, sizeof(read_buf));
count = read(fd, read_buf, sizeof(read_buf));
if (count > 0) {
for (i = 0; i < count; i++);
temp->temp_max1 = read_buf[7] << 8 | read_buf[6];
temp->temp_max2 = read_buf[9] << 8 | read_buf[8];
temp->temp_max3 = read_buf[11] << 8 | read_buf[10];
temp->temp_min = read_buf[13] << 8 | read_buf[12];
temp->temp_mean = read_buf[15] << 8 | read_buf[14];
printf("temp->temp_max1 = %f", temp->temp_max1 * 0.01);
printf("temp->temp_max2 = %f", temp->temp_max2 * 0.01);
printf("temp->temp_max3 = %f", temp->temp_max3 * 0.01);
printf("temp->temp_min = %f", temp->temp_min * 0.01);
printf("temp->temp_mean = %f", temp->temp_mean * 0.01);
} else {
printf("read temp failed");
return -1;
}
count = write(fd, cmd_buf3, 9);
if (count != 9) {
printf("send failed");
return -1;
}
usleep(365);
memset(read_buf, 0, sizeof(read_buf));
count = read(fd, read_buf, sizeof(read_buf));
if (count > 0) {
for (i = 0; i < count; i++);
temp->temp_enviromem = read_buf[7] << 8 | read_buf[6];
printf("temp->temp_enviromem = %f", temp->temp_enviromem * 0.01);
} else {
printf("read enviromem failed");
return -1;
}
count = write(fd, cmd_buf2, 9);
if (count != 9) {
printf("send failed");
return -1;
}
usleep(70000);
memset(read_buf, 0, sizeof(read_buf));
memset(temp->temp_col, 0, sizeof(temp->temp_col));
count = read(fd, read_buf, sizeof(read_buf));
printf("count = %d", count);
if (count > 0) {
for (i = 0; i < count - 7; i++)
temp->temp_col[i] = read_buf[i+6];
for (i = 0; i < 1536; i++)
{
if (!(i%10))
printf("");
printf("%#X ", temp->temp_col[i]);
}
} else {
printf("read temp colour failed");
return -1;
}
free(temp);
close(fd);
tcsetattr(fd, TCSANOW, &oldtio); //恢復原先的設置
return 0;
}
最新活動更多
- 1 AI狂歡遇上油價破百,全球股市還能漲多久? | 產聯看全球
- 2 OpenAI深夜王炸!ChatGPT Images 2.0實測:中文穩、細節炸,設計師慌了
- 3 6000億美元估值錨定:字節跳動的“去單一化”突圍與估值重構
- 4 Tesla AI5芯片最新進展總結
- 5 連夜測了一波DeepSeek-V4,我發現它可能只剩“審美”這個短板了
- 6 熱點丨AI“瑜亮之爭”:既生OpenClaw,何生Hermes?
- 7 AI界的殺豬盤:9秒刪庫跑路,全員被封號,還繼續扣錢!
- 8 2026,人形機器人只贏了面子
- 9 DeepSeek降價90%:價格屠夫不是身份,是戰略
- 10 AI Infra產業鏈卡在哪里了?



分享













