09:STM32-------USART串口通信+串口数据包

news2024/11/16 1:42:41

目录

一:串口协议

1:通信接口

2:串口通信

3:硬件电路

4:电平标准

5:串口参数及其时序

二:USART介绍

1:简历

2:USART框图

3:USART的基本结构

4:数据帧

5: 波特率发生器

6:数据模式

三:案例

A:串口发送--单发送

1:连接图

2:函数介绍

3:代码 

B:串口发送+接收 

1:函数介绍

 2:串口发送+接收 -----查询代码

3:函数介绍

4:串口发送+接收 -----中断代码

四:USART串口数据包

1:简历

2:HEX数据包

3: 文本数据包

4:HEX数据包接收

5:文本数据包接收

6: 案例

1:连接图

A:发送HEX数据包---固定数据长度

2:连接图

B:发送文本数据包---数据长度 


一:串口协议

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的基本结构

4:数据帧

5: 波特率发生器

        发送器和接收器的波特率由波特率寄存器BRR里的DIV确定

        计算公式:波特率 = fPCLK2/1 / (16 * DIV)

        配置USART1为9600的波特率

6:数据模式

        HEX模式/十六进制模式/二进制模式:以原始数据的形式显示

        文本模式/字符模式:以原始数据编码后的形式显示

三:案例

A:串口发送--单发送

1:连接图

2:函数介绍

在stm32f10x usart.h文件中-----配置USART

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)

在stm32f10x usart.h文件中-----发送数据

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

 在stm32f10x usart.h文件中-----检查某个寄存器的中断标志位

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

USART_GetFlagStatus :      获取标志位状态的函数

3:代码 

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include <stdarg.h>
void serial_init(void){
	//开启RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//配置TI的GPIO
	GPIO_InitTypeDef GPIO_structinit;
	GPIO_structinit.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设
	GPIO_structinit.GPIO_Pin=GPIO_Pin_9;
	GPIO_structinit.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_structinit);
	//USART的配置
	USART_InitTypeDef USART_structinit;
	USART_structinit.USART_BaudRate=9600;//通信的波特率
	USART_structinit.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制--不使用
	USART_structinit.USART_Mode=USART_Mode_Tx;//配置GPIO为TX发送数据模式
	USART_structinit.USART_Parity=USART_Parity_No;  //选择校验方式---无校验
	USART_structinit.USART_StopBits=USART_StopBits_1;//停止的位数
	USART_structinit.USART_WordLength=USART_WordLength_8b;//发送或接收的数据位---我们使用8为
	USART_Init(USART1,&USART_structinit);
	//启动USART
	USART_Cmd(USART1,ENABLE);
}

//发送数据
void Serial_SendByte(uint8_t Byte)
{	
	//发送数据的函数
	USART_SendData(USART1,Byte);
	//检查某个寄存器的中断标志位
	while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
	/*
	在完成数据发送的时候,TXE置1;
	下次发送数据自动置0,所以不用手动清除中断标志位.
	
	TXE:发送数据寄存器:
	当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。
	如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART DR的写操作,将该位清零。
	0:数据还没有被转移到移位寄存器
	1:数据已经被转移到移位寄存器。
	注意:单缓冲器传输中使用该位。
	*/
}	

//发送一个数组;Array:传递的数组,Length数组的长度
void Serial_SendArray(uint8_t *Array, uint16_t Length){
	 
	for(uint16_t i=0;i<Length;i++)
	{
		Serial_SendByte(Array[i]);
	}

}

//发送一个字符串
//发送字符串时自带结束标志位
void Serial_Sendstr(char *str)
{
	for (uint8_t i=0 ; str[i]!=0;i++){//也可写为str[i]!='\0'
		Serial_SendByte(str[i]);

	}
}

//取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉,%10就是把左边去掉
//12345取3 12345/100(10^2)%10=3   x:从左往右数,不包含要取的数字
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}
//传输数字,把数字中的每一位取出,然后发送出去
//Number:传输的数字;   Length:传输数字的长度
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');
	}
}

//printf函数的底层
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

//spinrf函数的封装
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_Sendstr(String);
}




int main(void)
{
	OLED_Init();
	serial_init();
	
	Serial_SendByte(0x41);
	
	uint8_t Array[]={0x01,0x08,0x43,0x45};
	Serial_SendArray(Array,4);
	
	//换行的话需要加上\r\n
	Serial_Sendstr("Helloword\r\n");
	Serial_Sendstr("123\r\n");

	
	Serial_SendNumber(231,3);
	
	//使得printf函数移植方法
	printf("NUM=%d,",123);
	
	char String[100];
	sprintf(String, "\r\nNum3=%d", 333);
	Serial_Sendstr(String);
	
	
	Serial_Printf("\r\nNum4=%d", 444);
	Serial_Printf("\r\n");
	
	printf("你好世界");
	
	
	while (1)
	{
		
	}
}

