华大MCU(三):HC32F460实现串口dma发送和中断接收

华大HC32F460实现串口dma发送和中断接收

1. 系统环境

  • 系统:win10
  • 开发板型号:官方评估板(EVB-HC32F460)
  • ide:keil5
  • sdk版本:hc32f460_ddl_Rev2.0.0
  • 时间:2021.7.1

2. 遇到的问题

由于hc32f460的配置方式非常灵活,这也就导致hc32f460在程序实现上和传统的mcu有明显的不同。

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

那篇文章还在修改中。。。。

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

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

3.1 宏定义

/* USART 类型 */
#define USART_CH                        (M4_USART2)
/* USART 波特率 */
#define USART_BAUDRATE                  (115200ul)

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

/* USART TX Port/Pin definition */
#define USART_TX_PORT                   (PortA)
#define USART_TX_PIN                    (Pin09)
#define USART_TX_FUNC                   (Func_Usart2_Tx)

/* DMA相关参数 */
#define DMA_UNIT                        (M4_DMA1)
#define DMA_CH                          (DmaCh0)
#define DMA_TRG_SEL                     (EVT_USART2_TI)

3.2 串口初始化函数

//串口初始化
void uart2_init(void)
{

    stc_irq_regi_conf_t stcIrqRegiCfg; //用于中断配置的结构体
    
    en_result_t enRet = Ok;
    uint32_t u32Fcg1Periph = PWC_FCG1_PERIPH_USART1 | PWC_FCG1_PERIPH_USART2 | \
        PWC_FCG1_PERIPH_USART3 | PWC_FCG1_PERIPH_USART4;
    const stc_usart_uart_init_t stcInitCfg = {
        UsartIntClkCkNoOutput,
        UsartClkDiv_1,
        UsartDataBits8,
        UsartDataLsbFirst,
        UsartOneStopBit,
        UsartParityNone,
        UsartSampleBit8,
        UsartStartBitFallEdge,
        UsartRtsEnable,
    };//串口参数结构体

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

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

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

    /* 初始化 USART */
    enRet = USART_UART_Init(USART_CH, &stcInitCfg);
    if (enRet != Ok)
    {
        while (1)
        {
        }
    }
    else
    {
    }

    /* 设置串口波特率 */
    enRet = USART_SetBaudrate(USART_CH, USART_BAUDRATE);
    if (enRet != Ok)
    {
        while (1)
        {
        }
    }
    else
    {
    }

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

    /* 当串口接收发生错误的时候产生中断 */
    stcIrqRegiCfg.enIntSrc = INT_USART2_EI;
    stcIrqRegiCfg.enIRQn = Int001_IRQn;
    stcIrqRegiCfg.pfnCallback = &UsartErrIrqCallback;
    enIrqRegistration(&stcIrqRegiCfg);
    
    NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
    NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
    NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
    
    
	//最后使能串口的引脚,串口正式工作
    USART_FuncCmd(USART_CH, UsartRx, Enable);
    USART_FuncCmd(USART_CH, UsartRxInt, Enable);

}

说明:串口时钟使能,配置rx、tx引脚,配置串口参数,这个简单不用说。

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

注意:对于同一个程序,每个中断号只能和一种中断类型进行便绑定,当把多个中断类型和同一个中断号(如:Int001_IRQn)绑定,那么只有一个中断会生效,其他中断不会生效。

3.3 串口初始化的时候调用的DMA初始化函数

void DmaInit(void)
{
    stc_dma_config_t stcDmaInit;
    stc_irq_regi_conf_t stcIrqRegiCfg;

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

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

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

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

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

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

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

    /* 设置dma发送完成中断 */
    stcIrqRegiCfg.enIntSrc = INT_DMA1_TC0;//dma1的通道0的完成中断
    stcIrqRegiCfg.enIRQn = Int002_IRQn;
    stcIrqRegiCfg.pfnCallback = &DmaBtcIrqCallback;
    enIrqRegistration(&stcIrqRegiCfg);
    
    NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
    NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
    NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
	
}

