1. 硬件 UART 模块的设计,硬件中断设计
UART(Universal Asynchronous Receiver/Transmitter,UART)作为一种串行、异 步、全双工的通信协议,将所需传输的数据一位接一位地传输。 UART 传输采用 LSB 的方式,也就是先发送一个字节的低位,再发送高位。其特点是通信线路简单, 只要一对传输线就可以实现双向通信,大大降低了成本。
异步通信以一个码流为传输单位,通信中两个码流间的时间间隔是不固定的,然而在 同一个码流中的两个相邻位间的时间间隔是固定的。通俗说是两个 UART 设备之间 通信的时候不需要时钟线,这时候就需要在两个UART设备上指定相同的传输速率, 以及空闲位、起始位、校验位、结束位,也就是遵循相同的协议。
一般一个数据流由 10 位组成,包含 1 位起始位, 8 位有效数据位,无奇偶校验位, 1 位停止位。
空闲位: UART 串口信号线上空闲时为高电平“1”。 起始位:信号线上空闲时为“1”,当检测到“0”即下降沿时,认为数据传输开始。 数据位:传输开始后传递的需要接收和发送的数据值,常用 8 位。 UART 协议支持 5 、 6 、 7 或者 8 位数据位。先发送低位,后发送高位。
奇偶校验位:校验数据位中“1”的个数是奇数个(奇校验)或偶数个(偶校验)。如果使用 奇偶校验功能,那么数据位的最后一位就是奇偶检验位。奇偶校验位一般是数据位的 最后一位,所以如果原来的数据位是 8 位,启用奇偶校验功能后数据位就是 7 位 了。
停止位:数据传输结束,信号线恢复“1”状态,常用 1 位。 UART 协议支持 1 、 1.5 或者 2 位停止位。
波特率:一秒内传输的码元数量。常见波特率为 9600 , 115200 。 例如 9600 表示一秒内传输了 9600 个码元信息。两相调制下一个码元负载一个bit的 信息。所以我们使用的波特率=比特率,单位bps(bit per second)。 需要注意每发送一个 10 位的数据包,都只有 8 位有效数据位,那么对于9600bps,每秒最多只能传输 9600 / 10 = 960 个字节的有效数据。

设计一个 UART 模块,要求可以发送字符,接收字符,并设计串口接收中断。 我们可以将 UART 模块作为一个 APB 外设,挂在 APB 总线上。那么就需要设计一 个 APB 接口来对寄存器进行读写操作。我们需要对每个 APB 接口分配一个地址,这 样才能通过译码电路区分开来不同的 APB 。在 config.h 文件中定了 9 路 APB 地 址,默认 GPIO 模块使用了 APB0 ,PWM 模块使用了 APB1 , HPET 模块使用了 APB2 , CONFREG 模块使用了 APB3 ,现在我们为 UART 模块分配 APB4 ,对应 地址为 bfe88000 ,如下所示,

不同于前面的功能模块设计,大多只要一个模块就可以完成。 UART 模块的复杂度 相对较高。

上图是 UART 外设的结构框图,简单介绍一下框图中的 4 个子模块,
UART 发送器: 按照字节读取发送 FIFO 中的数据; 将单字节数据转换为连续位; 向 tx 引脚发送一位一位发送数据,以波特生成器提供的固定频率计时;
UART 接收器: 使用波特生成器生成的时钟从 rx 引脚按位接收串行的数据; 将一位一位的数据重新组合成为单个字节; 将接受到的数据以字节为单位写入接受 FIFO;
UART FIFO: 临时缓冲要发送的数据或者已被接收的数据。 波特率控制器: 对系统时钟分频,产生对应 UART 波特率的时钟。
该模块包含下面这些文件,

串口发送缓存模块


串口发送模块

、
串口接收缓存模块


串口接收缓存模块和串口发送缓存模块都是缓存模块,所以设计思路基本一致, 仅部分信号的寄存器控制有差异。
串口接收模块


波特率控制模块.

中断 为了更好得处理串口接收到的数据,这里采用中断的方式。当串口接收缓存模块 的信号 rx_empty 不为 0 时表示触发了接收中断,如下所示,

uart_irq 是 UART 模块输出的,表示UART 模块的串口接收中断是否触发。 接下来需要把 int_o 给到软核,这里先不详细阐述过程,实现过程体现在代码 中,在中断学习的章节会重点介绍。
在顶层文件 godson_mcu_top.v 中例化我们设计的 UART 模块,如下所示,

在约束文件中,将例化好的 UART 的输出引脚与原理图上的合适引脚进行连接即 可,如下所示,

2. 软件设计,基于 UART 模块编写串口发送函数、串口接收中断服务函数
既然已经设计好了硬件电路,我们就可以进行软件程序的编写了。在硬件 UART 模 块设计过程中我们为 APB 分配的地址是 0xbfe88000 ,并且 UART 相关寄存器的偏 移地址是 0x00 0x04 ,所以软件上需要对应好。因为寄存器数量较少,所以这里没有 采用结构体的方式进行访问,直接进行宏定义,如下所示,

UART_FIFO 寄存器是一个位宽 32位的寄存器,但是根据硬件设计,实际使用的只有低 8 位。 UART_FIFO_CTRL寄存器是一个位宽 32位的寄存器,但是根据硬件设计,实际使用 的只有低 3 位。具体功能如下所示,

配置为奇校验模式,如下所示,

配置后需要延时至少 2000ms ,才能进行串口发送。 因为发送缓存模块的深度为 16byte ,所以一次性不能写入超过 16 个字符的数据, 否则发送出去的数据会出现乱码。 这里使用 printk 函数进行串口打印,这个函数类似 C 语言中的 printf 函数。 每次打印后都需要延时至少 25ms 。 串口发送函数是 soc_printf 基于 SDK 自带的 printf 修改而来的,实际上就是将最底 层的发送函数改为向 UART_FIFO_CTRL 写入数据,如下所示,

在 main.c 中编写代码验证串口发送,如下所示,


编写串口 1 接收中断的处理函数,如下所示,


3265

被折叠的 条评论
为什么被折叠?



