本文隶属于AVR单片机教程系列。
在第一期中,我们已经开始使用UART来实现单片机开发板与计算机之间的通信,但只是简单地讲了讲一些概念和库函数的使用。在这一篇教程中,我们将从硬件与软件等各方面更深入地了解UART。
USART组件
一直在讲的UART其实是USART组件的一部分,USART比UART多了同步的一部分,但这一部分用得太少(我从来没用过),而且缺乏实例,所以就略过了。然而,单片机的设计者很机智地把这个鸡肋功能升华了一下,USART组件可以支持SPI模式。SPI是一种同步串行总线,可以支持很高的传输速率。这个功能使得ATmega324PA支持最多3个SPI通道,其中一个是纯SPI,另两个就是SPI模式下的USART。我们将在下一讲中揭开SPI的神秘面纱。
回到UART模式下的USART组件。开发板引出的RX
和TX
引脚是属于USART0组件的,因此使用时以下n
都用0
代替。
UART共有5个寄存器:
UDRn
是收发数据寄存器,收(RXB
)和发(TXB
)使用不同的寄存器,但都通过UDRn
来访问。向TXB
写入一个字节,UART就开始发送;RXB
保存接收到的数据,带有额外一个字节的缓冲(如同下一节要讲的缓冲区)。UCSRnA
包含UART状态位,如三个中断对应的标志,以及一些不常用的设置位。UCSRnB
主要用于使能,包括收发器与三个中断的使能位,以及9位帧格式相关的位。UCSRnC
是最主要的控制寄存器,可以配置USART的模式与格式。UBRRnL
和UBRRnH
(可以通过UBRRn
来访问这个16位寄存器)用于设定波特率,在异步模式下,\(BAUD = \frac {f_{CPU}} {16(UBRRn + 1)}\)。
UART支持三个中断,分别是接收完成(RX
)、数据寄存器空(UDRE
)、发送完成(TX
)。第一个用于接收,后两个用于发送,一般使用UDRE
。
RX
中断允许程序在任何时刻及时地接收并处理总线上发来的数据。沿用“串口接收”一讲中的例子:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <ee1/led.h>
int main(void)
{
led_init();
PORTD |= 1 << 0; // RXD0 pull-up
UCSR0B = 1 << RXCIE0 // RX interrupt
| 1 << RXEN0 // RX enabled
| 1 << TXEN0; // TX enabled
UCSR0C = 0b00 << UMSEL00 // asynchronous USART
| 0b10 << UPM00 // even parity
| 0 << USBS0 // 1 stop bit
| 0b11 << UCSZ00; // 8-bit
UBRR0L = 40; // 38400bps
sei();
while (1)
;
}
ISR(USART0_RX_vect)
{
static const char led_char[4] = {'r', 'y', 'g', 'b'};
static uint8_t which = 4;
uint8_t byte = UDR0;
bool matched = false;
for (uint8_t i = 0; i != 4; ++i)
if (byte == led_char[i])