打印
[综合信息]

HC32F460 串口 DMA 发送 接收

[复制链接]
5242|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
晓伍|  楼主 | 2021-8-1 15:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
前言

利用DMA 进行串口读写,主要内容包括:
DMA 通道 *2
串口发送空中断,发送完成中断
串口接收中断,接收超时中断
定时器 (串口接收超时定时器 )

发送/接收功能 比较


接收超时定时器通道选择
默认关系,无法任意选择通道,根据实际使用的串口确认定时器通道
TIMEOUT 计数器采用Timer0 模块的计数器,具体对应关系如下:
USART1:Timer0 Unit1 A 通道
USART2:Timer0 Unit1 B 通道
USART3:Timer0 Unit2 A 通道
USART4:Timer0 Unit2 B 通道



使用特权

评论回复
沙发
晓伍|  楼主 | 2021-8-1 15:25 | 只看该作者
串口初始化

/**
*******************************************************************************
** \brief  Usart_init function of project
**
** \param  None
**
** \retval void
**
******************************************************************************/
void Usart_init(void)
{
    en_result_t enRet = Ok;
    stc_irq_regi_conf_t stcIrqRegiCfg;
          stc_port_init_t stc_485_RE_PortInit,stc_485_TX_PortInit;
    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,//时钟源可选:内部时钟源(内部波特率生成器生成的时钟)/外部时钟源( CKn管脚输入的时钟)
        UsartClkDiv_1,
        UsartDataBits8,//数据长度可编程:8位/9位
        UsartDataLsbFirst,
        UsartOneStopBit,//停止位可配置:1 位/2位
        UsartParityNone,//校验功能可配置:奇校验/偶校验/无校验
        UsartSampleBit8,
        UsartStartBitFallEdge,
        UsartRtsEnable,
    };

    /* Initialize Timer0 */
    Timer0Init();

        /* Initialize DMA */
    DmaInit();
        DmaInit_Tx();

    /* Enable peripheral clock */
    PWC_Fcg1PeriphClockCmd(u32Fcg1Periph, Enable);

    /* Initialize USART IO */

    PORT_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_FUNC, Disable);
        MEM_ZERO_STRUCT(stc_485_TX_PortInit);
        stc_485_TX_PortInit.enPullUp=Enable;
        PORT_Init(USART_TX_PORT,USART_TX_PIN,&stc_485_TX_PortInit);
    PORT_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_FUNC, Disable);

    /* configuration RS485_RE structure initialization */
    MEM_ZERO_STRUCT(stc_485_RE_PortInit);
    stc_485_RE_PortInit.enPinMode = Pin_Mode_Out;
    PORT_Init(USART_RE_PORT, USART_RE_PIN, &stc_485_RE_PortInit);               
        PORT_ResetBits(USART_RE_PORT, USART_RE_PIN);
               
    /* Initialize UART */
    enRet = USART_UART_Init(USART_CH, &stcInitCfg);
    if (enRet != Ok)
    {
        while (1)
        {
        }
    }

    /* Set baudrate */
    enRet = USART_SetBaudrate(USART_CH, USART_BAUDRATE);
    if (enRet != Ok)
    {
        while (1)
        {
        }
    }

    /* Set USART RX IRQ */
    stcIrqRegiCfg.enIRQn = USART_RI_IRQn;
    stcIrqRegiCfg.pfnCallback = &UsartRxIrqCallback;
    stcIrqRegiCfg.enIntSrc = USART_RI_NUM;
    enIrqRegistration(&stcIrqRegiCfg);
    NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
    NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
    NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

    /* Set USART RX error IRQ */
    stcIrqRegiCfg.enIRQn = USART_EI_IRQn;
    stcIrqRegiCfg.pfnCallback = &UsartErrIrqCallback;
    stcIrqRegiCfg.enIntSrc = USART_EI_NUM;
    enIrqRegistration(&stcIrqRegiCfg);
    NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
    NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
    NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

    /* Set USART RX timeout error IRQ */
    stcIrqRegiCfg.enIRQn = USART_RTO_IRQn;
    stcIrqRegiCfg.pfnCallback = &UsartTimeoutIrqCallback;
    stcIrqRegiCfg.enIntSrc = USART_RTO_NUM;
    enIrqRegistration(&stcIrqRegiCfg);
    NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
    NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
    NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

    /* Set USART TX IRQ */
    stcIrqRegiCfg.enIRQn = USART_TI_IRQn;
    stcIrqRegiCfg.pfnCallback = &UsartTxIrqCallback;
    stcIrqRegiCfg.enIntSrc = USART_TI_NUM;
    enIrqRegistration(&stcIrqRegiCfg);
    NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
    NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
    NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

    /* Set USART TX complete IRQ */
    stcIrqRegiCfg.enIRQn = USART_TCI_IRQn;
    stcIrqRegiCfg.pfnCallback = &UsartTxCmpltIrqCallback;
    stcIrqRegiCfg.enIntSrc = USART_TCI_NUM;
    enIrqRegistration(&stcIrqRegiCfg);
    NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
    NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
    NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

    /*Enable RX && RX interupt && timeout interrupt function*/
                USART_FuncCmd(USART_CH, UsartNoiseFilter,Enable);

    USART_FuncCmd(USART_CH, UsartRx, Enable);
    USART_FuncCmd(USART_CH, UsartRxInt, Enable);
    USART_FuncCmd(USART_CH, UsartTimeOut, Enable);
    USART_FuncCmd(USART_CH, UsartTimeOutInt, Enable);
    /*Enable TX && RX && RX interrupt function*/

}