printf函数三中移植的方法----使得可以通过串口通信打印到其他外设:

1:通过重写printf函数的底层,使他通过串口

//printf函数的底层
#include <stdio.h>
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

2:直接使用sprintf函数

    char String[100];
	sprintf(String, "\r\nNum3=%d", 333);
	Serial_Sendstr(String);
	

3:对sprintf函数进行封装

//spinrf函数的封装
#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_Sendstr(String);  //调用的Serial_Sendstr函数在代码里面可查询
}

Serial_Printf("\r\nNum4=%d", 444);

B:串口发送+接收 

对于串口接收来说,可以使用查询和中断两种方法

1:函数介绍

在stm32f10x usart.h文件中-----返回外设最近接收的数据

uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

USART_ReceiveData :  返回USARTx外设最近接收到的数据

 2:串口发送+接收 -----查询代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include <stdio.h>
#include <stdarg.h>
void serial_init(void){
	//开启RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//配置TI的GPIO
	GPIO_InitTypeDef GPIO_structinit;
	GPIO_structinit.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设
	GPIO_structinit.GPIO_Pin=GPIO_Pin_9; //PA9在引脚定义中为TX发送
	GPIO_structinit.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_structinit);
	
		
	GPIO_structinit.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
	GPIO_structinit.GPIO_Pin=GPIO_Pin_10; PA9在引脚定义中为RX接收
	GPIO_structinit.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_structinit);
	
	//USART的配置
	USART_InitTypeDef USART_structinit;
	USART_structinit.USART_BaudRate=9600;//通信的波特率
	USART_structinit.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制--不使用
	USART_structinit.USART_Mode=USART_Mode_Tx |USART_Mode_Rx;//配置GPIO为TX发送数据模式
	USART_structinit.USART_Parity=USART_Parity_No;  //选择校验方式---无校验
	USART_structinit.USART_StopBits=USART_StopBits_1;//停止的位数
	USART_structinit.USART_WordLength=USART_WordLength_8b;//发送或接收的数据位---我们使用8为
	USART_Init(USART1,&USART_structinit);
	//启动USART
	USART_Cmd(USART1,ENABLE);
}

//发送数据
void Serial_SendByte(uint8_t Byte)
{	
	//发送数据的函数
	USART_SendData(USART1,Byte);
	//检查某个寄存器的中断标志位
	while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
	/*
	在完成数据发送的时候,TXE置1;
	下次发送数据自动置0,所以不用手动清除中断标志位.
	
	TXE:发送数据寄存器:
	当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。
	如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART DR的写操作,将该位清零。
	0:数据还没有被转移到移位寄存器
	1:数据已经被转移到移位寄存器。
	注意:单缓冲器传输中使用该位。
	*/
}	

//发送一个数组;Array:传递的数组,Length数组的长度
void Serial_SendArray(uint8_t *Array, uint16_t Length){
	 
	for(uint16_t i=0;i<Length;i++)
	{
		Serial_SendByte(Array[i]);
	}

}

//发送一个字符串
//发送字符串时自带结束标志位
void Serial_Sendstr(char *str)
{
	for (uint8_t i=0 ; str[i]!=0;i++){//也可写为str[i]!='\0'
		Serial_SendByte(str[i]);

	}
}

//取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉,%10就是把左边去掉
//12345取3 12345/100(10^2)%10=3   x:从左往右数,不包含要取的数字
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}
//传输数字,把数字中的每一位取出,然后发送出去
//Number:传输的数字;   Length:传输数字的长度
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');
	}
}

//printf函数的底层
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

//spinrf函数的封装
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_Sendstr(String);
}


uint8_t RXdata;

int main(void)
{	
	
	OLED_Init();
	serial_init();
	

	
	
	while (1)
	{
		if (USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET){
			RXdata=USART_ReceiveData(USART1);//返回外设最近接收的数据
			OLED_ShowHexNum(1,1,RXdata,3);
		
		
		
		}
	}
}

PA9口和PA10口的GPIO的模式不同

引脚的定义

PA9-----TX发送引脚;          PA10--------Rx接收引脚     

GPIO工作模式的选择          

        输入工作模式----------将引脚的信号读取到微型控制器---PA10

        输出工作模式---------将微型控制器信号读取到阴极段--PA9

发送输出;   接收输入

3:函数介绍

在stm32f10x usart.h文件中-----使能中断输出信号

void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

USART_ITConfig : 启用或禁用指定的USART中断

配置NVIC在misc.h文件中的函数-----配置NVIC

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

NVIC_PriorityGroupConfig:用来中断分组的,参数是中断分组的方式

