USART串口协议 和va_list,va_start,va_end,vsprintf

串口接口

通信的目的:将一个设备的数据传送到另一个设备扩展硬件系统
通讯协议: 制定通信的规则,通信双方按照协议规则进行数据收发。

在这里插入图片描述

注意: 差分信号一般传输速度和距离都会非常高。
多设备相当于老师在教师给所有同学讲课,点对点相当于老师找你到办公室谈话。

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

单片机的串口可以使单片机与单片机、单片机与电脑(非常适合调试程序,USART串口协议的一大优势),单片机与各式各样的串口相互通讯,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。

TX: Transmit(发送)
RX: Receive(接收)

硬件电路

简单双向串口通讯有两根通信线(发送端TX,接收端RX)
TX与RX要交叉连接
当只需单向的数据传输时,可以只接一根通信线
当电平标准不一致时,需要加电平转换芯片。
在这里插入图片描述

电平标准

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

1.TTL 电平: +3.3V或者+5V 表示1,0V表示0
2. RS232电平: -3V—— -15V 表示1,+3—— +15V 表示0
3. RS485电平 : 两线压差+2——+6V表示1,-2—— -6 V表示0(差分信号)

异步通讯: 是一种数据传输方式,其中数据的发送和接受不需要双发时钟同步。也就是说,发送方和接收方可以在不同步的时钟频率下独立操作,并且数据传输时不预先协调的时刻进行的。

串口参数及时序

波特率:串口通讯的速度
起始位:标志一个数据帧的开始,固定为低电平
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
校验位: 用于数据验证,根据数据位计算得来。
停止位:用于数据帧间隔, 固定为高电平
在这里插入图片描述
在这里插入图片描述
USART (Universal Synchronous/ Asynchronous Receiver/Transmitter) 通用同步/异步收发器

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

自带波特率发生器,最高达4.5Mbit/s

可配置数据位长度(8/9),停止位长度(0.5/1/1.5/2)

可选校验位(无校验/奇校验/偶校验)
支持同步模式、硬件流控制,DMA、智能卡、IrDA,LIN

STM32F103C8T6 USART 资源: USART1, USART2, USART3.
USART框图

在这里插入图片描述

USART基本结构

在这里插入图片描述

数据帧

在这里插入图片描述

数据帧 配置停止位

在这里插入图片描述

USART电路输入数据策略

USART电路比较复杂,输入的采样频率和波特率一致, 输入要对噪声有一点判断能力

起始位侦测

在这里插入图片描述
以波特率的16倍进行采样
忽略了前面没3个里面没有至少有2个0。不算检测验到起始位
开始重新补充下降沿。如果通过了数据采样,接受状态就由空闲变为起始位。之后接受数据位,都在第8、9、10次,进行采样。

数据采样

在这里插入图片描述
你看在8、9、10 位进行数据采样。
NE 是噪声标志位

波特率发生器

1.发送器和接收器的波特率由波特率寄存器BRR里的DIV确定
2. 计算公式: 波特率 = fpclk/(16div)
假如要配置波特率为9600 由于USART的频率为72MHz,
那么计算公式就是 9600 = 72000,000/(16
div)
div = 72MHz / 9600 / 16 = 468.75
将10进制转化为2进制

在这里插入图片描述

将USART硬件

在这里插入图片描述
CH340G_VCC是选择供电引脚
在这里插入图片描述
但是将跳线帽拔掉,依然可以给CH340供电

代码的编写

想知道怎么配置外设的GPIO的模式,可以到参考手册的外设GPIO配置找

数据模式

1.HEX模式/十六进制/二进制模式 : 以原始数据的形式显示。
2.文本模式/字符模式:以原始数据编码的形式显示。
在这里插入图片描述
在这里插入图片描述

如何使用printf ,并将printf重定向到串口。
第一种方式

在这里插入图片描述
使用use MIcro LIB

将#include<stdio.h> 放在Serial.h 中。
int fputc(int ch,FILE *f)
{
Serial_SendByte(ch);
return ch;

}

第二种方式
char String[100];
sprintf(String,"Num=%d\r\n",666);/// 不涉及重定向
Serial_SendString(String);
第三种方式

