STM32
直播中

张娟

7年用户 2074经验值
私信 关注
[问答]

USART配置的详细步骤有哪些

UART异步通信方式的特点有哪些?
USART与UART的区别是什么?
USART配置的详细步骤有哪些?

回帖(1)

向芹

2021-12-6 09:57:22
USART实现步骤

处理器与外部通信概述



  • 串行通信





    -传输原理:数据按位顺序传输。
    -优点:占用引脚资源少
    -缺点:速度相对较慢
  • 并行通信





    -传输原理:数据各个位同时传输。
    -优点:速度快
    -缺点:占用引脚资源多

串口作为 MCU 的重要外部接口,基本上所有的 MCU 都会带有串口。而STM32F407ZET6 最多可提供 6 路串口。


  • 通信按传输方向以下几种方式:  
    a.单工:
    数据传输只支持数据在一个方向上传输(收音机、遥控器)
    b.半双工:
    允许数据在两个方向上传输,但是,在某一时刻,只允许数
    据在一个方向上传输,它实际上是一种切换方向的单工通信;(对讲机)
    c.全双工:
    允许数据同时在两个方向上传输,因此,全双工通信是两个
    单工通信方式的结合,它要求发送设备和接收设备都有独立
    的接收和发送能力。(电话机)
  • 串行通信的通信方式
    a.同步通信:带时钟同步信号传输。 -SPI,IIC通信接口
    b.异步通信:不带时钟同步信号。 -UART(通用异步收发器),单总线






USART原理及特点介绍



  • USART与UART的区别:
    USART:全双工通用同步/异步串行收发器
    UART:全双工通用异步串行收发器
    USART/UART是一种通用的标准接口,根据导线的电压等不同也分为很多的同类,比如: RS485, RS422,RS232
  • UART异步通信方式特点:




  • 全双工异步通信。
  • 小数波特率发生器系统,提供精确的波特率。
  • 可配置的16倍过采样或8倍过采样,因而为速度容差与时钟容差的灵活配置提供了可能。
  • 可编程的数据字长度(8位或者9位);
  • 可配置的停止位(支持1或者2位停止位);
  • 可配置的使用DMA多缓冲器通信。
  • 单独的发送器和接收器使能位。
  • 检测标志:① 接收缓冲器 ②发送缓冲器空 ③传输结束标志
  • 多个带标志的中断源。触发中断。
  • 其他:校验控制,四个错误检测标志。



  • STM32串口异步通信定义的参数传送格式:








  • 起始位:发送器是通过发送起始位而开始一个字符的传送。起始位使数据线处于“space”状态
  • 数据位(8位或9位):起始位之后就传送数据位。在数据位中,低位在前(左),高位在后(右)。由于字符编码方式的不同,数据位可以是5、6、7或8位。
  • 奇偶校验位(第九位):用于对字符传送作正确性检查,因
    此奇偶校验位是可选择的,共有3种可能,即奇校验、偶校验和无校验,由用户根据需要选定。
  • 停止位(1,1.5,2位):停止位在最后,用以标志一个字符传送的结束,它对应于mark状态。停止位可能是1、1.5或2位,在实际应用中根据需要确定。

波特率(band rate)= 1波特=1bps(位/秒)1
USART配置的详细步骤(USART1为例)






跳线帽的连接非常重要,不能接错










根据自己的开发板原理图,找到相应的IO口


  • 串口时钟使能,GPIO 时钟使能。
  • 设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。
  • GPIO 初始化设置:要设置模式为复用功能。
  • 串口参数初始化:设置波特率,字长,奇偶校验等参数。
  • 开启中断并且初始化 NVIC,使能中断(如果需要开启串口中断才需要这个步骤)。
  • 使能串口。
  • 编写中断处理函数:函数名格式为 USARTx_IRQHandler(x 对应串口号)。


