STM32F4_串口通信详解

目录

1. 串口相关介绍及使用

1.1 串口设置的一般步骤:

1.1.1 串口时钟和GPIO时钟使能

1.1.2 设置引脚复用器映射

1.1.3 GPIO端口模式设置

1.1.4 串口参数初始化

1.1.5 开启中断并且初始化NVIC,使能中断

1.1.6 使能串口

1.1.7 串口数据发送与接收

1.1.8 串口状态

1.1.9 获取中断状态

1.1.10 中断服务函数

2. 通信接口背景知识

2.1 处理器和外部设备通信的两种方式:

2.2 串行通信_单工_半双工_全双工

2.3 同步通信和异步通信

2.4 常见的串行通信接口

2.5 STM32串口通信的特点

2.6 STM32通信过程

2.7 STM32串口异步通信需要定义的参数

3. USART通用同步异步收发器

3.1 USART简介

3.2 USART功能

3.3 USART框图

4. STM32串口寄存器配置方法

4.1 USART_SR 状态寄存器

4.2 USART_DR 数据寄存器

4.3 USART_BRR 波特率寄存器

4.3.1 如何配置波特率寄存器USART_BRR

5. 串口通信程序实现(发送什么,就接收什么)

5.1 STM32串口通信出现乱码

6. STM32串口通信程序

6.1 usart 中断服务函数精讲

6.2 串口通信程序精讲

7. MDK5:main.c(17): error: #268: declaration may not appear after executable statement in block


        STM32F407最多可以提供6路串口USART1 和 USART2 和 USART3; 

1. 串口相关介绍及使用

1.1 串口设置的一般步骤:

1. 串口时钟使能,GPIO时钟使能;

2. 设置引脚复用器映射:调用GPIO_PinAFConfig();函数;

3. GPIO初始化设置:设置模式为复用功能;

4. 串口参数初始化:设置波特率,字长,奇偶检验等参数;        

5. 开启中断并且初始化NVIC,使能中断(需要开启中断时才会存在该步骤);

6. 使能串口;

7. 编写中断处理函数:函数名格式为USARTxIRQHandler();(x为对应的串口号,STM32F407的x取值为1 2 3 );注意区别51的中断命名格式;

8. 串口数据收发:void USART_SendData();  //发送数据到串口,本质是调用串口数据寄存器 DR

                             uint16_t USART_ReceiveData();   //接受数据,从 DR 读取接收到的数据

9. 串口传输状态获取:FlagStatus USART_GetFlagStatus();

                                    void USART_ClearITPendingBit();   //清除中断标志位

1.1.1 串口时钟和GPIO时钟使能

串口使能函数:RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟 (x取1);

GPIO时钟使能:RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟 

1.1.2 设置引脚复用器映射

引脚复用的意思就是说:STM32F407功能太多,引脚不够每个分配单独的功能,所以通过GPIO_PinAFConfig函数定义xx引脚复用另外的功能;

GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //PA9复用为USART1

GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//PA10复用为USART1 

GPIO_PinAFConfig函数的参数是:GPIO口,对应的引脚,复用的串口;注意:我们要把PA9、PA10都映射到串口1,我们要调用两次函数;

1.1.3 GPIO端口模式设置

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;   //复用功能

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉

GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10 

1.1.4 串口参数初始化

串口参数初始化和GPIO初始化基本一致:调用各自的初始化结构体函数即可;

USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;

USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式

USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 :所谓停止位实际上是一个时间长度。时间长度和串口通信的波特率有关,通信所用波特率的倒数即为一位,他在实际中表示为一个时间段;

USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位:奇偶检验位是一个表示给定位数的二进制数中1的个数是奇数还是偶数的二进制数;如果一组给定数据位中1的个数是奇数,偶检验位就置1,从而使得1的个数为偶数;反之也是;在串行通信中,奇偶校验位通常是由UART这样的接口硬件生成、校验的,在接收方,通过接口硬件中的寄存器的状态位传给CPU以及操作系统。

USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 串口硬件流设置

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式 :串口通信的两种模式TXD和RXD;

USART_Init(USART1, &USART_InitStructure); //初始化串口 

1.1.5 开启中断并且初始化NVIC,使能中断

如果需要开启中断就需要配置中断优先级NVIC;调用函数:NVIC_Init;

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;  // NVIC中断优先级通道选择为 串口通道

NVIC_InitStructure.NVIC_IRQChannelPremptionPriority=3; // 抢占优先级3

NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;  // 响应优先级(也就是子优先级)3

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  // IRQ通道使能

NVIC_Init(&NVIC_InitStructure);  // 根据指定的参数初始化VIC寄存器

使能相应串口中断:void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState) ;注意:这个函数的第二个入口参数是标示使能串口的类型,也就是使能哪种中断,因为串口的中断类型有很多种;

如果需要开启中断,那么我们在系统初始化的时候就需要设置系统的中断优先级分组(main函数开头设置)NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2 ;2位抢占优先级,2位响应优先级;

1.1.6 使能串口

使能串口调用函数USART_Cmd来实现;

USART_Cmd(USART1, ENABLE); //使能串口 

1.1.7 串口数据发送与接收

STM32F4的发送与接收是通过数据寄存器USART_DR来实现的,双寄存器,包括TDR和RDR。当写数据时,串口自动发送;收到数据以后,数据也是保存在该寄存器中;

操作USART_DR寄存器发送数据函数:void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 

操作USART_DR寄存器读取收到数据的函数:uint16_t USART_ReceiveData(USART_TypeDef* USARTx); 

1.1.8 串口状态

串口的状态通过状态寄存器USART_SR读取;

寄存器USART_SR:RXNE位(读数据寄存器非空):当寄存器的该位置1时,提示有数据接收到了,并且可以通过USART_DR寄存器进行读取;可以通过USART_DR将该位清0,也可以直接向该位写0;TC(发送完成):该位被置位时,表示USART_DR数据已经被发送完成了 ,可以通过USART_DR将该位清0,也可以直接向该位写0;

读取串口状态的函数:FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态,比如上面讲解的RXNE(读数据寄存器非空)以及TC(发送完成)。

ag. USART_GetFlagStatus(USART1, USART_FLAG_RXNE); 

      USART_GetFlagStatus(USART1, USART_FLAG_TC); 

1.1.9 获取中断状态

在中断过程中,判断中断是哪种中断,使用的函数是:ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT) ;

ag. 判断是否是串口发送完成中断:USART_GetITStatus(USART1, USART_IT_TC) ;返回值SET,表明发送完成中断发生;

1.1.10 中断服务函数

当发生中断的时候,程序就会执行中断服务函数。然后我们在中断服务函数中编写我们相应的逻辑代码即可   void USART1_IRQHandler(void);

2. 通信接口背景知识

2.1 处理器和外部设备通信的两种方式:

:并行通信:

数据的各个位同时传输;因为是各个位同时传输的,所以传输的速度比较快一次性的将8个位同时传输。

各个位同时传输,每个位都需要占用STM32的一个引脚,所以占用的引脚比较多

:串行通信:

数据按位顺序进行传输;因为是按照位的顺序进行传输的,所以传输速度相对较慢。按顺序来传输,就不再需要每一位都占用一个引脚,所以占用的引脚相对较少

2.2 串行通信_单工_半双工_全双工

串行通信按照数据传送方向分为:单工半双工全双工

单工:数据传输只支持数据在一个方向上传输。

半双工:允许数据在两个方向上传输。但是同一时刻不允许数据同时在两个方向上传输。简单来说,就是一方向的传输正在进行,不允许另一方向的传输同时进行。实际上是一种切换方向的单工通信。

全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。

               全双工就是TXD和RXD的交互通信方式。

