×

STM32F103C8T6------DMA+串口IDLE空闲中断

zxjy辉 zxjy辉 发表于2023-04-06 11:18:48 浏览361 评论0

抢沙发发表评论

使用DMA+串口IDLE中断,可以实现,DMA不定长接收数据

串口配置初始化:

void usart1_config( u32 Baud, u8 IT_Rx )
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE ); //配置串口1时钟
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); //配置GPIO时钟
    
    /*串口1引脚设置*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;    //配置A9为Tx
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置为推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //设置输出频率为50MHz
    GPIO_Init( GPIOA, &GPIO_InitStructure ); //写入寄存器
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //配置A10为Rx
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;    //设置为浮空输入
    GPIO_Init( GPIOA, &GPIO_InitStructure ); //写入寄存器
    
    /*串口1模式设置*/
    USART_InitStructure.USART_BaudRate = Baud; //设置波特率
    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 ); //向寄存器写入配置参数
    
  if( IT_Rx ) //如果为1则设置为串口1接收中断
    {
        usart1_NVIC_configuration();                      //串口1中断模式配置
//        USART_ITConfig( USART1, USART_IT_RXNE,ENABLE ); //使能串口1接收中断 配置串口DMA接收,就不要再使用串口中断接收,会导致数据接收错误
        USART_ITConfig( USART1,USART_IT_IDLE, ENABLE);    //使能串口1空闲中断

    }
    USART_Cmd( USART1, ENABLE ); //使能串口1
}

/***************************************************************************
 *函数名:USART1_NVIC_Configuration
 *描述:配置USART1接收中断
 *输入:无
 *输出:无
 *调用:内部调用
***************************************************************************/
void usart1_NVIC_configuration( void )
{
    NVIC_InitTypeDef NVIC_InitStructure; 
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );        //设置中断组别为4组,如果设置过了,就不要再设置,一个程序,只能设置一次
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; //主优先级6
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断
    NVIC_Init( &NVIC_InitStructure ); //写入寄存器
}

串口中断函数:

图片.png

void USART1_IRQHandler(void){
    u8 clearflag = 0;
    if( USART_GetITStatus( USART1, USART_IT_IDLE) != RESET ){       //总线空闲,表示数据接收完成
//        USART_ClearFlag( USART1, USART_FLAG_IDLE );               //清除标志,这个清除无法使用,如果使用这个方法,是清除不掉标志位的(如果开启串口接收中断,然后用这个则可以清除标志位)
//        USART_ClearITPendingBit(USART1,USART_FLAG_IDLE);        
            clearflag = USART1 ->SR;                                //只能通过读取标志位的方式将数据清零
            clearflag = USART1 ->DR;
//            
        printf("接收完成");
        flag_send = 1;
      //重新初始化DMA,等待下一次接收数据,也就是接着从0开始接收  
      USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
            DMA_Cmd( DMA1_Channel5, DISABLE );
            DMA_SetCurrDataCounter( DMA1_Channel5,5 );  //这个长度最好是和数组的长度一样,这个代表着接收到5个数据后又回从0开始(覆盖),也就是一次传输多少个数据
            DMA_Cmd( DMA1_Channel5, ENABLE );
    
    }        
    
}

注意事项:需要注意的就是在中断函数中,需要注意清除标志位的方法,以及每一次DMA接收完都需要重新开启DMA接收

DMA初始化

void  DMA_Config(){
    
    DMA_InitTypeDef DMA_InitStructure;
    
    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );               //启动DMA时钟
    
    DMA_DeInit( DMA1_Channel5 );                                       //将通道设置为缺省
    
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;  //外设地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Data;             //内存地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                 //将外设地址设置为源地址,也就是数据由外设到内存 
  DMA_InitStructure.DMA_BufferSize = 0;                            //设置DMA缓冲区大小为128个 字节/半字/字
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设的数据宽度为字节
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;    //内存的数据宽度为字节
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   //外设地址,不增加
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;            //内存地址,增加,一次加1
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                       //不是内存到内存
    DMA_InitStructure.DMA_Mode =DMA_Mode_Circular;                       //正常传输,也就是传输一次完成后,不在重复传输
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;              //设置优先级为中等
    
    DMA_Init( DMA1_Channel5, &DMA_InitStructure );
    }

启动DMA,开始接收数据

///启动DMA
void DMA_Start(){    
    // USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
      USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
      DMA_Cmd( DMA1_Channel5, DISABLE );
      DMA_SetCurrDataCounter( DMA1_Channel5,5 );  //这个长度最好是和数组的长度一样,这个代表着接收到5个数据后又回从0开始(覆盖),也就是一次传输多少个数据
      DMA_Cmd( DMA1_Channel5, ENABLE );
        
    
    while( 1 ){
//    if( DMA_GetFlagStatus( DMA1_IT_TC5 ) == SET ){  //这个标志位和DMA_SetDataCurrenter中的数据长度有关,这里的完成是指:如果设置为10,那么传输10个就表示完成了,就置位了
//        printf("传输完成\r\n");
//        printf( "%s \r\n", Data);
//        DMA_ClearFlag( DMA1_IT_TC5 );
//    }
//    if( DMA_GetFlagStatus( DMA1_IT_HT5 ) == SET ){
//        printf("传输一半\r\n");
//        printf( "%s \r\n", Data);
//        DMA_ClearFlag( DMA1_IT_HT5 );
//    }
    //////如果串口空闲,就代表传输完成,可以继续下一次接受了
    if( SReceive_OK == 1 ){
        SReceive_OK = 0;
        printf("%s",Data);
//        ///重新初始化DMA
//        USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
//      DMA_Cmd( DMA1_Channel5, DISABLE );
//      DMA_SetCurrDataCounter( DMA1_Channel5,5 );  //这个长度最好是和数组的长度一样,这个代表着接收到5个数据后又回从0开始(覆盖),也就是一次传输多少个数据
//      DMA_Cmd( DMA1_Channel5, ENABLE );
        
    }

//    printf("当前传输进度:%0.2f %c", ( 1 - DMA_GetCurrDataCounter( DMA1_Channel5 )/ 10.0) * 100 , bfh );
//    printf( "%s \r\n", Data);
    //memset(USART_RX_BUF,,);    //清空数组
    }
}


#好好学习!

群贤毕至

访客