STM32F4_串口通信详解

news/2025/3/15 0:58:44/

目录

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引脚映射为串口1GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//PA10引脚映射为串口1GPIO_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/RxUSART_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);//使能串口1USART_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;//抢占优先级1NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//子优先级1NVIC_Init(&NVIC_InitStructure);//初始化NVIC中断优先级}
void USART1_IRQHandler(void)//中断服务函数
{u8 res;if(USART_GetITStatus(USART1, USART_IT_RXNE))//判断开启的中断是否接收到了相关信息,当该寄存器是1时,表示有数据接收到了{res=USART_ReceiveData(USART1);//将串口1接收到的数据给resUSART_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);	//读取接收到的数据给Resif((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状态标志位置0else USART_RX_STA|=0x8000;	//接收完成了 //否则,表示接收到了,将第16位置1}else //还没收到0X0D{	if(Res==0x0d)USART_RX_STA|=0x4000;//如果接收到的数据为0x0D,则将第15位置1else//如果没有接收到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);//设置系统的中断优先级分组2delay_init(168);//初始化延迟函数uart_init(115200);//串口初始化波特率设置为115200LED_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即可;


http://www.ppmy.cn/news/39415.html

相关文章

java基础之抽象类与接口

文章目录1.抽象方法和抽象类2.抽象类的作用3.接口4.接口和抽象类的异同5.面向接口编程1.抽象方法和抽象类 抽象方法和抽象类必须使用abstract修饰符来定义&#xff0c;有抽象方法的类只能被定义成抽象类&#xff0c;抽象类里可以没有抽象方法。 抽象类必须使用abstract修饰符来…

咖啡业:混战不止

配图来自Canva可画 不知不觉间&#xff0c;作为舶来品的咖啡已经深入到人们的日常生活中&#xff0c;尤其是对于上班族而言&#xff0c;咖啡更是成为了提神醒脑的一大“利器”&#xff0c;因此&#xff0c;大街小巷出现了越来越多咖啡店的身影。而随着消费者消费观念的不断改变…

pt05Encapsulationinherit

Encapsulation &inherit 封装继承 封装 向类外提供必要的功能&#xff0c;隐藏实现的细节, 代码可读性更高优势&#xff1a;简化编程&#xff0c;使用者不必了解具体的实现细节&#xff0c;只需要调用对外提供的功能。私有成员&#xff1a;作用&#xff1a;无需向类外提供…

有效的数独(数组)

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff09; …

ChatGPT的技术路线

目录ChatGPT的技术路线基于 GPT-3.5&#xff0c;GPT-4 预计提升更明显GPT、Bert 均源自 Transformer 模型领先的 NLP 模型RLHF 与 TAMER 是重要架构支撑ChatGPT的技术路线 基于 GPT-3.5&#xff0c;GPT-4 预计提升更明显 ChatGPT 是基于 GPT-3.5 的主力模型。在互联网开源数据…

使用GIS空间分析进行植物生长区域选址

经过观察可以发现某种珍贵植物在山区的某个区域位置生长状况明显要比其他区域好很多&#xff0c;通过研究了解到这种植物生长具有严格的生长条件。为了能更好地保护该种植物的生长环境&#xff0c;现在需要使用GIS空间分析方法&#xff0c;将适合植物生长的区域找出来。 树谷资…

Ruby设计-基础语法

一、脚本语言 ​ 我似乎对于 Python 和 Ruby 强求太多了&#xff0c;我要让他们实现 C 的所有功能。他们确实可以实现所有的功能&#xff0c;但是他们的目的不是为了这个。 ​ 我也可以用 shell 实现很多复杂的算法&#xff0c;但是我绝对不会用他去写图着色或者动态规划。但…

TypeScript01-基础知识

基础类型 boolean 类型 let isDone: boolean false; // ES5&#xff1a;var isDone false;number 类型 let count: number 10; // ES5&#xff1a;var count 10;string 类型 let name: string "semliker"; // ES5&#xff1a;var name semlinker;Symbol 类…