STM32F0 USART 常用配置,DMA不定长接收,pingpong接收,硬件流控制,过采样,接收超时中断和空闲中断

USART 是Universal synchronous asynchronous receiver transmitter(通用同步和异步收发器)的简写,这里的同步和异步是根据是否同用一个信号时钟区分,如常见的串口通信,就是异步,SPI是同步。

 

一、串口的初始化

/*************************************************

uart1
配置串口一的发送和接收的GPIO口功能,以及中断

**************************************************/
void uart1_init(u32 pclk,u32 baud)
 {     
        GPIO_InitTypeDef  GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef  NVIC_InitStructure;
   
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);		//Tx
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);		//Rx
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_1);		//RTS/CTS
        
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9 |GPIO_Pin_10|GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;	
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
     
        
    //配置串口中断优先级
	NVIC_InitStructure.NVIC_IRQChannel                   = USART1_IRQn; 
	NVIC_InitStructure.NVIC_IRQChannelPriority           = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
	NVIC_Init(&NVIC_InitStructure);        

        USART_DeInit(USART1); 
	USART_InitStructure.USART_BaudRate            = baud;                              //波特率
	USART_InitStructure.USART_WordLength          = USART_WordLength_8b;                //数据长度8
	USART_InitStructure.USART_StopBits            = USART_StopBits_1;                   //1个停止位
	USART_InitStructure.USART_Parity              = USART_Parity_No;                    //无奇偶校验
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_RTS_CTS;     //硬件流控制
	USART_InitStructure.USART_Mode                = USART_Mode_Tx | USART_Mode_Rx;
        

        
        USART_DECmd(USART1,ENABLE); //485硬件控制驱动使能
        USART_DEPolarityConfig(USART1,USART_DEPolarity_High); //DE 触发后极性
        USART_SetDEAssertionTime(USART1,4);  
        USART_SetDEDeassertionTime(USART1,4);    
        
        
        USART_OverSampling8Cmd(USART1,ENABLE);   //设置过采样率为 8  默认为16        
	    USART_Init(USART1, &USART_InitStructure);
			
	//USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//使能串口空闲中断
    //USART_ClearITPendingBit(USART1, USART_IT_IDLE);//清除串口空闲中断标志位
        
        
        USART_SetReceiverTimeOut(USART1,2);//空闲2bit后出发中断
        
        USART_ITConfig(USART1,USART_IT_RTO,ENABLE);//使能接收超时中断
        
        USART_ReceiverTimeOutCmd(USART1, ENABLE);
        
        USART_ClearITPendingBit(USART1, USART_IT_RTO);
        
	    USART_Cmd(USART1, ENABLE);//使能串口    
        
        DMA1_Config();
        
        StaFlag.D_A_Pro = BUF_NO2;//首次进来让 dma使用数组2
        
        StaFlag.Busy_Buf = BUF_NO2;
 }

DMA不定长接收,pingpong接收

        所谓DMA不定长接收是 作为接收方的单片机不知道 主机发送的 通信命令字节长度,因此需要完全接收,然后再解析。一个命令通常包含十几~几十个甚至几百个字节,通常都会有相应的协议规定,命令头,命令长度,校验位,命令大小,命令的参数内容等等。通常人们会将一整条命令称为一个包或者一帧。

        高速通信的情况下,用接收中断一字节一字节的接收,会占用大量CPU的时间,而且存在丢数据的可能,因此一般采用DMA进行接收。

        DMA是什么不再赘述,只需要知道它是个“搬运工”,设置好要去那里“搬运”,“搬运”到那里,它就会不断的干活。

        每当接收完成一个字节后,DMA控制模块,会将这个字节移动我们设置好的内存区域(自定义的数组),当整条命令接收完成后,总线会空闲一段时间,我们可以设置串口空闲中断或者接收超时中断,当总线不再传输数据是,就会进入中断,在中断中我们搞个标志位标志一下,然后在主函数中,去解析这条命令。

        有时候主机发送实在是太快了,单片机处理到一半,又来数据了,如果只有一个接收数组,新的命令会同样会被搬运到 这个数组上,造成覆盖。此时解析命令就会出现混乱。为此需要进行pingpong接收,pingpong接收是指采用两个数组进行接收,,数组1接收完成一个包后,如果此时又有数据来了,新的数据将“搬运”到数组2,数组2接收完成一个包后,下一个包的数据将“搬运”到数组1,像来回的乒乓球。

下面为DMA接收的初始化:

extern u8 Getfinsh_BUF;	
extern u8 Busy_Buf_flag;       // 用于切换两个数组PINGPONG接收

