| 本帖最后由 鸡蛋鸭蛋荷包蛋 于 2023-2-1 13:40 编辑 
 #技术资源#
 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时钟
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;    //收发模式
需使用DMA,故需在串口处使能DMA传输USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_Cmd(USART1, ENABLE);                    //使能串口1 
DMA配置USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
到此处,串口配置完毕,接下来配置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;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);    //使能DMA传输
先调用函数,清除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);
到此,DMA1 4通道配置完成。//DMA1通道4中断,判断上次数据串口是否已经发送完成
void DMA1_Channel4_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC4)!= RESET)    //DMA接收完成标志
    {
        LED1=!LED1;
        DMA_ClearITPendingBit(DMA1_IT_TC4);     //清除中断标志 
    }
}
主函数
 
 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初始化函数之前。
 注意代码执行顺序及代码逻辑,无法进入中断的问题找了好久。 
 |