目录
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即可;文章来源:https://www.toymoban.com/news/detail-502033.html
文章来源地址https://www.toymoban.com/news/detail-502033.html
到了这里,关于STM32F4_串口通信详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!