STM32F4_串口通信详解

2023-05-16

目录

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即可;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F4_串口通信详解 的相关文章

  • STM32F4应用DMA——串口收发不定长数据

    STM32F4应用DMA 串口收发不定长数据 使用STM32自带DMA传输数据 xff0c 可以减轻CPU负担 xff0c 只需设置一些参数即可发送想要发送的数据 xff0c 以下是STM32F407VE芯片测试过的部分代码 xff0c 可
  • 手把手入门stm32f4 (1)

    GPIO 1 一共有7组IO xff0c 每组有16个口 即一共有16 7 61 112个口 2 每个口基本上都可以触发中断 xff08 区别于51 xff0c 51只有P3 2 P3 2 xff09 3 共有8中输入输出模式 xff08
  • STM32-串口通信详解

    目录 前言 一 通信接口背景知识 1 并行通信和串行通信 2 串行通信的分类 二 STM32的串口通信基础 1 串口通信接口 2 串口通信框图 3 串口通信相关寄存器 4 波特率计算方法 三 库函数配置 1 串口配置一般步骤 总结 前言 众
  • PX4中通过串口读取STM32F4串口发送过来消息并发布UORB主题

    PX4中通过串口读取STM32F4串口发送过来消息并发布UORB主题 本次小项目是通过PX4读取STM32F4发过来的数据 xff0c 之前博客介绍了我做的STM32端项目 xff0c 再稍微啰嗦一下 xff1a 解析AIRMAR和测深仪数
  • STM32F4无人机动力旋翼拉力测试

    更多交流欢迎关注作者抖音号 xff1a 81849645041 目的 了解无人机电调 电机转速 旋翼拉力与油门给定信号之间的关系 xff0c 可以通过相关设备进行拉力测试 实验原理 飞机旋翼绕旋翼旋转轴旋转时 xff0c 每个叶片的工作类同
  • stm32F4 hal库之CAN通信的实现

    本文的目的是为了能够实现功能 xff0c 故写的时候比较简略 参考资料 xff1a https blog csdn net u012308586 article details 81001102 正点原子开发手册 目标 xff1a 通过ca
  • STM32F4 使用结构体配置功能

    1 IIC配置 void IIC Mode Config void I2C InitTypeDef I2C InitStructure I2C InitStructure I2C Mode 61 I2C Mode I2C IIC模式 I2C
  • STM32F4 DMA

    STM32F4有2个DMA xff0c 每个DMA控制器有8个数据流 xff0c 每个数据流有多达8个通道 xff0c 但是DMA1 控制器 AHB 外设端口与 DMA2 控制器的情况不同 xff0c 不连接到总线矩阵 xff0c 因此 x
  • STM32F4的FSMCTFT接口配置

    利用安富莱的V5开发板 xff0c 根据原理图配置 xff1b LCD接口原理图如下 xff1a 从原理图看出LCD接的是BANK1 xff0c 片选是NE4 xff0c LCD的RS为A18 xff0c 配置如下 然后安富莱的LCD片选是
  • 串口通信详解

    一 串口通讯简介 串口通信 Serial Communications 的概念非常简单 xff0c 串口按位 bit 发送和接收字节 尽管比按字节 byte 的并行通信慢 xff0c 但是串口可以在使用一根线发送数据的同时用另一根线接收数据
  • 关于stm32f4上对(28byj-48)步进电机的应用控制和解读。

    1 关于stm32f4上对 xff08 28byj 48 xff09 步进电机的应用控制和解读 xff08 1 ULN2003驱动模块电路 步进电机不能直接和单片机连接 xff0c 需要对应驱动模块提供电机所需的电流 电路图分析 xff1a
  • STM32F4 422串口通信

    STM32F429 422串口通信 422串口的硬件原理图如下 xff1a 422串口和232串口不同的是引脚的电平控制 xff0c 还有DE是发送使能 xff0c 置高即可 xff1a RE是接收使能 xff0c 置低即可 422接出来的
  • stm32f4串口接收与发送

    之前有写一篇stm32f1串口接收与发送的文章 xff0c stm32f4与f1只有配置上的一点不同 xff0c 今天把f4的串口接收与发送代码分享一下 详细解释推荐大家看f1那篇 xff0c 都是一样的 xff0c stm32f1串口发送
  • 【STM32】UART串口通信详解

    目录 一 数据通信方式 1 串行与并行通信2 全双工 半双工及单工通讯3 同步通讯与异步通讯 二 串口通讯协议 STM32串口简介1 物理层1 RS232标准2 USB转串口 重点 3原生的串口到串口2 协议层1 xff09 通讯的起始和停
  • STM32F4应用-串口通信

    STM32F4应用 串口通信 1 基本介绍1 1 简介1 2 串口协议1 3 通信过程 2 配置过程2 1 引脚复用2 2 配置步骤2 3 例子 参考文献 1 基本介绍 1 1 简介 串口通信涉及USART TX RX xff0c GND三
  • 家庭IOT监测之摄像头OV7670测试

    本篇目标 使用STM32F407驱动摄像头OV7670 并上位机显示照片结果 材料准备 STM32F4标准工程 stm32f407自建标准工程 stm32f4标准工程 git仓库地址 STM32F4摄像头测试工程 里面包含ov7670驱动文
  • 使用HAL库开发STM32:系统时间基础及进阶使用

    文章目录 目的 基础使用 进阶使用 总结 目的 HAL库默认提供了系统时间 系统时间默认情况下由SysTick定时器计数产生 系统时间一方面用于HAL库自身调用 另一方面用户也可以使用 为开发带来便利 本文提到的相关使用主要应用于未使用OS
  • 当数据大小较小时,内存到内存 DMA 传输是否需要权衡?

    我正在学习 STM32 F4 微控制器 我正在尝试找出使用 DMA 的限制 根据我的理解和研究 我知道如果数据量较小 即设备使用DMA生成或消耗少量数据 则开销会增加 因为DMA传输需要DMA控制器执行操作 从而不必要地增加系统成本 我做了
  • STM32的HAL中实现单按、长按和双按功能

    我正在尝试实现单击 双击和长按功能来执行不同的功能 到目前为止 我已经理解了单击和长按的逻辑 但我不知道如何检测双击 至于代码 我使用计数器实现了单击和长按 但代码仅停留在第一个 if 条件上 bool single press false
  • STM32:从自定义引导加载程序跳转到应用程序时发生硬故障

    我正在开发带有自定义引导加载程序和应用程序的 STM32F401 MCU 编译器是GCC 5 2 1 没有运行优化 在以下跳转序列后的第一次中断后 我遇到了硬故障 引导加载程序 gt 应用程序 gt 引导加载程序 gt 应用程序 从引导加载