2.3 同步通信和异步通信

同步通信和异步通信都属于串行通信的通信方式。

同步通信带时钟同步信号传输。比如:SPI、IIC协议

同步通信前,通信的双方必须建立同步,需要借助时钟,设置双方的时钟达到同一频率。字符是可以一个接着一个传输,但是每组信息(也称为信息帧)的开始要加上同步字符,在没有信息传输时,需要加上空字符,因为同步通信是不允许有间隙的。

发送一位是按照时钟来发送的,时钟上升沿,发送一位;下一个时钟上升沿,我再发送一位。

异步通信不带时钟同步信号。比如:UART(通用异步收发器)、单总线协议

异步通信中两个数据字符之间的传输间隔可以是任意的,不需要传输时钟。但是在异步通信方式中,发送和接收的双方必须约定相同的帧格式,否则会造成传输错误。

异步通信不需要时钟,通信双方约定好通信速率(波特率)、起始位、终点位、高电平1占的时间、低电平0占的时间等。就像51单片机中的单总线协议一样,高电平1这样定义:一个电位中电平占2/3记为高电平1,电平占1/3记为低电平0;

同步和异步是按照通信双方发送和接收的时钟来确定的;同一时钟下发,对应同一时钟下收,就是同步通信,反之就是异步通信;为了保证收发的同步,需要时钟的参与。

2.4 常见的串行通信接口

UART(通用异步收发器)

        TXD:发送端;

        RXD:接收端;

        GND:公共端;

        通信方式:异步通信;

        通信方向:全双工;

单总线(one-wire)

        DQ:发送/接收端;

        通信方式:异步通信;

        通信方向:半双工;

SPI

        SCK:同步时钟;

        MISO:主机输入,从机输出;

        MOSI:主机输出,从机输入;

        通信方式:同步通信;

        通信方向:全双工;

I2C

        SCL:同步时钟;

        SDA:数据输入/输出端;

        通信方式:同步通信;

        通信方向:半双工;

STM32的串行通信接口:UART---通用异步收发器;USART---通用同步异步收发器;STM32F407一般是6个;

2.5 STM32串口通信的特点

1. 全双工异步通信

2. 小数波特率发生器系统,提供精确的波特率

3. 可配置的16倍过采样或8倍过采样,因而为速度容差与时钟容差的灵活配置提供了可能

(过采样:采样频率高于信号最高频率的两倍,称过采样。)

4. 可编程的数据字长度(8位或者9位)

5. 可配置的停止位(支持1或者2位停止位)

6. 可配置的使用DMA多缓冲器通信

7. 单独的发送器和接收器使能位

8. 检测标志:①接收缓冲器 ②发送缓冲器空 ③传输结束标志

9. 多个带标志的中断源。触发中断

10. 其他:校验控制,四个错误检测标志

2.6 STM32通信过程

首先UART是串行发送,所以是按照双方约定的波特率进行一位一位的接收发送;RXD接收过程,MCU内核接收外设一位一位传来的数据到移位寄存器,移位寄存器存放满之后同时发送到缓冲寄存器,然后被MCU内核所接收;

数据发送的过程同样如此,MCU内核将所要发送的数据发送给数据缓冲器,数据缓冲器同时将数据发送给移位寄存器,移位寄存器一位一位的将数据发送给外设。

2.7 STM32串口异步通信需要定义的参数

①起始位  ---通信双方约定好的起始位,比方说,双方通信前都是高电平1,设置起始位为低电平0,设定一旦遇到0双方就开始通信。

②数据位(8位或者9位)

③奇偶校验位(第9位)  ---8位+1位(奇偶校验位)  ; 奇偶校验位的意思就是:如果设定为奇校验,如果8位中1的个数为偶数,则第九位补1,否则补0;

                                                                                                                       如果是偶校验,8位中1的个数是奇数,则第九位补1,否则补0;

奇偶校验的区分就是1的个数为奇数还是偶数;

