[STM32F0] STM32F0 USART 常用配置,DMA不定长接收

[复制链接]
 楼主| 初级工程渣 发表于 2021-12-31 23:15 | 显示全部楼层 |阅读模式
CD, DM, ST, ar
USART 是Universal synchronous asynchronous receiver transmitter(通用同步和异步收发器)的简写,这里的同步和异步是根据是否同用一个信号时钟区分,如常见的串口通信,就是异步,SPI是同步。
 楼主| 初级工程渣 发表于 2021-12-31 23:17 | 显示全部楼层
一、串口的初始化
  1. /*************************************************
  2. uart1
  3. 配置串口一的发送和接收的GPIO口功能,以及中断
  4. **************************************************/
  5. void uart1_init(u32 pclk,u32 baud)
  6. {     
  7.         GPIO_InitTypeDef  GPIO_InitStructure;
  8.         USART_InitTypeDef USART_InitStructure;
  9.         NVIC_InitTypeDef  NVIC_InitStructure;
  10.    
  11.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
  12.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  13.        
  14.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);                //Tx
  15.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);                //Rx
  16.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_1);                //RTS/CTS
  17.         
  18.         GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9 |GPIO_Pin_10|GPIO_Pin_12;
  19.         GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;       
  20.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  21.         GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  22.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  23.         GPIO_Init(GPIOA, &GPIO_InitStructure);
  24.      
  25.         
  26.     //配置串口中断优先级
  27.         NVIC_InitStructure.NVIC_IRQChannel                   = USART1_IRQn;
  28.         NVIC_InitStructure.NVIC_IRQChannelPriority           = 2;
  29.         NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
  30.         NVIC_Init(&NVIC_InitStructure);        

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

  39.         
  40.         USART_DECmd(USART1,ENABLE); //485硬件控制驱动使能
  41.         USART_DEPolarityConfig(USART1,USART_DEPolarity_High); //DE 触发后极性
  42.         USART_SetDEAssertionTime(USART1,4);  
  43.         USART_SetDEDeassertionTime(USART1,4);   
  44.         
  45.         
  46.         USART_OverSampling8Cmd(USART1,ENABLE);   //设置过采样率为 8  默认为16        
  47.             USART_Init(USART1, &USART_InitStructure);
  48.                        
  49.         //USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//使能串口空闲中断
  50.     //USART_ClearITPendingBit(USART1, USART_IT_IDLE);//清除串口空闲中断标志位
  51.         
  52.         
  53.         USART_SetReceiverTimeOut(USART1,2);//空闲2bit后出发中断
  54.         
  55.         USART_ITConfig(USART1,USART_IT_RTO,ENABLE);//使能接收超时中断
  56.         
  57.         USART_ReceiverTimeOutCmd(USART1, ENABLE);
  58.         
  59.         USART_ClearITPendingBit(USART1, USART_IT_RTO);
  60.         
  61.             USART_Cmd(USART1, ENABLE);//使能串口   
  62.         
  63.         DMA1_Config();
  64.         
  65.         StaFlag.D_A_Pro = BUF_NO2;//首次进来让 dma使用数组2
  66.         
  67.         StaFlag.Busy_Buf = BUF_NO2;
  68. }
 楼主| 初级工程渣 发表于 2021-12-31 23:19 | 显示全部楼层
