訂閱
糾錯
加入自媒體

技術(shù)文章:基于Linux的tty架構(gòu)及UART驅(qū)動詳解

三. 模塊詳細設(shè)計

3.1. 關(guān)鍵函數(shù)接口3.1.1. uart_register_driver功能:  uart_register_driver用于串口驅(qū)動uart_driver注冊到內(nèi)核(串口核心層)中,通常在模塊初始化函數(shù)調(diào)用該函數(shù)。
*參數(shù):drv:要注冊的uart_driver
*返回值:成功,返回0;否則返回錯誤碼

int uart_register_driver(struct uart_driver *drv)
3.1.2. uart_unregister_driver功能:uart_unregister 用于注銷我們已注冊的uart_driver,通常在模塊卸載函數(shù)調(diào)用該函數(shù),
*參數(shù) : drv:要注銷的uart_driver
*返回值:成功返回0,否則返回錯誤碼

void uart_unregister_driver(struct uart_driver *drv)
3.1.3. uart_add_one_port功能:uart_add_one_port用于為串口驅(qū)動添加一個串口端口,通常在探測到設(shè)備后(驅(qū)動的設(shè)備probe方法)調(diào)用該函數(shù)
*參數(shù):
*     drv:串口驅(qū)動
*     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用于刪除一個已經(jīng)添加到串口驅(qū)動中的串口端口,通常在驅(qū)動卸載時調(diào)用該函數(shù)
*參數(shù):
*     drv:串口驅(qū)動
*     port:要刪除的串口端口
*返回值:成功,返回0;否則返回錯誤碼

int uart_remove_one_port(struct uart_driver *drv,struct uart_port *port)
3.1.5. uart_write_wakeup功能:uart_write_wakeup喚醒上層因串口端口寫數(shù)據(jù)而堵塞的進程,通常在串口發(fā)送中斷處理函數(shù)中調(diào)用該函數(shù)
*參數(shù):
*     port: 需要喚醒寫堵塞進程的串口端口

void uart_write_wakeup(struct uart_port *port)
3.1.6. uart_suspend_port功能:uart_suspend_port用于掛起特定的串口端口
*參數(shù):
*     drv:要掛起的串口端口鎖所屬的串口驅(qū)動
*     port:要掛起的串口端口
*返回值:成功返回0;否則返回錯誤碼

int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)
3.1.7. uart_resume_port功能:uart_resume_port用于恢復某一已掛起的串口
*參數(shù):
*     drv:要恢復的串口端口所屬的串口驅(qū)動
*     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結(jié)構(gòu)體來獲取指定串口的波特率
*參數(shù):
*     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 用于計算某一波特率的串口時鐘分頻數(shù)(串口波特率除數(shù))
*參數(shù):
*     port:要計算分頻數(shù)的串口端口
*     baud:期望的波特率
*返回值:串口時鐘分頻數(shù)

unsigned int uart_get_divisor(struct uart_port *port, unsigned int baund)
3.1.10. uart_update_timeout功能:uart_update_timeout用于更新(設(shè)置)串口FIFO超出時間
*參數(shù):
*     port:要更新超時間的串口端口
*     cfalg:termios結(jié)構(gòu)體的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層插入一個字符
*參數(shù):
*     port:要寫信息的串口端口
*     status:RX buffer狀態(tài)
*     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用于向串口端口寫一控制臺信息
*參數(shù):
*     port:要寫信息的串口端口
*     s:要寫的信息
*     count:信息的大小
*     putchar:用于向串口端口寫字符的函數(shù),該函數(shù)有兩個參數(shù):串口端口和要寫的字符

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. 串口控制函數(shù)屬性說明tcgetatrr取屬性(termios結(jié)構(gòu))tcsetarr設(shè)置屬性(termios結(jié)構(gòu))cfgetispeed得到輸入速度cfsetispeed得到輸出速度cfstospeed設(shè)置輸出速度tcdrain等待所有輸出都被傳輸tcflow掛起傳輸或接收tcflush刷請未決輸出和/或輸入tcsendbreak送BREAK字符tcgetpgrp得到前臺進程組IDTcsetpgrp設(shè)置前臺進程組ID4.1.2. 串口配置流程(1) 保持原先串口配置,使用tegetatrr(fd, &oldtio);struct termious newtio, oldtio;
tegetattr(fd, &oldtio);
(2) 激活選項有CLOCAL和CREAD,用于本地連接和接收使用newtio.cflag |= CLOCAL|CREAD;
(3) 設(shè)置波特率newtio.c_cflag = B115200;
(4) 設(shè)置數(shù)據(jù)位,需使用掩碼設(shè)置newtio.c_cflag &= ~CSIZE;
Newtio.c_cflag |= CS8;
(5) 設(shè)置停止位,通過激活c_cflag中的CSTOP實現(xiàn)。若停止位為1,則清除CSTOPB,若停止位為2,則激活CSTOPnewtio.c_cflag &= ~CSTOPB; 停止位設(shè)置為1
Newtio.c_cflag |= CSTOPB; 停止位設(shè)置為2
(6) 設(shè)置流控newtio.c_cfag |= CRTSCTS 開啟硬件流控
newtio.c_cfag |= (IXON | IXOFF | IXANY); 開啟軟件流控
(7) 奇偶檢驗位設(shè)置,使用c_cflag和c_ifag.設(shè)置奇校驗newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);

