目录
前言
一:串口协议
1:通信接口
2:串口通信
3:硬件电路
4:电平标准
5:串口参数及其时序
二:USART介绍
1:简历
2:USART框图
3:USART的基本结构
4:数据帧
5: 波特率发生器
6:数据模式
三:HAL
A:HAL库回调
B:配置步骤
四:案例
A:STM32发送数据
B:STM32接收数据
C:STM32发送接收数据
前言
一:串口协议
1:通信接口
通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统
通信协议:制定通信的规则,通信双方按照协议规则进行数据收发
USART通信:
TX: 发送数据的引脚
RX : 接收数据的引脚
I2C通信:
SCL: 时钟 SDA:数据
SPI通信:
SCLK:时钟 MOSl:主机输出数据脚 MISO : 主机输入数据脚 CS : 片选,用于指定通信的对象
CAN通信:
是差分数据脚,用两个引脚表示一个差分数据
USB通信:
也是 是差分数据脚
双工
全双工:就是指通信双方能够同时进行双向通信, 两个数据线分别负责发送和接收数据
半双工 : 一根数据线负责发送和接收数据, eg:I2C通信的SDA线
时钟
同步: 接收方可以在时钟信号的指引下进行采样
异步 : 没有时钟线, 所以需要双方约定一个采样频率, 还需要加一些帧头帧尾等,进行采样位置的对齐
电平
单端: 它们引脚的高低电平都是对GND的电压差, 所以单端信号通信的双方必须要共地,就是把GND接在一起
差分 : 差分信号, 它是靠两个差分引脚的电压差来传输信号的, 在通信的时候,可以不需要GND, 使用差分信号可以极大地提高抗干扰特性
设备
点对点 : 直接传输数据就可以了
多设备 : 一对多, 需要有一个导址的过程,以确定通信的对象
2:串口通信
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力
3:硬件电路
简单双向串口通信有两根通信线(发送端TX和接收端RX)
TX与RX要交叉连接
当只需单向的数据传输时,可以只接一根通信线
当电平标准不一致时,需要加电平转换芯片-----------相同的电平标准才可以通信
VCC的连接
上面的VCG,如果两个设备都有独立供电, VCC可以不接
如果一个设备没有独立供电, 需要VCC把他们连接起来
4:电平标准
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
TTL电平:+3.3V或+5V表示1,0V表示0
RS232电平:-3~-15V表示1,+3~+15V表示0
RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)
5:串口参数及其时序
波特率:串口通信的速率--------波特率表示单位时间内传送的码元符号的个数. 规定串口通信的速率, 串口一般是使用异步通信, 发送和接收,必须要约定好速率, 速率参数,就是波特率. 双方规定波特率为1000bps, 那就表示,1s要发1000位,每一位的时间就是1ms
起始位:标志一个数据帧的开始,固定为低电平------串口的空闲状态是高电平,起始位产生一个下降沿, 告诉接收设备要开始传输数据了
停止位:用于数据帧间隔,固定为高电平-------停止位固定为1,把引脚恢复成高电平,方便下次的数据传输, 可以选择1位、1.5位、2位等
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
校验位:用于数据验证,根据数据位计算得来------校验可以选择3种方式,无校验、奇校验和偶校验
串口中,每一个字节都装载在一个数据帧里面, 每个数据帧都由起始位、数据位和停止位组成
左边: 这里数据位有8个,代表一个字节的8位 (一个字节为8位)
右边 : 数据帧里面,还可以在数据位的最后,加一个奇偶校验位 ,这样数据位就9位
奇偶校验位----实际是对高电频1的校验
奇校验 : 发送数据0000 1111 采用右边的数据位为9位, 给第9位补1, 这时候1就为5个为奇数, 接收方一验证,发现1的个数不是奇数,那就认为传输出错, 就可以选择丢弃,或者要求重传
偶校验: 发送数据0000 1111 采用右边的数据位为9位, 给第9位补0, 这时候1就为4个为偶数, 接收方一验证,发现1的个数不是偶数,那就认为传输出错, 就可以选择丢弃,或者要求重传
奇偶校验只能保证一定程度上的数据校验
数据位的2中表示方法
一种是把校验位作为数据位的一部分, 分为8位数据和9位数据, 其中9位数据,就是8位有效载荷和1位校验位, 另一种就是把数据位和校验位独立开, 数据位就是有效载荷,校验位就是独立的1位
二:USART介绍
1:简历
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器
USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
自带波特率发生器,最高达4.5Mbits/s 可配置数据位长度(8/9)、停止位长(0.5/1/1.5/2)
可选校验位(无校验/奇校验/偶校验)
支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
STM32F103C8T6 USART资源: USART1、 USART2、 USART3
硬件流控制,-----------比如A设备有个TX向B设备的RX发送数据, A设备一直在发,发的太快了, 如果没有硬件流控制, 那B就只能抛弃新数据或者覆盖原数据了. 如果有硬件流控制,在硬件电路上,会多出一根线, 如果B没准备好接收,就置高电平,如果准备好了,就置低电平; A接收到了B反馈的准备信号,就只会在B准备好的时候,才发数据
硬件流控制,可以防止因为B处理慢而导致数据丢失的问题
2:USART框图
寄存器
DR寄存器 : TDR和RDR数据寄存器占用同一个地址,在程序上他们表现为一个寄存器DR寄存器, TDR是只写的RDR是只读的, 当你进行写操作时 数据就写入到TDR寄存器. 当你进行读操作时,数据就是从RDR读出来的
发送(接收)移位寄存器: 发送移位寄存器的作用就是,把个字节的数据一位一位地移出去
标志位-----移位完成产生标志位
TXE : 在存器里就是二进制存储,0101 0101, 硬件检测到你写入数据了, 就会检查,当前移位寄存器是不是有数据正在移位; 如果没有,这个0101 0101就会立刻全部移动到发送移位寄存器, 准备发送. 当数据从TDR移动到移位寄存器时会置一个标志位(TXE置1),叫TXE, 发送寄存器空, 就可以在TDR写入下一个数据了
当TXE标志位置1时, 数据其实还没有发送出去, 只要数据从TDR转移到发送移位寄存器了 , TXE就会置1,我们就可以写入新的数据了
while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
检查标志位USART1的TEX标志为是否等于0-----TDR寄存器的数据有没有移动到发送移位寄存器里面去
RXNE: 和TXE相同的道理: 当数据从接收移位寄存器, 移动到移位RDR寄存器时会置一个标志位(RXNE置1), 这个也不用手动清除标志位, 和TXE原理相同
3:USART的基本结构
发送数据:STM32向外部发送数据;
接收数据:STM32接收外设发送来的数据
TDR和RDR不可见,我们只需要操作DR寄存器就ok了。
发送数据
STM32向外设发送数据。CPU首先把数据发送到数据寄存器(DR)里面,DR会把数据发送到发送数据寄存器(TDR)里面,然后等待发送移位寄存器空,把数据移动到发送移位寄存器里里面等待发送出去
接收数据
.........
4:数据帧
5: 波特率发生器
发送器和接收器的波特率由波特率寄存器BRR里的DIV确定
计算公式:波特率 = fPCLK/ (16 * DIV)
其中fck是串口的时钟,如:USART1的时钟是PCLK2,其他串口都是PCLK1
APB1的时钟最大频率为:36MHZ;APB2的时钟最大频率为:72MHZ;
结论:只有在USART1的时钟频率可以拿72MHZ计算
配置USART1为9600的波特率
6:数据模式
HEX模式/十六进制模式/二进制模式:以原始数据的形式显示
文本模式/字符模式:以原始数据编码后的形式显示
三:HAL
A:HAL库回调
HAL_USART_IROHandler()同步UART是异步的,不经常使用这个函数
HAL_UART_IRQHandler()异步的,经常使用这个函数
B:配置步骤
使用HAL的配置步骤
1:配置串口工作参数-----stm32f1xx hal_uart.h
2:串口底层初始化----stm32f1xx hal_uart.h
3:开启串口异步接收中断------stm32f1xx hal_uart.h
4:设置优先级,使能中断-----------stm32f1xx hal cortex.h
5:编写中断服务函数------stm32f1xx hal_uart.h
6:串口数据发送------stm32f1xx hal_uart.h
注意:
HAL_UART_IRQHandler会清除中断(使HAL_UART_Receive_IT失能),如果HAL_UART_Receive_IT开启异步接收函数在HAL_UART_IRQHandler之前调用了,需要在HAL_UART_IRQHandler清除中断后在调用一遍HAL_UART_Receive_IT
四:案例
A:STM32发送数据
使用STM32向PC端发送一个数据,不要忘记使用串口的时候使用USB串口连接STM32
我们使用的是UART1
UART_HandleTypeDef UART_HandleUART1; /*UART的句柄*/
char g_rx_buffer[100] = "Hello, STM32!"; // 全局数组,用于存储接收到的数据
/**
* @brief 使用UART1,配置为发送和输入模式
* PA9--TX PA10--RX
* @param 波特率
*/
void Uart_Init(uint32_t Baud_rate)
{
/*句柄初始化*/
UART_HandleUART1.Instance=USART1;
UART_HandleUART1.Init.BaudRate=Baud_rate;
UART_HandleUART1.Init.HwFlowCtl=UART_HWCONTROL_NONE;
UART_HandleUART1.Init.Mode=UART_MODE_TX_RX;
UART_HandleUART1.Init.Parity=UART_PARITY_NONE;
UART_HandleUART1.Init.StopBits=UART_STOPBITS_1;
UART_HandleUART1.Init.WordLength=UART_WORDLENGTH_8B;
HAL_UART_Init(&UART_HandleUART1);
/*开启串口异步接收中断*/
HAL_UART_Receive_IT(&UART_HandleUART1,(uint8_t*)g_rx_buffer,1);
}
/**
* @brief 串口底层初始化
*
* @param 波特率
*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Init;
if(huart->Instance==USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_USART1_CLK_ENABLE();
//PA9_TX发送
GPIO_Init.Mode=GPIO_MODE_AF_PP; /*复用推挽输出*/
GPIO_Init.Pin=GPIO_PIN_9;
GPIO_Init.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
//PA10_RX接收
GPIO_Init.Mode=GPIO_MODE_INPUT; /*输入*/
GPIO_Init.Pin=GPIO_PIN_10;
GPIO_Init.Pull=GPIO_PULLUP; /*上拉--停止时是上拉 */
GPIO_Init.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "exit.h"
#include "UART.h"
#include "OLED.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
OLED_Init();
Uart_Init(115200);
OLED_ShowString(1, 1, "TxPacket:");//发送的数据
OLED_ShowString(3, 1, "RxPacket:");//接收的数据
//HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,0);
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, g_rx_buffer);
while(1)
{
HAL_UART_Transmit(&UART_HandleUART1,(uint8_t*)g_rx_buffer,sizeof(g_rx_buffer),1000);
while(__HAL_UART_GET_FLAG(&UART_HandleUART1, UART_FLAG_TC) != 1); //发送完成
}
}
发送数据判断下它是否发送完了,在发送下一个;1---完成发送
while(__HAL_UART_GET_FLAG(&UART_HandleUART1, UART_FLAG_TC) != 1); //发送完成
在stm32f1xxx_hal uart.h文件中定义
B:STM32接收数据
STM接收来自PC端的数据
UART_HandleTypeDef UART_HandleUART1; /*UART的句柄*/
uint8_t g_usart1_rx_flag = 0; // 全局变量,用于标记接收状态
char g_rx_buffer[100] = {0}; // 全局数组,用于存储接收到的数据
/**
* @brief 使用UART1,配置为发送和输入模式
* PA9--TX PA10--RX
* @param 波特率
*/
void Uart_Init(uint32_t Baud_rate)
{
/*句柄初始化*/
UART_HandleUART1.Instance=USART1;
UART_HandleUART1.Init.BaudRate=Baud_rate;
UART_HandleUART1.Init.HwFlowCtl=UART_HWCONTROL_NONE;
UART_HandleUART1.Init.Mode=UART_MODE_TX_RX;
UART_HandleUART1.Init.Parity=UART_PARITY_NONE;
UART_HandleUART1.Init.StopBits=UART_STOPBITS_1;
UART_HandleUART1.Init.WordLength=UART_WORDLENGTH_8B;
HAL_UART_Init(&UART_HandleUART1);
/*开启串口异步接收中断*/
HAL_UART_Receive_IT(&UART_HandleUART1,(uint8_t*)g_rx_buffer,1);
}
/**
* @brief 串口底层初始化
*
* @param 波特率
*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Init;
if(huart->Instance==USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_USART1_CLK_ENABLE();
//PA9_TX发送
GPIO_Init.Mode=GPIO_MODE_AF_PP; /*复用推挽输出*/
GPIO_Init.Pin=GPIO_PIN_9;
GPIO_Init.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
//PA10_RX接收
GPIO_Init.Mode=GPIO_MODE_INPUT; /*输入*/
GPIO_Init.Pin=GPIO_PIN_10;
GPIO_Init.Pull=GPIO_PULLUP; /*上拉--停止时是上拉 */
GPIO_Init.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
}
/**
* @brief 中断服务函数
*/
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&UART_HandleUART1);
HAL_UART_Receive_IT(&UART_HandleUART1,(uint8_t*)g_rx_buffer,1);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
if(g_rx_buffer[0]==0)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,1);
}
if(g_rx_buffer[0]==1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,0);
}
}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "exit.h"
#include "UART.h"
#include "OLED.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
OLED_Init();
Uart_Init(115200);
OLED_ShowString(1, 1, "TxPacket:");//发送的数据
OLED_ShowString(3, 1, "RxPacket:");//接收的数据
OLED_ShowString(4, 1, " ");
while(1)
{
OLED_ShowString(4, 1, g_rx_buffer);
}
}
C:STM32发送接收数据
UART_HandleTypeDef UART_HandleUART1; /*UART的句柄*/
uint8_t g_usart1_rx_flag = 0; // 全局变量,用于标记接收状态
char g_rx_buffer[100] = {0} ; // 全局数组,用于存储接收到的数据
/**
* @brief 使用UART1,配置为发送和输入模式
* PA9--TX PA10--RX
* @param 波特率
*/
void Uart_Init(uint32_t Baud_rate)
{
/*句柄初始化*/
UART_HandleUART1.Instance=USART1;
UART_HandleUART1.Init.BaudRate=Baud_rate;
UART_HandleUART1.Init.HwFlowCtl=UART_HWCONTROL_NONE;
UART_HandleUART1.Init.Mode=UART_MODE_TX_RX;
UART_HandleUART1.Init.Parity=UART_PARITY_NONE;
UART_HandleUART1.Init.StopBits=UART_STOPBITS_1;
UART_HandleUART1.Init.WordLength=UART_WORDLENGTH_8B;
HAL_UART_Init(&UART_HandleUART1);
/*开启串口异步接收中断*/
HAL_UART_Receive_IT(&UART_HandleUART1,(uint8_t*)g_rx_buffer,1);
}
/**
* @brief 串口底层初始化
*
* @param 波特率
*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Init;
if(huart->Instance==USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_USART1_CLK_ENABLE();
//PA9_TX发送
GPIO_Init.Mode=GPIO_MODE_AF_PP; /*复用推挽输出*/
GPIO_Init.Pin=GPIO_PIN_9;
GPIO_Init.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
//PA10_RX接收
GPIO_Init.Mode=GPIO_MODE_INPUT; /*输入*/
GPIO_Init.Pin=GPIO_PIN_10;
GPIO_Init.Pull=GPIO_PULLUP; /*上拉--停止时是上拉 */
GPIO_Init.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
}
/**
* @brief 中断服务函数
*/
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&UART_HandleUART1);/*会清除中断,所以还要在调用一遍HAL_UART_Receive_IT*/
HAL_UART_Receive_IT(&UART_HandleUART1,(uint8_t*)g_rx_buffer,1);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
//向PC端发送的数据
HAL_UART_Transmit(&UART_HandleUART1,(uint8_t*)g_rx_buffer,sizeof(g_rx_buffer),1000);
}
g_usart1_rx_flag=1; //接收完数据中断标志位制1
}
/**
* @brief STM32向PC端发送数据
*/
void UART1_TX_DATA(){
HAL_UART_Transmit(&UART_HandleUART1,(uint8_t*)g_rx_buffer,sizeof(g_rx_buffer),1000);
while(__HAL_UART_GET_FLAG(&UART_HandleUART1, UART_FLAG_TC) != 1); //发送完成
g_usart1_rx_flag=0;
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "exit.h"
#include "UART.h"
#include "OLED.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
LED_Exit_Init();
OLED_Init();
Uart_Init(115200);
OLED_ShowString(1, 1, "TxPacket:");//发送的数据
OLED_ShowString(3, 1, "RxPacket:");//接收的数据
OLED_ShowString(4, 1, " ");
while(1)
{
if(g_usart1_rx_flag==1)
{
UART1_TX_DATA();
OLED_ShowString(4, 1, g_rx_buffer);
}
}
}
链接:https://pan.baidu.com/s/1l1ZZG84F8s0niKmXUNnBkQ?pwd=5j2p
提取码:5j2p
--来自百度网盘超级会员V3的分享