④停止位(1,15,2位)  ---传输的最后一位是停止位;停止位以后就开始下一个起始位;

⑤波特率设置  ---传输的效率

3. USART通用同步异步收发器

3.1 USART简介

        通用同步异步收发器 (USART) 能够灵活地与外部设备进行全双工数据交换,满足外部设备对 工业标准 NRZ 异步串行数据格式的要求。USART 通过小数波特率发生器提供了多种波特率。它支持同步单向通信和半双工单线通信;还支持 LIN(局域互连网络)、智能卡协议与 IrDA (红外线数据协会)SIR ENDEC 规范,以及调制解调器操作 (CTS/RTS)。而且,它还支持 多处理器通信。通过配置多个缓冲区使用 DMA 可实现高速数据通信。

3.2 USART功能

接口通过三个引脚从外部连接到其他设备

任何USART双向通信均需要至少两个引脚:接收数据输入引脚(RX)和发送数据输出引脚(TX);

其中过采样技术可区分有效输入数据和噪音,从而用于恢复数据。

正常USART模式下,通过以下引脚以帧的形式发送和接收串行数据:

1. 发送或接收前保持空闲线路

2. 起始位

3. 数据(字长8位或者9位),最低有效位在前

4. 用于指示帧传输已完成的0.5个、1个、1.5个、2个停止位

5. 该接口使用小数波特率发生器-带12位尾数和4位小数

6. 状态寄存器(USART_SR)

7. 数据寄存器(USART_DR)

8. 波特率寄存器(USART_BRR)

9. 智能卡模式下的保护时间寄存器(USART_GTPR)

同步模式下需要:SCLK发送器时钟输出。该引脚用于输出发送器数据时钟,以便按照 SPI 主模式进行同步发送。

nCTS:“清除已发送”用于在当前传输结束时阻止数据发送(高电平时)

nRTS:“请求已发送”用于指示USART已准备好接收数据(低电平时)

3.3 USART框图

4. STM32串口寄存器配置方法

常用的串口相关寄存器:

        USART_SR:状态寄存器

        USART_DR:数据寄存器

        USART_BRR:波特率寄存器

4.1 USART_SR 状态寄存器

USART_SR:状态寄存器  Status register

状态寄存器主要是操作其0-9位,

接下来介绍几个比较重要的状态标志位:

P0:PE(奇偶校验错误)Parity error

0:无奇偶校验错误  1:奇偶校验错误

P5:RXNE(读取数据寄存器不为空)Read data regiter not empty

当RDR移位寄存器的内容已经传输到USART_DR寄存器时,该位由硬件置1;  0:未接收到数据  1:已准备好读取接收到的数据

P6:TC(发送完成)Transmission complete

如果已完成对TX的发送,则该位由硬件置1; 0:数据未传输到移位寄存器  1:数据传输到移位寄存器

P7:TXE(发送数据寄存器为空)Transmit data register empty

当TDR寄存器的内容已传输到移位寄存器中,该位由硬件置1;(该位的作用同P5_RXNE 判断发送是否成功)

4.2 USART_DR 数据寄存器

USART_DR:数据寄存器   Data register

位 8:0 DR[8:0]:

        数据值包含接收到数据字符或已发送的数据字符(我们想要写的程序是存储在数据寄存器中的),具体取决于所执行的操作是“读取”操作还是“写入”操作。

        因为数据寄存器包含两个寄存器,一个用于发送 (TDR),一个用于接收 (RDR),因此它具有 双重功能(读和写)。

        TDR 寄存器在内部总线和输出移位寄存器之间提供了并行接口。

        RDR 寄存器在输入移位寄存器和内部总线之间提供了并行接口。

        在使能奇偶校验位的情况下(USART_CR1 寄存器中的 PCE 位被置 1)进行发送时,由于 MSB 的写入值(位 7 或位 8,具体取决于数据长度)会被奇偶校验位所取代,因此该值不 起任何作用。

        在使能奇偶校验位的情况下进行接收时,从 MSB 位中读取的值为接收到的奇偶校验位。

