技术文章:基于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;
}
最新活动更多
-
11月28日立即报名>>> 2024工程师系列—工业电子技术在线会议
-
12月19日立即报名>> 【线下会议】OFweek 2024(第九届)物联网产业大会
-
即日-12.26火热报名中>> OFweek2024中国智造CIO在线峰会
-
即日-2025.8.1立即下载>> 《2024智能制造产业高端化、智能化、绿色化发展蓝皮书》
-
精彩回顾立即查看>> 2024 智能家居出海论坛
-
精彩回顾立即查看>> 【在线会议】多物理场仿真助跑新能源汽车
推荐专题
发表评论
请输入评论内容...
请输入评论/评论长度6~500个字
暂无评论
暂无评论