DMA不定长接收,pingpong接收
        所谓DMA不定长接收是 作为接收方的单片机不知道 主机发送的 通信命令字节长度,因此需要完全接收,然后再解析。一个命令通常包含十几~几十个甚至几百个字节,通常都会有相应的协议规定,命令头,命令长度,校验位,命令大小,命令的参数内容等等。通常人们会将一整条命令称为一个包或者一帧。、
 楼主| 初级工程渣 发表于 2021-12-31 23:21 | 显示全部楼层
    高速通信的情况下,用接收中断一字节一字节的接收,会占用大量CPU的时间,而且存在丢数据的可能,因此一般采用DMA进行接收。

        DMA是什么不再赘述,只需要知道它是个“搬运工”,设置好要去那里“搬运”,“搬运”到那里,它就会不断的干活。
 楼主| 初级工程渣 发表于 2021-12-31 23:22 | 显示全部楼层
   每当接收完成一个字节后,DMA控制模块,会将这个字节移动我们设置好的内存区域(自定义的数组),当整条命令接收完成后,总线会空闲一段时间,我们可以设置串口空闲中断或者接收超时中断,当总线不再传输数据是,就会进入中断,在中断中我们搞个标志位标志一下,然后在主函数中,去解析这条命令。
 楼主| 初级工程渣 发表于 2021-12-31 23:23 | 显示全部楼层
有时候主机发送实在是太快了,单片机处理到一半,又来数据了,如果只有一个接收数组,新的命令会同样会被搬运到 这个数组上,造成覆盖。此时解析命令就会出现混乱。为此需要进行pingpong接收,pingpong接收是指采用两个数组进行接收,,数组1接收完成一个包后,如果此时又有数据来了,新的数据将“搬运”到数组2,数组2接收完成一个包后,下一个包的数据将“搬运”到数组1,像来回的乒乓球。
 楼主| 初级工程渣 发表于 2021-12-31 23:24 | 显示全部楼层
下面为DMA接收的初始化:
  1. extern u8 Getfinsh_BUF;       
  2. extern u8 Busy_Buf_flag;       // 用于切换两个数组PINGPONG接收

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

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

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

  10. **************************************************/
  11. static DMA_InitTypeDef DMA_InitStructure;
  12. void DMA1_Config(void)
  13. {
  14.     NVIC_InitTypeDef    NVIC_InitStructure;       
  15.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
  16.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);      //使能SYSCFG时钟,必须的       
  17.      //和SPI1 DMA冲突,重映射
  18.    
  19.     SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_USART1Tx, ENABLE);//将DMA串口发送从通道2重映射到通道4
  20.     SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_USART1Rx, ENABLE);//将DMA串口发送从通道3重映射到通道5
  21.    
  22.           /*****DMA发送配置 Memory->Peripheral *****************/
  23.     DMA_DeInit(DMA1_Channel4);                       //将DMA1_Channel4信道寄存器初始化为默认的重置值。
  24.     DMA_InitStructure.DMA_BufferSize = USART_RX_LEN;   //设置DMA1的Buffer缓冲区大小,DMA1_MEM_LEN_RX为自定义的变量
  25.     DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;       //关闭内存到内存
  26.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;      //正常模式
  27.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //内存到外设
  28.     DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //优先级非常高
  29.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
  30.     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->TDR;   //设置外设接收地址
  31.     DMA_InitStructure.DMA_PeripheralInc =  DMA_PeripheralInc_Disable;    //外设地址不变
  32.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;      //设置内存数据长度以Byte为单位
  33.     DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF2;             //设置内存发送地址,DMA_Tx为自定义变量
  34.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //设置外设数据长度以Byte为单位
  35.     DMA_Init(DMA1_Channel4,&DMA_InitStructure);
  36.    
  37.     DMA_ClearITPendingBit(DMA1_IT_TC4); //清除DMA_ch4传输完成中断标志                
  38.     DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//使能DMA1_CH4传输完成中断               
  39.     USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);//发送数组和接收数组2 共用一个
  40.        
  41.    /*****DMA接收配置 Peripheral->Memory*****************/
  42.     DMA_DeInit(DMA1_Channel5);
  43.     DMA_InitStructure.DMA_BufferSize = USART_RX_LEN;  
  44.     DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;   
  45.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;   
  46.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  
  47.     DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  48.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  49.     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->RDR;   
  50.     DMA_InitStructure.DMA_PeripheralInc =  DMA_PeripheralInc_Disable;
  51.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  52.     DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF2;  
  53.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  54.     DMA_Init(DMA1_Channel5,&DMA_InitStructure);
  55.    
  56.     DMA_ClearITPendingBit(DMA1_IT_TC5);
  57.     DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
  58.     USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
  59.     DMA_Cmd(DMA1_Channel5,ENABLE);  //使能DMA通道3
  60.                
  61.     NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_5_IRQn;
  62.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  63.     NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
  64.                
  65.     NVIC_Init(&NVIC_InitStructure);
  66. }
 楼主| 初级工程渣 发表于 2021-12-31 23:26 | 显示全部楼层
