[方案相关] HC32F460实现串口dma发送和中断接收

[复制链接]
12889|38
 楼主| l63t89 发表于 2023-10-27 12:12 | 显示全部楼层 |阅读模式
系统环境
系统:win10
开发板型号:官方评估板(EVB-HC32F460)
ide:keil5
sdk版本:hc32f460_ddl_Rev2.0.0
时间:2021.7.1
2. 遇到的问题
由于hc32f460的配置方式非常灵活,这也就导致hc32f460在程序实现上和传统的mcu有明显的不同。

本节就是通过官方sdk实现华大HC32F460的dma发送和中断接收,关于hc32f460的灵活配置的说明,请看我另一篇文章。

那篇文章还在修改中。。。。
3. 代码实现(以uart2为例)

3. 代码实现(以uart2为例)

若要实现hc32f460的串口中断接收和dma数据发送,必须实现以下函数。


 楼主| l63t89 发表于 2023-10-27 12:13 | 显示全部楼层
3.1 宏定义
  1. /* USART 类型 */
  2. #define USART_CH                        (M4_USART2)
  3. /* USART 波特率 */
  4. #define USART_BAUDRATE                  (115200ul)

  5. /* USART RX Port/Pin definition */
  6. //包括端口、引脚、和将被映射的功能
  7. #define USART_RX_PORT                   (PortA)
  8. #define USART_RX_PIN                    (Pin10)
  9. #define USART_RX_FUNC                   (Func_Usart2_Rx)

  10. /* USART TX Port/Pin definition */
  11. #define USART_TX_PORT                   (PortA)
  12. #define USART_TX_PIN                    (Pin09)
  13. #define USART_TX_FUNC                   (Func_Usart2_Tx)

  14. /* DMA相关参数 */
  15. #define DMA_UNIT                        (M4_DMA1)
  16. #define DMA_CH                          (DmaCh0)
  17. #define DMA_TRG_SEL                     (EVT_USART2_TI)
 楼主| l63t89 发表于 2023-10-27 12:13 | 显示全部楼层
3.2 串口初始化函数
  1. //串口初始化
  2. void uart2_init(void)
  3. {

  4.     stc_irq_regi_conf_t stcIrqRegiCfg; //用于中断配置的结构体
  5.    
  6.     en_result_t enRet = Ok;
  7.     uint32_t u32Fcg1Periph = PWC_FCG1_PERIPH_USART1 | PWC_FCG1_PERIPH_USART2 | \
  8.         PWC_FCG1_PERIPH_USART3 | PWC_FCG1_PERIPH_USART4;
  9.     const stc_usart_uart_init_t stcInitCfg = {
  10.         UsartIntClkCkNoOutput,
  11.         UsartClkDiv_1,
  12.         UsartDataBits8,
  13.         UsartDataLsbFirst,
  14.         UsartOneStopBit,
  15.         UsartParityNone,
  16.         UsartSampleBit8,
  17.         UsartStartBitFallEdge,
  18.         UsartRtsEnable,
  19.     };//串口参数结构体

  20.     /* 调用DMA初始化函数,初始化对应的dma通道 */
  21.     DmaInit();

  22.     /* 开启外设时钟 clock */
  23.     PWC_Fcg1PeriphClockCmd(u32Fcg1Periph, Enable);

  24.     /* USART对应的引脚初始化,但是引脚先失能,防止串口还没配置完成就接收导致错误 */
  25.     PORT_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_FUNC, Disable);
  26.     PORT_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_FUNC, Disable);

  27.     /* 初始化 USART */
  28.     enRet = USART_UART_Init(USART_CH, &stcInitCfg);
  29.     if (enRet != Ok)
  30.     {
  31.         while (1)
  32.         {
  33.         }
  34.     }
  35.     else
  36.     {
  37.     }

  38.     /* 设置串口波特率 */
  39.     enRet = USART_SetBaudrate(USART_CH, USART_BAUDRATE);
  40.     if (enRet != Ok)
  41.     {
  42.         while (1)
  43.         {
  44.         }
  45.     }
  46.     else
  47.     {
  48.     }

  49.     /* 设置串口读取方式为中断读取 */
  50.     stcIrqRegiCfg.enIntSrc = INT_USART2_RI; //中断类型为读中断
  51.     stcIrqRegiCfg.enIRQn = Int000_IRQn; //每个中断必须有一个对应的中断号
  52.     stcIrqRegiCfg.pfnCallback = &UsartRxIrqCallback;//中断发生时候的回调函数
  53.     enIrqRegistration(&stcIrqRegiCfg);
  54.    
  55.     NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);//中断优先级
  56.     NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);//先清一下这个中断的标志位(置零)
  57.     NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);//在使能这个中断

  58.     /* 当串口接收发生错误的时候产生中断 */
  59.     stcIrqRegiCfg.enIntSrc = INT_USART2_EI;
  60.     stcIrqRegiCfg.enIRQn = Int001_IRQn;
  61.     stcIrqRegiCfg.pfnCallback = &UsartErrIrqCallback;
  62.     enIrqRegistration(&stcIrqRegiCfg);
  63.    
  64.     NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
  65.     NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
  66.     NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
  67.    
  68.    
  69.         //最后使能串口的引脚,串口正式工作
  70.     USART_FuncCmd(USART_CH, UsartRx, Enable);
  71.     USART_FuncCmd(USART_CH, UsartRxInt, Enable);

  72. }
 楼主| l63t89 发表于 2023-10-27 12:14 | 显示全部楼层
