华大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 宏定义
#define USART_CH (M4_USART2)
#define USART_BAUDRATE (115200ul)
#define USART_RX_PORT (PortA)
#define USART_RX_PIN (Pin10)
#define USART_RX_FUNC (Func_Usart2_Rx)
#define USART_TX_PORT (PortA)
#define USART_TX_PIN (Pin09)
#define USART_TX_FUNC (Func_Usart2_Tx)
#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,
};
DmaInit();
PWC_Fcg1PeriphClockCmd(u32Fcg1Periph, Enable);
PORT_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_FUNC, Disable);
PORT_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_FUNC, Disable);
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;
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_DMA1 | PWC_FCG0_PERIPH_DMA2,Enable);
DMA_Cmd(DMA_UNIT,Enable);
MEM_ZERO_STRUCT(stcDmaInit);
stcDmaInit.u16BlockSize = 1u;
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;
stcDmaInit.stcDmaChCfg.enTrnWidth = Dma8Bit;
DMA_InitChannel(DMA_UNIT, DMA_CH, &stcDmaInit);
DMA_ChannelCmd(DMA_UNIT, DMA_CH, Enable);
DMA_ClearIrqFlag(DMA_UNIT, DMA_CH, TrnCpltIrq);
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS,Enable);
DMA_SetTriggerSrc(DMA_UNIT, DMA_CH, DMA_TRG_SEL);
stcIrqRegiCfg.enIntSrc = INT_DMA1_TC0;
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发送函数
void Uart3_SendArray(u8 *pData,u16 Leng)
{
DMA_ChannelCmd(DMA_UNIT, DMA_CH, Enable);
DMA_SetSrcAddress(DMA_UNIT, DMA_CH, (uint32_t)pData);
DMA_SetTransferCnt(DMA_UNIT, DMA_CH, (uint16_t)Leng);
USART_FuncCmd(USART_CH, UsartTxAndTxEmptyInt, Enable);
}
3.5 DMA发送完成回调函数
void DmaBtcIrqCallback(void)
{
DMA_ClearIrqFlag(DMA_UNIT, DMA_CH, TrnCpltIrq);
while(Reset == USART_GetStatus(USART_CH, UsartTxComplete));
USART_FuncCmd(USART_CH, UsartTxAndTxEmptyInt, Disable);
}
说明:这里必须有这个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发送和中断接收。
|