設(shè)置偶校驗

newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag |= ~PARODD;
(8) 設(shè)置最少字符和等待時間,對于接收字符和等待時間沒有什么特別的要求,可設(shè)置為0:newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN]  = 0;
(9) 處理要寫入的引用對象tcflush函數(shù)刷清(拋棄)輸入緩沖(終端程序已經(jīng)接收到,但用戶程序尚未讀)或輸出緩沖(用戶程序已經(jīng)寫,但未發(fā)送)。int tcflash(int filedes, int quene)
quene數(shù)應當是下列三個常數(shù)之一:
 *TCIFLUSH 刷清輸入隊列
 *TCOFLUSH 刷清輸出隊列
 *TCIOFLUSH 刷清輸入、輸出隊列
例如:
tcflush(fd, TCIFLUSH);
(10) 激活配置,在完成配置后,需要激活配置使其生效。使用tcsetattr()函數(shù):int tcsetarr(int filedes, const struct termios *termptr);
opt 指定在什么時候新的終端屬性才起作用,
  *TCSANOW:更改立即發(fā)生
  *TCSADRAIN:發(fā)送了所有輸出后更改才發(fā)生。若更改輸出參數(shù)則應使用此選項
  *TCSAFLUSH:發(fā)送了所有輸出后更改才發(fā)生。更進一步,在更改發(fā)生時未讀的
                所有輸入數(shù)據(jù)都被刪除(刷清)
例如: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這個程序并不關(guān)心DCD信號線的狀態(tài),也就是不管串口是否有數(shù)據(jù)到來,都是非阻塞的,程序繼續(xù)執(zhí)行。
(2)恢復串口狀態(tài)為阻塞狀態(tài),用于等待串口數(shù)據(jù)的讀入,用fcntl函數(shù):fcntl(fd,F(xiàn)_SETFL,0);  //F_SETFL:設(shè)置文件flag為0,即默認,即阻塞狀態(tài)
(3)接著測試打開的文件描述符是否應用一個終端設(shè)備,以進一步確認串口是否正確打開。isatty(STDIN_FILENO);
(4)讀寫串口串口的讀寫與普通文件一樣,使用read,write函數(shù)
read(fd, buf ,8);
write(fd,buff,8);
4.1.4. Demo

以下給出一個測溫模塊收取數(shù)據(jù)的例子

#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設(shè)備文件------------------
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);

//-----------設(shè)置操作參數(shù)-----------------------
tcgetattr(fd, &oldtio);//獲取當前操作模式參數(shù)
memset(&newtio, 0, sizeof(newtio));
//波特率=230400 數(shù)據(jù)位=8 使能數(shù)據(jù)接收
newtio.c_cflag = B230400 | CS8 | CLOCAL | CREAD | CSTOPB;
newtio.c_iflag = IGNPAR;
tcflush(fd, TCIFLUSH);//清空輸入緩沖區(qū)和輸出緩沖區(qū)
tcsetattr(fd, TCSANOW, &newtio);//設(shè)置新的操作參數(shù)
//printf("input: %s, len = %d", cmd_buf, strlen(cmd_buf));
//------------向urat發(fā)送數(shù)據(jù)-------------------
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); //恢復原先的設(shè)置
return 0;


<上一頁  1  2  3  4  
聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權(quán)或其他問題,請聯(lián)系舉報。

發(fā)表評論

0條評論,0人參與

請輸入評論內(nèi)容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗證碼繼續(xù)

暫無評論

暫無評論

人工智能 獵頭職位 更多
掃碼關(guān)注公眾號
OFweek人工智能網(wǎng)
獲取更多精彩內(nèi)容
文章糾錯
x
*文字標題:
*糾錯內(nèi)容:
聯(lián)系郵箱:
*驗 證 碼:

粵公網(wǎng)安備 44030502002758號