本帖最后由 woai32lala 于 2024-2-28 10:12 编辑
[url=home.php?mod=space&uid=760190]@21小跑堂 #申请原创#[/url] DMA串口超时中断+DMA数据传输
1、前言
在实际应用中,经常会用到串口接收不定长数据的问题,一帧数据中包括帧头、帧尾、数据、校验码等。常用的接收数据的方式是用串口中断,每接收一个数据,中断一次,然后去读取,去判断是不是帧头,再去接收其他的数据,这种不停中断的方式很浪费CPU的资源。 因此通常在STM32中,会有串口空闲中断这种操作方式,这种操作方式在我之前的帖子中已有详细的介绍,采用串口dma接收+空闲中断的时候,如果两帧数据的发送时间间隔大于设置波特率发送一个字节的时间,则stm32会认为触发依次空闲中断。
这次我们采用的是HC32F460,找遍了数据手册也没有找到空闲中断这一条说明,后来发现他有一个叫做超时接收中断,其实和STm32的串口空闲中断差不多,只不过STM32间隔时间是对应波特率下一个字节的时间,而HC32F460的超时中断时间可自己设置,如果也设置为对应波特率下1个字节的时候,那么就和STM32作用一致,可以说是HC32F460空闲中断时间可设置。 它是HC32中USART主要特性之一,如下图所示。 2、应用 通过数据手册可以发现,用于超时中断判定的定时器只能是定时器0,根据选择的串口选择对应的定时器通道,比如这次我们选用的串口3,就选择定时器0 单元2 A通道 下面有个比较坑的点,就是定时器0的时钟源设置,默认为Tim0_XTAL32,如下图所示。 这里当时我忘改了,我没有外接32.768KHz时钟,但是莫名其妙的能用,一直没找到原因。 查看寄存器CMU,发现XTAL32 和LRC都在震荡,就挺晕的,以为没有接XTLA32它会自动判断切换为内部LRC,但是查看定时器的时钟源还是XTLA32. 后来找了供应商说产生的时钟是外部干扰造成。 因为你本身开启这个时钟源 输入这个时钟引脚是浮空的,会继续计数,但是计数频率肯定是不对的,还是要焊接32.768KHz晶振或者切换为内部LRC。 我们将时钟切换为内部LRC,内部RC也是32.768Khz. 计数模式我们设置为异步,因为时钟为非同步时钟。
- static void Timer0Init(void)
- {
- stc_clk_freq_t stcClkTmp;
- stc_tim0_base_init_t stcTimerCfg;
- stc_tim0_trigger_init_t StcTimer0TrigInit;
- MEM_ZERO_STRUCT(stcClkTmp);
- MEM_ZERO_STRUCT(stcTimerCfg);
- MEM_ZERO_STRUCT(StcTimer0TrigInit);
- /* Timer0 peripheral enable */
- PWC_Fcg2PeriphClockCmd(TMR_FCG_PERIPH, Enable);
- /* Clear CNTAR register for channel A */
- TIMER0_WriteCntReg(TMR_UNIT, Tim0_ChannelA, 0u);
- TIMER0_WriteCntReg(TMR_UNIT, Tim0_ChannelB, 0u);
- /* Config register for channel A */
- stcTimerCfg.Tim0_CounterMode = Tim0_Async;
- stcTimerCfg.Tim0_AsyncClockSource = Tim0_LRC;
- stcTimerCfg.Tim0_ClockDivision = Tim0_ClkDiv8;
- stcTimerCfg.Tim0_CmpValue = 32u;
- TIMER0_BaseInit(TMR_UNIT, Tim0_ChannelA, &stcTimerCfg);
- /* Clear compare flag */
- TIMER0_ClearFlag(TMR_UNIT, Tim0_ChannelA);
- /* Config timer0 hardware trigger */
- StcTimer0TrigInit.Tim0_InTrigEnable = false;
- StcTimer0TrigInit.Tim0_InTrigClear = true;
- StcTimer0TrigInit.Tim0_InTrigStart = true;
- StcTimer0TrigInit.Tim0_InTrigStop = false;
- TIMER0_HardTriggerInit(TMR_UNIT, Tim0_ChannelA, &StcTimer0TrigInit);
- }
设置比较时间为320,当两次串口数据接收时间间隔大于320 * 1/32.768k = 0.009765625s时,即触发一次串口接收超时中断。串口波特率为115200,发送一个字节的时间为1/115200*10 = 0.00008680555555555556(这里发送一个字节数据=起始位1bit+数据位8bit+停止位1bit,加起来就是10bit),我们设置的间隔时间已经远远超过发送一个字节的时间。 - static void UsartTimeoutIrqCallback(void)
- {
- TIMER0_Cmd(TMR_UNIT, Tim0_ChannelA,Disable);
- DMA_ChannelCmd(M4_DMA1, DmaCh0, Disable); //超时重启DMA,以进行新一轮的接收
- USART_ClearStatus(USART_CH, UsartRxTimeOut);
- rx_cnt = BUFF_SIZE - M4_DMA1->MONDTCTL0_f.CNT;//获取接收到的数据量
- DMA_SetTransferCnt(M4_DMA1, DmaCh0, BUFF_SIZE);
- DMA_ChannelCmd(M4_DMA1, DmaCh0, Enable);
- }
设置最大的接收数据为200个字节,M4_DMA1->MONDTCTL0_f.CNT这个数值是倒数值,值为设置的接收值数量,比如接收10个数据,该值为190。 因此用BUFF_SIZE - M4_DMA1->MONDTCTL0_f.CNT 设置值- 剩余的值就是实际接收到的数据量。 - DMA初始化
- /**
- *******************************************************************************
- ** \brief Initialize DMA.
- **
- ** \param [in] None
- **
- ** \retval None
- **
- ******************************************************************************/
- static void DmaInit(void)
- {
- stc_dma_config_t stcDmaInit;
- stc_irq_regi_conf_t stcIrqRegiCfg;
- /* Enable peripheral clock */
- PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_DMA1 | PWC_FCG0_PERIPH_DMA2,Enable);
- /* Enable DMA. */
- DMA_Cmd(DMA_UNIT,Enable);
- /* Initialize DMA. */
- MEM_ZERO_STRUCT(stcDmaInit);
- stcDmaInit.u16BlockSize = 1u; /* 1 block */
- stcDmaInit.u16TransferCnt = (uint16_t)m_stcRxBufHanlde.u8Size; /* Transfer count */
- stcDmaInit.u32SrcAddr = ((uint32_t)(&USART_CH->DR)+2ul); /* Set source address. */
- stcDmaInit.u32DesAddr = (uint32_t)(m_stcRxBufHanlde.au8Buf); /* Set destination address. */
- stcDmaInit.stcDmaChCfg.enSrcInc = AddressFix; /* Set source address mode. */
- stcDmaInit.stcDmaChCfg.enDesInc = AddressIncrease; /* Set destination address mode. */
- stcDmaInit.stcDmaChCfg.enIntEn = Enable; /* Enable interrupt. */
- stcDmaInit.stcDmaChCfg.enTrnWidth = Dma8Bit; /* Set data width 8bit. */
- DMA_InitChannel(DMA_UNIT, DMA_CH, &stcDmaInit);
- DMA_SetTransferCnt(M4_DMA1, DmaCh0, BUFF_SIZE);
- /* 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);
- /* Set DMA block transfer complete IRQ */
- stcIrqRegiCfg.enIRQn = DMA_BTC_INT_IRQn;
- stcIrqRegiCfg.pfnCallback = &DmaBtcIrqCallback;
- stcIrqRegiCfg.enIntSrc = DMA_BTC_INT_NUM;
- enIrqRegistration(&stcIrqRegiCfg);
- NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
- NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
- NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
- }
3、测试 我们通过串口助手一次发送92个数据,然后我们可以看到watch2里面的数据跟串口助手发送的数据一致。
|