打印
[其他]

华大HC32F460之串口超时中断使用

[复制链接]
2354|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
aoyi|  楼主 | 2022-11-1 14:18 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
因为缺芯问题,近期一直在折腾换国产芯片,从灵动微,锦瑞,瑞萨。。。一直到华大,用到了串口功能,这里分享一下华大的串口使用
串口使用我用过三种方案:

       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;
}

使用特权

评论回复
沙发
tpgf| | 2024-2-4 11:27 | 只看该作者
在什么情况下串口会产生超时中断呢

使用特权

评论回复
板凳
磨砂| | 2024-2-4 11:55 | 只看该作者
应该把所有能考虑到的情况都做一下处理

使用特权

评论回复
地板
八层楼| | 2024-2-4 12:25 | 只看该作者
如果产生串口中断的话 之前的数据还要不要呢

使用特权

评论回复
5
caigang13| | 2024-2-4 12:34 | 只看该作者
如果通过库函数不能直接看出怎么配的话,那就有点麻烦了。

使用特权

评论回复
6
晓伍| | 2024-2-4 12:51 | 只看该作者
这里的超时是说发送超时还是接收超时呢

使用特权

评论回复
7
观海| | 2024-2-4 13:27 | 只看该作者
中断回调函数是不是就是中断处理函数啊

使用特权

评论回复
8
guanjiaer| | 2024-2-4 19:45 | 只看该作者
为什么发送使能里边的代码那么多呢 如果多次调用岂不是很浪费资源?

使用特权

评论回复
9
xdvca| | 2024-7-31 22:42 | 只看该作者
直接使用串口接收中断,每接收到一个字节触发一次中断,容易实现数据接收和处理逻辑。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

101

主题

3307

帖子

3

粉丝