NVIC_Init: 根据结构体里面指定的参数初始化NMIC

 在stm32f10x usart.h文件中-----检查某个寄存器的中断标志位

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

 在stm32f10x usart.h文件中-----清除中断标志位

void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)

 USART_ClearITPendingBit  :        清除USARTx的中断挂起位

4:串口发送+接收 -----中断代码

        在开启USART之前

1: 启动USART的RXNE中断,  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

2: 配置NVIC

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include <stdio.h>
#include <stdarg.h>
uint8_t Serial_RxData;
uint8_t Serial_RxFlag;
void serial_init(void){
	//开启RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//配置TI的GPIO
	GPIO_InitTypeDef GPIO_structinit;
	GPIO_structinit.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设
	GPIO_structinit.GPIO_Pin=GPIO_Pin_9; //PA9在引脚定义中为TX发送
	GPIO_structinit.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_structinit);
	
		
	GPIO_structinit.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
	GPIO_structinit.GPIO_Pin=GPIO_Pin_10; PA9在引脚定义中为RX接收
	GPIO_structinit.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_structinit);
	
	//USART的配置
	USART_InitTypeDef USART_structinit;
	USART_structinit.USART_BaudRate=9600;//通信的波特率
	USART_structinit.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制--不使用
	USART_structinit.USART_Mode=USART_Mode_Tx |USART_Mode_Rx;//配置GPIO为TX发送数据模式
	USART_structinit.USART_Parity=USART_Parity_No;  //选择校验方式---无校验
	USART_structinit.USART_StopBits=USART_StopBits_1;//停止的位数
	USART_structinit.USART_WordLength=USART_WordLength_8b;//发送或接收的数据位---我们使用8为
	USART_Init(USART1,&USART_structinit);
	
	//启动USART的RXNE中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	//开启NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_initstruct;
	NVIC_initstruct.NVIC_IRQChannel=USART1_IRQn; //通道
	NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_initstruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_initstruct);
	

	//启动USART
	USART_Cmd(USART1,ENABLE);
}

//发送数据
void Serial_SendByte(uint8_t Byte)
{	
	//发送数据的函数
	USART_SendData(USART1,Byte);
	//检查某个寄存器的中断标志位
	while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
	/*
	在完成数据发送的时候,TXE置1;
	下次发送数据自动置0,所以不用手动清除中断标志位.
	
	TXE:发送数据寄存器:
	当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。
	如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART DR的写操作,将该位清零。
	0:数据还没有被转移到移位寄存器
	1:数据已经被转移到移位寄存器。
	注意:单缓冲器传输中使用该位。
	*/
}	

//发送一个数组;Array:传递的数组,Length数组的长度
void Serial_SendArray(uint8_t *Array, uint16_t Length){
	 
	for(uint16_t i=0;i<Length;i++)
	{
		Serial_SendByte(Array[i]);
	}

}

//发送一个字符串
//发送字符串时自带结束标志位
void Serial_Sendstr(char *str)
{
	for (uint8_t i=0 ; str[i]!=0;i++){//也可写为str[i]!='\0'
		Serial_SendByte(str[i]);

	}
}

//取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉,%10就是把左边去掉
//12345取3 12345/100(10^2)%10=3   x:从左往右数,不包含要取的数字
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}
//传输数字,把数字中的每一位取出,然后发送出去
//Number:传输的数字;   Length:传输数字的长度
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');
	}
}

//printf函数的底层
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

//spinrf函数的封装
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_Sendstr(String);
}


uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}


//USART的中断函数----在启动文件中找
void USART1_IRQHandler(){
	//判断RXNE的中断标志位
	if (USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET){
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		USART_ClearITPendingBit(USART1,USART_FLAG_RXNE);
	}
}





uint8_t RxData;

int main(void)
{
	OLED_Init();
	OLED_ShowString(1, 1, "RxData:");
	
	serial_init();
	
	while (1)
	{
		if (Serial_GetRxFlag() == 1)
		{
			RxData = Serial_GetRxData();
			Serial_SendByte(RxData);
			OLED_ShowHexNum(1, 8, RxData, 3);
		}
	}
}

四:USART串口数据包

1:简历

  数据包作用 : 把一个个单独的数据给打包起来,   方便我们进行多字节的数据通信

需要把多个字节打包为一个整体进行发送, 极大的方便了我们的使用

2:HEX数据包

        数据包的包头和包尾是可以自己进行设定的,  它并不是一个固定的

        固定包长, 含包头包尾 : 每个数据包的长度都固定不变 (自己定数据包的长度) ,  数据包前面是包头,后面是包尾

        可变包长,含包头包尾 : 每个数据包的长度可以是不一样的,  数据包前面是包头,后面是包尾