说明:串口时钟使能,配置rx、tx引脚,配置串口参数,这个简单不用说。

但必须设置串口接收中断和接收错误中断,华大的中断很特别,中断类型和对应的中断号之间是可配置的,需要相互绑定。而stm32是直接写死的,那个中断号对应那个中断是固定的。这是一个区别。开启接收错误中断的原因是我发现当串口接收数据的时候受到干扰(做静电干扰实验),接收中断会停止工作,后来发现是进入接收错误中断了。

注意:对于同一个程序,每个中断号只能和一种中断类型进行便绑定,当把多个中断类型和同一个中断号(如:Int001_IRQn)绑定,那么只有一个中断会生效,其他中断不会生效。
 楼主| l63t89 发表于 2023-10-27 12:14 | 显示全部楼层
3.3 串口初始化的时候调用的DMA初始化函数
  1. void DmaInit(void)
  2. {
  3.     stc_dma_config_t stcDmaInit;
  4.     stc_irq_regi_conf_t stcIrqRegiCfg;

  5.     /* 使能dma外设时钟 */
  6.     PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_DMA1 | PWC_FCG0_PERIPH_DMA2,Enable);

  7.     /* 使能DMA外设 */
  8.     DMA_Cmd(DMA_UNIT,Enable);

  9.     /* 配置 DMA. 当传输的数量为一个块的时候,配置方式和stm32类似。但是多个快的时候,一定要算好*/
  10.     MEM_ZERO_STRUCT(stcDmaInit);//格式化dma配置结构体内容
  11.     stcDmaInit.u16BlockSize = 1u; /* 1 block */
  12.     stcDmaInit.u16TransferCnt = (uint16_t)UART3_FIFO_SIZE;/* 传输数据的多少*/
  13.     stcDmaInit.u32SrcAddr = (uint32_t)(&DMA3_DR_BASE[0]);//源地址
  14.     stcDmaInit.u32DesAddr = (uint32_t)(&USART_CH->DR);     /* 目的地址. */
  15.     stcDmaInit.stcDmaChCfg.enSrcInc = AddressIncrease;  /* 源地址递增. */
  16.     stcDmaInit.stcDmaChCfg.enDesInc = AddressFix;  /* 目标地址不变 */
  17.     stcDmaInit.stcDmaChCfg.enIntEn = Enable;       /* Enable interrupt. */
  18.     stcDmaInit.stcDmaChCfg.enTrnWidth = Dma8Bit;   /* 数据宽度为 8bit. */
  19.     DMA_InitChannel(DMA_UNIT, DMA_CH, &stcDmaInit);

  20.     /* Enable the specified DMA channel. */
  21.     DMA_ChannelCmd(DMA_UNIT, DMA_CH, Enable);

  22.     /* Clear DMA flag. */
  23.     DMA_ClearIrqFlag(DMA_UNIT, DMA_CH, TrnCpltIrq);

  24.     /* Enable peripheral circuit trigger function. */
  25.     PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS,Enable);

  26.     /* Set DMA trigger source. */
  27.     DMA_SetTriggerSrc(DMA_UNIT, DMA_CH, DMA_TRG_SEL);

  28.     /* 设置dma发送完成中断 */
  29.     stcIrqRegiCfg.enIntSrc = INT_DMA1_TC0;//dma1的通道0的完成中断
  30.     stcIrqRegiCfg.enIRQn = Int002_IRQn;
  31.     stcIrqRegiCfg.pfnCallback = &DmaBtcIrqCallback;
  32.     enIrqRegistration(&stcIrqRegiCfg);
  33.    
  34.     NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
  35.     NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
  36.     NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
  37.        
  38. }
 楼主| l63t89 发表于 2023-10-27 12:14 | 显示全部楼层