使用特权

评论回复
板凳
晓伍|  楼主 | 2021-8-1 15:26 | 只看该作者
DMA初始化


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.u32SrcAddr = ((uint32_t)(&USART_CH->DR)+2ul); /* Set source address. */
    stcDmaInit.u32DesAddr = (uint32_t)(&m_stcRingBuf.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. */
          stcDmaInit.u16DesRptSize=m_stcRingBuf.u16Capacity;
       
           /* Disable linked list transfer. */
    stcDmaInit.stcDmaChCfg.enLlpEn = Disable;
                 /* Enable repeat function. */
    stcDmaInit.stcDmaChCfg.enDesRptEn = Enable;
               
    DMA_InitChannel(DMA_UNIT, DMA_CH, &stcDmaInit);

    /* 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);
}
static void DmaInit_Tx(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_TX,Enable);

    /* Initialize DMA. */
    MEM_ZERO_STRUCT(stcDmaInit);
    stcDmaInit.u16BlockSize = 1u; /* 1 block */
    stcDmaInit.u32SrcAddr =  (uint32_t)(&m_stcTingBuf.au8Buf); /* Set source address. */
    stcDmaInit.u32DesAddr = ((uint32_t)(&USART_CH->DR));     /* Set destination address. */ //TDR
    stcDmaInit.stcDmaChCfg.enSrcInc = AddressIncrease;  /* Set source address mode. */
    stcDmaInit.stcDmaChCfg.enDesInc = AddressFix;  /* Set destination address mode. */
    stcDmaInit.stcDmaChCfg.enIntEn = Enable;       /* Enable interrupt. */
    stcDmaInit.stcDmaChCfg.enTrnWidth = Dma8Bit;   /* Set data width 8bit. */
          stcDmaInit.u16TransferCnt=11;//根据长度  
    DMA_InitChannel(DMA_UNIT_TX, DMA_CH_TX, &stcDmaInit);

    /* Enable the specified DMA channel. */
    DMA_ChannelCmd(DMA_UNIT_TX, DMA_CH_TX, Enable);

    /* Clear DMA flag. */
    DMA_ClearIrqFlag(DMA_UNIT_TX, DMA_CH_TX, TrnCpltIrq);

    /* Enable peripheral circuit trigger function. */
    PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS,Enable);

    /* Set DMA trigger source. */
    DMA_SetTriggerSrc(DMA_UNIT_TX, DMA_CH_TX, DMA_TRG_SEL_TX);

    /* Set DMA transfer complete IRQ */
    stcIrqRegiCfg.enIRQn = DMA_BTC_INT_IRQn_TX;
    stcIrqRegiCfg.pfnCallback = &DmaTcIrqCallback_TX;
    stcIrqRegiCfg.enIntSrc = DMA_BTC_INT_NUM_TX;
    enIrqRegistration(&stcIrqRegiCfg);
    NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
    NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
    NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
}


使用特权

评论回复
地板
晓伍|  楼主 | 2021-8-1 15:27 | 只看该作者
定时器初始化
/**
*******************************************************************************
** \brief Initliaze Timer0.
**
** \param [in] None
**
** \retval None
**
******************************************************************************/
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);
    CLK_LrcCmd(Enable);
       
    /* Clear CNTAR register for channel B */
