提示:
实验采用芯片:STM32F407ZGT6
开发平台:keil5
目录
一、通讯接口相关知识
1.并行通讯和串行通讯
并行通讯:
-传输原理:数据各个位同时传输
-优点:速度快
-缺点:占用资源
串行通讯:
-传输原理: 数据按位传输
-优点: 占用引脚资源少
-缺点: 速度相对较慢
2.单工、半双工、全双工通讯
a.单工通讯:
数据传输只支持数据在一个方向上传输
b.半双工通讯:
允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际是一种切换方向的单工通讯。
c.全双工通讯:
允许数据同时在两个方向上传输,因此,全双工通讯是两个单工通讯方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
3.同步通讯和异步通讯
a.同步通讯:
收发设备双方会使用一根信号线表示时钟信号,在时钟信号的驱动下双方进行协调,同步数据,通讯中通常双方会统一规定在时钟信号的上升沿或者下降沿对数据进行采样,对应时钟极性和时钟相位
b.异步通讯:
不需要时钟信号进行同步,他们直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据,某些通讯中还需要双方约定数据的传输速率(波特率),以便更好的同步。
4.常见的串口通信接口
二、STM32的串行通信接口
UART:通用异步收发器
USART:通用同步异步收发器
STM32F4xx目前最多支持8个UART,STM32F407一般是6个。具体可以对照选型手册和数据手册来看。 STM32F103目前最多支持5个UART
1.UART异步通信方式引脚连接方法:
2.UART异步通讯方式特点
- 全双工异步通讯
- 小数波特率发生器系统,提供精确的波特率
- 可配置的16倍过采样或8倍过采样,因而为速度容差与时钟容差的灵活配置提供了可能。
- 可编程的数据字长度(8位或者9位)
- 可配置的停止位(支持1或者2位停止位)
- 可配置的使用DMA多缓冲器通讯
- 单独的发生器和接收器使能位
- 检测标志:1.接受缓冲器2.发送缓冲器空3.传输结束标志
- 多个带标志的中断源。触发中断
- 其他:校验控制,四个错误检测标志。
3.STM32串口通信过程
数据接收过程:
数据发送过程:
4.STM32串口异步通信需要的参数
- 起始位
- 数据位(8位或者9位)
- 奇偶校验位(第9位)
- 停止位(1、1.5、2位)
- 波特率设置
一串数据帧示例:
5.USART功能框图
三、STM32串口常用寄存器和库函数
1.常用的串口相关寄存器
- USART_SR状态寄存器
- USART_DR数据寄存器
- USART波特率寄存器
2.串口相关库函数
1、串口初始化函数
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
参数1:要初始化的串口外设可以是USART1、USART2等。
参数2:是指向一个结构体(USART_InitTypeDef)的指针,该结构体保存 USART 模块的配置设置。该结构体通常包括波特率、数据位、停止位、奇偶校验、硬件流控制等设置。
2.使能或失能串口
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
参数1:要使能的串口外设可以是USART1、USART2等
参数2:ENABLE使能或者DISABLE失能
3.使能相关中断
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
参数1:要使能中断的串口外设可以是USART1、USART2等
参数2:要配置的 USART 中断类型
- USART_IT_CTS:CTS(Clear to Send)变化中断。当 CTS 状态发生变化时触发中断。
- USART_IT_LBD:LIN(Local Interconnect Network)中断。用于检测 LIN 通信中的 Break 信号。
- USART_IT_TXE:发送数据寄存器空中断。当发送数据寄存器为空并且可以发送新数据时触发中断。
- USART_IT_TC:传输完成中断。当所有数据都已经被发送完毕,且传输寄存器为空时触发中断。
- USART_IT_RXNE:接收数据寄存器非空中断。当接收到新的数据时触发中断。
- USART_IT_IDLE:空闲线检测中断。当检测到空闲线时触发中断,表示当前数据传输已经完成。
- USART_IT_PE:奇偶校验错误中断。当接收到的数据的奇偶校验位与期望的不匹配时触发中断。
- USART_IT_ERR:错误中断。当发生帧错误、噪声错误或溢出错误时触发中断。
参数3:要设置中断的状态,可以是ENABLE或者DISABLE。
4.发送数据函数
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
参数1:要发送数据的串口外设可以是USART1、USART2等
参数2:要发送的数据
5.接收数据函数
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
参数1:要接收数据的串口外设可以是USART1、USART2等
返回值:串口接收到的数据
6.获取状态标志函数
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)
参数1:要获取状态标志的串口外设可以是USART1、USART2等
参数2:要检查的标志类型
- USART_IT_CTS:CTS(Clear to Send)变化中断。当 CTS 状态发生变化时触发中断。
- USART_IT_LBD:LIN(Local Interconnect Network)中断。用于检测 LIN 通信中的 Break 信号。
- USART_IT_TXE:发送数据寄存器空中断。当发送数据寄存器为空并且可以发送新数据时触发中断。
- USART_IT_TC:传输完成中断。当所有数据都已经被发送完毕,且传输寄存器为空时触发中断。
- USART_IT_RXNE:接收数据寄存器非空中断。当接收到新的数据时触发中断。
- USART_IT_IDLE:空闲线检测中断。当检测到空闲线时触发中断,表示当前数据传输已经完成。
- USART_IT_PE:奇偶校验错误中断。当接收到的数据的奇偶校验位与期望的不匹配时触发中断。
- USART_IT_ERR:错误中断。当发生帧错误、噪声错误或溢出错误时触发中断。
返回值:SET或者RESET
7.清除状态标志函数
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
参数1:要清除状态标志的串口外设可以是USART1、USART2等
参数2:清除的状态
8.获取中断标志位函数
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
参数1:要获取中断标志的串口外设可以是USART1、USART2等
参数2:要获取的中断类型
返回值:SET或者RESET
9.清除中断标志位函数
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)
参数1:要清除中断标志的串口外设可以是USART1、USART2等
参数2:要清除的中断类型
四、STM32串口配置一般步骤
- 串口时钟使能:RCC_APBxPeriphClockCmd();
GPIO时钟使能:RCC_AHBxPeriphClockCmd(); - 引脚复用(STM32F4才需要) :GPIO_PinAFConfig();
- GPIO端口模式设置:GPIO_Init();模式设置为:GPIO_Mode_AF
- 串口参数初始化:USART_Init();
- 开启中断并初始化NVIC(开启中断才需要此步骤):NVIC_Init();USART_ITConfig();
- 使能串口:USART_Cmd();
- 编写中断处理函数:USARTx_IRQHandler();
- 串口数据手发:USART_SendData();USART_Receive();
- 串口传输状态获取:USART_GetFlagStatus();USART_ClearITPendingBit();
五、串口通信实验(通过串口控制LED)
======================================LED.c文件============================================
#include "led.h"
//LED0-PF9、LED1-PF10
void Led_Init(void)//LED初始化
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);//开启时钟
GPIO_InitTypeDef GPIO_InitStruct={
0};
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 |GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOF,&GPIO_InitStruct);
LED1(0);
LED2(0);
}
========================================LED.h文件===========================================
#ifndef _LED_H_
#define _LED_H_
#include "stm32f4xx_gpio.h"
void Led_Init(void);
#define LED1(x) x?(GPIO_ResetBits(GPIOF,GPIO_Pin_9)):(GPIO_SetBits(GPIOF,GPIO_Pin_9))
#define LED2(x) x?(GPIO_ResetBits(GPIOF,GPIO_Pin_10)):(GPIO_SetBits(GPIOF,GPIO_Pin_10))
#endif
========================================USART.c文件===========================================
#include "usart1.h"
//PA9-TX、PA10-RX、USART1
void usart1_init(u32 Bound)
{
//1.开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
//2.USART1对应引脚复用
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
//3.GPIO初始化
GPIO_InitTypeDef GPIO_InitStruct={
0};
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//速度
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//复用推挽输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA,&GPIO_InitStruct);
//4.USART1初始化
USART_InitTypeDef USART_InitStruct={
0};
/* USART_InitStruct members default value */
USART_InitStruct.USART_BaudRate = Bound;//波特率
USART_InitStruct.USART_WordLength = USART_WordLength_8b;//一次传输数据8位
USART_InitStruct.USART_StopBits = USART_StopBits_1;//停止位1位
USART_InitStruct.USART_Parity = USART_Parity_No ;//没有奇偶校验位
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//模式
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//没有硬件数据流控制
USART_Init(USART1,&USART_InitStruct);//串口初始化
USART_Cmd(USART1,ENABLE);//使能串口
#if EN_USART1_RX //串口接收中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//配置串口接收中断
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);//配置空闲中断
//初始化NVIC
NVIC_InitTypeDef NVIC_InitStruct={
0};
NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;//子优先级
NVIC_Init(&NVIC_InitStruct);
#endif
}
//发送一个字节数据
void Send_onebyte(u8 data)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==0);
USART_SendData(USART1,data);
}
//发送固定长度字符串
void Send_buff(u8*buff,u16 len)
{
for(u16 i=0;i<len;i++)
{
Send_onebyte(buff[i]);
}
}
//发送不定长字符串
void Send_str(u8* str)
{
while(*str!='\0')
{
Send_onebyte(*str);
str++;
}
}
void USART1_IRQHandler(void)
{
u8 rxdata=0;
if(USART_GetITStatus(USART1,USART_IT_RXNE)==1)//判断是否为接收中断
{
rxdata=USART_ReceiveData(USART1);
Send_onebyte(rxdata);
switch(rxdata)
{
case '1':
LED1(1);break;
case '2':
LED2(1);break;
case '0':
LED1(0);LED2(0);break;
default:
break;
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除接收中断标志
}
if(USART_GetITStatus(USART1,USART_IT_IDLE)==1)//判断是否为空闲中断(当发送连续数据时,要通过空闲中断判断是否发送完成。)
{
rxdata=USART1->DR;//清除中断标志,(根据手册)
rxdata=USART1->SR;
}
}
========================================USART.h文件===========================================
#ifndef _USART1_H_
#define _USART1_H_
#include "stm32f4xx_usart.h"
#include "led.h"
#define EN_USART1_RX 1
void usart1_init(uint32_t Bound);
void Send_onebyte(uint8_t data);
void Send_buff(uint8_t*buff,uint16_t len);
void Send_str(uint8_t* str);
#endif
实验现象:
注意:
若发送的数据出现乱码现象,1.先检查波特率是否设置正确,代码设置的波特率和串口助手的要保持一致。2.检查自己开发板的外部时钟晶振和代码中的外部时钟晶振是否一致,可以在stm32f4xx.h头文件中查看。