第十一章 串口
这部分代码,查看我的github网址:https://github.com/weiqi7777/...
1、210的UART控制器
210提供了4个UART,4个UART可以工作在中断或者DMA模式。串口能产生中断或者DMA请求数据传输信号。UART支持最大比特率3Mbps。每个UART包含了两个FIFO,用来接收和发送数据。
对于ch0,有256字节fifo,对于ch1,有64字节fifo,对于ch2和ch3,有16字节fifo。
UART包括一个波特率产生器,发送器,接收器和控制单元。
时钟源可以有两个选择,PCLK和SCLK_UART。
当数据写入到发送FIFO,硬件会自动的将数据拷贝到发送移位器中,然后通过发送管脚(TXDN)一位一位发送出去。
当从接收移位器从接收数据管脚(RXDN)中接收到数据后,会将该数据拷贝到接收FIFO中。
2、自动流控(AFC,auto flow control)
解决发送方和接收方之间数据传输不一致导致数据丢失的情况,因此加了几个信号,实现流控制。
对于发送方,要发送数据的时候,要将一根控制线拉高,接收方接收到该控制信号为高,表示要接收数据。
对于接收方,可以接收数据的时候,将一根控制线拉高,发送方接收到该控制信号为高,表示可以发送数据。
所以,流控的目的是使串口通信变得可靠,在发送方速率比接收方快的时候,流控可以保证发送和接收均不会漏掉数据。不过,现在已经不使用该功能了。
3、IrDA模式及其用法
1) IrDA其实就是红外,红外就是红外线通信(电视机,空调遥控器就是红外通信)。
2) 红外编码传输和串口传输很类似,按照一定时间,发送红外,有发送红外线,表示传输1,没有发送红外线,表示传输0。
3) 红外通信的原理:发送方固定间隔时间向接收方发送红外信号(表示1或0)或者不发送红外信号(表示0或1),接收方每隔固定时间去判断有无红外线信号来接收1和0。
4) 因此红外通信和串口通信很类似,都是每隔固定时间发送1或者0(判断1或0的物理方式不同)给接收方来通信。因此,210就利用串口通信来实现了红外发送和接收
5) 210的某个串口支持IrDA,开启红外模式后,我们只需要向串口写数据,这些数据就会以红外光的方式向外发射出去(当然是需要一些外部硬件支持),然后接收方接收这些红外数据即可解码得到我们的发送信息。
4、串口中断
串口中断,有三类
1) 接收中断,当接收到数据,并接收数据达到FIFO的触发数目,产生中断
2) 发送中断,方发送数据,FIFO中的数据小于或等于FIFO的触发数目,产生中断。
3) 错误中断,传输发生一些其他错误,产生中断
对于发送,有一个状态寄存器,其中有一位叫做发送缓冲器空标志,发送器发送完成((发送缓冲区空了)就会给这个标志位置位,CPU就是通过不断查询这个标志位为1还是0来知道发送是否已经完成。
对于接收,有一个状态寄存器,其中有一位叫做接收缓冲器满标志,CPU通过不断查询这个标志位判断是否接收到数据。但是这样,效率就很低了,因此,对于接收,要使用中断的模式。
5、波特率设置
串口,有一个很重要的东西,就是波特率的设置。波特率如果设置的不正确,那么串口通道是不能正常通信的。
波特率设置,有两个重要寄存器,UBRDIVn和UDIVSLOTn。其中UBRDIVn为主要波特率设置的寄存器,UDIVSLOTn为辅助的寄存器,目的是为了校准波特率。
计算公式如下:
关于UDIVSLOTn,推荐的值
我们写程序的时候,就使用手册上推荐的参数,设置波特率就行了。
6、串口初始化
6.1、将串口管脚设置为串口功能
设置GPA0CON寄存器,将GPIO设置为串口功能。
6.2、设置串口功能的关键寄存器
UCONx, ULCONx, UMCONx, UFCONx, UBRDIVx, UIDVSLOTOx。
对于UCONx(867页),设置串口的数据传输的格式。包括数据位,停止位,奇偶校验位,以及工作模式。
对于ULCONx,设置串口的发送和接收工作模式,以及时钟源选择和一些其他设置。
对于UFCONx,设置关于FIFO的功能。
对于UMCONx,设置关于流控和modem功能。
对于UBRDIVx, UIDVSLOTOx寄存器(878页),设置波特率。
6.3、读取串口发送接收状态
状态保存在UTRSTAT寄存器中。
发送的时候,有两个信号,一个是Transmitter empty ,一个是transmitter buffer empty。两个在没有使用FIFO的时候,效果是一样的,但是如果使用FIFO的话,就要使用transmitter buffer empty。
Transmitter empty是包括传输的FIFO和传输的移位寄存器,两个都为空,这一位的值才会为1。
Transmitter buffer empty只是指FIFO,当FIFO空的时候,该位才为1。所以当使用串口FIFO功能的时候,要检测这一位,判断发送是否完成。
当Receive buffer data ready为1,表示接收到数据。
6.4、发送数据
直接往寄存器UTXH写入值即可。但是前提是发送FIFO,没有满。因此在写入之前,需要判断FIFO是否满。
6.5、读取数据
直接读取寄存器URXH即可。但是前提是接收FIFO中有数据。因此在读取之前,需要判断FIFO是否为空。
7、移植stdio
printf函数和scanf函数可以和底层输入、输出函数绑定,然后这两个函数就可以和stdio绑定起来。也就是直接调用printf函数输出,内容就会从标准输出输出出去。
因此,这个时候标准输出就不是屏幕了,而是串口。标准输入也不是键盘,而是串口。
7.1、printf函数原理
printf函数工作时内部实际调用了2个关键函数:
一个是vsprintf函数(主要功能是格式化打印信息,最终得到纯字符串格式的打印信息等待输出)
另一个就是真正的输出函数(操控标准输出的硬件,将信息发送出去)
移植printf函数可以有3个途径获取printf的实现源码:
1) 最原始最原本的来源就是linux内核中的printk。难度较大而且麻烦。
2) 从uboot中移植
3) 直接使用别人移植好的
使用全局变量定义了两个缓冲区,一个是接收数据用,一个是发送数据用。
最终,printf函数调用putc函数,所以要实现putc,实现数据发送。scanf函数调用getc函数,所以要实现getc。
移植,修改顶层的makefile,增加一些变量,并将变量导出,让子文件夹的makefile使用这些变量。
然后再进入到子文件夹,运行makefile。
make –C lib,就是进入到lib文件夹,执行make。lib文件夹,放的就是实现printf,scanf函数的库函数代码。将库函数编译成,静态库,然后在顶层链接到我们写的程序当中去。
7.2、printf函数
printf
调用vsprintf
调用vsnprintf
调用number
vsprintf函数的作用是按照我们的printf传进去的格式化文本,对变参进行处理,然后将之格式化后缓存在一个实现分配好的缓冲区中。
printf后半段调用putc函数将缓冲区中格式化后的字符串直接输出到标准输出(也就是串口)。
系列其他篇
原文首发于骏的世界博客
作者:卢骏.
更多Arm技术相关的文章请关注Arm技术博客极术专栏,每日更新。