本帖最后由 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的地址:
  1. //APB2EN 串口挂载在  APB2 使能寄存器(RCU_APB2EN)偏移 0x44
  2. #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)
配置为:
  1. //配置 GPIOA9、10复用模式。为输出高速上拉,
  2.         LU_GPIOA_AFSEL1 &= ~((uint32_t)0b1111<<4);
  3.         LU_GPIOA_AFSEL1 |= ((uint32_t)0b0111<<4);
  4.         
  5.         LU_GPIOA_AFSEL1 &= ~((uint32_t)0b1111<<8);
  6.         LU_GPIOA_AFSEL1 |= ((uint32_t)0b0111<<8);

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

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

  10. void led_task_entry(void *Parameter)
  11. {

  12.         while(1)
  13.         {
  14.                 TG_LED2;
  15.                 printf("\r\ntask2\r\n\r\n");
  16.                 tos_task_delay(500);
  17.                
  18.         }
  19.         

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

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