STM32 DMA定长发送中断完成机制详解

代码测试完成,可以正常运行。

DMA发送需用到串口,故需配置串口。

串口配置

串口配置很简单,照抄就行,这里用的正点原子的源码。

串口配置主要分为定义句柄、开启时钟、配置IO并写入相关寄存器、配置串口模式、开启中断(本代码没有用到接收,所以无所谓)、

  • 定义相关句柄

  • GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
  • 开启时钟(USART1是APB2时钟,IO口是PA9 PA10,A组IO口时钟也是APB2,故一起设置)

  • RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);    //使能USART1,GPIOA时钟
  • 配置IO

  •  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //复用推挽输出
     GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
     GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  
  • 配置串口模式

  • USART_InitStructure.USART_BaudRate = bound;//串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;    //收发模式
  • 根据上述配置,初始化串口

  • USART_Init(USART1, &USART_InitStructure); //初始化串口1
    USART_Cmd(USART1, ENABLE);                    //使能串口1 

    需使用DMA,故需在串口处使能DMA传输

  • 使能DMA传输

  • USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);

    DMA配置

    到此处,串口配置完毕,接下来配置DMA。

    因为使用的是串口1,根据手册查询到对应的DMA为DMA1 4通道(TX)和DMA1 5通道(RX),只使用了TX串口,只需配置TX即可(即DMA1 4通道)。

    根据结构体及成员相关解释,配置DMA1 4通道

    先配置NVIC中断。

    void DMA1_NVIC_Init(void)
    { 
        NVIC_InitTypeDef NVIC_InitStructure;
            //DMA1通道4 NVIC 配置
      NVIC_InitStructure.NVIC_IRQChannel =  DMA1_Channel4_IRQn;    
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级3
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        //子优先级3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
        
    //        //DMA1通道5 NVIC 配置
    //    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;                //NVIC通道设置
    //    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0 ;                //抢占优先级
    //    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;                        //子优先级
    //    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                            //IRQ通道使能
    
        NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器
    }

    配置DMA

  • 定义句柄

  • DMA_InitTypeDef DMA_InitStructure;
  • 开启DMA1时钟

  • RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);    //使能DMA传输
  • 调用NVIC中断函数,提前配置DMA1 4通道中断

  • DMA1_NVIC_Init();   //DMA通道配置中断
  • 根据DMA结构体 配置DMA1 4通道

  • 先调用函数,清除DMA1 4通道配置,配置完成后调用函数

    DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); //DMA通道4传输完成中断

    配置DMA1 4通道传输完成中断,使能DMA1 4通道。

    DMA_DeInit(DMA1_Channel4);  //重置DMA1 4通道  USART1 TX
    /********************************传输方向*****************************/    
        DMA_InitStructure.DMA_PeripheralBaseAddr=(u32)&USART1->DR;
        DMA_InitStructure.DMA_MemoryBaseAddr=(u32) usart_txbuf;  //自定义数组
        DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
        
    /********************************数据大小及模式*****************************/        
        DMA_InitStructure.DMA_BufferSize=USART1_MAX_TX_LEN;   //数组大小
        DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
        DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
        DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
        DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
        
    /********************************传输模式*****************************/    
        DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
        DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
        
        DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
        
        DMA_Init(DMA1_Channel4,&DMA_InitStructure);
        DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);        //DMA通道4传输完成中断
        DMA_Cmd(DMA1_Channel4,ENABLE);
  • 配置4通道中断函数

  • //DMA1通道4中断,判断上次数据串口是否已经发送完成
    void DMA1_Channel4_IRQHandler(void)
    {
        if(DMA_GetITStatus(DMA1_IT_TC4)!= RESET)    //DMA接收完成标志
        {
            LED1=!LED1;
            DMA_ClearITPendingBit(DMA1_IT_TC4);     //清除中断标志 
        }
    }

    到此,DMA1 4通道配置完成。

    主函数

    u16 t;
     int main(void)
     { 
        u16 i=0;
        delay_init();             //延时函数初始化
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
        uart_init(115200);         //串口初始化为9600
        LED_Init();                  //初始化与LED连接的硬件接口     
        DMA_init();
         for(i=0;i<USART1_MAX_RX_LEN;i++)
         {
         usart_txbuf[i]=i;
         }
         
        while(1)
        {    
            delay_ms(1000);
            t++;
            if(t%5==0){
            dma1_channel4();
            }
        }
    }

    注意事项

    DMA发送完成一次后,需重新设置发送长度才可以再次使用。

    /***********************设置DMA1 4缓存大小***********************/
    void dma1_channel4(void)
    {
         DMA_Cmd(DMA1_Channel4,DISABLE);   //关闭USART1 TX DMA1 所指示的通道  
         DMA_SetCurrDataCounter(DMA1_Channel4,USART1_MAX_RX_LEN);//DMA通道的DMA缓存的大小
         DMA_Cmd(DMA1_Channel4, ENABLE);     //使能USART1 TX DMA1 所指示的通道        
    }

    使用DMA中断,需提前使能,才可以进入中断。我把串口DMA使能放在串口的初始化函数里,所以串口初始化需在DMA初始化函数之前。

    注意代码执行顺序及代码逻辑,无法进入中断的问题找了好久。

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 DMA定长发送中断完成机制详解

    发表评论