一 項(xiàng)目背景
????????項(xiàng)目需要使用一款UART串口編碼器,編碼器的數(shù)據(jù)以波特率57600持續(xù)向外發(fā)送。但這組數(shù)據(jù)包沒有固定的包頭和校驗(yàn)尾,僅僅是由多圈圈數(shù)和單圈角度組成的六字節(jié)數(shù)據(jù)碼,這樣接收到的數(shù)組無法確定實(shí)際的下標(biāo),所以這邊考慮用串口接收超時(shí)中斷+DMA來實(shí)現(xiàn)。
二 原理說明
【1】UART原理說明:參考【嵌入式】NXP/LPC使用GPIO+定時(shí)器模擬UART串口接收
【2】超時(shí)中斷原理說明:接收的數(shù)據(jù)包通過邏輯分析儀,如下所示:
文章來源地址http://www.zghlxwxcb.cn/news/detail-632507.html
? ? ? ? 由上面的數(shù)據(jù)可以看到,兩個(gè)包之間的發(fā)送間隔為500us左右,而一個(gè)包的發(fā)送時(shí)間為170us(波特率為57600,那么每位數(shù)據(jù)是17us,一個(gè)包10位數(shù)據(jù),就是170us),所以只要在串口收發(fā)的過程中加一個(gè)定時(shí)器,設(shè)定超時(shí)時(shí)間為400us(大于170us,小于500us即可),那么?只要超時(shí)了,說明下一次收到的位即為起始位。
????????STM32中有一個(gè)空閑中斷(IDLE)的概念,而HC32中沒有,取而代之的是串口接收超時(shí)中斷,兩者基本功能是類似的,都是在串口超過一段時(shí)間沒有接收數(shù)據(jù)之后觸發(fā)的一個(gè)中斷功能。HC32F460的用戶手冊(cè)中對(duì)此也有詳細(xì)說明(我們這邊用的是USART4串口,所以相對(duì)應(yīng)的需需要使用 Timer0 Unit2 B 通道):
【3】DMA原理說明:DMA(Direct Memory Access,直接存儲(chǔ)器訪問) 是單片機(jī)的一個(gè)外設(shè),它的主要功能是用來搬移數(shù)據(jù),但是不需要占用 CPU,即在傳輸數(shù)據(jù)的時(shí)候, CPU 可以干其他的事情,好像多線程一樣。(具體可以參考:串口DMA傳輸模式)
? ? ? ? ?這邊用到DMA,是因?yàn)榫幋a器發(fā)送數(shù)據(jù)比較快,若是一直進(jìn)中斷會(huì)擠占CPU的資源,所以考慮用DMA改進(jìn)。
三 設(shè)計(jì)實(shí)現(xiàn)--超時(shí)定時(shí)器部分
【1】超時(shí)定時(shí)器初始化( Timer0 Unit2 B 通道 ),這個(gè)過程中主要關(guān)注一下定時(shí)器時(shí)間的設(shè)置,如下面的 stcTimerCfg.Tim0_CmpValue = 4200 ,它的時(shí)鐘源是 Tim0_Pclk1 ,在HC32F460中,這個(gè)時(shí)鐘是168MHz的一半,即84MHz,時(shí)鐘的分頻系數(shù)為8,根據(jù)公式:
T=CmpValue*ClockSource*ClockDivision
其中,T = 400us,ClockSource=1/84MHz,ClockDivision=8,計(jì)算出CmpValue=4200:
void Timer0_Config(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(PWC_FCG2_PERIPH_TIM02, Enable);
/* Clear CNTAR register for channel B */
TIMER0_WriteCntReg(M4_TMR02, Tim0_ChannelB, 0u);
/* Config register for channel B */
stcTimerCfg.Tim0_CounterMode = Tim0_Sync;
stcTimerCfg.Tim0_SyncClockSource = Tim0_Pclk1;
stcTimerCfg.Tim0_ClockDivision = Tim0_ClkDiv8;
stcTimerCfg.Tim0_CmpValue = 4200;
TIMER0_BaseInit(M4_TMR02, Tim0_ChannelB, &stcTimerCfg);
/* Clear compare flag */
TIMER0_ClearFlag(M4_TMR02, Tim0_ChannelB);
/* Config timer0 hardware trigger */
StcTimer0TrigInit.Tim0_InTrigEnable = false;
StcTimer0TrigInit.Tim0_InTrigClear = true;
StcTimer0TrigInit.Tim0_InTrigStart = true;
StcTimer0TrigInit.Tim0_InTrigStop = false;
TIMER0_HardTriggerInit(M4_TMR02, Tim0_ChannelB, &StcTimer0TrigInit);
}
四 設(shè)計(jì)實(shí)現(xiàn)--串口部分
【1】串口初始化:
/* USART baudrate definition */
#define USART4_BAUDRATE (57600)
/* USART Interrupt Number */
#define USART4_IRQn (Int025_IRQn)
#define USART4_ERR_IRQn (Int026_IRQn)
#define USART4_RTO_IRQn (Int029_IRQn)
/* USART RX Port/Pin definition */
#define USART4_RX_PORT (PortE)
#define USART4_RX_PIN (Pin14)
#define USART4_RX_FUNC (Func_Usart4_Rx)
void initUART4(void)
{
en_result_t enRet = Ok;
stc_irq_regi_conf_t stcIrqRegiCfg;
/*配置串口使用的時(shí)鐘和基本通信配置*/
const stc_usart_uart_init_t stcInitCfg = {
UsartIntClkCkOutput,
UsartClkDiv_1,
UsartDataBits8,
UsartDataLsbFirst,
UsartOneStopBit,
UsartParityNone,
UsartSampleBit8,
UsartStartBitFallEdge,
UsartRtsEnable,
};
/*打開時(shí)鐘*/
PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_USART4, Enable);
/*配置相應(yīng)的IO作為串口的RX引腳*/
PORT_SetFunc(USART4_RX_PORT, USART4_RX_PIN, USART4_RX_FUNC, Disable);
/*初始化串口配置*/
enRet = USART_UART_Init(M4_USART4, &stcInitCfg);
if (enRet != Ok)while (1);
/*串口波特率設(shè)置*/
enRet = USART_SetBaudrate(M4_USART4, USART4_BAUDRATE);
if (enRet != Ok)while (1);
/*設(shè)置串口接收中斷*/
stcIrqRegiCfg.enIRQn = USART4_IRQn;
stcIrqRegiCfg.pfnCallback = &Usart4RxIrqCallback;
stcIrqRegiCfg.enIntSrc = INT_USART4_RI;
enIrqRegistration(&stcIrqRegiCfg);
NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
/*設(shè)置串口接收錯(cuò)誤中斷*/
stcIrqRegiCfg.enIRQn = USART4_ERR_IRQn;
stcIrqRegiCfg.pfnCallback = &Usart4ErrIrqCallback;
stcIrqRegiCfg.enIntSrc = INT_USART4_EI;
enIrqRegistration(&stcIrqRegiCfg);
NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
/*設(shè)置接收超時(shí)中斷*/
stcIrqRegiCfg.enIRQn = USART4_RTO_IRQn;
stcIrqRegiCfg.pfnCallback = &Usart4TimeoutIrqCallback;
stcIrqRegiCfg.enIntSrc = INT_USART4_RTO;
enIrqRegistration(&stcIrqRegiCfg);
NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
USART_FuncCmd(M4_USART4, UsartRx, Enable);//使能接收
USART_FuncCmd(M4_USART4, UsartRxInt, Enable);//使能接收中斷
USART_FuncCmd(M4_USART4, UsartTimeOut, Enable);//使能超時(shí)
USART_FuncCmd(M4_USART4, UsartTimeOutInt, Enable);//使能超時(shí)中斷
}
【2】串口接收中斷回調(diào):
#define ENCODER_LEN 6
uint8_t ecd_buf[ENCODER_LEN];
uint8_t ecd_timeout_flag;
static void Usart4RxIrqCallback(void)
{
static uint8_t cnt = 0;
while(1)
{
if (Set == USART_GetStatus(M4_USART4, UsartRxNoEmpty))
{
if(ecd_timeout_flag == 1) //如果超時(shí),下一個(gè)接收到的即為起始位
cnt = 0;
ecd_buf[cnt++] = USART_RecData(M4_USART4);
ecd_timeout_flag = 0;
if(cnt > 5)
cnt = 0;
}
else
break;
}
}
【3】串口接收錯(cuò)誤中斷回調(diào):
static void Usart4ErrIrqCallback(void)
{
if (Set == USART_GetStatus(M4_USART4, UsartFrameErr))
USART_ClearStatus(M4_USART4, UsartFrameErr);
if (Set == USART_GetStatus(M4_USART4, UsartParityErr))
USART_ClearStatus(M4_USART4, UsartParityErr);
if (Set == USART_GetStatus(M4_USART4, UsartOverrunErr))
USART_ClearStatus(M4_USART4, UsartOverrunErr);
}
【4】串口接收超時(shí)中斷回調(diào):
static void Usart4TimeoutIrqCallback(void)
{
ecd_timeout_flag = 1; //下一次接收為通訊碼的開始位
TIMER0_Cmd(M4_TMR02, Tim0_ChannelB,Disable);
USART_ClearStatus(M4_USART4, UsartRxTimeOut);
}
? ? ? ? 到這邊為止,就可以正常的讀到編碼器的數(shù)據(jù)了,而且是以編碼器的發(fā)送順序排列在ecd_buf數(shù)組中,只要處理該數(shù)組就可以取到編碼器的多圈圈數(shù)和單圈角度。
? ? ? ? 下面的DMA部分是想改進(jìn)一下控制方案,使得不那么頻繁地進(jìn)入接收中斷,以減小CPU的資源消耗。
五 設(shè)計(jì)實(shí)現(xiàn)--DMA部分
【1】DMA初始化和中斷,其中主要關(guān)注幾點(diǎn):
????????一是接收的數(shù)據(jù)需要映射到ecd_buf的地址:
????????(stcDmaInit.u32DesAddr = (uint32_t)(&ecd_buf))
????????二是發(fā)送數(shù)據(jù)模式需要改為遞增:
????????(stcDmaInit.stcDmaChCfg.enDesInc = AddressIncrease):
static void DmaBtcIrqCallback(void)
{
USART_ClearStatus(M4_USART4, UsartRxTimeOut); //清楚接收超時(shí)標(biāo)志
DMA_ClearIrqFlag(M4_DMA1, DmaCh0, BlkTrnCpltIrq);
}
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(M4_DMA1,Enable);
/* Initialize DMA. */
MEM_ZERO_STRUCT(stcDmaInit);
stcDmaInit.u16BlockSize = 1u; /* 1 block */
stcDmaInit.u32SrcAddr = ((uint32_t)(&M4_USART4->DR)+2ul); /* Set source address. */
stcDmaInit.u32DesAddr = (uint32_t)(&ecd_buf); /* 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(M4_DMA1, DmaCh0, &stcDmaInit);
/* Enable the specified DMA channel. */
DMA_ChannelCmd(M4_DMA1, DmaCh0, Enable);
/* Clear DMA flag. */
DMA_ClearIrqFlag(M4_DMA1, DmaCh0, TrnCpltIrq);
/* Enable peripheral circuit trigger function. */
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS,Enable);
/* Set DMA trigger source. */
DMA_SetTriggerSrc(M4_DMA1, DmaCh0, EVT_USART4_RI);
/* Set DMA block transfer complete IRQ */
stcIrqRegiCfg.enIRQn = Int030_IRQn;
stcIrqRegiCfg.pfnCallback = &DmaBtcIrqCallback;
stcIrqRegiCfg.enIntSrc = INT_DMA1_BTC0;
enIrqRegistration(&stcIrqRegiCfg);
NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
}
【2】串口接收中斷就不用了,由DMA直接接收即可:
//串口接收錯(cuò)誤中斷回調(diào)
static void Usart4ErrIrqCallback(void)
{
if (Set == USART_GetStatus(M4_USART4, UsartFrameErr))
USART_ClearStatus(M4_USART4, UsartFrameErr);
if (Set == USART_GetStatus(M4_USART4, UsartParityErr))
USART_ClearStatus(M4_USART4, UsartParityErr);
if (Set == USART_GetStatus(M4_USART4, UsartOverrunErr))
USART_ClearStatus(M4_USART4, UsartOverrunErr);
}
//串口接收超時(shí)中斷回調(diào)
static void Usart4TimeoutIrqCallback(void)
{
TIMER0_Cmd(M4_TMR02, Tim0_ChannelB,Disable);
USART_ClearStatus(M4_USART4, UsartRxTimeOut);
DMA_ChannelCmd(M4_DMA1, DmaCh0, Disable); //超時(shí)重啟DMA,以進(jìn)行新一輪的接收
DMA_SetDesAddress(M4_DMA1, DmaCh0, (uint32_t)(ecd_buf));
DMA_SetTransferCnt(M4_DMA1, DmaCh0, ENCODER_LEN);
DMA_ChannelCmd(M4_DMA1, DmaCh0, Enable);
}
void initUART4(void)
{
en_result_t enRet = Ok;
stc_irq_regi_conf_t stcIrqRegiCfg;
/*配置串口使用的時(shí)鐘和基本通信配置*/
const stc_usart_uart_init_t stcInitCfg = {
UsartIntClkCkOutput,
UsartClkDiv_1,
UsartDataBits8,
UsartDataLsbFirst,
UsartOneStopBit,
UsartParityNone,
UsartSampleBit8,
UsartStartBitFallEdge,
UsartRtsEnable,
};
DmaInit();
/*打開時(shí)鐘*/
PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_USART4, Enable);
/*配置相應(yīng)的IO作為串口的RX引腳*/
PORT_SetFunc(USART4_RX_PORT, USART4_RX_PIN, USART4_RX_FUNC, Disable);
/*初始化串口配置*/
enRet = USART_UART_Init(M4_USART4, &stcInitCfg);
if (enRet != Ok)while (1);
/*串口波特率設(shè)置*/
enRet = USART_SetBaudrate(M4_USART4, USART4_BAUDRATE);
if (enRet != Ok)while (1);
/*設(shè)置串口接收中斷舍棄*/
/*設(shè)置串口接收錯(cuò)誤中斷*/
stcIrqRegiCfg.enIRQn = USART4_ERR_IRQn;
stcIrqRegiCfg.pfnCallback = &Usart4ErrIrqCallback;
stcIrqRegiCfg.enIntSrc = INT_USART4_EI;
enIrqRegistration(&stcIrqRegiCfg);
NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
/*設(shè)置接收超時(shí)中斷*/
stcIrqRegiCfg.enIRQn = USART4_RTO_IRQn;
stcIrqRegiCfg.pfnCallback = &Usart4TimeoutIrqCallback;
stcIrqRegiCfg.enIntSrc = INT_USART4_RTO;
enIrqRegistration(&stcIrqRegiCfg);
NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
USART_FuncCmd(M4_USART4, UsartRx, Enable);//使能接收
USART_FuncCmd(M4_USART4, UsartRxInt, Enable);//使能接收中斷
USART_FuncCmd(M4_USART4, UsartTimeOut, Enable);//使能超時(shí)
USART_FuncCmd(M4_USART4, UsartTimeOutInt, Enable);//使能超時(shí)中斷
}
? ? ? ? 項(xiàng)目中只需要用到串口數(shù)據(jù)的接收,所以這邊沒有DMA發(fā)送的內(nèi)容。
六 總結(jié)
?????????綜上,便可以通過串口接收超時(shí)中斷或者串口接收超時(shí)中斷+DMA進(jìn)行接收了。通過DEBUG也可以看到ecd_buf中的數(shù)據(jù)按順序排列為0x00,0x00,0xDD,0x2E,0x38,0x77,與邏輯分析儀中的一致:文章來源:http://www.zghlxwxcb.cn/news/detail-632507.html
到了這里,關(guān)于【嵌入式】HC32F460串口接收超時(shí)中斷+DMA的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!