4.3 USART_BRR 波特率寄存器

USART_BRR:波特率寄存器   Baud rate register

位 15:4 DIV_Mantissa[11:0]

USARTDIV 的尾数这 12 个位用于定义 USART 除数 (USARTDIV) 的尾数(整数)

位 3:0 DIV_Fraction[3:0]

USARTDIV 的小数这 4 个位用于定义 USART 除数 (USARTDIV) 的小数(小数)。当 OVER8 = 1 时,不考虑 DIV_Fraction3 位,且必须将该位保持清零。

:位 15:4的意思是第4-15位; 位3:0的意思是第0-3位;

4.3.1 如何配置波特率寄存器USART_BRR

首先,先来介绍一下STM32F4波特率的计算(过采样OVER8=0):

 其中:(f PCLK)是给串口的时钟(PCLK1 用于USART2~5,PCLK2 用于USART1 和USART6)

        USARTDIV是一个无符号定点数。

整个公式,只要我们知道了USARTDIV就可以计算出波特率;同样的,我们知道了波特率,也可以反过来求USARTDIV;

到这里,我们需要明确我们求波特率的目的是什么?明确了目的我们才能知道究竟是正着求波特率,还是反过来求USARTDIV;(这很重要)

我们希望通过USARTDIV得到串口USART_BRR寄存器的值。通过上述公式计算出USARTDIV;

ag. 假设我们串口1设置波特率115200,PCLK2的时钟(APB2总线时钟频率)为84M(通过时钟的学习,我们知道总线时钟为168M,APB2通过分频器得到的时钟频率为168M/2=84M),因此:

        USARTDIV=84000000/(115200*16)=45.572

再次看这个图:通过波特率寄存器计算出的值会返回到发送数据寄存器TDR接受数据寄存器RDR,作为发送和接收的波特率;

所以最终得到DIV_Fraction=16*0.572=9=0X09;(小数部分)

                     DIV_Mantissa=45=0X2D;(整数部分)

照应上部分波特率寄存器的配置:

位 15:4 DIV_Mantissa[11:0]:

USARTDIV 的尾数这 12 个位用于定义 USART 除数 (USARTDIV) 的尾数(整数)

位 3:0 DIV_Fraction[3:0]:

USARTDIV 的小数这 4 个位用于定义 USART 除数 (USARTDIV) 的小数(小数)。当 OVER8 = 1 时,不考虑 DIV_Fraction3 位,且必须将该位保持清零。

注:位 15:4的意思是第4-15位; 位3:0的意思是第0-3位;

5. 串口通信程序实现(发送什么,就接收什么)

1. 串口时钟使能:RCC_APBxPeriphClockCmd();    GPIO时钟使能:RCC_AHB1PeriphClockCmd();

2. 引脚复用映射:GPIO_PinAFConfig();

3. GPIO端口模式设置:GPIO_Init();  模式设置:GPIO_Mode_AF;

4. 串口参数初始化:USART_Init();

5. 开启中断并且初始化NVIC:NVIC_Init();     USART_ITConfig();

6. 使能串口:USART_Cmd();

7. 编写中断服务函数:USARTx_IQRHandler();

8. 串口数据收发:void USART_SendData();    uint16_t USART_ReceiveData();

9. 串口传输状态获取:FlagStatic USART_GetFlagStatus();    void USART_ClearITPendingBit();

#include "stm32f4xx.h"
#include "delay.h"
#include "LED.h"
#include "BEEP.h"
#include "Key.h"
#include "usart.h"


