006、USART串口协议

一、简介

通信:将一个设备的数据传送到另一个设备,扩展硬件系统

通信协议:制定通信的规则,通信双方按照协议规则进行数据收发

USART:引脚TX、RX  全双工  同步/异步时钟  单端电平  点对点设备

全双工:通信双方能够同时进行双向通信

串口:串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的相互通信

单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块相互通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。

二、硬件电路连接

简单双向串口通信有两根通信线(发送TX、接收RX)

TX、RX要交叉连接

当只需要单向数据传输时,可以只接一根通信线

当电平标准不一致时,需要加电平转换芯片

三、电平标准

电平标准是数据1(高电平)和数据0(低电平)的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

TTL电平:+3.3V或+5V表示1,0V表示0;

RS232电平:-3~-15V或+5V表示1,+3~+15V表示0;

RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)

四、串口参数及时序

波特率:串口通信的速率。  二进制调制:波特率=比特率

起始位:标志一个数据帧的开始,固定为低电平。串口空闲状态为高电平,使用固定低电平的起始位产生下降沿,来判断数据开始发送

数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行。发送一个字节,低位先行 

校验位:用于数据验证,根据数据位计算得来。

停止位:用于数据帧间隔,固定位高电平。停止位将电平拉高,这样有新的数据的时候,起始位的低电平才会产生下降沿。

五、USART简介(STM32)

1、USART(Universal Synchronous/Asynchronous Receiver/ Transmitter)通用同步/异步收发器

2、USART是STM32内部集成的硬件外设,可根据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送,也可以自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里。

3、自带波特率发生器,最高达4.5Mbits/s 

4、可配置数据位长度(8/9)、停止位长度(0、1、1.5、2)

5、可选校验位(无校验、奇校验、偶校验)

6、支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN

STM32F103C8T6拥有三个串口(串口1、串口2、串口3)它们对应的引脚如下:

这是通过STM32CubeMX软件进行的查询,该软件主要用于hal库的快速开发,也可以通过查询手册来查找引脚功能。当标准库学习打好一定基础后,可以学习hal库,hal库的来快速进行功能配置。

六、标准库串口配置流程

1、使能时钟(USART时钟及其GPIO时钟)

2、初始化GPIO引脚(引脚、模式、速度)

3、配置USART参数(波特率、数据位、停止位、校验位、传输和接收模式等)

4、使能USART外设

5、配置中断(可以不使用中断触发串口接收,但是不使用中断可能会导致接收数据不全)

6、发送数据函数

7、接收数据函数

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>  //通过这个头文件,可以在函数中处理不定数量的参数,比如printf()函数

uint8_t Serial_RxData;   //用于存储从串口接收到的单个字节数据
uint8_t Serial_RxFlag;	//接收标志位,用于指示是否有新数据接收到

void Serial_Init(void)  //串口初始化函数
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);  //打开串口时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//打开GPIOA时钟
	
	GPIO_InitTypeDef GPIO_InitStructure;				//GPIO引脚配置 结构体定义
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		//复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;			//设置USART1 TX引脚 PA9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//设置速度
	GPIO_Init(GPIOA, &GPIO_InitStructure);				//应用配置
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;		//输入上拉模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;			//USART1 RX 引脚 PA10
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度,可以不设置
	GPIO_Init(GPIOA, &GPIO_InitStructure);				//应用配置
	
	USART_InitTypeDef USART_InitStructure;				//串口配置结构体定义
	USART_InitStructure.USART_BaudRate = 9600;			//波特率 9600
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;		//串口发送和串口接收
	USART_InitStructure.USART_Parity = USART_Parity_No;					//无校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;				//1个停止位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;			//8个数据位
	USART_Init(USART1, &USART_InitStructure);							//应用配置
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);				//使能接收中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//设置中断优先级分组
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;			//USART1 中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//使能中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;			//响应优先级
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1, ENABLE);									//使能USART1(即开启串口)
}
//串口数据发送函数
void Serial_SendByte(uint8_t Byte)     //发送一个字节数据,并等待发送缓存区为空
{
	USART_SendData(USART1, Byte);		//发送单字节数据
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)		  //发送一个字节数组,逐个字节发送
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);	//逐个字节发送数组中的数据
	}
}

void Serial_SendString(char *String)	//发送一个字符串,以空字符“\0”为结束标志
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)  //如果数据不为\0则继续发送  0A
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)  //计算X的Y次幂,用于后续的数字转换
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)		// 将一个整数按位转换为字符并逐个发送,以显示数值。
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}
//fputc: 标准库函数printf重定向到串口输出。这样,printf输出的字符将通过串口发送
int fputc(int ch, FILE *f)  //重定向 printf 函数输出
{
	Serial_SendByte(ch);  //将字符发送到串口
	return ch;
}


/*
char *format: 第一个参数是一个字符串指针,它定义了输出的格式。类似于 printf 的格式字符串,可以包含格式说明符,如 %d、%s 等。
...: 三个点表示这是一个可变参数函数,可以接受任意数量的参数。

*/
void Serial_Printf(char *format, ...)  //变参格式化输出函数
{
	char String[100];  //定义字符串缓冲区
	va_list arg;		//va_list 是一个类型,用于访问可变参数列表
	va_start(arg, format);	//初始化 va_list
	/*va_start 是一个宏,用于初始化 va_list 变量,使其指向可变参数的第一个参数。
	format 是最后一个固定参数,用于告诉 va_start 从哪里开始处理可变参数。*/
	vsprintf(String, format, arg);
	/*
	vsprintf 是标准C库函数,用于将格式化的输出写入 String 缓冲区中。
	format 是格式字符串,arg 是初始化后的 va_list,它包含了所有可变参数的值。
    这一步实现了将传入的可变参数按照指定的格式组合成一个字符串。
	*/
	va_end(arg);//va_end 宏用于清理 va_list 变量。这是一个好的实践,可以避免潜在的错误
	Serial_SendString(String);	//通过串口发送字符串
}


//串口接收标志函数
uint8_t Serial_GetRxFlag(void)  //检查是否有新数据接收,如果有则清除标志并返回1。
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;  //清除标志位
		return 1;			//返回1,表示有新数据
	}
	return 0;				//返回0,表示没有新数据
}

uint8_t Serial_GetRxData(void)  //返回接收到的字节数据
{
	return Serial_RxData;		//返回接收到的数据
}

void USART1_IRQHandler(void)  //串口1中断服务函数
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //检查接收中断标志
	{
		Serial_RxData = USART_ReceiveData(USART1);		//读取接收到的数据
		Serial_RxFlag = 1;								//设置接收标志位
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);	//清除中断标志
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值