包头包尾和数据载荷重复的问题 

        当包头包尾的数据和传输的数据重复时-----定义FF为包头,FE为包尾,  如果我传输的数据本身就是FF和FE,  可能会引起误判 

        解决:

        A: 限制载荷数据的范围-------可以在发送的时候,对数据进行限幅.   比如XYZ,3个数据,变化范围都可以是0~100.   我们可以在载荷中只发送0~100的数据

        B:无法避免载荷数据和包头包尾重复-----尽量使用固定长度的数据包,  由于载荷数据是固定的,  只要通过包头和包尾对齐的数据.   就可以哪个数据应该是包头包尾,哪个数据应该是载荷数据.        在接收载荷数据的时候,我们并不会判断它是否是包头包尾,   而在接收包头包尾的时候,我们会判断它是不是确实是包头包尾

        C:增加包头包尾的数量---------尽量呈现出载荷数据出现不了的状态

固定包长和可变包长的选择问题 

        载荷会出现和包头包尾重复的情况--------最好选择固定包长

        反之选择可变包长

3: 文本数据包

在HEX数据包里面,数据都是以原始的字节数据本身呈现的,   而在文本数据包里面,每个字节就经过了一层编码和译码,  最终表现出来的,就是文本格式.   其实都还是一个字节的HEX数据

4:HEX数据包接收

        每收到一个字节,程序都会进一遍中断 .

状态机:

        最开始,S=0,  收到一个数据,进中断,   判断数据是不是包头FF,  如果是FF,则代表收到包头,  之后置S=1,退出中断,结束.   下次再进中断,根据S=1,就可以进行接收数据的程序了.   这时再收到数据,我们就直接把它存在数组中,  另外再用一个变量,记录收了多少个数据,  如果没收够规定的数据,就一直是接收状态, 如果收够了,就置S=2.   下次中断时,就可以进入下一个状态了.       最后一个状态就是等待包尾了,判断数据是不是FE,  这样就可以置S=0,回到最初的状态,开始下一个轮回

5:文本数据包接收

6: 案例

1:连接图

A:发送HEX数据包---固定数据长度


#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Key.h"
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_RxFlag;//一个数据包发送完成的标志位

uint8_t Serial_TXPacket[4];//存放发送的数据---STM32向外设发送
uint8_t Serial_RXPacket[4];//存放接收的数据---外设向STM32发送数据

void serial_init(void){
	//开启RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//配置TI的GPIO
	GPIO_InitTypeDef GPIO_structinit;
	GPIO_structinit.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设
	GPIO_structinit.GPIO_Pin=GPIO_Pin_9; //PA9在引脚定义中为TX发送
	GPIO_structinit.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_structinit);
	
		
	GPIO_structinit.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
	GPIO_structinit.GPIO_Pin=GPIO_Pin_10; PA9在引脚定义中为RX接收
	GPIO_structinit.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_structinit);
	
	//USART的配置
	USART_InitTypeDef USART_structinit;
	USART_structinit.USART_BaudRate=9600;//通信的波特率
	USART_structinit.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制--不使用
	USART_structinit.USART_Mode=USART_Mode_Tx |USART_Mode_Rx;//配置GPIO为TX发送数据模式
	USART_structinit.USART_Parity=USART_Parity_No;  //选择校验方式---无校验
	USART_structinit.USART_StopBits=USART_StopBits_1;//停止的位数
	USART_structinit.USART_WordLength=USART_WordLength_8b;//发送或接收的数据位---我们使用8为
	USART_Init(USART1,&USART_structinit);
	
	//启动USART的RXNE中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	//开启NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_initstruct;
	NVIC_initstruct.NVIC_IRQChannel=USART1_IRQn; //通道
	NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_initstruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_initstruct);
	

	//启动USART
	USART_Cmd(USART1,ENABLE);
}

/**
  * @brief  发送一个字节的数据--8位STM32向外设发送===通过USARTx外设传输单个数据
  * @param  需要发送的数据
  * @retval 无
  */
void Serial_SendByte(uint8_t Byte)
{	
	//发送数据的函数
	USART_SendData(USART1,Byte);
	//检查某个寄存器的中断标志位
	while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
	/*
	在完成数据发送的时候,TXE置1;
	下次发送数据自动置0,所以不用手动清除中断标志位.
	
	TXE:发送数据寄存器:
	当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。
	如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART DR的写操作,将该位清零。
	0:数据还没有被转移到移位寄存器
	1:数据已经被转移到移位寄存器。
	注意:单缓冲器传输中使用该位。
	*/
}	


/**
  * @brief  发送一个数组
  * @param  Array:传递的数组
	* @param  Length数组的长度
  * @retval 无
  */
void Serial_SendArray(uint8_t *Array, uint16_t Length){
	 
	for(uint16_t i=0;i<Length;i++)
	{
		Serial_SendByte(Array[i]);
	}

}