说明:我通过使用发现,dma发送需要开启dma发送完成中断,在中断里面要关闭串口dma发送使能,否则的话串口将无法出发下一次dma发送。
 楼主| l63t89 发表于 2023-10-27 12:14 | 显示全部楼层
3.4 串口接收中断回调函数的实现

  1. //串口接收回调函数
  2. void UsartRxIrqCallback(void)
  3. {
  4.     uint16_t u16Data = USART_RecData(USART_CH);//取出数据
  5.         //其他用户自己的程序
  6.     //下面是例子
  7.     uart3_recv_fifo[uart3_recv_fifo_in++]=((uint8_t)u16Data)&0xff;
  8.     if(uart3_recv_fifo_in>=UART3_FIFO_SIZE)
  9.         uart3_recv_fifo_in=0;       
  10. }
 楼主| l63t89 发表于 2023-10-27 12:15 | 显示全部楼层
3.4 串口DMA发送函数
  1. //华大dma发送
  2. void Uart3_SendArray(u8 *pData,u16 Leng)
  3. {
  4.         DMA_ChannelCmd(DMA_UNIT, DMA_CH, Enable); //使能dma传输
  5.         DMA_SetSrcAddress(DMA_UNIT, DMA_CH, (uint32_t)pData); //从新设置源地址
  6.         DMA_SetTransferCnt(DMA_UNIT, DMA_CH, (uint16_t)Leng);//从新设置发送的数据量                                       
  7.         USART_FuncCmd(USART_CH, UsartTxAndTxEmptyInt, Enable);//开启dma发送
  8. }

 楼主| l63t89 发表于 2023-10-27 12:15 | 显示全部楼层
3.5 DMA发送完成回调函数
  1. //HC的DMA传输完回调函数
  2. void DmaBtcIrqCallback(void)
  3. {               
  4.     DMA_ClearIrqFlag(DMA_UNIT, DMA_CH, TrnCpltIrq);

  5.     while(Reset == USART_GetStatus(USART_CH, UsartTxComplete));//解决dma发送少字节问题

  6.     USART_FuncCmd(USART_CH, UsartTxAndTxEmptyInt, Disable);//发送完,关闭串口dma发送使能
  7.        
  8. }


 楼主| l63t89 发表于 2023-10-27 12:15 | 显示全部楼层