//    TIMER0_WriteCntReg(TMR_UNIT, Tim0_ChannelA, 0u);
    TIMER0_WriteCntReg(TMR_UNIT, Tim0_ChannelB, 0u);

    /* Config register for channel B */
    stcTimerCfg.Tim0_CounterMode = Tim0_Async;
    stcTimerCfg.Tim0_AsyncClockSource = Tim0_LRC;
    stcTimerCfg.Tim0_ClockDivision = Tim0_ClkDiv8;//32/8   =4K   0.25ms
    stcTimerCfg.Tim0_CmpValue = 2;//0.25*2=0.5ms    3.5个字节
    TIMER0_BaseInit(TMR_UNIT, Tim0_ChannelB, &stcTimerCfg);

    /* Clear compare flag */
    TIMER0_ClearFlag(TMR_UNIT, Tim0_ChannelB);

    /* 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_ChannelB, &StcTimer0TrigInit);
}


使用特权

评论回复
5
晓伍|  楼主 | 2021-8-1 15:28 | 只看该作者
串口DMA 发送
en_result_t UART_Transmit_DMA(M4_USART_TypeDef *huart, uint8_t *pData, uint16_t Size)
{
        if(DMA_TxComplete==0)
        {
                  if ((pData == NULL) || (Size == 0U))
    {
      return Error;
    }
      DMA_TxComplete=1;
                        DMA_SetSrcAddress (DMA_UNIT_TX,DMA_CH_TX,(uint32_t)pData);
                        DMA_SetTransferCnt(DMA_UNIT_TX,DMA_CH_TX, Size);
                        PORT_SetBits(USART_RE_PORT, USART_RE_PIN);
                        while( PORT_GetBit(USART_RE_PORT, USART_RE_PIN)!=1){}
                        DMA_ChannelCmd(DMA_UNIT_TX, DMA_CH_TX, Enable);                       
                        USART_FuncCmd(huart, UsartTxAndTxEmptyInt, Enable);       
                        return Ok;
        }
          else
  {
    return ErrorNotReady;
  }
}


使用特权

评论回复
6
晓伍|  楼主 | 2021-8-1 15:29 | 只看该作者
DMA_TX发送流程图


使用特权

评论回复
7
晓伍|  楼主 | 2021-8-1 15:29 | 只看该作者
发送相关中断
/**
*******************************************************************************
** \brief DMA block transfer complete irq callback function.
**
** \param [in] None
**
** \retval None
**
******************************************************************************/
static void DmaTcIrqCallback_TX(void)
{

        DMA_ClearIrqFlag(DMA_UNIT_TX, DMA_CH_TX, TrnCpltIrq);
    DMA_ChannelCmd(DMA_UNIT_TX, DMA_CH_TX, Disable);
    USART_FuncCmd(USART_CH, UsartTxEmptyInt, Disable);
    USART_FuncCmd(USART_CH, UsartTxCmpltInt, Enable);

}
/**
*******************************************************************************
** \brief USART TX complete irq callback function.
**
** \param [in] None
**
** \retval None
**
******************************************************************************/
static void UsartTxCmpltIrqCallback(void)
{
    USART_FuncCmd(USART_CH, UsartTx, Disable);
    USART_FuncCmd(USART_CH, UsartTxCmpltInt, Disable);
        PORT_ResetBits(USART_RE_PORT, USART_RE_PIN);
        while( PORT_GetBit(USART_RE_PORT, USART_RE_PIN)!=0){}       
        DMA_TxComplete=0;
}
static void UsartTxIrqCallback(void)
{
//    uint8_t u8Data = 0u;

//    if (Ok == RingBufRead(&m_stcRingBuf, &u8Data))
//    {
//                                PORT_SetBits(USART_RE_PORT, USART_RE_PIN);
//        USART_SendData(USART_CH, (uint16_t)u8Data);
                         USART_SendData(USART_CH, 0x55);
//    }
//    if (IS_RING_BUFFER_EMPTY(&m_stcRingBuf))
//    {
//        USART_FuncCmd(USART_CH, UsartTxEmptyInt, Disable);
//        USART_FuncCmd(USART_CH, UsartTxCmpltInt, Enable);
//    }
}


使用特权

评论回复
8
晓伍|  楼主 | 2021-8-1 15:30 | 只看该作者
串口DMA 接收DMA_RX 流程图

使用特权