串口一中断处理,pingpong切换

  1. ///*************************************************
  2. //       USART1_IRQHandler
  3. //              串口2中断
  4. //**************************************************/
  5. void USART1_IRQHandler(void)
  6. {

  7.    if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)
  8.     {
  9.      // USART_ReceiveData(USART1);
  10.       USART_ClearFlag(USART1, USART_FLAG_ORE);      
  11.     }
  12.    
  13.            if(USART_GetITStatus(USART1, USART_IT_RTO) != RESET) //超时中断
  14.         {
  15.          
  16.            USART_ClearITPendingBit(USART1, USART_IT_RTO);        //清除中断标志位          
  17.          
  18.            DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR_EN); //关DMA
  19.           USART_RX_len= USART_RX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5);    //获得传输的数据个数       
  20.          
  21.           if(USART_RX_len>7) //命令大小不可能小于 6
  22.           {
  23.             StaFlag.RX_Finsh_BUF = StaFlag.Busy_Buf;           //标志接收完成的BUF
  24.             StaFlag.RX_Finsh =1;
  25.           }
  26.           if(StaFlag.Busy_Buf!=BUF_NO1||StaFlag.D_A_Pro==BUF_NO2) // 1号数组没有被使用,没有正在解析
  27.           {   
  28.               DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF1;
  29.               DMA_Init(DMA1_Channel5, &DMA_InitStructure);
  30.               StaFlag.Busy_Buf=BUF_NO1;
  31.              // GPIOB->ODR|=1<<7;
  32.           }
  33.           else
  34.           {
  35.               DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF2;
  36.               DMA_Init(DMA1_Channel5, &DMA_InitStructure);
  37.               StaFlag.Busy_Buf=BUF_NO2;
  38.              // GPIOA->ODR|=1<<4;
  39.           }  
  40.         }
  41.        DMA1_Channel5->CCR |= DMA_CCR_EN; //开DMA   
  42. }
 楼主| 初级工程渣 发表于 2021-12-31 23:27 | 显示全部楼层
硬件收发控制
因为单片机出来的串口信号是TTL信号,传输距离较近,为了实现较远距离的通信,会将串口的TTL信号转成485或232,其中485采用的是差分信号,抗共模干扰能力强,无论是485芯片还是232芯片,都有一个引脚用于控制通信的方向(接收 or 发送),单片机出来TX引脚和RX引脚,还需要额外一个RTS/CTS引脚去控制,STM32F0 的串口中有对应的功能,调用函数启用即可。
 楼主| 初级工程渣 发表于 2021-12-31 23:29 | 显示全部楼层
        USART_DECmd(USART1,ENABLE); //485硬件控制驱动使能
        USART_DEPolarityConfig(USART1,USART_DEPolarity_High); //DE 触发后极性
        USART_SetDEAssertionTime(USART1,4);  //
        USART_SetDEDeassertionTime(USART1,4);
 楼主| 初级工程渣 发表于 2021-12-31 23:31 | 显示全部楼层
过采样率
48MHZ主频下,USART波特率最大为3MHZ ,因为USART默认配置成16位采样率,可以通过下面函数来进行设置。
 楼主| 初级工程渣 发表于 2021-12-31 23:32 | 显示全部楼层
  1. USART_OverSampling8Cmd(USART1,ENABLE);   //设置过采样率为 8  默认为16  


主频48M,过采样率为8情况下,USART波特率最大为6MHZ
您需要登录后才可以回帖 登录 | 注册

本版积分规则

71

主题

815

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部