1、操控UART寄存器实现输出功能

在这一章里,重点需要了解如何通过寄存器操控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位。

cpp 复制代码
uint32_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;

对于数据位、停止位、奇偶校验位的操作如下:

cpp 复制代码
    uint32_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寄存器里写入字符:

cpp 复制代码
void 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)来判断是否有新字符已被接收。只有在接收到字符后,才能继续进行后续的处理和发送。

cpp 复制代码
uint32_t Uart_CheckLSR()
{

    return *(UART_REG(LSR)) & 1;
}    
while (1)
{
    while (Uart_CheckLSR() == 0);
    Uart_putc(Uart_getc());
}
相关推荐
代码游侠5 小时前
ARM开发——阶段问题综述(二)
运维·arm开发·笔记·单片机·嵌入式硬件·学习
DLGXY6 小时前
STM32——旋转编码器计次(七)
stm32·单片机·嵌入式硬件
羽获飞6 小时前
从零开始学嵌入式之STM32——3.使用寄存器点亮一盏LED灯
单片机·嵌入式硬件
浩子智控7 小时前
商业航天计算机抗辐射设计
单片机·嵌入式硬件
独处东汉10 小时前
freertos开发空气检测仪之输入子系统结构体设计
数据结构·人工智能·stm32·单片机·嵌入式硬件·算法
czy878747511 小时前
机智云 MCU OTA可以对MCU程序进行无线远程升级。
单片机·嵌入式硬件
A9better13 小时前
嵌入式开发学习日志52——二值与计数信号量
单片机·嵌入式硬件·学习
日更嵌入式的打工仔14 小时前
(实用向)中断服务程序(ISR)的优化方向
笔记·单片机
想放学的刺客15 小时前
单片机嵌入式试题(第25)嵌入式系统可靠性设计与外设驱动异常处理
stm32·单片机·嵌入式硬件·mcu·物联网
wotaifuzao15 小时前
STM32+FreeRTOS 长期可维护架构设计(事件驱动篇)-- 告别“屎山”代码
c语言·stm32·嵌入式硬件·freertos·状态机·事件驱动·嵌入式架构