09:STM32-------USART串口通信+串口数据包
目录
一:串口协议
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的GPIOGPIO_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);//启动USARTUSART_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\nSerial_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的GPIOGPIO_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);//启动USARTUSART_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的GPIOGPIO_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);//开启NVICNVIC_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);//启动USARTUSART_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的GPIOGPIO_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);//开启NVICNVIC_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);//启动USARTUSART_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的GPIOGPIO_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);//开启NVICNVIC_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);//启动USARTUSART_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;}}
}
相关文章:
09:STM32-------USART串口通信+串口数据包
目录 一:串口协议 1:通信接口 2:串口通信 3:硬件电路 4:电平标准 5:串口参数及其时序 二:USART介绍 1:简历 2:USART框图 3:USART的基本结构 4:数据帧 5: 波特率发生器 6:数据模式 三:案例 A:串口发送--单发送 1:连接图 2:函数介绍 3:代码 B:串口发送接收 1…...
“安全即服务”为网络安全推开一道门
8月30日,三六零(下称“360”)集团发布了2023年半年报,其中安全业务第二季度收入6.54亿元,同比增长98.76%,环比增长157.16%,安全第二增长曲线已完全成型!特别值得一提的是,…...
vue3的生命周期
1.vue3生命周期官方流程图 2.vue3中的选项式生命周期 vue3中的选项式生命周期钩子基本与vue2中的大体相同,它们都是定义在 vue实例的对象参数中的函数,它们在vue中实例的生命周期的不同阶段被调用。生命周期函数钩子会在我们的实例挂载,更新…...
[E2E Test] Python Behave Selenium 一文学会自动化测试
前言 本文将使用Python Behave与Selenium,和同学们一起认识自动化测试,并附上完整的实践教程。 项目源码已上传:CSDN 郭麻花 Azure Repo python-behave-selenium 核心概念 1. 什么是E2E Test E2E即End-to-end,意思是从头到尾…...
Knowledge Graph Prompting for Multi-Document Question Answering
本文是LLM系列文章,针对《Knowledge Graph Prompting for Multi-Document Question Answering》的翻译。 多文档问答中的知识图谱提示 摘要1 引言2 符号3 知识图谱构建4 LM引导的图形遍历器5 实验6 相关工作7 结论 摘要 大型语言模型的“预训练、提示、预测”范式…...
ElMessageBox.prompt 点击确认校验成功后关闭
ElMessageBox.prompt(, 验证取货码, {inputPattern: /^.{1,20}$/,inputErrorMessage: 请输入取货码,inputPlaceholder: 请输入取货码,beforeClose: (action, instance, done) > {if (action confirm) {if (instance.inputValue) {let flag false;if (flag) {done()} else …...
调整Windows11桌面图标间隔
调整Windows11桌面图标间隔 WinR 快捷键如何使用 在Windows系统中,通过 WinR 的快捷键可以快速打开Windows系统的“运行”窗口,然后在这里输入相应的命令就可以快速执行指定的任务。 具体的操作方法是,同时按下键盘上的Windows键和R键即可。…...
Spring最佳实践: 构建高效可维护的Java应用程序
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...
stable diffusion webui中的sampler
Stable Diffusion-采样器篇 - 知乎采样器:Stable Diffusion的webUI中,提供了大量的采样器供我们选择,例如Eular a, Heum,DDIM等,不同的采样器之间究竟有什么区别,在操作时又该如何进行选择&…...
MySQL表的内连和外连
文章目录 MySQL表的内连和外连1. 内连接(1) 显示SMITH的名字和部门名称 2. 外连接2.1 左外连接(1) 查询所有学生的成绩,如果这个学生没有成绩,也要将学生的个人信息显示出来 2.2 右外连接(1) 对stu表和exam表联合查询,把所有的成绩都显示出来…...
StatefulSets In K8s
摘要 StatefulSets是Kubernetes的一种资源对象,用于管理有状态应用程序的部署。与Deployment不同,StatefulSets保证应用程序的有序部署和有状态的维护,确保每个Pod都有唯一的标识和稳定的网络标识。这些特性使得StatefulSets非常适合部署需要…...
leetcode刷题笔记——单调栈
1.模板: stack<int> st; for(int i 0; i < nums.size(); i){while(!st.empty() && st.top() > nums[i]){st.pop();//计算、存放结果}st.push(nums[i]); }2.注意事项:需要注意单调栈中stack存放元素为nums数组的『下标』还是nums数…...
关于 ogbg-molhi数据集的个人解析
cs224w_colab2.py这个图属性预测到底咋预测的 dataset.meta_info.T Out[2]: num tasks 1 eval metric rocauc download_name …...
RabbitMQ:hello结构
1.在Linux环境上面装入rabbitMQ doker-compose.yml version: "3.1" services:rabbitmq:image: daocloud.io/library/rabbitmq:managementrestart: alwayscontainer_name: rabbitmqports:- 6786:5672- 16786:15672volumes:- ./data:/var/lib/rabbitmq doker-compos…...
SpringBoot整合Redis 并 展示使用方法
步骤 引入依赖配置数据库参数编写配置类构造RedisTemplate创建测试类测试 1.引入依赖 不写版本号,也是可以的 在pom中引入 <!--redis配置--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-…...
js中如何实现字符串去重?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用 Set 数据结构⭐ 使用循环遍历⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为那些对Web开发感…...
Axure RP仿QQ音乐app高保真原型图交互模板源文件
Axure RP仿QQ音乐app高保真原型图交互模板源文件。本套素材模板的机型选择华为的mate30,在尺寸和风格方面,采用标准化制作方案,这样做出来的原型图模板显示效果非常优秀。 原型中使用大量的动态面板、中继器、母版,涵盖Axure中技…...
2023牛客暑假多校第四场(补题向题解:J)
终于有时间来慢慢补补题了 J Qu’est-ce Que C’est? 作为队内的dp手,赛时想了好久,等学弟学妹都出了还是不会,羞愧,还好最终队友做出来了。 链接J Qu’est-ce Que C’est? 题意 长度为 n n n 的数组 a a a,每…...
第 362 场 LeetCode 周赛题解
A 与车相交的点 数据范围小直接暴力枚举 class Solution { public:int numberOfPoints(vector <vector<int>> &nums) {unordered_set<int> vis;for (auto &p: nums)for (int i p[0]; i < p[1]; i)vis.insert(i);return vis.size();} };B 判断能否…...
C++ if 语句
一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。 语法 C 中 if 语句的语法: if(boolean_expression) {// 如果布尔表达式为真将执行的语句 }如果布尔表达式为 true,则 if 语句内的代码块将被执行。如果布尔表达式为 false,则 if 语…...
业务安全及实战案例
业务安全 关于漏洞: 注入业务逻辑信息泄露 A04:2021 – Insecure Design 在线靶场PortSwigger 1. 概述 1.1 业务安全现状 1.1.1 业务逻辑漏洞 近年来,随着信息化技术的迅速发展和全球一体化进程的不断加快,计算机和网络已经成为与…...
十一)Stable Diffussion使用教程:人物三视图
现在我们通过一个个具体的案例,去进阶SD的使用。 本篇案例:绘制Q版人物三视图 1)我们先选择一个偏3D的模型,选择文生图,输入魔法; 2)然后选择触发三视图的Lora:<lora:charturnerbetaLora_charturnbetalora:0.6>,注意这里的名称都是本地重新命名的,非原来C站下…...
超级等级福利礼包
文章目录 一、 介绍二、 设计等级礼包的目的1. 提升游戏玩家活跃度2. 提升游戏用户吸引力3. 提高游戏用户留存率4. 实现间接收入5. 持续营收 三、 玩家心理总结四、总结该模式的赢利点五、 该模式的应用场景举例 一、 介绍 超级等级福利礼包,玩家每升级5级即可获得…...
如何用Jmeter提取和引用Token
1.执行获取token接口 在结果树这里,使用$符号提取token值。 $根节点,$.data.token表示提取根节点下的data节点下的token节点的值。 2.使用json提取器,提取token 变量路径就是把在结果树提取的路径写上。 3.使用BeanShell取样器或者BeanShell后…...
C#文件拷贝工具
目录 工具介绍 工具背景 4个文件介绍 CopyTheSpecifiedSuffixFiles.exe.config DataSave.txt 拷贝的存储方式 文件夹介绍 源文件夹 目标文件夹 结果 使用 *.mp4 使用 *.* 重名时坚持拷贝 可能的报错 C#代码如下 Form1.cs Form1.cs设计 APP.config Program.c…...
Redis——Java中的客户端和API
Java客户端 在大多数的业务实现中,我们还是使用编码去操作Redis,对于命令的学习只是知道这些数据库可以做什么操作,以及在后面学习到了Java的API之后知道什么方法对应什么命令即可。 官方推荐的Java的客户端网页链接如下: 爪哇…...
Brief. Bioinformatics2021 | sAMP-PFPDeep+:利用三种不同的序列编码和深度神经网络预测短抗菌肽
文章标题:sAMP-PFPDeep: Improving accuracy of short antimicrobial peptides prediction using three different sequence encodings and deep neural networks 代码:https://github.com/WaqarHusain/sAMP-PFPDeep 一、问题 短抗菌肽(sAMPs)&#x…...
问道管理:华为产业链股再度拉升,捷荣技术6连板,华力创通3日大涨近70%
华为产业链股6日盘中再度拉升,到发稿,捷荣技能涨停斩获6连板,华映科技亦涨停收成3连板,华力创通大涨超19%,蓝箭电子涨约11%,力源信息涨超4%。 捷荣技能盘中再度涨停,近7日已累计大涨超90%。公司…...
面试设计模式-责任链模式
一 责任链模式 1.1 概述 在进行请假申请,财务报销申请,需要走部门领导审批,技术总监审批,大领导审批等判断环节。存在请求方和接收方耦合性太强,代码会比较臃肿,不利于扩展和维护。 1.2 责任链模式 针对…...
Qt 开发 CMake工程
Qt 入门实战教程(目录) 为何要写这篇文章 目前CMake作为C/C工程的构建方式在开源社区已经成为主流。 企业中也是能用CMake的尽量在用。 Windows 环境下的VC工程都是能不用就不用。 但是,这个过程是非常缓慢的,所以࿰…...
电子商务网站设计与开发案例教程/网站优化排名怎么做
该参数只表示一个参数类型,但是如果有多个参数的话,就不要使用该参数了。一个参数的这种其实没有必要写parameterType,而且还有就是多个参数的时候也没有必要写parameterType,也就是说,其实该参数的存在是不是特别必要…...
wordpress网页图标/bt磁力搜索
题目:随机产生10个0-9的整数,用冒泡排序改进版对10个数进行排序。(升序) 程序分析: 对于冒泡排序法,如果输入六个数,则需要5趟排序。如果在第3趟排序之后就完成排序,则后2趟排序就…...
宁波最新发布/seo排名优化价格
strokeStyle "red" //指定描边颜色 fillStyle "red" //指定填充颜色 fillRect(x,y,width,height) //绘制一个自带填充的矩形,接收4个参数,xy代表开始绘制的点,后两个参数表示要绘制的矩形的宽高 strokeRect() //…...
石家庄个人谁做网站/关键词排名监控批量查询
Docker独立IP及容器关联 内置 brldge (nat) 缺点:需要配套服务注册/发现,否则宿主上端口分配困难,容易冲突。由于每个容器暴露的端口都不一致,造成前端路由层nginx配置(peoxy_pass)里无法使用dn…...
国际交友网站源码/代发新闻稿的网站
废话几句: 关于技术学习我的理解是三部曲,What,How,Why,具体释义请参见:学习技术的三步曲:What、How、Why。后续所有的技术博客我都均会按照这种章节来写我的技术博客。 Why: 在一个…...
上海的企业网站备案/百度指数网站
题意 一个字符串,问一段区间内的不重复子串有多少个。 关于后缀自动机 这道题基本上算是后缀自动机的模板题了。但是后缀自动机看了好久,刘汝佳在那本紫书中提到了三个数据结构的难点,现在看来后缀自动机算是第二难的。(毕竟可…...