/**
  * @brief  发送一个字符串
  * @param  需要发送的字符串
  * @retval 无
  */
void Serial_Sendstr(char *str)
{	//发送字符串时自带结束标志位
	for (uint8_t i=0 ; str[i]!=0;i++){//也可写为str[i]!='\0'
		Serial_SendByte(str[i]);

	}
}


/**
  * @brief  取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉,%10就是把左边去掉
	12345取3 12345/100(10^2)%10=3   x:从左往右数,不包含要取的数字
	* @param  X:底数
	* @param  Y:指数
  * @retval 无
  */
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  输数字,把数字中的每一位取出,然后发送出去
  * @param  传输的数字
	* @param  传输数字的长度
  * @retval 无
  */
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');
	}
}


//printf函数的底层
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

//spinrf函数的封装
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_Sendstr(String);
}


/**
  * @brief  知道把中断标志位置位0
  * @retval 无
  */
uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}



//------------------HEX包----------------------------
/**
  * @brief  发送HEX数据包---STM32发送到其他外设
  * @retval 无
  */
uint16_t Send_HEX()
{
	Serial_SendByte(0XFF);
	Serial_SendArray(Serial_TXPacket,4);
	Serial_SendByte(0XFE);
}

/**
* @brief  接收HEX数据包---其他外设发送到STM32上面
  * @retval 无
  */
void USART1_IRQHandler(){
	//每收到一个字节,程序都会进一遍中断 
	//static---- 函数进入只会初始化一次,在函数退出后,数据仍然有效
	static uint8_t RxState = 0;//状态机的标志位
	static uint8_t pRxPacket = 0;//数据的包长
	//判断RXNE的中断标志位
	if (USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET){
		
		uint8_t data=USART_ReceiveData(USART1);//32接收的数据
		if (RxState==0)
		{
			if (data==0XFF)
			{
				RxState=1;
			}		
		}
		else if (RxState==1)
		{
			Serial_RXPacket[pRxPacket]=data; //右边的值赋值给等号的左边
			pRxPacket++;
			if (pRxPacket>=4)
			{
				pRxPacket=0;
				RxState=2;
			}	
		}
		else if (RxState==2)
		{
			if (data==0xFE)
			{
				RxState=0;
				Serial_RxFlag=1;//一个数据包发送完成的标志位
			}

		}
		
		USART_ClearITPendingBit(USART1,USART_FLAG_RXNE);
	}
}



uint8_t RxData;
uint8_t num;
int main(void)
{
	Key_Init();
	OLED_Init();
	serial_init();
	OLED_ShowString(1,1,"RX_Data:");
	OLED_ShowString(3,1,"TX_Data:");
	
	Serial_TXPacket[0]=0x01;
	Serial_TXPacket[1]=0x02;
	Serial_TXPacket[2]=0x03;
	Serial_TXPacket[3]=0x04;
	
	
	while (1)
	{	num=Key_GetNum();
		if (num==1)
		{
			//发送数据
		Serial_TXPacket[0]++;
		Serial_TXPacket[1]++;
		Serial_TXPacket[2]++;
		Serial_TXPacket[3]++;
		
		Send_HEX();
		
		OLED_ShowHexNum(2, 1, Serial_TXPacket[0], 2);
		OLED_ShowHexNum(2, 4, Serial_TXPacket[1], 2);
		OLED_ShowHexNum(2, 7, Serial_TXPacket[2], 2);
		OLED_ShowHexNum(2, 10, Serial_TXPacket[3], 2);

		}
	
		//接收数据
		if (Serial_GetRxFlag()==1)
		{
			OLED_ShowHexNum(4, 1, Serial_RXPacket[0], 2);
			OLED_ShowHexNum(4, 4, Serial_RXPacket[1], 2);
			OLED_ShowHexNum(4, 7, Serial_RXPacket[2], 2);
			OLED_ShowHexNum(4, 10, Serial_RXPacket[3], 2);
		
		
		}
		
	}
}

2:连接图

B:发送文本数据包---数据长度 


#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Key.h"
#include "LED.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_RxFlag;//一个数据包发送完成的标志位

uint8_t Serial_TXPacket[4];//存放发送的数据---STM32向外设发送
char Serial_RXPacket[100];//存放接收的数据---外设向STM32发送数据