u8 USART_RX_BUF1[USART_RX_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
u8 USART_RX_BUF2[USART_RX_LEN];     //接收缓冲,最大USART_REC_LEN个字节.

u16 USART_RX_len=0;       //接收状态标记(字节数)

// 用USART_RX_BUF2来做发送数组,
u8 USART1_TX_BUF[USART_TX_LEN];     //发送缓冲,最大USART_TX_LEN个字节.
/*************************************************

DMA1
用

**************************************************/
static DMA_InitTypeDef DMA_InitStructure;
void DMA1_Config(void)
{
    NVIC_InitTypeDef    NVIC_InitStructure;	
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);      //使能SYSCFG时钟,必须的	
     //和SPI1 DMA冲突,重映射
    
    SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_USART1Tx, ENABLE);//将DMA串口发送从通道2重映射到通道4
    SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_USART1Rx, ENABLE);//将DMA串口发送从通道3重映射到通道5
    
	  /*****DMA发送配置 Memory->Peripheral *****************/
    DMA_DeInit(DMA1_Channel4);                       //将DMA1_Channel4信道寄存器初始化为默认的重置值。
    DMA_InitStructure.DMA_BufferSize = USART_RX_LEN;   //设置DMA1的Buffer缓冲区大小,DMA1_MEM_LEN_RX为自定义的变量 
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;       //关闭内存到内存
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;      //正常模式
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //内存到外设
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //优先级非常高
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->TDR;   //设置外设接收地址 
    DMA_InitStructure.DMA_PeripheralInc =  DMA_PeripheralInc_Disable;    //外设地址不变
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;      //设置内存数据长度以Byte为单位
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF2;             //设置内存发送地址,DMA_Tx为自定义变量
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //设置外设数据长度以Byte为单位
    DMA_Init(DMA1_Channel4,&DMA_InitStructure);
    
    DMA_ClearITPendingBit(DMA1_IT_TC4); //清除DMA_ch4传输完成中断标志 		
    DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//使能DMA1_CH4传输完成中断		
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);//发送数组和接收数组2 共用一个
	
   /*****DMA接收配置 Peripheral->Memory*****************/
    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_BufferSize = USART_RX_LEN;  
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;    
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;   
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; 
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->RDR;    
    DMA_InitStructure.DMA_PeripheralInc =  DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; 
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF2;  
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_Init(DMA1_Channel5,&DMA_InitStructure);
    
    DMA_ClearITPendingBit(DMA1_IT_TC5); 
    DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
    DMA_Cmd(DMA1_Channel5,ENABLE);  //使能DMA通道3
		
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
		
    NVIC_Init(&NVIC_InitStructure);
}

串口一中断处理,pingpong切换

///*************************************************
//       USART1_IRQHandler
//              串口2中断
//**************************************************/
void USART1_IRQHandler(void)
{

   if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)
    {
     // USART_ReceiveData(USART1);
      USART_ClearFlag(USART1, USART_FLAG_ORE);      
    }
   
   	if(USART_GetITStatus(USART1, USART_IT_RTO) != RESET) //超时中断
        {
          
	   USART_ClearITPendingBit(USART1, USART_IT_RTO);	//清除中断标志位	   
          
           DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR_EN); //关DMA
          USART_RX_len= USART_RX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5);    //获得传输的数据个数	
          
          if(USART_RX_len>7) //命令大小不可能小于 6 
          {
            StaFlag.RX_Finsh_BUF = StaFlag.Busy_Buf;           //标志接收完成的BUF
            StaFlag.RX_Finsh =1;
          }
          if(StaFlag.Busy_Buf!=BUF_NO1||StaFlag.D_A_Pro==BUF_NO2) // 1号数组没有被使用,没有正在解析
          {   
              DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF1;
              DMA_Init(DMA1_Channel5, &DMA_InitStructure);
              StaFlag.Busy_Buf=BUF_NO1;
             // GPIOB->ODR|=1<<7;
          }
          else
          {
              DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF2;
              DMA_Init(DMA1_Channel5, &DMA_InitStructure);
              StaFlag.Busy_Buf=BUF_NO2;
             // GPIOA->ODR|=1<<4; 
          }  
        }
       DMA1_Channel5->CCR |= DMA_CCR_EN; //开DMA   
}

硬件收发控制

因为单片机出来的串口信号是TTL信号,传输距离较近,为了实现较远距离的通信,会将串口的TTL信号转成485或232,其中485采用的是差分信号,抗共模干扰能力强,无论是485芯片还是232芯片,都有一个引脚用于控制通信的方向(接收 or 发送),单片机出来TX引脚和RX引脚,还需要额外一个RTS/CTS引脚去控制,STM32F0 的串口中有对应的功能,调用函数启用即可。

        USART_DECmd(USART1,ENABLE); //485硬件控制驱动使能
        USART_DEPolarityConfig(USART1,USART_DEPolarity_High); //DE 触发后极性
        USART_SetDEAssertionTime(USART1,4);  //
        USART_SetDEDeassertionTime(USART1,4);

过采样率

48MHZ主频下,USART波特率最大为3MHZ ,因为USART默认配置成16位采样率,可以通过下面函数来进行设置。

USART_OverSampling8Cmd(USART1,ENABLE);   //设置过采样率为 8  默认为16  

主频48M,过采样率为8情况下,USART波特率最大为6MHZ


版权声明:本文为qq_36720691原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。