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());
}
相关推荐
我先去打把游戏先1 小时前
TCP、TLS、HTTP、HTTPS、MQTT、MQTTS几种网络协议的对比与解释
嵌入式硬件·mcu·物联网·网络协议·tcp/ip·http·aws
IT方大同1 小时前
实时时钟RTC
嵌入式硬件·实时音视频
点灯小铭1 小时前
基于单片机的社区医院小型高压蒸汽灭菌自动控制器设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
youcans_2 小时前
【动手学STM32G4】(3)STM32G431之定时器
stm32·单片机·嵌入式硬件·定时器
悠哉悠哉愿意2 小时前
【嵌入式学习笔记】AD/DA
笔记·单片机·嵌入式硬件·学习
松涛和鸣2 小时前
DAY47 FrameBuffer
c语言·数据库·单片机·sqlite·html
阿凉07022 小时前
新版本JLink安装目录中缺失JLinkDevices.xml添加方法
xml·嵌入式硬件
范纹杉想快点毕业2 小时前
《嵌入式通信与数据管理:从状态机到环形队列的完整实战指南》
单片机·嵌入式硬件
点灯小铭2 小时前
基于单片机的硫化氢、氨气、甲烷、一氧化碳气体多种有害气体检测与声光报警系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
小李做物联网3 小时前
【单片机毕设】c24基于单片机stm32蓝牙温室大棚物联网毕业设计
stm32·单片机·嵌入式硬件·物联网