linux uart 驱动,Linux开发板
本篇文章给大家谈谈linux uart 驱动,以及Linux开发板对应的知识点,文章可能有点长,但是希望大家可以阅读完,增长自己的知识,最重要的是希望对各位有所帮助,可以解决了您的问题,不要忘了收藏本站喔。
基于Linux的tty架构及UART驱动详解
通用异步收发传输器(UART)是嵌入式设备中的关键组件,它在串行通信与并行通信之间转换数据。UART是一种异步串口通信协议,用于主机与辅助设备之间的双向通信。在嵌入式设备中,UART广泛用于与PC机通信,如监控调试器、外部设备等,还包括与汽车音频系统和外接AP之间的连接。UART的通信协议将传输数据的每个字符以位为单位进行顺序传输,其中包括停止位以供计算机校正时钟同步。
波特率是衡量数据传输速率的指标,它表示每秒传输的符号数。波特率与数据的阶数有关,阶数越高,数据传输速率越慢,但同步的容忍程度越大。UART接收和发送数据按照相同的波特率进行,波特率发生器产生的时钟频率为波特率的16倍,用于在接收时进行精确采样,确保数据传输的正确性。
UART的工作原理分为发送和接收过程。发送数据时,从空闲状态开始,拉低线路发送数据位,接着发送奇偶检验位和停止位。接收数据时,从空闲状态开始,检测数据下降沿,按约定的波特率接收数据位,并在接收奇偶检验位后比较其正确性。
UART的接收数据时序包括:检测数据下降沿,计数器开始计数;计数器为8时,采样值为开始位;计数器为24时,采样值为bit0数据;计数器为40时,采样值为bit1数据;以此类推,直至完成一帧数据的收发。
RS232与RS485是两种电气协议,用于定义数据传输的电气特性与物理特性。UART协议则定义了数据帧格式和波特率等。RS232使用3-15V的电压信号,而UART通常使用CPU的TTL电平(0-3.3V)。RS232允许单线全双工传输,而RS485需要使用两根线实现全双工。
流控制是解决数据传输中出现的丢失数据问题或处理速度不同情况的机制。硬件流控如RTS/CTS和DTR/DSR通过信号控制数据传输,而软件流控如XON/XOFF通过控制字符实现数据传输的暂停与恢复。
在Linux系统中,TTY驱动程序框架用于管理终端设备。TTY设备包括串口终端(/dev/ttyS*)、控制台终端(/dev/console)和虚拟终端(/dev/tty*)。TTY架构分为下层串口驱动和上层TTY层,实现数据的发送和接收。关键数据结构如struct uart_driver、struct console、struct uart_state和struct uart_port封装了串口驱动的逻辑和操作。数据收发流程包括打开设备、发送数据、接收数据、关闭设备和注销流程。对于RS485通信,需考虑电压和信号的转换,以及使用不同的模式实现全双工或半双工通信。
UART驱动的注册和操作涉及到uart_register_driver、uart_unregister_driver、uart_add_one_port、uart_remove_one_port等关键函数接口。串口编程时,需要使用相应的控制函数进行配置和操作,如设置偶校验。此外,提供了一个测温模块收取数据的示例程序。
如何在S3C2440上linux操作系统下将串口的波特率提高以致921600
在说说我做的这个事情,其实听起来很简单,就是把串口的波特率提上去,硬件环境呢,就是采用飞凌的TE2440-II(比较古老了,大家勿喷)操作系统是linux2.6.28,大家都知道,正常情况下,Linux下串口波特率最高到115200,因为我们特殊需要的原因,需要把波特率提高到至少460800,当然最理想的结果就是波特率达到921600,大的背景就是这个样子了。
然后先考究硬件,看看在硬件上到底能不能满足我们的要求,主控芯片S3C2440,在UART一章说在系统时钟下,波特率最高可达115200,然后注释中说如果Pclk达到60M,可以实现921600,我就按他说的,将主频提高,顺便将pclk提高到了60M,发现921600根本实现不了,230400波特率虽然能通,但是错误率很高,根本无法用,然后我又尝试着将Pclk提高到了70M,通过这种饮鸩止渴的方式,波特率可以提高到230400并且稳定传输,但是更高的波特率则无法实现,而Pclk不能无限提高,因为我们开发板还连接了触摸屏,在Pclk70M的情况下,触摸屏经常重启,说明这个方案不可行,所以就pass掉了,下面简单说一下我怎么更改的系统时钟Fclk,Hclk,Pclk。这三个时钟的关系以及计算方法我就不赘述了,我主要参考博客进行修改
1)首先找到bootloader中 INC文件夹下的Option.inc文件,打开以后,找到如下代码段,这段代码就是主频400M时对应的M,P和S值设置,需要更改主频的话更改其中相应的数值几个(后来我发现,其实这个地方不改也行,因为最终起作用的是第二步)
[ FCLK= 400000000
CLKDIV_VAL EQU5
;1:4:8
M_MDIV EQU
127;127
M_PDIV EQU
2;2
[ CPU_SEL= 32440001
M_SDIV EQU
1; 2440A
|
M_SDIV EQU
0; 2440X
]
]
2)找到u2440mon.c,然后在main()函数中找到如下代码,修改case2中的mpll_val=(92<<12)|(1<<4)|(1);这一行(为啥修改这一行?因为在这个switch代码有个j=2),其中三个数分别代表M,P,S。这才是决定主频的关键。
switch(j){
case 0:
//240
key= 14;
mpll_val=(112<<12)|(4<<4)|(1);
break;
case 1:
//320
key= 14;
mpll_val=(72<<12)|(1<<4)|(1);
break;
case 2:
//400
key= 14;
mpll_val=(92<<12)|(1<<4)|(1);
break;
case 3:
//420!!!
key= 14;
mpll_val=(97<<12)|(1<<4)|(1);
break;
default:
key= 14;
mpll_val=(92<<12)|(1<<4)|(1);
break;
}
3)然后再 2440lib.c文件中,找到 ChangeClockDivider()函数,这个函数是控制分频比的,代码如下,这两个一个控制h_div,一个控制p_div。其中case 18: hdivn=2; break;这一行控制H分频,具体怎么改可以参考手册。
switch(hdivn_val){
case 11: hdivn=0; break;
case 12: hdivn=1; break;
case 13:
case 16: hdivn=3; break;
case 14:
case 18: hdivn=2; break;
}
switch(pdivn_val){
case 11: pdivn=0; break;
case 12: pdivn=1; break;
}
只需以上三步,就可以更改系统主频以及分频比,得到自己想要的Fclk和Pclk。
然后再说说我把上一个方案否定了以后,再仔细阅读芯片手册,发现串口的时钟源可以有三种方式获得:pclk,fclk/n,exclk,而且手册上说采用外部时钟的话,可以做到更高的波特率,但是这需要更改硬件,从指定那个引脚引入一个时钟,然后还要更改驱动程序,所以放弃了,所以只剩下一个路可以走,就是采用fclk/n的方式作为串口的时钟源,因为fclk频率很高,所以时钟源提高了,就可以把波特率提上来。然后就开始看linux内核源代码,因为串口的驱动早就集成到了linux内核之中,然后我就跳进了一个大坑。
其实串口本身的驱动并不复杂,如果裸机开发的话我感觉不难(强调一下,这个串口的裸机开发我没有做过,请做过的人不要喷我),因为串口被封装到了linux系统中,并且是层层封装,最终被封装成了tty的形式,所以我就从tty的驱动看起,抽丝剥茧,从里面寻找蛛丝马迹,
首先发现了s3c2440.c这个文件,通过调试得知,初始化的时候调用了其中的s3c2440_serial_init()函数,刚开始以为在这个文件中就这个函数有用,其实后来才知道,这个文件中的s3c2440_serial_getsource()和s3c2440_serial_setsource()在驱动中多次被调用。
然后考虑到,在上位机设置波特率的时候,调用的是系统函数cfsetispeed(),后经调试得知,这个函数调用了Samsung.c这个文件中的s3c24xx_serial_set_termios()这个函数,所有与串口相关的配置都与这个函数有关,因此锁定了方向,只要从这个函数中找到与波特率以及时钟源相关的语句,更改成我想要的即可,而这个函数又调用了很多子函数,但真正与波特率及时钟源相关的函数就是如下几句
/*
* Ask the core to calculate the divisor for us.
*/
baud= uart_get_baud_rate(port, termios, old, 0, 115200*8);
if(baud== 38400&&(port->flags& UPF_SPD_MASK)== UPF_SPD_CUST)
quot= port->custom_divisor;
else
quot= s3c24xx_serial_getclk(port,&clksrc,&clk, baud);
/* check to see if we need to change clock source*/
if(ourport->clksrc!= clksrc|| ourport->baudclk!= clk){
s3c24xx_serial_setsource(port, clksrc);
if(ourport->baudclk!= NULL&&!IS_ERR(ourport->baudclk)){
clk_disable(ourport->baudclk);
ourport->baudclk= NULL;
}
clk_enable(clk);
ourport->clksrc= clksrc;
ourport->baudclk= clk;
}
其中,uart_get_baud_rate()函数用于计算出上位机程序到设置的波特率的值,经我调试得知,上位机波特率从2400到921600都可以被准确的计算出来;所以这个函数跳过,然后看最后那个if语句,这个语句的作用是产看目前的时钟源是否与设置的时钟源相同,如果不相同,则按照设置的时钟源进行更改,这里面还涉及linux下的关于管理时钟的一个结构体clk结构体,参照博客以及我找到了linux下的mach-smdk2440.c这个文件,这个文件中定义了串口所用的clk结构体,这也是linux系统启动时对串口的初始化配置结构体都在这,但是我更改过这个地方,让他初始化配置是首选fclk作为串口的时钟源,但是我发现这并没有效果,所以继续寻找中。
这样就剩下一个函数可以考虑了,s3c24xx_serial_getclk(),进入这个函数你会发现,这个函数是对串口时钟及波特率一个全面的配置,进入这个函数中,就有个结构体tmp_clksrc,这个结构体很关键,他的内容如下:
static struct s3c24xx_uart_clksrc tmp_clksrc={
.name="pclk",
.min_baud
= 0,
.max_baud
= 0,
.divisor
= 1,
};
从这个名字中就可以看出,它把串口的时钟源内定成为了pclk,这也是罪魁祸首,但是当我把name更改为fclk时,整个系统就无法启动了,包括前面说的更改mach-smdk2440.c中初始化配置,也是无法启动,后来在配置串口是做了一个判断,当波特率低于200000时,才有系统源配置不变,当波特率高于200000时,不在采用tmp_clksrc这个结构体,而是采用我自己定义的一个结构体,当然就是把name改成fclk,发现虽然只是能够更改里面部分参数的时钟源,而正在的时钟源还是pclk,说明我的更改根本么有生效,由于这个linux调用太庞杂了,我就抱着试试看的态度,也是没有办法的办法,在配置完串口时钟的代码之后,添加了如下几行代码,直接更改S3C2440的寄存器,我知道这样做是很不“道德”的,而且很容易引起系统混乱,但是我只是这么试试,没想到还真的有用。
在 samsung.c文件中添加
if(baud>= 200000)
{
printk("baud>= 200000@-------------samsung.c\n");
__raw_writel(0x1fc5,S3C24XX_VA_UART0+ S3C2410_UCON);
__raw_writel(0x0fc5,S3C24XX_VA_UART1+ S3C2410_UCON);
__raw_writel(0x8fc5,S3C24XX_VA_UART2+ S3C2410_UCON);
__raw_writel(32,S3C24XX_VA_UART0+ S3C2410_UCON+0x24);//保证控制台的波特率还是115200用于显示
__raw_writel(3,S3C24XX_VA_UART1+ S3C2410_UCON+0x24);//921600
//__raw_writel(3,S3C24XX_VA_UART1+ S3C2410_UCON+0x24);
}
上面这段代码经我多次试验得到的,因为一开始用的系统主时钟fclk为400M,这样算出来UBRDIV1分频应该为3,但是这样的话错误率比较高,还是导致无法传输,至此我终于明白手册上为什么说pclk在60M可以实现921600了,因为用60M时钟计算的话,分频UBRDIV1为3.069,最接近整数3,所以在这个错误率下可以实现921600的波特率传输,所以我将系统时钟fclk设置为420M,其中MDIV=97,PDIV=1,SDIV=1,而ucon0=0x1fc5,ucon1=0x0fc5,ucon2=0x8fc5,这样n=1+6=7,所以串口的时钟源为fclk/n=60M,可以得到精确的921600波特率,所以实现我刚开始的目标,其实要实现其他的波特率也可以,比如460800,计算后主时钟fclk(尽量算出的分频UBRDIV1最贴近整数),然后就可以实现了。
在这还有个小想法,提高串口波特率,还可以使用USB转串口,因为USB转串口可以实现921600,而linux中以及集成了USB转串口的驱动,只需要在调用串口的那个open函数中改为调用USB转串口的节点即可,当然,这个方案我没有试,因为我们就一个USB口,而且还被占用了,所以希望有需要的朋友可以试一下。
linuxusb串口驱动linuxusb串口
如何在linux下在应用层得到USB设备名称信息?
先获取一下/dev的列表,然后得到插播事件后,在获取一下当前的/dev列表,然后做一下比较linux系统会自动识别USB接口为串口磁盘sda(通常为sda1,可通过fdisk-l命令查询),挂载就可用。例:新建挂载目录mkdir/mnt/usb,挂载mount/dev/sda1/mnt/usb,用完卸载挂起点unmount/dev/sda1/mnt/usb。
如何查看linux下串口是否可用?串口名称等?
1、查看串口是否可用,可以对串口发送数据比如对com1口,echolyjie126>/dev/ttyS0
2、查看串口名称使用ls-l/dev/ttyS*一般情况下串口的名称全部在dev下面,如果你没有外插串口卡的话默认是dev下的ttyS*,一般ttyS0对应com1,ttyS1对应com2,当然也不一定是必然的;
3、查看串口驱动:cat/proc/tty/drivers/serial
4、查看串口设备:dmesg|grepttyS*
LinuxRS485串口编程?
对于编程来说,没什么区别,通过控制485的使能端该程序完全可以使用。唯一的区别就是你在发送的时候通过程序把485的控制脚拉高,接收的时候把他拉低就可以了。至于电气方面的区别:RS232是全双工,可以同时收发,RS485是半双工,不能同时收发,还有电平信号不一样,这个编程你就不要理了。
上位机labview怎么通过串口接收下位机发来的数据并进行处理?
方法一找个仪器(示波器,daq)读电平时序自己解析,方法二找个i2c的转换芯片转串口或usb或者芯片提供dll,方法三搞个单片机自己写下位机解析时序再用串口传到电脑,方法四找一台带i2c模块的主机然后调用win_api(Linux就不知道了)r
linux查找串口?
1、设备入口可以查/dev/ttyS*、/dev/*uart*(主设备号4或者204),第一串口一般为ttyS0、*uart0等 USB转串口设备一般为/dev/ttyUSB*(主设备号188),第一口一般为ttyUSB0 2、以上/dev下只是串口的入口,具体设备存在与否需要按关键字(ttyS、ttyUSB、uart)查询/proc/devices以确定。 3、串口为通讯端口,有多个串口设备时,要确定正在被连接的串口是哪个,需要检测一下,如: cat/dev/ttyS0