STM32串口使用详解

一、关于串口需要了解的几个知识点:
1.波特率:在串行通讯中,数据是按位进行传送的,因此传送速率用每秒钟传送格式位的数目来表示,称为波特率。
   波特率决定了串口传输的速度,1波特=1bps(位/秒)。波特率为9600的话就是1s传输9600位的数据。
   串口的传输与网络等其他的传输有着相似之处,比如常用的wifi,区别在与这些网络的单位是k,只有串口是按位来计数的。
2.单工,半双工,全双工:
   单工:只能一个方向传输
   半双工:可以两个方向传输,但需要分时复用
   全双工:两个方向传输

二、初始化函数
串口的初始化包括以下几部分:
1.时钟初始化:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//端口复用
初始化的时候一共有三个时钟需要打开,一是IO口,二是串口,三是端口复用。
STM32的串口1用的是PA9、PA10两个端口,串口2用的是PA2、PA3两个端口,因此初始化的时候打开的是GPIOA的时钟。加粗部分是重点,我在用端口的时候就没有加这句话,导致一直不好使。要区分开端口复用和端口重映射,它们完全是两个不同的概念。在51单片机里面,没有端口复用这种用法,某个端口在用作串口的时候也会被当做普通IO来区分,这就像你洗衣服的时候各种衣服都混在一起洗,不加以区分。但是到了STM32的时候,这里有一个复用功能,当你洗浅色衣服的时候,深色的衣服不会进入到这个盆里,这就避免了许多问题,这也是STM32比51高级的地方。

2.IO初始化:
PA9是发送口,在设置的时候注意要相应的设为复用推挽输出。
PA10是接收口,在设置的时候要设为浮空输入,由于是输入,所以没有必要再设置口线翻转速度。
  
 //USART1_TX   GPIOA.9
  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
3.串口初始化:
USART_InitTypeDef  USART_InitStructure;
USART_InitStructure.USART_BaudRate=9600;//波特率:9600
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_None;//数据流:无
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//使能接收、发送模式
USART_Init(USART1,&USART_InitStructure);
以上是串口的结构体变量的初始化设置。一般情况下的串口初始化都是上面这种情况,数据流这个我们一般是用不到的,它是一个关于调制解调的东西,这里不做深入探究。

USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//打开接收中断标志使能
USART_Cmd(USART1,ENABLE);//打开串口使能
USART_ClearFlag(USART1,USART_FLAG_TC);  //清除发送完成标志位
接收中断标志使能是在使用串口接收中断的时候才需要的。在这里清除发送完成标志位是为了避免接收不到第一个数据的情况。

4.中断初始化:
串口的使用方式有两种:查询、中断
一般情况下,发送数据常用查询方式,接收数据常用中断的方式。这和51的串口类似,查询就是判断发送或接收标志位是否被置位,中断是当接收到别的地方发来的数据的时候中断标志位就会溢出从而触发中断,打断主程序去执行中断服务程序。

中断:
与其他的中断设置方式类似,要设置中断分组,响应优先级,抢占优先级。
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; //定义结构体
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置中断分组
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1的中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级为1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //打开中断
    NVIC_Init(&NVIC_InitStructure);
}
注意如果用到中断的话还要使能接收中断,这个使能在串口初始化中完成,见上面。

三、串口的使用
1.查询方式:
在主程序里面使用时有两句话:
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待发送完成标志位被置位
USART_SendData(USART1,xxx);//向上位机发送数据
发送完成标志位被置位说明上一个数据已经发送完成了,于是进行下一个数据的发送

2.中断方式:
中断服务函数的编写套路如下:
void USART1_IRQHandler(void)
{
   if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)//判断接收中断标志位是否置位,即是否有数据发送过来
   {
        xxx=USART_ReceiveData(USART1);//将接收到的数据赋给xxx
        xxxxxxx。。。。       
   }
}
四:完整代码

#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误     
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,  接收完成标志
//bit14,  接收到0x0d
//bit13~0,  接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记    
  
void uart_init(u32 bound)
{
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
   
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
  
  //USART1_TX   GPIOA.9
  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
   
  //USART1_RX   GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;           //PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);               //初始化GPIOA.10  


  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;   //子优先级3
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //IRQ通道使能
  NVIC_Init(&NVIC_InitStructure);                 //根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置
  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; //收发模式


  USART_Init(USART1, &USART_InitStructure); //初始化串口1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART1, ENABLE);                    //使能串口1 


}


void USART1_IRQHandler(void)                  //串口1中断服务程序
  {
  u8 Res;
#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
  OSIntEnter();    
#endif
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    {
    Res =USART_ReceiveData(USART1); //读取接收到的数据
    
    if((USART_RX_STA&0x8000)==0)//接收未完成
                    {
        if(USART_RX_STA&0x4000)//接收到了0x0d
                            {
                              if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
                              else USART_RX_STA|=0x8000;  //接收完成了 
                            }
        else //还没收到0X0D
                            { 
                              if(Res==0x0d)USART_RX_STA|=0x4000;
                              else
                                {
                                  USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
                                  USART_RX_STA++;
                                  if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收    
                                }    
                            }
    }        
     } 
#if SYSTEM_SUPPORT_OS   //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
  OSIntExit();                         
#endif
} 
#endif  


猜你喜欢

转载自blog.csdn.net/weibo1230123/article/details/80596981