本帖最后由 lulugl 于 2024-3-1 21:27 编辑

【前言】
GD32H759拥有最多8个通用同步异步收发器(USART),其中USART0、USART5是挂载在APB2总线上,而其余6个是挂载在APB1总线之上。具体见下如:
image.png
根据开发板原理图,USART0通过PA9、PA10的端口接到了板载的CH340上面,可以用来打印参数等。其原理图如下:
image.png
因此,从USART0进行试用。
【工程以及具体配置】
1、在工程中,在bsp下面新建uart.h、uart.c,用于uart的业务。
由于要开启UART总线时钟,在lugl_gd32h7xx.h头文件中定时RCU_APB2EN的地址:
//APB2EN 串口挂载在  APB2 使能寄存器(RCU_APB2EN)偏移 0x44
  • #define LU_RCU_APB2EN  (*(volatile uint32_t *)(uint32_t)(0x58024444))
  • 复制代码
    2、在uart.c中新建串口初始化配置函数,首先要确一下,IO复用为哪一个线,经查数据手册表2-5,可以看到使用AF7,来选择USART0
    image.png
    3、在PIN9、PIN10,备用选择寄存器为GPIOx_AFSEL1,偏移地址为:0x24,因此在lugl_gd32h7xx.h中定义:
    #define LU_GPIOA_AFSEL1 (*(volatile uint32_t *)(uint32_t)0x58020024)
    配置为:
    //配置 GPIOA9、10复用模式。为输出高速上拉,
  •         LU_GPIOA_AFSEL1 &= ~((uint32_t)0b1111<<4);
  •         LU_GPIOA_AFSEL1 |= ((uint32_t)0b0111<<4);
  •         
  •         LU_GPIOA_AFSEL1 &= ~((uint32_t)0b1111<<8);
  •         LU_GPIOA_AFSEL1 |= ((uint32_t)0b0111<<8);
  • 复制代码

    //APB2EN 串口挂载在  APB2 使能寄存器(RCU_APB2EN)偏移 0x44
    #define LU_RCU_APB2EN  (*(volatile uint32_t *)(uint32_t)(0x58024444))
    //打开USART0总线时钟
  •         LU_RCU_APB2EN |= ((uint32_t)0x01 << 4);
  • 复制代码

    然后配置GPIO的AF复用,PA9 为推挽输出,上拉,60M速度
    LU_GPIOA_CTL &= ~((uint32_t)0b11<<(2U*9));
  •         LU_GPIOA_CTL |= ((uint32_t)0b10<<(2U*9)); //10
  •         LU_GPIOA_OMODE &= ~((uint32_t)0x01<<9);  //0输出模式  输出推挽模式(复位值)
  •         LU_GPIOA_OSPD  &= ~((uint32_t)0b11<<(2U*9)); //清零
  •         LU_GPIOA_OSPD  |= ((uint32_t)0b01<<(2U*9));  //01:输出最大速度60M
  •         LU_GPIOA_PUD   &= ~((uint32_t)0b11<<(2U*9)); //清零
  •         LU_GPIOA_PUD   |= ((uint32_t)0b01<<(2U*9));  //01:端口上拉模式
  • 复制代码
    PA10 为输入,上拉,60M速度
    LU_GPIOA_CTL &= ~((uint32_t)0b11<<(2U*10)); //
  •         LU_GPIOA_CTL |= ((uint32_t)0b10<<(2U*9));
  •         LU_GPIOA_OSPD  &= ~((uint32_t)0b11<<(2U*9)); //清零
  •         LU_GPIOA_OSPD  |= ((uint32_t)0b01<<(2U*9));  //01:最大速度60M
  •         LU_GPIOA_PUD   &= ~((uint32_t)0b11<<(2U*9)); //清零
  •         LU_GPIOA_PUD   |= ((uint32_t)0b01<<(2U*9));  //01:端口上拉模式
  • 复制代码
    4、接着配置USART0
    1)首先重置USART0,经学习官方示例,他就是先开启睡眠模式,然后关闭,代码如下:
    /* 关闭睡眠时钟使能  USART0SPEN 在睡眠模式下 USART0 时钟使能
  •                         由软件置位或复位
  •                         0:关闭在睡眠模式下 USART0 时钟
  •                 1:开启在睡眠模式下 USART0 时钟  
  •                         4bit
  •                         */
  •         LU_RCU_ADDAPB2SPEN |= ((uint32_t)0x01<<4U);
  •         LU_RCU_ADDAPB2SPEN &= ~((uint32_t)0x01<<4U);
  • 复制代码
    2)从数据手册与开发指南上的描述,如果配置USART_CTL0、USART_CTL1时,如果UEN位为1是不能被写入的,所以,首先要向USART_CTL0的UEN位写0,才能继续配置
         /* 关闭USART */
  •                         /*
  •                         USART使能
  •                                 0:USART预分频器和输出禁用
  •                                 1:USART预分频器和输出被使能        
  •                         */
  •     LU_USART0_CTL0 &= ~((uint32_t)0x01<<0);  
  • 复制代码
    3)首先定义数据长度,长度由USART_CTL0的WL1与WL0组合来实现,在数据手册上写明了如下图所示:
    image.png
    因此我位配置代码如下:
        LU_USART0_CTL0 &= ~(((uint32_t)0x01<<12) | ((uint32_t)0x01<<28));  //定义为00 即8数据位
  •     /* configure USART word length */
  •     LU_USART0_CTL0 |= (((uint32_t)0x00<<12) | ((uint32_t)0x00<<28));  //8bit为00
  • 复制代码
    4)接着配置停止位,该寄存器为USART_CTL1的第【13:12】位,描述如下:
    image.png
    我们配置为1位停止位时,给这两个BIT写0x00,代码如下:
      LU_USART0_CTL1 &=  ~((uint32_t)0b11<<12);   //请除【13:12】位 STB
  •                 LU_USART0_CTL1 |=  ((uint32_t)0b00<<12);    // STOP位长 00:1停止位
  • 复制代码
    5)波特率设置,由于波特率计算比较复杂,而且需要用到RCU获取APB2的频率等,我这里直接计算出波特率设轩为0x00000A2C,代码下:
      //设置波特率 这里定为115200 总线频率为300M,先不计算先,写死0x00000A2C
  •                 LU_USART0_BAUD = 0x00000A2C;
  • 复制代码
    6)接下来,设置接收、发送使能位,并打开EN位:
      LU_USART0_CTL0 &= ~((uint32_t)0x01<<2);   //先清除REN 位
  •                 LU_USART0_CTL0 |= ((uint32_t)0x01<<2);    //接收使能
  •                
  •                 LU_USART0_CTL0 &= ~((uint32_t)0x01<<3);   //先清除REN 位
  •                 LU_USART0_CTL0 |= ((uint32_t)0x01<<3);    //接收使能
  •                 LU_USART0_CTL0 |= 0x01;                   //1:USART预分频器和输出被使能
  • 复制代码
    到此就成功的配置好了USART0。工程中如果需要修改波特率,只需要修改波特率寄存器就行了。
    5、接着编写串口阻塞式的发送一个Byte的函数,非常简单,就是往USART0_TDATA的[9:0]位写入需要发送的数据就行了。
    void usart0_data_transmit(uint16_t data)
  • {
  •     LU_USART0_TDATA = ((uint32_t)0x3FF & (uint32_t)data);  //0-9位
  • }
  • 复制代码
    6、为了实现printf,需要编写重定身函数,函数中,我们需要查找USART0的状态寄存器USART_STAT的TC位,阻塞等这个位的出现。当然这个位,我们只需要读取,就对他实现清除标志位:
    /* retarget the C library printf function to the USART */
  • int fputc(int ch, FILE *f)
  • {
  •     usart0_data_transmit((uint8_t)ch);
  •     while(RESET == ((LU_USART0_STAT >>6) & 0x01));
  •     return ch;
  • }
  • 复制代码
    7、当然还要打开keil的选项:
    image.png
    【实现效果】
    当然配置好后,需要进行验证,在TobudOS的工程中,添加uart.c进来,并在任务中添回printf,其任务函数如下:
    void test_task(void *Parameter)
  • {
  •         while(1)
  •         {
  •                 TG_LED1;
  •                 printf("\r\ntask1\r\n\r\n");
  •                 tos_task_delay(2000);
  •         }
  • }

  • void led_task_entry(void *Parameter)
  • {

  •         while(1)
  •         {
  •                 TG_LED2;
  •                 printf("\r\ntask2\r\n\r\n");
  •                 tos_task_delay(500);
  •                
  •         }
  •         

  • }
  • 复制代码
    在串品助手中,可以看到如所设计的一样使用printf打印出来的字符串:
    image.png

    【总结】
    经过一天的学习,熟悉了USART0的几个普通寄存器,以及相关的配置与应用,并成功的编写好了应用,实现了printf。
    使用寄存器配置,相比库函数,代码要简洁,运行速度也要快!
      附:基于TobudOS的工程源码: GD32H759_tobudos_uart.zip (877.64 KB, 下载次数: 0)
    全部回复 0
    暂无评论,快来抢沙发吧