【WCH蓝牙系列芯片】-基于CH582开发板—利用定时器加DMA方式模拟串口输出

------------------------------------------------------------------------------------------------------------------------------------

  在使用CH582芯片开发测试中,有个实际的用途是利用串口输出日志的方式,来进行程序的调试。CH582芯片一共提供了 4 组全双工的异步串口 UART0/1/2/3;但是如果在应用中四个串口都用完了,没有单独空的串口作为日志输出,此时,可以利用芯片的定时器加DMA方式模拟串口输出的方式解决这一问题。

  在CH582芯片上,只有TMR1和TMR2支持DMA功能,所以使用PA11所在的定时器2引脚作为模拟串口的输出脚。

  先初始化定时器和DMA,配置PA11为推挽输出模式,设置主频和波特率参数

static uint32_t timer_dma_buf[11];   //存储DMA(直接内存访问)传输的数据
static uint32_t bit_cnt;   //用于存储每个位的计数值

//接收主频(sys_frq)和波特率(baudrate)
void ch5xx_timer_uart_init(uint32_t sys_frq,uint32_t baudrate)  //初始化UART接口的函数
{
    bit_cnt = sys_frq/baudrate;   //计算每个位的计数值
    timer_dma_buf[0] = bit_cnt;   //将计算出的位计数值存储在DMA缓冲区的第一个元素中
    timer_dma_buf[9] = 0;   //将DMA缓冲区的第十个元素设置为0
    timer_dma_buf[10] = 0;   //将DMA缓冲区的第十一个元素设置为0
    timer_dma_buf[11] = 0;  //将DMA缓冲区的第十二个元素设置为0
    GPIOA_SetBits(GPIO_Pin_11);
    GPIOA_ModeCfg(GPIO_Pin_11, GPIO_ModeOut_PP_5mA);   //推挽输出
#if 0
//LED闪烁
    uint32_t i=100;
    while(i--)
    {
      GPIOB_InverseBits(GPIO_Pin_22);
      DelayMs(1);
    }
    GPIOB_ResetBits(GPIO_Pin_22);
    DelayMs(10);
    GPIOB_SetBits(GPIO_Pin_22);
#endif

    TMR2_PWMInit(Low_Level, PWM_Times_1);  //初始化定时器2为PWM(脉冲宽度调制)模式
    TMR2_PWMCycleCfg(bit_cnt);    //配置PWM周期为bit_cnt
    TMR2_Disable();  //禁用定时器2

    TMR2_PWMActDataWidth(0); // 占空比 50%, 修改占空比必须暂时关闭定时器
    TMR2_PWMEnable();  //启用PWM
    TMR2_Enable();   //启用定时器2
    TMR2_ITCfg(ENABLE, RB_TMR_IE_DMA_END);  //启用定时器2的DMA结束中断

    R32_TMR2_DMA_BEG = (uint32_t)&timer_dma_buf[0];   //设置DMA传输的起始地址为timer_dma_buf数组的开始
    R32_TMR2_DMA_END = (uint32_t)&timer_dma_buf[10];   //设置DMA传输的结束地址为timer_dma_buf数组的第十个元素
    ch5xx_timer_printf("ch5xx_timer uart init done\r\n");  //调用ch5xx_timer_printf函数打印初始化完成的消息
}

 

 

//用于通过定时器发送一系列字节数据
//uint8_t *data:指向要发送数据的指针
//uint32_t len:要发送的数据长度
__HIGH_CODE
void ch5xx_timer_send(uint8_t *data,uint32_t len)
{
    while(len)
    {
      ch5xx_timer_send_byte(*data);  //传入当前指向的数据字节
      data ++;
      len --;
    }
}



//通过定时器和DMA(直接内存访问)发送单个字节的数据。将一个字节的数据位逐个发送出去
__HIGH_CODE
void ch5xx_timer_send_byte(uint8_t byte)
{
  uint32_t shift_data = byte;  //用于存储要发送的字节数据
  uint32_t *p = &timer_dma_buf[1];  //定义一个指向timer_dma_buf数组第二个元素的指针p。timer_dma_buf是一个预先定义的数组,用于存储DMA传输的数据

  //一个字节有8位
  for(uint8_t j=0;j<8;j++)
  {
      if(shift_data & 0x01u)  //检查shift_data的最低位是否为1,通过与操作0x01u(即二进制的00000001)
      {
          *p = 0;  //如果最低位为1,则将指针p指向的数组元素设置为0,表示在UART信号中发送一个逻辑高电平
      }
      else
      {
          *p = bit_cnt; // 将指针p指向的数组元素设置为bit_cnt,用于存储定时器的计数值,表示在UART信号中发送一个逻辑低电平。
      }
      shift_data >>=1;  //将shift_data右移一位,这样下一次循环将检查下一个位
      p++;  //指针p向前移动
  }

  R32_TMR2_DMA_BEG = (uint32_t)&timer_dma_buf[0]; // 设置DMA传输的起始地址为timer_dma_buf数组的开始
  TMR2_ClearITFlag(RB_TMR_IE_DMA_END);  //清除定时器2的DMA结束中断标志
  R8_TMR2_CTRL_DMA = RB_TMR_DMA_ENABLE;  //启用定时器2的DMA

  //wait dma end   等待DMA传输结束
  while(!TMR2_GetITFlag(RB_TMR_IF_DMA_END));  //使用一个while循环来轮询DMA结束中断标志,直到它被设置,表示DMA传输已经完成
  R8_TMR2_CTRL_DMA = 0;   //禁用定时器2的DMA
}



//将格式化的字符串发送到一个串行接口
void ch5xx_timer_printf(char *fmt,...)
{
    char buffer[128];   //一个字符数组buffer,大小为128字节,用于存储格式化后的字符串
    unsigned char i=0;
    va_list arg_ptr;    // 定义了一个变量参数列表arg_ptr
    va_start(arg_ptr,fmt);  //初始化变量参数列表arg_ptr
    uint32_t len = vsnprintf(buffer,128,fmt,arg_ptr);  // 调用vsnprintf函数将格式化的字符串写入buffer
    va_end(arg_ptr);  //结束对变量参数列表arg_ptr的使用
    ch5xx_timer_send(buffer,len);  //调用ch5xx_timer_send函数发送buffer中的数据,发送的长度由len指定
}

 

在主函数中,只要调试初始化函数,传入对应的主频和波特率参数,这里设置主频时60M,波特率在115200,再调用ch5xx_timer_printf函数直接就可以输出日志。2M波特率测试也是可以。

通过串口助手设置好对应的波特率,可以看到正确打印的数据参数。

 

posted on 2024-10-10 11:03  凡仕  阅读(45)  评论(0编辑  收藏  举报