因为缺芯问题,近期一直在折腾换国产芯片,从灵动微,锦瑞,瑞萨。。。一直到华大,用到了串口功能,这里分享一下华大的串口使用
串口使用我用过三种方案:
1)串口接收中断,也就是接收一个字节进一次中断,一直做解析,这样做势必会占用MCU运行资源;
2)串口接收使用DMA方案,再使用定时器超时来断帧,这样使用一般情况下没有问题,但是 如果作为从机的时候,如果要求ACK在特别短的时间里(比如20ms以内)去响应,就会有一定的风险存在;
3)串口接收使用DMA方案,配合使用UART的超时中断来使用(STM32有串口闲中断更好用),这个是华大HC32F460的一个功能,今天重点说这个方案。
我觉得华大有意思的是,GPIO复用功能的配置,比较灵活,大部分GPIO口可以配置成自己需要的外设功能,比如i2c,spi,uart等等。还有一个就是它的中断配置有点特别,需要查表,很容易配错。
上代码,配置串口参数,根据自己的通讯参数配置:
/* 串口参数配置 */
int hal_uart_conf_init (uint32_t baude)
{
PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_USART2, Enable); /* 使能串口时钟 */
stc_irq_regi_conf_t stcIrqRegiCfg;
const stc_usart_uart_init_t stcInitCfg = { /* 配置串口参数 */
UsartIntClkCkNoOutput,
UsartClkDiv_16,
UsartDataBits8,
UsartDataLsbFirst,
UsartOneStopBit,
UsartParityNone,
UsartSampleBit8,
UsartStartBitFallEdge,
UsartRtsEnable,
};
if (USART_UART_Init (M4_USART2, &stcInitCfg) != Ok)
{
while (1)
{
}
}
/* Set baudrate */
if (Ok != USART_SetBaudrate (M4_USART2, baude)) /* 配置波特率 */
{
while (1)
{
}
}
else
{
}
/* 串口接收错误中断配置 */
stcIrqRegiCfg.enIRQn = Int000_IRQn; /* 中断号,可通过参考手册查阅对应的中断号 */
stcIrqRegiCfg.pfnCallback = &uart2_err_cb; /* 中断回调函数 */
stcIrqRegiCfg.enIntSrc = INT_USART2_EI; /* 错误中断向量号,可通过参考手册查阅对应的中断号*/
enIrqRegistration (&stcIrqRegiCfg);
NVIC_SetPriority (stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_00); /* 配置中断优先级 */
NVIC_ClearPendingIRQ (stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ (stcIrqRegiCfg.enIRQn);
/* 串口接收超时中断配置 */
stcIrqRegiCfg.enIRQn = Int001_IRQn; /* 中断号,可通过参考手册查阅对应的中断号 */
stcIrqRegiCfg.pfnCallback = uart1_rx_timeout_cb; /* 串口超时中断回调函数 */
stcIrqRegiCfg.enIntSrc = INT_USART2_RTO; /* 串口2超时中断向量号,可通过参考手册查阅对应的中断号 */
enIrqRegistration (&stcIrqRegiCfg);
NVIC_SetPriority (stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_00);
NVIC_ClearPendingIRQ (stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ (stcIrqRegiCfg.enIRQn);
/*使能串口接收、发送、中断等 function*/
USART_FuncCmd (M4_USART2, UsartRx, Enable); /* 使能串口接收 */
USART_FuncCmd (M4_USART2, UsartRxInt, Enable); /* 使能串口接收中断,这里主要处理串口故障中断清标志位 */
USART_FuncCmd (M4_USART2, UsartTimeOut, Enable); /* 使能串口超时功能 */
USART_FuncCmd (M4_USART2, UsartTimeOutInt, Enable); /* 使能串口超时中断功能 */
USART_FuncCmd (M4_USART2, UsartTx, Enable); /* 使能串口发送 */
return 0;
}
/* 串口超时中断回调函数 */
static void uart2_err_cb (void)
{
/* 清除错误标志位 */
USART_ClearStatus(M4_USART2, UsartParityErr);
USART_ClearStatus(M4_USART2, UsartFrameErr);
USART_ClearStatus(M4_USART2, UsartOverrunErr);
USART_ClearStatus(M4_USART2, UsartParityErr);
USART_ClearStatus(M4_USART2, UsartRxNoEmpty);
USART_ClearStatus(M4_USART2, UsartTxComplete);
USART_ClearStatus(M4_USART2, UsartTxEmpty);
USART_ClearStatus(M4_USART2, UsartRxTimeOut);
USART_ClearStatus(M4_USART2, UsartRxMpb);
}
/* 串口超时中断回调函数 */
static void uart1_rx_timeout_cb (void)
{
/* 清除标记位 */
TIMER0_Cmd (M4_TMR01, Tim0_ChannelB, Disable);
TIMER0_ClearFlag (TIMER_ID, Tim0_ChannelB);
USART_ClearStatus (M4_USART2, UsartRxTimeOut);
uart_parm.rx_cnt = uart_parm.rx_len - M4_DMA1->MONDTCTL0_f.CNT;
if (uart_parm.rx_cnt > 0)
{
/* 这里可以添加串口接收标志位置位或者发送事件去处理串口接收到的数据 */
extern void uart_evt_r_send (void);
uart_evt_r_send (); /* 这里我使用的是rt-thread,所以发送接收事件给串口处理线程 */
/* 串口接收使能 */
uart_dma_rx_start (uart_parm.rx_buf, uart_parm.rx_len); /* uart_parm.rx_len=接收buf的大小 */
}
}
/* 定时器配置(串口超时是基于定时器的,串口和定时器的对应关系可以通过参考手册查阅,我是用uart2对应的就是timer01的b通道) */
static void timer0b_init (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);
/* 定时器时钟使能 */
PWC_Fcg2PeriphClockCmd (PWC_FCG2_PERIPH_TIM01, Enable);
/* 计数器清零 */
TIMER0_WriteCntReg (M4_TMR01, Tim0_ChannelB, 0u);
/* 配置B通道 */
/* 这里是我的参数,仅供参考。各位可以根据自己的参数实际计算得到一个可靠的参数配置
PLCK1=100M 32分频=3125K 计数500=160us 1000=320us 波特率=57600 一个bit大概=17us */
/* 同步时钟=LRC或者XTAL32 异步时钟=PCLK1或者内部硬件触发事件 */
stcTimerCfg.Tim0_CounterMode = Tim0_Sync; // Tim0_Async;
/* 如果选异步时钟,就在这里选择LRC或者XTAL32 */
// stcTimerCfg.Tim0_AsyncClockSource = Tim0_LRC;
/* 如果选同步时钟,就在这里选择PCLK1或者内部硬件触发事件 */
stcTimerCfg.Tim0_SyncClockSource = Tim0_Pclk1;
/* 分频系数 */
stcTimerCfg.Tim0_ClockDivision = Tim0_ClkDiv32;
/* 串口从空闲开始计数,=500时产生超时中断 */
stcTimerCfg.Tim0_CmpValue = 500; // 1000; //
TIMER0_BaseInit (M4_TMR01, Tim0_ChannelB, &stcTimerCfg);
/* 清除标志位 */
TIMER0_ClearFlag (M4_TMR01, Tim0_ChannelB);
/* 这几个参数没有深入研究,是直接从官网例程中提取的 */
StcTimer0TrigInit.Tim0_InTrigEnable = false;
StcTimer0TrigInit.Tim0_InTrigClear = true;
StcTimer0TrigInit.Tim0_InTrigStart = true;
StcTimer0TrigInit.Tim0_InTrigStop = false;
TIMER0_HardTriggerInit (M4_TMR01, Tim0_ChannelB, &StcTimer0TrigInit);
}
/* dma初始化 */
static int hal_uart_dma_init (void)
{
/* 使能DMA1时钟 */
PWC_Fcg0PeriphClockCmd (PWC_FCG0_PERIPH_DMA1, Enable)
/* 使能 DMA1. */
DMA_Cmd (M4_DMA1, Enable);
return 0;
}
/* dma接受使能 */
int uart_dma_rx_start (uint8_t * buf, uint32_t len)
{
stc_dma_config_t stcDmaInit;
stc_irq_regi_conf_t stcIrqRegiCfg;
/* Initialize DMA. */
MEM_ZERO_STRUCT (stcDmaInit);
/* 传输大小,0=1024bytes 1=2048bytes,我理解是传输的最大数据长度 */
stcDmaInit.u16BlockSize = 1u;
/* 接收缓存长度 */
stcDmaInit.u16TransferCnt = (uint16_t)len;
/* 配置DMA源,这里选择UART2的接收寄存器 */
stcDmaInit.u32SrcAddr = ((uint32_t)(&M4_USART2->DR) + 2ul);
/* 接收缓存地址 */
stcDmaInit.u32DesAddr = (uint32_t)(buf);
/* 源地址固定 */
stcDmaInit.stcDmaChCfg.enSrcInc = AddressFix;
/* 接收缓存地址递增 */
stcDmaInit.stcDmaChCfg.enDesInc = AddressIncrease;
stcDmaInit.stcDmaChCfg.enIntEn = Enable; /* 使能中断 */
stcDmaInit.stcDmaChCfg.enTrnWidth = Dma8Bit; /* 传输数据宽度8bit */
DMA_InitChannel (M4_DMA1, DmaCh0, &stcDmaInit);
/* 使能DMA1通道0,这个可以通过参考手册查阅uart对应的dma通道 */
DMA_ChannelCmd (M4_DMA1, DmaCh0, Enable);
DMA_ClearIrqFlag(M4_DMA1, DmaCh0, TrnCpltIrq); /* 清除标记位 */
PWC_Fcg0PeriphClockCmd (PWC_FCG0_PERIPH_AOS, Enable); /* 启用外围电路触发功能 */
/* 配置传输触发源,这里选择UART2的数据接收 */
DMA_SetTriggerSrc (M4_DMA1, DmaCh0, EVT_USART2_RI);
stcIrqRegiCfg.enIRQn = Int002_IRQn; /* DMA1通道0完成中断号 */
stcIrqRegiCfg.pfnCallback = &dma_ch0_rx_tc_cb; /* DMA传输完成中断回调函数 */
stcIrqRegiCfg.enIntSrc = INT_DMA1_TC0; /* DMA1通道0传输完成中断向量号 */
enIrqRegistration (&stcIrqRegiCfg);
NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_00);
NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
return 0;
}
/* dma发送使能 */
int uart_dma_tx_start (uint8_t * buf, uint32_t len)
{
stc_dma_config_t stcDmaInit;
stc_irq_regi_conf_t stcIrqRegiCfg;
USART_FuncCmd (M4_USART2, UsartTx, Disable); /* 失能串口发送 */
MEM_ZERO_STRUCT (stcDmaInit);
/* 传输大小,0=1024bytes 1=2048bytes,我理解是传输的最大数据长度 */
stcDmaInit.u16BlockSize = 1u;
/* 发送数据长度 */
stcDmaInit.u16TransferCnt = (uint16_t)len;
/* 配置DMA源=发送缓存首地址 */
stcDmaInit.u32SrcAddr = (uint32_t)(buf);
/* 目标地址=UART2的发送寄存器 */
stcDmaInit.u32DesAddr = ((uint32_t)(&M4_USART2->DR));
stcDmaInit.stcDmaChCfg.enSrcInc = AddressIncrease; /* 源地址递增 */
stcDmaInit.stcDmaChCfg.enDesInc = AddressFix; /* 目标地址固定 */
stcDmaInit.stcDmaChCfg.enIntEn = Enable; /* 使能中断 */
stcDmaInit.stcDmaChCfg.enTrnWidth = Dma8Bit; /* 传输数据宽度8bit */
DMA_InitChannel (M4_DMA1, DmaCh1, &stcDmaInit);
DMA_ClearIrqFlag(M4_DMA1, DmaCh1, TrnCpltIrq); /* 清除标记位 */
PWC_Fcg0PeriphClockCmd (PWC_FCG0_PERIPH_AOS, Enable); /* 启用外围电路触发功能 */
/* 配置传输触发源,这里选择UART2的数据发送 */
DMA_SetTriggerSrc (M4_DMA1, DmaCh1, EVT_USART2_TI);
stcIrqRegiCfg.enIRQn = DMA1_CH1_TC_IRQn; /* DMA1通道1完成中断号 */
stcIrqRegiCfg.pfnCallback = &dma_ch1_tx_tc_cb; /* DMA传输完成中断回调函数 */
stcIrqRegiCfg.enIntSrc = DMA1_CH1_TC_NUM; /* DMA1通道1完成中断向量号 */
enIrqRegistration (&stcIrqRegiCfg);
NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_00);
NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
/* 使能DMA1通道1,这个可以通过参考手册查阅uart对应的dma通道 */
DMA_ChannelCmd (M4_DMA1, DmaCh1, Enable);
/* 使能串口发送 */
USART_FuncCmd (M4_USART2, UsartTx, Enable);
return 0;
}
/* dma接收完成中断回调函数 */
static void dma_ch0_rx_tc_cb (void)
{
DMA_ClearIrqFlag (M4_DMA1, DmaCh0, BlkTrnCpltIrq);
}
/* dma发送完成中断回调函数 */
static void dma_ch1_tx_tc_cb (void)
{
DMA_ClearIrqFlag (M4_DMA1, DmaCh1, BlkTrnCpltIrq);
extern void uart_evt_w_tc_send (void);
uart_evt_w_tc_send ();
}
/* gpio初始化 */
static int hal_uart_gpio_init (void)
{
PORT_SetFunc (PortA, Pin10, Func_Usart2_Rx, Disable);
PORT_SetFunc (PortA, Pin09, Func_Usart2_Tx, Disable);
return 0;
}
/******************************************************************************************************
* name : hal_uart_init
* fuction : uart底层硬件初始化
* author : cong
* time : 2020-11-24
* input : re_buf --> 注册接收缓存
* len --> 缓存长度
* baude --> 波特率
* output :
* version : v1.00
* other :
******************************************************************************************************/
int hal_uart_init (uint8_t *re_buf, uint16_t len, uint32_t baude)
{
int ret = 0;
uart_parm.rx_buf = re_buf;
uart_parm.rx_len = len;
timer0b_init ();
hal_uart_gpio_init ();
ret = hal_uart_conf_init (baude);
hal_uart_dma_init ();
uart_dma_rx_start (uart_parm.rx_buf, uart_parm.rx_len);
return ret;
}
|
|