在这一章里,重点需要了解如何通过寄存器操控GPIO、UART,使得MCU通过UART总线输出字符,实现打印功能。
一、GPIO相关寄存器
如果开发板上引脚资源够用的话,并不需要额外配置GPIO的复用功能。但如果想要复用GPIO为某一路UART的功能引脚,就需要了解如何操作GPIO的相关寄存器了。
GPIO 寄存器通常属于外设寄存器的一部分,其在芯片的数据手册中会详细列出对应的内存映射地址 。比如,GPIO 寄存器组的基地址可能为
0x10012000
。在实际操作中,通过该基地址及各个寄存器的偏移量,可以访问和控制对应的硬件功能。这些偏移量通常以特定的单位(如字节或字)表示,允许程序通过对特定寄存器地址的读写操作来控制 GPIO 引脚的状态、模式、配置等功能。
cpp#define GPIOA_BASE 0x10012000
在查询到 GPIO 外设的基地址后,还需要了解各个寄存器的具体偏移地址。例如,用于控制 GPIO 引脚输入输出方向的寄存器通常位于偏移地址
0x00
处,用于读取 GPIO 引脚输入电平的寄存器通常位于偏移地址0x04
处,而配置引脚复用功能的寄存器可能位于偏移地址0x1C
处。通过了解这些偏移地址,可以定义适当的宏来访问和修改相应寄存器的值。通过基地址与偏移地址的组合,能够精确地对对应寄存器进行读写操作,以实现 GPIO 引脚的配置与控制。
cpp#define GPIOA_REG(reg) ((volatile uint32_t *)(GPIOA_BASE + reg))
其中volatile关键字主要用于告知编译器某个变量的值可能会被外部因素改变,因此不应对该变量进行优化。确保每次访问时都是直接从内存中读取变量的值。
因此在配置复用功能时,只需要对应的修改0x1C处 管理GPIO复用功能的寄存器即可。
cpp*(GPIOA_REG(MUXCFG)) &= (~((uint32_t)3<<gpio_mux_cfg_shift[gpio_pin])); *(GPIOA_REG(MUXCFG)) |= (af_fun[gpio_pin]<<gpio_mux_cfg_shift[gpio_pin]);
二、UART相关寄存器
一般来说,MCU 通常会配备多个 UART 外设,每个 UART 外设都拥有独立的寄存器组。根据实际需求,你需要查阅芯片文档,确定对应 UART 外设的寄存器基地址,以便进行相应的配置和操作。
cpp#define UART1_BASE 0x10023000
UART有几个必须要配置的参数,这是收发端能够正常通信的关键。包括波特率(每秒发送bit数)、数据位、停止位、奇偶校验位。这4个参数需要收发端一致,否则无法正常收发数据。在配置波特率时,涉及到三个寄存器,分别是DLL(偏移地址:0x00)、DLM(偏移地址:0x04)、LCR(偏移地址:0x0C)。
根据数据手册得,我们需要先将LCR寄存器第7位置1,从而允许对DLL和DLM寄存器的操控。 随后根据波特率计算公式得到DLL、DLM寄存器的值。其中DLL存储除数低8位,DLM存储除数高8位。
cppuint32_t mcu_clk = 2000000; uint32_t divisor = mcu_clk / baud - 1; *UART_REG(LCR)= 0x80;//使能操控DLL寄存器 *UART_REG(DLM) = (divisor >> 8 )& 0xFF; *UART_REG(DLL) = (divisor) & 0xFF;
对于数据位、停止位、奇偶校验位的操作如下:
cppuint32_t stopbit_val = (stopbit == 1) ? 0x0 : 0x1; uint32_t parity_val = (parity_en==1) ? 1 : 0; *UART_REG(LCR) = 0X03 | (stopbit_val << 2); *UART_REG(LCR) |= (parity_val << 3);
三、收发字符
UART收发字符也需要操控相关寄存器,即RBR(偏移地址:0x00)、THR(偏移地址:0x00)、LSR(偏移地址:0x14)。可以看到RBR、THR、和上述DLL寄存器共用一个偏移地址。MCU通过LCR寄存器的第7位判断具体操纵的哪个寄存器。为1则操纵DLL配置波特率。为0则操纵RBR、THR控制数据收发。
最简单的发送方式如下,即 往RBR寄存器里写入字符:
cppvoid Uart_putc(char c) { *(UART_REG(THR)) = (uint8_t)c; return; } void Uart_puts(char *s) { while (*s) { Uart_putc(*s++); } }
最简单的回显输入字符的过程如下:首先从接收缓冲区读取输入字符,然后将该字符发送到发送缓冲区。在此过程中,可以通过读取 LSR(Line Status Register) 寄存器的 第 0 位(Receiver Data Ready, RDR)来判断是否有新字符已被接收。只有在接收到字符后,才能继续进行后续的处理和发送。
cppuint32_t Uart_CheckLSR() { return *(UART_REG(LSR)) & 1; } while (1) { while (Uart_CheckLSR() == 0); Uart_putc(Uart_getc()); }