评论回复
9
晓伍|  楼主 | 2021-8-1 15:31 | 只看该作者
接收相关中断
static void DmaBtcIrqCallback(void)
{
          DMA_ClearIrqFlag(DMA_UNIT, DMA_CH, BlkTrnCpltIrq);
    RingBufWrite_DMA(&m_stcRingBuf);
}
static void UsartTimeoutIrqCallback(void)//
{
    TIMER0_Cmd(TMR_UNIT, Tim0_ChannelB,Disable);
    USART_ClearStatus(USART_CH, UsartRxTimeOut);
//          DMA_SetDesAddress(DMA_UNIT, DMA_CH, (uint32_t)(&m_stcRingBuf.au8Buf));//  Reset DesAddress       
          DMA_RxComplete=1;
}


使用特权

评论回复
10
yangjiaoshai| | 2021-9-2 14:39 | 只看该作者
做个标记

使用特权

评论回复
11
skyred| | 2021-9-2 15:36 | 只看该作者
串口行,DMA不熟

使用特权

评论回复
12
binoo7| | 2022-4-14 12:55 | 只看该作者
写的挺多的,给楼主总结一下操作过程 ,第一步配置串口,第二步根据串口配置定时器,第三步配置DMA,第四步串口中断接收或者发送,第五步,接收完成清除DMA。华大这个串口超时是需要自己配置定时器的,和STM32不同,每个定时器对应不同的串口,

使用特权

评论回复
13
mdc9| | 2022-4-21 12:52 | 只看该作者
看到手册上说 “触发选择寄存器(TMR0_HTSSR)是一个独立的寄存器,为 2 个单元的 Timer0 所
共有。” 同时串口 TIME—OUT功能又是对应使用了定时器0,这么说 是不是就不能同时使用串口 1 和 3的TIME—OUT功能了

定时器 0 两个单元 4个通道 可以单独作为4个定时器独立使用 么?  独立启动、关闭...

使用特权

评论回复
14
mdc9| | 2022-4-21 12:54 | 只看该作者
binoo7 发表于 2022-4-14 12:55
写的挺多的,给楼主总结一下操作过程 ,第一步配置串口,第二步根据串口配置定时器,第三步配置DMA,第四步 ...

可以同时使用4个串口 超时接收功能么?   
因为看到用户手册有提到    “触发选择寄存器(TMR0_HTSSR)是一个独立的寄存器,为 2 个单元的 Timer0 所共有。”
同时串口 TIME—OUT功能又是对应使用了定时器0,这么说 是不是就不能同时使用串口 1 和 3的TIME—OUT功能了

定时器 0 两个单元 4个通道 可以单独作为4个定时器独立使用 么?  独立启动、关闭...

使用特权

评论回复
15
mdc9| | 2022-4-21 13:22 | 只看该作者
晓伍 发表于 2021-8-1 15:27
定时器初始化
/**
*******************************************************************************



定时器 比较计数值 手册上说的这么计算,到底是怎么回事?  看手册意思是  不管定时器时钟源是什么 都是按  定义的超时接收到的 bits 为单位计算的。
意思就是 如果超时定义为  3.5个字符 ,
那么RTB=3.5*(1起始+8数据+1停止)=3.5*10= 35 bits;
比较计数值CMPA = 35/分频数- 定值(根据分频不同,对应为 4,2,1);
假如分频为8;
则CMPA = 35/ 8 -1 = 3

请问:
1、是这个意思么?  上述计算对么?  


2、 楼主给出的例程 为什么是 直接按照定时器时钟来计算呢?
      是直接用超时时间值 / 定时器时钟周期计算来的吧。

使用特权

评论回复
16
嵌入式栗| | 2023-3-6 15:38 | 只看该作者
请问下为什么源地址最后要加个 2呢?     stcDmaInit.u32SrcAddr = ((uint32_t)(&USART_CH->DR)+2ul); /* Set source address. */      

使用特权

评论回复
17
嵌入式栗| | 2023-3-6 15:40 | 只看该作者
请问下为什么源地址最后要加个 2呢?     stcDmaInit.u32SrcAddr = ((uint32_t)(&USART_CH->DR)+2ul); /* Set source address. */      

使用特权

评论回复
18
hearstnorman323| | 2023-4-7 09:37 | 只看该作者
串口dma缓冲区是先进先出吗               

使用特权

评论回复
19
youtome| | 2023-4-7 23:00 | 只看该作者
串口的DMA串口传输完成中断怎么理解

使用特权

评论回复
20
51xlf| | 2023-4-8 13:34 | 只看该作者
dma发送串口数据会延后吗               

使用特权

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

本版积分规则

60

主题

4113

帖子

1

粉丝