void Usart1_Init(void)
{
        GPIO_InitTypeDef  GPIO_InitStructure;
        USART_InitTypeDef USART_InitStruct;
        NVIC_InitTypeDef  NVIC_InitStruct;
       
        // 串口是挂载在 APB2 下面的外设,(多goto函数查看原理有助于理解)
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
        //使用的是串口 1,串口 1 对应着芯片引脚 PA9,PA10 需要使能PA的时钟
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);


        //设置引脚复用器映射
        //引脚复用器映射配置,需要配置PA9,PA10 的引脚,调用函数为:
        //PA9 复用为 USART1
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
        //PA10 复用为 USART1
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
               
        GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9 与 GPIOA10
        GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;                                //配置IO口复用功能
        GPIO_InitStructure.GPIO_Speed         = GPIO_Speed_50MHz;                 //速度 50MHz
        GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;                         //推挽复用输出
        GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;                         //上拉
        //初始化 PA9,PA10
        GPIO_Init(GPIOA,&GPIO_InitStructure);
       
        USART_InitStruct.USART_BaudRate         = 115200;                                //一般设置为 115200;
        USART_InitStruct.USART_WordLength         = USART_WordLength_8b;        //字长为 8 位数据格式
        USART_InitStruct.USART_StopBits         = USART_StopBits_1;                //一个停止位
        USART_InitStruct.USART_Parity                 = USART_Parity_No;                //无奇偶校验位
        USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件控制流
        USART_InitStruct.USART_Mode                 = USART_Mode_Rx | USART_Mode_Tx;        //收发模式  双全工
        //初始化串口
        USART_Init(USART1, &USART_InitStruct);
       
        //配置串口接收中断
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
        USART_Cmd(USART1, ENABLE);
}


void NVIC_Configuration(void)
{
        //NVIC分组(一个工程当中只能配置一次分组)抢占优先级2位,值范围:0~3;响应优先级2位,值范围:0~3;
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
       
        NVIC_InitStruct.NVIC_IRQChannel                                                = USART1_IRQn;  //NVIC通道,在stm32f4xx.h可查看通道 (可变)
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority        = 0x01;                        //抢占优先级
        NVIC_InitStruct.NVIC_IRQChannelSubPriority                        = 0x01;                        //响应优先级
        NVIC_InitStruct.NVIC_IRQChannelCmd                                        = ENABLE;                //使能
        //配置中断分组(NVIC),并使能中断。
    NVIC_Init(&NVIC_InitStruct);       
}


main.c


#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"


#define LED0_ON    GPIO_ResetBits(GPIOF, GPIO_Pin_9)
#define LED0_OFF   GPIO_SetBits(GPIOF, GPIO_Pin_9)


u8 Usart_Data;   //值范围:0~255
u8 rx_flag = 0;  //接受数据完成 rx_flag = 1


//服务函数
void USART1_IRQHandler(void)
{
   //若是非空,则返回值为1,与RESET(0)判断,不相等则判断为真
   if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
   {       
                //判断为真后,为下次中断做准备,则需要对中断的标志清零
                USART_ClearITPendingBit(USART1,USART_IT_RXNE);
                /* DR读取接受到的数据*/
                Usart_Data = USART_ReceiveData(USART1);       
                rx_flag = 1; //接受数据完成 rx_flag = 1
   }
}


int main(void)
{
        //时钟初始化,详细步骤可看前几篇文章
        Delay_Init();
        //需要对led进行初始化,详细步骤可看前几篇文章
        Led_Init();
        Usart1_Init();
        while(1)
        {
                if(rx_flag == 1)
                {
                        //串口输入1
                        if(Usart_Data == '1') //亮灯
                        {
                                LED0_ON;
                        }
                       
                        if(Usart_Data == '0') //灭灯
                        {
                                LED0_OFF;
                        }                       


                        rx_flag = 0;
                }
        }
        return 0;
}


案例补充:
调用标准库,使用printf函数


#include "stdio.h"


#pragma import(__use_no_semihosting)            
//标准库需要的支持函数                 
struct __FILE
{
        int handle;
};