void serial_init(void){
	//开启RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//配置TI的GPIO
	GPIO_InitTypeDef GPIO_structinit;
	GPIO_structinit.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出:把控制权交给片上外设
	GPIO_structinit.GPIO_Pin=GPIO_Pin_9; //PA9在引脚定义中为TX发送
	GPIO_structinit.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_structinit);
	
		
	GPIO_structinit.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
	GPIO_structinit.GPIO_Pin=GPIO_Pin_10; PA9在引脚定义中为RX接收
	GPIO_structinit.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_structinit);
	
	//USART的配置
	USART_InitTypeDef USART_structinit;
	USART_structinit.USART_BaudRate=9600;//通信的波特率
	USART_structinit.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制--不使用
	USART_structinit.USART_Mode=USART_Mode_Tx |USART_Mode_Rx;//配置GPIO为TX发送数据模式
	USART_structinit.USART_Parity=USART_Parity_No;  //选择校验方式---无校验
	USART_structinit.USART_StopBits=USART_StopBits_1;//停止的位数
	USART_structinit.USART_WordLength=USART_WordLength_8b;//发送或接收的数据位---我们使用8为
	USART_Init(USART1,&USART_structinit);
	
	//启动USART的RXNE中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	//开启NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_initstruct;
	NVIC_initstruct.NVIC_IRQChannel=USART1_IRQn; //通道
	NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_initstruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_initstruct);
	

	//启动USART
	USART_Cmd(USART1,ENABLE);
}

/**
  * @brief  发送一个字节的数据--8位STM32向外设发送===通过USARTx外设传输单个数据
  * @param  需要发送的数据
  * @retval 无
  */
void Serial_SendByte(uint8_t Byte)
{	
	//发送数据的函数
	USART_SendData(USART1,Byte);
	//检查某个寄存器的中断标志位
	while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
	/*
	在完成数据发送的时候,TXE置1;
	下次发送数据自动置0,所以不用手动清除中断标志位.
	
	TXE:发送数据寄存器:
	当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。
	如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART DR的写操作,将该位清零。
	0:数据还没有被转移到移位寄存器
	1:数据已经被转移到移位寄存器。
	注意:单缓冲器传输中使用该位。
	*/
}	


/**
  * @brief  发送一个数组
  * @param  Array:传递的数组
	* @param  Length数组的长度
  * @retval 无
  */
void Serial_SendArray(uint8_t *Array, uint16_t Length){
	 
	for(uint16_t i=0;i<Length;i++)
	{
		Serial_SendByte(Array[i]);
	}

}



/**
  * @brief  发送一个字符串
  * @param  需要发送的字符串
  * @retval 无
  */
void Serial_Sendstr(char *str)
{	//发送字符串时自带结束标志位
	for (uint8_t i=0 ; str[i]!=0;i++){//也可写为str[i]!='\0'
		Serial_SendByte(str[i]);

	}
}


/**
  * @brief  取数字的某一位:数字/10^x/%10 ----/10^x就是把这一位的右边去掉,%10就是把左边去掉
	12345取3 12345/100(10^2)%10=3   x:从左往右数,不包含要取的数字
	* @param  X:底数
	* @param  Y:指数
  * @retval 无
  */
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //X^Y
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  输数字,把数字中的每一位取出,然后发送出去
  * @param  传输的数字
	* @param  传输数字的长度
  * @retval 无
  */
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');
	}
}


//printf函数的底层
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

//spinrf函数的封装
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_Sendstr(String);
}


/**
  * @brief  知道把中断标志位置位0
  * @retval 无
  */
uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}



//------------------HEX包----------------------------
/**
  * @brief  发送HEX数据包---STM32发送到其他外设
  * @retval 无
  */
uint16_t Send_HEX()
{
	Serial_SendByte(0XFF);
	Serial_SendArray(Serial_TXPacket,4);
	Serial_SendByte(0XFE);
}

/**
* @brief  接收HEX数据包---其他外设发送到STM32上面
  * @retval 无
  */

void USART1_IRQHandler(){
	//每收到一个字节,程序都会进一遍中断 
	//static---- 函数进入只会初始化一次,在函数退出后,数据仍然有效
	static uint8_t RxState = 0;//状态机的标志位
	static uint8_t pRxPacket = 0;//数据的包长
	//判断RXNE的中断标志位
	if (USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET){
		
		uint8_t data=USART_ReceiveData(USART1);//32接收的数据
		if (RxState==0)
		{
			if (data=='@')
			{
				RxState=1;
				pRxPacket=0;
			}		
		}
		else if (RxState==1)
		{
			if (data=='\r') //\r 的意思是: 回车。将当前位置移到本行的开头。


			{
				RxState=2;
			}
			else
			{
			Serial_RXPacket[pRxPacket]=data; //右边的值赋值给等号的左边
			pRxPacket++;
			}
				
		}
		else if (RxState==2)
		{
			if (data=='\n')//\n 的意思是:回车换行。将当前位置移到下一行的开头。

			{
				RxState=0;
				Serial_RXPacket[pRxPacket]='\0';
				Serial_RxFlag=1;//一个数据包发送完成的标志位
			}

		}
		
		USART_ClearITPendingBit(USART1,USART_FLAG_RXNE);
	}
}