随机推荐

  • Linux应用程序之Helloworld入门

    对于初学者来说 xff08 本人就是 xff09 xff0c 如何开始写第一个程序至关重要 有的时候一个简单的问题会严重影响到学习的积极性和自信心 这里结合实际工作中的一些经验 xff0c 总结方法步骤 xff0c 对Linux下应用程序H
  • ctags简明使用方法

    ctags xff08 Generate tag files for source code xff09 是vim下方便代码阅读的工具 xff0c 它可以在命令行下帮助程序员很容易地浏览源代码 ctags 最先是用来生成C代码的tags文件
  • char和unsigned char强制转换成int后的差异

    最近有人提到char和unsigned char有什么区别 xff0c 当然这个问题如果刚学计算机或者编程语言的人来说 xff0c 非常简单 我也这么认为 xff0c 无非就是有符号和无符号的差别嘛 这个问题让我想到了以前学习计算机常识的时
  • 如何使用mstsc进行远程登录?

    如何使用mstsc进行远程登录 xff1f 步骤一 xff1a 点击 开始 gt 运行 xff0c 输入mstsc xff0c 如下图所示 xff1a 步骤二 xff1a 输入连接PC的IP地址 xff0c 如下图所示 xff1a 步骤三
  • VNC远程ubuntu时,右击无法打开terminal

    参考博客 xff1a https blog csdn net qq 44132116 article details 103960393 问题描述 xff1a 我通过命令行连接实验室服务器 xff0c 装了anaconda xff0c 之后
  • TCP实时传图像

    目的 xff1a QT 43 openCV xff0c 在Ubuntu16 04版本下 xff0c 通过TCP实现图片的传输 步骤 xff1a 客户端建一个相机线程 xff0c 一个TCP线程 xff0c 相机线程捕获画面并将Mat传到TC
  • Ubuntu mate 16.04安装ROS

    官方文档有详细的安装步骤 xff1a http wiki ros org kinetic Installation Ubuntu 配置Ubuntu属性如下 xff1a https help ubuntu com community Repo
  • 关于ROS中的namespace

    当我们给发布的消息起名字时 xff0c 注意 34 points image 34 和 34 points image 34 是不一样的 xff0c 前者表示这个话题的名字是一个绝对名称 xff0c 它不在任何的namespace中 xff
  • 算法移植arm开发板小结(一)

    将windows的c c 43 43 代码移植到友善Tinny4412的arm上运行 首先要先将windows代码在ubuntu系统下编译通过 xff0c 然后在ubuntu系统下建立Tinny4412的arm交叉编译器 xff0c 并将代
  • CVTE嵌入式软件实习面经-已offer

    面试通过 时间线 4月份投的简历 xff0c 后面因为考试错过了 xff0c 后面月尾赶上最后了最后一场笔试 xff0c 笔试完四天左右通过 xff0c 通过两天后接到面试官电话 xff0c 那时候投了挺多公司的 xff0c 以为是其他的
  • 非对称加密详解

    非对称加密 1 非对称加密1 1 什么是非对称加密1 2 非对称加密通信流程1 3 RSA1 3 1 RSA加密1 3 2 RSA解密1 3 3 总结 1 4 ECC椭圆曲线 1 非对称加密 1 1 什么是非对称加密 非对称加密也叫公钥密码
  • PHP函数usort()解释

    定义和用法 usort 函数使用用户自定义的函数对数组排序 注释 xff1a 如果两个元素比较结果相同 xff0c 则它们在排序后的数组中的顺序未经定义 到 PHP 4 0 6 之前 xff0c 用户自定义函数将保留这些元素的原有顺序 但是
  • strchr()、strrchr()、strchrnul()…

    头文件 xff1a include 函数原型 xff1a char strchr char str int c char strrchr char str int c define GNU SOURCE 头文件 xff1a include
  • freertos- 任务调度器-vTaskStartScheduler()解析(笔记)

    1 全局状态量 系统时钟节拍计数器tick static volatile TickType t xTickCount 61 TickType t 0U 全局下一任务调度需要的阻塞时间 xff0c 用于及其唤醒任务static volati
  • freertos- 重要管理数据结构-列表List及其操作API (笔记)

    1 xff0c 源码中的位置 list h xff0c list c 2 xff0c 列表和列表项结构 列表项分为2种 xff1a struct xLIST ITEM listFIRST LIST ITEM INTEGRITY CHECK
  • 技术分享 | Javaer 如何做单元测试?

    前言 xff1a 本文适用于 javaer xff0c 其他开发者或许可以借鉴 写本文的主旨有两个 xff0c 一是简单的给大家介绍下单元测试 xff0c 二是通过一个简单的示例来介绍一些单元测试的技巧 xff0c 希望以此来降低大家写单元
  • 扩展卡尔曼滤波【转】

    1 重点看 SLAM中的EKF xff0c UKF xff0c PF原理简介 半闲居士 博客园 2 机器人重点看 定位 xff08 一 xff09 xff1a 扩展卡尔曼滤波 windSeS的博客 3 重点实例 扩展卡尔曼滤波 xff08
  • AGV - Background(1)- Company

    Company 米克力美 DZ 80无轨导航AGV小车采用windows10智能交互系统 xff0c xff08 米克力美工业AGV小车机器人采用安卓交互系统 xff09 可自动编程和程序化 xff0c 实现自主学习 使用人员无需培训即可轻
  • 无线路由器CPU浅析 MT7621A、 BCM47189 到底谁强?

    转自 xff1a http bbs 360 cn thread 14459037 1 1 html 在第一讲中 xff0c 已经粗略介绍过了目前路由芯片的四大厂 xff1a Broadcom xff08 博通 xff09 Qualcomm
  • STM32F4_串口通信详解

    目录 1 串口相关介绍及使用 1 1 串口设置的一般步骤 xff1a 1 1 1 串口时钟和GPIO时钟使能 1 1 2 设置引脚复用器映射 1 1 3 GPIO端口模式设置 1 1 4 串口参数初始化 1 1 5 开启中断并且初始化NVI