FILE __stdout;      
//定义_sys_exit()以避免使用半主机模式   
int _sys_exit(int x)
{
        x = x;
}
//重定义fputc函数   printf 是一个宏
int fputc(int ch, FILE *f)
{        
        USART_SendData(USART1,ch);  //通过串口发送数据
        while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);      
        return ch;
}


升级版串口服务函数,接收字符串


void USART1_IRQHandler(void)
{
       
         
   //若是非空,则返回值为1,与RESET(0)判断,不相等则判断为真
   if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
   {       
                //判断为真后,为下次中断做准备,则需要对中断的标志清零
                USART_ClearITPendingBit(USART1,USART_IT_RXNE);
                /* DR读取接受到的数据*/
                buffer[count++] = USART_ReceiveData(USART1);       
          
           if(buffer[count-1] == ':')
           {
                   //输入以‘:’结尾,并剔除
                        for(rx_i=0; rx_i< (count-1); rx_i++)
                        {
                                rx_buffer[rx_i] = buffer[rx_i];
                        }
          
                        memset(buffer, 0, sizeof(buffer));
                        count = 0;  //置为0,下一帧数据从buffer[0]开始存储
                        rx_flag = 1; //接受数据完成 rx_flag = 1
           }       
   }
}


升级版控制引脚宏定义
sys.h


#ifndef __SYS_H_
#define __SYS_H_
#include "stm32f4xx.h"


//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    20 = 0x14
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14   
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     


#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010    16 = 0x10
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010

//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入


#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入


#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入


#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入


#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入


#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入


#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入


#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入


#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入


#endif


升级版串口,通过蓝牙手机控制硬件


就是将USART1的初始化全部改成2,并初始化相应引脚,及蓝牙的波特率9600


#include "usart.h"


void Usart2_Init(void)
{
        GPIO_InitTypeDef  GPIO_InitStructure;
        USART_InitTypeDef USART_InitStruct;
        NVIC_InitTypeDef  NVIC_InitStruct;
       
        // 串口是挂载在 APB1 下面的外设,所以使能函数为
        //使能 USART2 时钟
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
        //使用的是串口 2,串口 2 对应着芯片引脚 PA2,PA3 需要使能PA的时钟
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);


        //设置引脚复用器映射
        //引脚复用器映射配置,需要配置PA2,PA3 的引脚,调用函数为:
        //PA2 复用为 USART2
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2);
        //PA3 复用为 USART2
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2);
               
        GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_2 | GPIO_Pin_3; //GPIOA2 与 GPIOA3
        GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;                                //配置IO口复用功能
        GPIO_InitStructure.GPIO_Speed         = GPIO_Speed_50MHz;                 //速度 50MHz
        GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;                         //推挽复用输出
        GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;                         //上拉
        //初始化 PA2,PA3
        GPIO_Init(GPIOA,&GPIO_InitStructure);
       
        USART_InitStruct.USART_BaudRate         = 9600;                                //蓝牙为9600;
        USART_InitStruct.USART_WordLength         = USART_WordLength_8b;        //字长为 8 位数据格式
        USART_InitStruct.USART_StopBits         = USART_StopBits_1;                //一个停止位
        USART_InitStruct.USART_Parity                 = USART_Parity_No;                //无奇偶校验位
        USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件控制流
        USART_InitStruct.USART_Mode                 = USART_Mode_Rx | USART_Mode_Tx;        //收发模式  双全工
        //初始化串口
        USART_Init(USART2, &USART_InitStruct);


        NVIC_InitStruct.NVIC_IRQChannel                                                = USART2_IRQn;          //NVIC通道,在stm32f4xx.h可查看通道 (可变)
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority        = 0x01;                        //抢占优先级
        NVIC_InitStruct.NVIC_IRQChannelSubPriority                        = 0x01;                        //响应优先级
        NVIC_InitStruct.NVIC_IRQChannelCmd                                        = ENABLE;                //使能
        //配置中断分组(NVIC),并使能中断。
    NVIC_Init(&NVIC_InitStruct);       
       
        //配置串口接收中断
        USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);


        USART_Cmd(USART2, ENABLE);
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分