uint8_t RxData;
uint8_t num;
int main(void)
{

	OLED_Init();
	serial_init();
	LED_Init();
	OLED_ShowString(1,1,"RX_Data:");
	OLED_ShowString(3,1,"TX_Data:");
	

	
	
	while (1)
	{
		
		//接收数据
		if (Serial_RxFlag==1)
			
		{	OLED_ShowString(4,1,"                ");			
			OLED_ShowString(4,1,Serial_RXPacket);
			
			if (strcmp(Serial_RXPacket, "LED_ON")==0)
			{
				LED1_ON();
				Serial_Sendstr("LED_ON_OK\r\n");//使用32把数据发送到外设中去
				OLED_ShowString(2, 1, "                ");
				OLED_ShowString(2, 1, "LED_ON_OK");
			}
			else if(strcmp(Serial_RXPacket, "LED_OFF")==0)
			{
				LED1_OFF();
				Serial_Sendstr("LED_Off_OK\r\n");//使用32把数据发送到外设中去
				OLED_ShowString(2, 1, "                ");
				OLED_ShowString(2, 1, "LED_Off_OK");
			}
			else

			{	
				Serial_Sendstr("ERROR_COMMAND\r\n");//使用32把数据发送到外设中去
				OLED_ShowString(2, 1, "                ");
				OLED_ShowString(2,1,"ERROR_COMMAND");
			}
			
			Serial_RxFlag = 0;
		}
		
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/985501.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务06-Dockerfile自定义镜像+DockerCompose部署多个镜像

常见的镜像在DockerHub能找到&#xff0c;但是我们自己写项目得自己构造镜像 1 镜像结构 作用&#xff1a;提高复用性&#xff0c;当应用需要更新时&#xff0c;不再是整个系统重装进行更新 &#xff0c;而是对需要更新的部分进行更新&#xff0c;其他地方不动——>这就是分…

第67步 时间序列建模实战:ARIMA建模(Stata)

基于WIN10的64位系统演示 一、写在前面 这一期&#xff0c;我们使用Stata进行SARIMA模型的构建。 同样&#xff0c;使用某省2005年1月至2016年12月AIDS死亡率的时间序列数据。 二、Stata建立SARIMA实战 &#xff08;1&#xff09;问GPT怎么用 咒语&#xff1a;我有一批{数…

【Arduino28】LM35温度传感器实验

硬件准备 LM35温度传感器&#xff1a;1 个 面包板&#xff1a;1个 杜邦线&#xff1a;3根 硬件连线 VCC引脚接 5V 电源 OUT引脚接 A0接口 GND引脚接 GND 接口 软件程序 #define temp_pin A0 //温度传感器引脚void setup(){Serial.begin(9600); }void loop(){float val …

【C++】STL-常用算法-常用查找算法

0.前言 1.find #include <iostream> using namespace std;// 常用查找算法 find #include<vector> #include<algorithm>//查找 内置数据类型 void test01() {vector<int>v;for (int i 0; i < 10; i){v.push_back(i);}//查找 容器中 是否有 5 这个元…

C--小Why的商品归位-- 牛客小白月赛77

输入 3 3 1 1 2 1 3 2 3 输出 2 解析&#xff1a; 先不考虑购物车的容量&#xff0c;即购物车容量为无限大。这样我们可以通过每种货物上车、下车的时间计算出每一个时间点内&#xff0c;购物车中货物的数量。 统计出所有时间点内&#xff0c;货物数量的最大值max。这个最大…

CRM软件系统的三大优势

随着市场环境的不断发展和变化&#xff0c;企业的管理模式也在不断地调整。CRM管理系统是企业数字化转型的重要一环&#xff0c;可以帮助企业更好地管理客户关系和市场销售&#xff0c;提高企业运营效率和经济效益&#xff0c;CRM管理系统有哪些优势呢&#xff1f;今天小编就来…

Vue2+Vue3基础入门到实战项目(七)——智慧商城项目

Vue 核心技术与实战 智慧商城 接口文档&#xff1a;https://apifox.com/apidoc/shared-12ab6b18-adc2-444c-ad11-0e60f5693f66/doc-2221080 演示地址&#xff1a;http://cba.itlike.com/public/mweb/#/ 01. 项目功能演示 1.明确功能模块 启动准备好的代码&#xff0c;演示…

深入了解苹果证书及其分类,提升iOS应用开发效率

目录 1. 企业证书 2. 开发者证书 开发证书&#xff1a; 发布证书&#xff1a; 3. 推送证书 4. 分发证书 5. MDM证书 摘要&#xff1a;本文将详细介绍苹果证书的作用及分类&#xff0c;包括企业证书、开发者证书、推送证书、分发证书和MDM证书&#xff0c;帮助开发者了解…

Win11如何显示文件后缀名

有小伙伴发现在更新了Win11之后&#xff0c;文件的后缀名就被隐藏起来了&#xff0c;那么Win11怎么显示文件的后缀名呢&#xff0c;下面小编就给大家带来Win11显示文件后缀名的方法&#xff0c;大家感兴趣的话可以来看一看。 Win11显示文件后缀名的方法&#xff1a; 方法一&a…

低代码平台是否能替代电子表格?

在计算机技术普及之前&#xff0c;会计、助理或者是销售人员&#xff0c;都需要用纸和笔来记录和维护每一笔交易。计算机技术兴起之后&#xff0c;一项技术发明——电子表格的出现改变了低效的状况。电子表格的第一个版本出现在1977年&#xff0c;一个名为“VisiCalc”的程序。…

OpenCV(二十七):图像距离变换

1.像素间距离 2.距离变换函数distanceTransform() void cv::distanceTransform ( InputArray src, OutputArray dst, int distanceType, int maskSize, int dstType CV_32F ) src:输入图像&#xff0c;数据类型为CV8U的单通道图像dst:输出图像&#xff0c;与输入图像…

2023-09-07 LeetCode每日一题(修车的最少时间)

2023-09-07每日一题 一、题目编号 2594. 修车的最少时间二、题目链接 点击跳转到题目位置 三、题目描述 给你一个整数数组 ranks &#xff0c;表示一些机械工的 能力值 。ranksi 是第 i 位机械工的能力值。能力值为 r 的机械工可以在 r * n2 分钟内修好 n 辆车。 同时给你…

WebServer 解析HTTP 请求报文

一、TCP 状态转换 浏览器访问网址&#xff0c;TCP传输全过程 二、TCP协议的通信过程 三、TCP 通信流程 // TCP 通信的流程 // 服务器端 &#xff08;被动接受连接的角色&#xff09; 1. 创建一个用于监听的套接字- 监听&#xff1a;监听有客户端的连接- 套接字&#xff1a;这…

C#开发的OpenRA游戏之系统参数选项按钮

C#开发的OpenRA游戏之系统参数选项按钮 前面分析了信标按钮,从图上可以看到,靠右边的按钮,就是系统参数选项按钮: 这个按钮与前面三个按钮是不一样的,虽然它们在排列位置上是放在一起,但是处理的方法方式是不一样的,因为这个选项按钮,并不需要发命令给服务器,再返回来…

Kafka的文件存储与稀疏索引机制

![在这里插入图片描述](https://img-blog.csdnimg.cn/dde7fc866d214985baaa87300a472578.png)这些是存储在分区(分区才是实际的存储)文件中的. seg是逻辑概念 而实际由log存储的. index是偏移量索引而timeindex是时间戳索引 log就是seg 找数据就是先找log 再从log去找

使用aidlux进行模型迁移、部署、推理

AidLux是一个构建在ARM硬件上&#xff0c;基于创新性跨Android/鸿蒙 Linux融合系统环境的智能物联网 (AIoT) 应用开发和部署平台。 说的直白点&#xff0c;aidlux就是一个在arm架构芯片的设备上运行的linux系统&#xff0c;我们可以将身边的安卓设备当作边缘设备&#xff0c;在…

stm32f4_奇怪的bug_串口数据错乱,一个串口收到另一个串口的数据

1、开发环境简介 芯片型号&#xff1a;stm32f407igt6 官方库函数&#xff1a;HAL库 2、bug现象描述和原因推测 使用了2个串口&#xff0c;一个是串口5-波特率115200&#xff0c;一个是串口4-波特率9600&#xff0c;但是串口4时不时会收到上一次发给串口5的数据。不是同一个串…

【计算机网络】 IP协议格式以及以太网帧结构

文章目录 IP协议格式以太网帧结构 IP协议格式 IP工作在网络层 IP头分为两部分&#xff0c;固定部分和可变部分&#xff0c;固定部分就是一定要带这些数据&#xff0c;正常存储应该是连续的&#xff0c;并不是像图中这样会换行&#xff0c;图中只是为了方便观察。 首先是一个版…

论数据库的种类

摘要 数据库是现代信息管理和数据存储的重要工具&#xff0c;几乎在各个领域都有广泛应用。不同类型的数据库适用于不同的应用场景和需求。本文将介绍几种常见的数据库种类&#xff0c;并探讨它们的特点和适用范围。 正文 一、关系型数据库&#xff08;RDBMS&#xff09; 关…

自定义封装异步任务组件,实现FutureTask功能

FutureTask 在 JDK1.8 后的异步编排API中的CompletableFuture&#xff0c;提供了 异步任务的成功回调、异常回调。 public class FutureTaskTest {public static void main(String[] args) throws Exception {CompletableFuture<String> future CompletableFuture.sup…