说明:我通过使用发现,dma发送需要开启dma发送完成中断,在中断里面要关闭串口dma发送使能,否则的话串口将无法出发下一次dma发送。

3.4 串口接收中断回调函数的实现

//串口接收回调函数
void UsartRxIrqCallback(void)
{
    uint16_t u16Data = USART_RecData(USART_CH);//取出数据
	//其他用户自己的程序
    //下面是例子
    uart3_recv_fifo[uart3_recv_fifo_in++]=((uint8_t)u16Data)&0xff;
    if(uart3_recv_fifo_in>=UART3_FIFO_SIZE)
        uart3_recv_fifo_in=0;	
}

3.4 串口DMA发送函数

//华大dma发送
void Uart3_SendArray(u8 *pData,u16 Leng)
{
	DMA_ChannelCmd(DMA_UNIT, DMA_CH, Enable); //使能dma传输
	DMA_SetSrcAddress(DMA_UNIT, DMA_CH, (uint32_t)pData); //从新设置源地址
	DMA_SetTransferCnt(DMA_UNIT, DMA_CH, (uint16_t)Leng);//从新设置发送的数据量					
	USART_FuncCmd(USART_CH, UsartTxAndTxEmptyInt, Enable);//开启dma发送
}

3.5 DMA发送完成回调函数

//HC的DMA传输完回调函数
void DmaBtcIrqCallback(void)
{		
    DMA_ClearIrqFlag(DMA_UNIT, DMA_CH, TrnCpltIrq);

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

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


说明:这里必须有这个while判断,否则会导致发送的时候,数据最后的那个字节丢失,猜测原因是dma发送数据减到零认为没数据了,然后触发了dma发送完成中断,但是这时候最后一个字节刚被抛到串口发送缓冲区,还没发完,所以这个时候要等串口发完,参能关闭串口dma发送使能。

3.6 当串口接收数据发生错误触发的回调函数

//接收错误回调函数
void UsartErrIrqCallback(void)
{
    if (Set == USART_GetStatus(USART_CH, UsartFrameErr))
    {
        USART_ClearStatus(USART_CH, UsartFrameErr);
    }
    else
    {
    }

    if (Set == USART_GetStatus(USART_CH, UsartParityErr))
    {
        USART_ClearStatus(USART_CH, UsartParityErr);
    }
    else
    {
    }

    if (Set == USART_GetStatus(USART_CH, UsartOverrunErr))
    {
        USART_ClearStatus(USART_CH, UsartOverrunErr);
    }
    else
    {
    }
}

说明: 以前我使用stm32,很少关注串口接收错误中断,但是这次华大mcu的中断不进问题调了好久,因为当时首次使用HC芯片,还以为是sdk的bug,没想到是这个问题。通过总结我发现其实任何芯片的串口在实现串口接收中断的时候也必须实现接收错误中断,否则会存在隐患。

这样:通过串口接收回调函数和串口dma发送函数就可实现华大HC32F460实现串口dma发送和中断接收。

版权声明:本文为CSDN博主「Little Grey Bear」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Zhichao_Zhang/article/details/122792004

生成海报
点赞 0

Little Grey Bear

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

基于8051单片机实现电子时钟+数字秒表设计

概述 电子时钟是一种利用数字电路来显示秒、分、时的计时装置,与传统的机械钟相比,它具有走时准确、显 示直观、无机械传动装置等优点,因而得到广泛应用。随着人们生活环境的不断改善和美化,在许

【电子系统综合实践】--2021电赛F题智能送药小车

主要内容     设计并制作智能送药小车,模拟完成在医院药房与病房间药品的送取作业。院区结构示意如图1所示。院区走廊两侧的墙体由黑实线表示。走廊地面上画有居中的红实线,并放置标识病房号的黑色数字可移动纸张。药房和近端