void My_USART1_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;//设置GPIOA结构体变量
	USART_InitTypeDef USART_InitStructure;//设置串口结构体变量
	NVIC_InitTypeDef NVIC_InitStructure;//设置中断优先级NVIC结构体变量
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能串口1时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//GPIOA使能
	
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);//PA9引脚映射为串口1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//PA10引脚映射为串口1
	
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//初始化引脚
	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_100MHz;//速度
	GPIO_Init(GPIOA,&GPIO_InitStructure);//GPIOA初始化
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//初始化引脚
	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_100MHz;//速度
	GPIO_Init(GPIOA,&GPIO_InitStructure);//GPIOA初始化
	
	
	USART_InitStructure.USART_BaudRate=115200;//设置波特率
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//设置串口模式使能Tx/Rx
	USART_InitStructure.USART_Parity=USART_Parity_No;//设置奇偶校验位
	USART_InitStructure.USART_StopBits=USART_StopBits_1;//设置停止位
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;//设置8位字长(设置9位字长通常最后一位是奇偶校验位)
	USART_Init(USART1,&USART_InitStructure);//串口初始化
	USART_Cmd(USART1,ENABLE);//使能串口1
	
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启相关中断   USART_IT_RXNE使能非空
	NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//IQR通道使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//子优先级1
	NVIC_Init(&NVIC_InitStructure);//初始化NVIC中断优先级
	
	
	
}
void USART1_IRQHandler(void)//中断服务函数
{
	u8 res;
	if(USART_GetITStatus(USART1, USART_IT_RXNE))//判断开启的中断是否接收到了相关信息,当该寄存器是1时,表示有数据接收到了
	{
		res=USART_ReceiveData(USART1);//将串口1接收到的数据给res
		USART_SendData(USART1,res);//将res在发送给串口1
	}
}
int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组,2位抢占优先级,2位响应优先级
	My_USART1_Init();
	while(1)
	{
		
	}

}

串口调试助手: 

 

5.1 STM32串口通信出现乱码

STM32在串口通信实验的过程中:可能会出现串口调试助手出现乱码的现象;

原因如下:

1. 串口发送的数据线损坏。(这种可能微乎其微)

2. 串口通信是异步通信,双方设定的通信准则不一致;可能是双方通信的波特率不一致;

3. STM32F4库函数:stm32f4xx.h设定的波特率是25(系统默认设置的),而开发板的外部时钟跟系统库函数设定的不一致,进而串口通信时出现乱码。需要根据自己开发板的外部时钟进行更改。

解决方法:

首先查看自己开发板的外部时钟是多少?打开stm32f4xx.h头文件大概123行,将系统默认的25000000改为自己开发板的外部时钟大小即可;

6. STM32串口通信程序

6.1 usart 中断服务函数精讲

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;//设置8位Res,用来接收存储的数据,作为中间变量,进行数据的发送与接收
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)  USART_IT_RXNE 表示读取数据寄存器不为空 
	{//RESET库函数定义为0,SET为1,SER=!RESET
		//USART_GetITStatus表示获取中断状态标志位;
		//判断如果获取的中断状态标志位不为0,则if判断语句为真,则执行下述程序
		Res =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据给Res
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			//USART_RX_STA为16位状态标志位,其中第15位和第16位分别为状态标志位和停止位,置1表示停止和完成接收
		//(USART_RX_STA&0x8000)==0表示将USART_RX_STA的最高位,也就是16位拿出来,如果等于0,表示接收未完成,继续接收
		{
			//收到继续接收的指令后,紧接着需要判断第15位是否为1,也就是是否已经接收到了0x0D;
			if(USART_RX_STA&0x4000)//接收到了0x0d
				//USART_RX_STA&0x4000表示将第15位拿出来判断是否为1
			{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				//如果第15位已经接收到了0x0D,那么协议规定第16位为0x0A,如果没有接收到0x0A,USART_RX_STA状态标志位置0
				else USART_RX_STA|=0x8000;	//接收完成了 
				//否则,表示接收到了,将第16位置1
			}
			else //还没收到0X0D
			{	
				if(Res==0x0d)USART_RX_STA|=0x4000;//如果接收到的数据为0x0D,则将第15位置1
				else//如果没有接收到0x0D,则继续在0-14位接收数据
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;//将Res接收到的数据存储到BUF中,存储的字节为
					//USART_RX_STA&0X3FFF前14位;
					USART_RX_STA++;//每存储一位,状态位++,表示前0-14位一直在存储数据
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  表示存储的位数超过了数据位
					//USART_RX_STA>(USART_REC_LEN-1)表示前0-14的数据位++,存储量大于USART_REC_LEN-1,超过存储的字节长,状态位置0
					//USART_REC_LEN-1是因为最后一位是换行符
				}		 
			}
		}   		 
  } 
} 