添加头文件 #include<stdarg.h>
void Serial_Printf(char *format,…)// 可变参数
{
char String[100];
va_list arg;
va_start(arg,format);
vsprintf(String,format,arg);
va_end(arg);
Serial_SendString(String);
}

(1). va_list 是C语言的一个宏定义,用于表示一个变长参数列表的指针,可以通过宏va_start,va_arg 和 va_end 对变长参数列表进行访问和操作。在函数中需要接受不定数量的参数时,可以使用va_list来处理这些参数。

(2). va_start 是一个宏定义,用于在使用可变参数函数获取可变参数的起始地址。它的作用是初始化一个va_list类型的变量,使其指向可变参数列表的第一个参数。va_start宏需要两个参数,分别是一个va_list类型的变量和可变参数的在最后一个固定参数的地址.

(3).va_arg: 是一个宏,用于获取可变参数列表中的下一个参数值

va_arg(ap, type)

其中,ap 是 va_list 类型的可变参数列表指针,type 是要获取的参数的类型。va_arg 宏返回指定类型的参数值,并将可变参数列表指针 ap 向后移动到下一个参数。注意,调用 va_arg 宏之前必须先调用 va_start 宏初始化可变参数列表指针。
(4). va_end:是一个宏,用于结束使用 va_start 和 va_arg 宏定义的可变参数列表。它的作用是清理 va_list 类型变量,以便该变量可以被再次使用。

(5). vsnprintf 是一个函数,用于将格式化的字符串输出到指定的缓存区中,其参数列表。与printf类似,vsnprintf可以根据格式化字符串中的占位符,将后续的参数转化为对应的字符串并输出到缓存区中,但是与printf 不同的是,vsnprintf可以指定的最大长度,以避免缓存区溢出的问题。

(6).vsprintf 是 C 标准库中的一个函数,用于将格式化的数据写入一个字符串。与 sprintf 不同,vsprintf 允许你传递一个 va_list 类型的参数列表,而不是可变数量的参数。它通常用于编写需要处理可变参数的函数.

int vsprintf(char *str, const char *format, va_list ap);

#include <stdio.h>
#include <stdarg.h>

void my_printf(const char *format, ...) {
    char buffer[1024];   // 创建一个缓冲区来存储结果字符串
    va_list args;        // 声明一个 va_list 类型的变量
    va_start(args, format); // 初始化 va_list 变量
    vsprintf(buffer, format, args); // 使用 vsprintf 将格式化的数据写入缓冲区
    va_end(args);        // 清理 va_list 变量
    printf("%s", buffer); // 打印结果字符串
}

int main() {
    my_printf("Hello %s! You have %d new messages.\n", "Alice", 5);
    return 0;
}

(1).参数处理方式:

sprintf 直接接受可变数量的参数。
vsprintf 接受一个 va_list 类型的参数列表。
(2).使用场景:

sprintf 适合直接使用固定数量的参数进行格式化。
vsprintf 适合编写需要处理可变参数的函数,例如自定义的打印函数。
(3).代码复杂度:

sprintf 使用起来比较简单,直接传入格式化字符串和参数即可。
vsprintf 需要使用 va_list、va_start、va_end 等宏来处理参数列表。

如何打印汉字

–no-multibyte-chars
在这里插入图片描述

另一种方式

选为GB2312
在这里插入图片描述
在串口助手选择:GBK

串口读取

有查询和中断两种方式进入

查询的代码
while(1)
	{
		if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
		{
			RXData = USART_ReceiveData(USART1);
			//读DR
			
			OLED_ShowHexNum(1,1,RXData,2);
			
		}
}
USART串口数据包

当发送连续的数据包的时候,串口可能错位。这时候就可以数据进行分割。
在这里插入图片描述

这里使用了固定包长,可变包长 ,含包头包尾

如果数据和包头包尾重复,可能引起误判,
第一种 限制载荷数据的范围
第二种 无法避免载荷数据和包头包尾重复,尽量使用固定长度的数据包

如果载荷会出现和包头包尾重复的情况,尽量使用固定包长。

文本数据包

在这里插入图片描述

HEX数据包接收

状态机

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值