说明:这里必须有这个while判断,否则会导致发送的时候,数据最后的那个字节丢失,猜测原因是dma发送数据减到零认为没数据了,然后触发了dma发送完成中断,但是这时候最后一个字节刚被抛到串口发送缓冲区,还没发完,所以这个时候要等串口发完,参能关闭串口dma发送使能。
 楼主| l63t89 发表于 2023-10-27 12:15 | 显示全部楼层
3.6 当串口接收数据发生错误触发的回调函数

  1. //接收错误回调函数
  2. void UsartErrIrqCallback(void)
  3. {
  4.     if (Set == USART_GetStatus(USART_CH, UsartFrameErr))
  5.     {
  6.         USART_ClearStatus(USART_CH, UsartFrameErr);
  7.     }
  8.     else
  9.     {
  10.     }

  11.     if (Set == USART_GetStatus(USART_CH, UsartParityErr))
  12.     {
  13.         USART_ClearStatus(USART_CH, UsartParityErr);
  14.     }
  15.     else
  16.     {
  17.     }

  18.     if (Set == USART_GetStatus(USART_CH, UsartOverrunErr))
  19.     {
  20.         USART_ClearStatus(USART_CH, UsartOverrunErr);
  21.     }
  22.     else
  23.     {
  24.     }
  25. }
 楼主| l63t89 发表于 2023-10-27 12:16 | 显示全部楼层
3.6 当串口接收数据发生错误触发的回调函数
  1. //接收错误回调函数
  2. void UsartErrIrqCallback(void)
  3. {
  4.     if (Set == USART_GetStatus(USART_CH, UsartFrameErr))
  5.     {
  6.         USART_ClearStatus(USART_CH, UsartFrameErr);
  7.     }
  8.     else
  9.     {
  10.     }

  11.     if (Set == USART_GetStatus(USART_CH, UsartParityErr))
  12.     {
  13.         USART_ClearStatus(USART_CH, UsartParityErr);
  14.     }
  15.     else
  16.     {
  17.     }

  18.     if (Set == USART_GetStatus(USART_CH, UsartOverrunErr))
  19.     {
  20.         USART_ClearStatus(USART_CH, UsartOverrunErr);
  21.     }
  22.     else
  23.     {
  24.     }
  25. }
 楼主| l63t89 发表于 2023-10-27 12:16 | 显示全部楼层
说明: 以前我使用stm32,很少关注串口接收错误中断,但是这次华大mcu的中断不进问题调了好久,因为当时首次使用HC芯片,还以为是sdk的bug,没想到是这个问题。通过总结我发现其实任何芯片的串口在实现串口接收中断的时候也必须实现接收错误中断,否则会存在隐患。
 楼主| l63t89 发表于 2023-10-27 12:16 | 显示全部楼层
这样:通过串口接收回调函数和串口dma发送函数就可实现华大HC32F460实现串口dma发送和中断接收。
tpgf 发表于 2023-11-7 11:22 | 显示全部楼层
使用dma之后数据的吞吐量会变大吗
heimaojingzhang 发表于 2023-11-7 11:58 | 显示全部楼层
tpgf 发表于 2023-11-7 11:22
使用dma之后数据的吞吐量会变大吗

应该不会变大 只是解放了cpu而已
keaibukelian 发表于 2023-11-7 12:30 | 显示全部楼层
配置好之后进入dma模式调用两个函数就搞定了
paotangsan 发表于 2023-11-7 12:59 | 显示全部楼层
多个串口发送接收的话都可以使用dma吗
renzheshengui 发表于 2023-11-7 14:12 | 显示全部楼层
可以使用正常的发送模式以及dma接收模式吗
wakayi 发表于 2023-11-7 21:21 | 显示全部楼层
renzheshengui 发表于 2023-11-7 14:12
可以使用正常的发送模式以及dma接收模式吗

你这样做感觉没有什么实际的意义啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则

101

主题

1077

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部