6.2 串口通信程序精讲

#include "stm32f4xx.h"
#include "delay.h"
#include "LED.h"
#include "BEEP.h"
#include "Key.h"
#include "usart.h"

int main(void)
{
	unsigned char t,len;//定义最大接收的字节数
	unsigned int times=0;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统的中断优先级分组2
	delay_init(168);//初始化延迟函数
	uart_init(115200);//串口初始化波特率设置为115200
	LED_Init();//LED初始化
	LED0=0;//默认程序输入时LED0点亮
	while(1)
	{
		if(USART_RX_STA&0x8000)//USART_RX_STA接收状态标记  USART_RX_STA&0x8000如果为真,则表示最高位为1,也就是bit15接收完成标志
			//bit15:接收完成标志   bit14:接收到0x0D标志  bit13-0:接收到的有效数据个数
		//程序要求,发送的字符是以回车换行结束(0x0D,0x0A)
		//0x0D是回车的ASCII码
		{
			len=USART_RX_STA&0x3FFF;
			//因为USART_RX_STA是16位,第16位和第15位是判断是否接收完成和停止的标志位,0-14位是数据位
			//USART_RX_STA&0x3FFF是USART_RX_STA&0011 1111 1111 1111把数据位全部拿出来
			printf("\r\n您发送的消息为:\r\n");//打印您发送的消息为:不断发送到串口
			for(t=0;t<len;t++)//串口通信是串行通信,需要一位一位的传
			{
				USART1->DR=USART_RX_BUF[t];//接收缓冲,最大USART_REC_LEN个字节,末字节为换行符
				//串口接收到的数据保存在USART_RX_BUF中,t是接收到的字节数
				//将接收到的数据保存在DR数据寄存器中
				while((USART1->SR&0x40)==0);//SR是状态寄存器,状态寄存器的最高位如果是0,表示数据传输完成,可以执行下一步
			}
			printf("\r\n\r\n");//打印换行
			USART_RX_STA=0;//状态标志位置0,表示本次传输完成,可以执行下一次的传输了
		}
		else//最高位不是1,数据还没有接收完成,继续接收,传至数据位
		{
			times++;//设置一个时间位,时间++,类似于定时器中断的T0Count;
			if(times%5000==0)//每过5s,打印一次下述程序
			{
				printf("\r\nALIENTEK 探索者 STM32F407开发板 串口实验\r\n");
				printf("正点原子@ALIENTEK\r\n\r\n\r\n");
			}
			if(times%200==0)//每过200ms,打印一次:请输入数据,以回车键结束
				printf("请输入数据,以回车键结束\r\n");
			if(times%30==0)//每过30ms,LED0闪烁
				LED0=!LED0;
			delay_ms(10);//延迟10ms
		}
	}
}

7. MDK5:main.c(17): error: #268: declaration may not appear after executable statement in block

该错误是在运行上述程序的过程中出现的;

出现该错误的主要原因是MDK5默认的编程方式是C89,是不支持C/C++的空格编程定义也就是上述在定义结构体变量时,使用了空格编程的定义),因此会报错。

解决方法:点击MDK5的魔术棒

在C/C++中勾选  C99 Mode即可;

  • 14
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值