打印
[STM32F0]

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

[复制链接]
547|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
初级工程渣|  楼主 | 2021-12-31 23:15 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
CD, DM, ST, ar
USART 是Universal synchronous asynchronous receiver transmitter(通用同步和异步收发器)的简写,这里的同步和异步是根据是否同用一个信号时钟区分,如常见的串口通信,就是异步,SPI是同步。

使用特权

评论回复
沙发
初级工程渣|  楼主 | 2021-12-31 23:17 | 只看该作者
一、串口的初始化
/*************************************************
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;
}

使用特权

评论回复
板凳
初级工程渣|  楼主 | 2021-12-31 23:19 | 只看该作者
DMA不定长接收,pingpong接收
        所谓DMA不定长接收是 作为接收方的单片机不知道 主机发送的 通信命令字节长度,因此需要完全接收,然后再解析。一个命令通常包含十几~几十个甚至几百个字节,通常都会有相应的协议规定,命令头,命令长度,校验位,命令大小,命令的参数内容等等。通常人们会将一整条命令称为一个包或者一帧。、

使用特权

评论回复
地板
初级工程渣|  楼主 | 2021-12-31 23:21 | 只看该作者
    高速通信的情况下,用接收中断一字节一字节的接收,会占用大量CPU的时间,而且存在丢数据的可能,因此一般采用DMA进行接收。

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

使用特权

评论回复
5
初级工程渣|  楼主 | 2021-12-31 23:22 | 只看该作者
   每当接收完成一个字节后,DMA控制模块,会将这个字节移动我们设置好的内存区域(自定义的数组),当整条命令接收完成后,总线会空闲一段时间,我们可以设置串口空闲中断或者接收超时中断,当总线不再传输数据是,就会进入中断,在中断中我们搞个标志位标志一下,然后在主函数中,去解析这条命令。

使用特权

评论回复
6
初级工程渣|  楼主 | 2021-12-31 23:23 | 只看该作者
有时候主机发送实在是太快了,单片机处理到一半,又来数据了,如果只有一个接收数组,新的命令会同样会被搬运到 这个数组上,造成覆盖。此时解析命令就会出现混乱。为此需要进行pingpong接收,pingpong接收是指采用两个数组进行接收,,数组1接收完成一个包后,如果此时又有数据来了,新的数据将“搬运”到数组2,数组2接收完成一个包后,下一个包的数据将“搬运”到数组1,像来回的乒乓球。

使用特权

评论回复
7
初级工程渣|  楼主 | 2021-12-31 23:24 | 只看该作者
下面为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);
}

使用特权

评论回复
8
初级工程渣|  楼主 | 2021-12-31 23:26 | 只看该作者
串口一中断处理,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   
}

使用特权

评论回复
9
初级工程渣|  楼主 | 2021-12-31 23:27 | 只看该作者
硬件收发控制
因为单片机出来的串口信号是TTL信号,传输距离较近,为了实现较远距离的通信,会将串口的TTL信号转成485或232,其中485采用的是差分信号,抗共模干扰能力强,无论是485芯片还是232芯片,都有一个引脚用于控制通信的方向(接收 or 发送),单片机出来TX引脚和RX引脚,还需要额外一个RTS/CTS引脚去控制,STM32F0 的串口中有对应的功能,调用函数启用即可。

使用特权

评论回复
10
初级工程渣|  楼主 | 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);

使用特权

评论回复
11
初级工程渣|  楼主 | 2021-12-31 23:31 | 只看该作者
过采样率
48MHZ主频下,USART波特率最大为3MHZ ,因为USART默认配置成16位采样率,可以通过下面函数来进行设置。

使用特权

评论回复
12
初级工程渣|  楼主 | 2021-12-31 23:32 | 只看该作者
USART_OverSampling8Cmd(USART1,ENABLE);   //设置过采样率为 8  默认为16  


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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

70

主题

682

帖子

0

粉丝