STM32 串口收发文本数据包
单片机学习!
目录
前言
一、文本数据包格式
二、串口收发文本数据包代码
三、代码解析
3.1 标志位清除
3.2 数据包接收
四、代码问题改进
总结
前言
本文介绍了串口收发文本数据包程序设计的思路并详解代码作用。
一、文本数据包格式
文本数据包的格式的定义如下图所示:可变包长,含包头包尾,其中包头为@,包尾为换行的两个符号\r和\n,中间的载荷字符数量不固定。
二、串口收发文本数据包代码
程序就只写接收的部分,因为发送不像HEX数组一样,方便一个个更改。这里发送就直接在主函数里SendString或者printf就行了。
总代码示例:
#include "stm32f10x.h" // Device header
#include <stdio.h>char Serial_RxPacket[100];//接收缓存区
uint8_t Serial_RxFlag;//标志位void Serial_Init(void)
{//第一步开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIO的时钟//第二步初始化GPIO引脚GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;//引脚模式GPIO_InitStruct.GPIO_Pin= GPIO_Pin_9;//引脚选择Pin_9GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOAGPIO_InitStruct.GPIO_Mode= GPIO_Mode_IPU;//引脚模式GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;//引脚选择Pin_10GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOA//第三步初始化USARTUSART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 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;//停止位USART_InitStructure.USART_WordLength =USART_WordLength_8b; //字长USART_Init(USART1,&USART_InitStructure);//配置USART1的接收中断USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;NVIC_Init(&NVIC_InitStructure);USART_Cmd(USART1,ENABLE);}//发送数据的函数
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)
{uint8_t i;for(i = 0;String[i] != '\0';i++){Serial_SendByte(String[i]);}
}//这个函数的返回值是X的Y次方
uint32_t Serial_Pow(uint32_t X,uint32_t 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');}
}//printf函数重定向到串口
int fputc(int ch,FILE *f)
{Serial_SendByte(ch);return ch;
}//函数实现一个Serial_RxData变量读后自动清除标志位Serial_RxFlag的功能
uint8_t Serial_GetRxFlag(void)
{if(Serial_RxFlag == 1){Serial_RxFlag = 0;return 1;}return 0;
}//中断函数
void USART1_IRQHandler(void)
{static uint8_t RxState = 0;//做状态变量Sstatic uint8_t pRxPacket = 0;//这个静态变量用于指示接收到数据包中哪一个数据了,最开始默认为0//先判断标志位if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)//如果RXNE确实置1了,就进入if{//首先获取一下RxDatauint8_t RxData = USART_ReceiveData(USART1);if(RxState == 0)//等待包头{if(RxData == '@'){RxState = 1;pRxPacket = 0;}}else if(RxState == 1)//接收数据{if(RxData == '\r'){RxState = 2;}else {Serial_RxPacket[pRxPacket] = RxData;pRxPacket ++;}}else if(RxState == 2)//等待第二个包尾{if(RxData == '\n'){RxState = 0;Serial_RxPacket[pRxPacket] = '\0';Serial_RxFlag = 1;}}USART_ClearITPendingBit(USART1,USART_IT_RXNE); }
}
串口配置部分和数据发送、接收的代码详解可以看前两篇博文:
STM32 USART串口发送_串口发送代码-CSDN博客https://blog.csdn.net/Echo_cy_/article/details/142794600?spm=1001.2014.3001.5501
STM32 USART串口接收_stm32 uart发送数据-CSDN博客https://blog.csdn.net/Echo_cy_/article/details/143817933?spm=1001.2014.3001.5501本文只分析新设计的接收文本数据包函数。
三、代码解析
程序最前面为了收发数据包,先定义了一个缓存区的数组和一个标志位。
char Serial_RxPacket[100];
uint8_t Serial_RxFlag;
接收缓存用于接收字符,数量可以给多一点防止溢出,这要求单条指令最长不超过100个字符。
char Serial_RxPacket[100];
自定义的标志位,如果收到一个数据包,就置Serial_RxFlag为1:
uint8_t Serial_RxFlag;
3.1 标志位清除
Serial_GetRxFlag函数实现一个Serial_RxData变量读后自动清除标志位Serial_RxFlag的功能。
uint8_t Serial_GetRxFlag(void)
{if(Serial_RxFlag == 1){Serial_RxFlag = 0;return 1;}return 0;
}
3.2 数据包接收
在中断函数USART1_IRQHandler里需要用状态机来执行接收逻辑,接收数据包,然后把载荷数据存在Serial_RxPacket数组里。
根据状态转移图首先要定义一个标志当前状态的变量S,在中断函数里面定义一个静态变量。
代码示例:
//中断函数接收数据,执行状态机逻辑
void USART1_IRQHandler(void)
{static uint8_t RxState = 0;//当做状态变量Sstatic uint8_t pRxPacket = 0;//这个静态变量用于指示接收到数据包中哪一个数据了,最开始默认为0//先判断标志位if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)//如果RXNE确实置1了,就进入if{//首先获取一下RxDatauint8_t RxData = USART_ReceiveData(USART1);if(RxState == 0)//等待包头{if(RxData == '@'){RxState = 1;pRxPacket = 0;}}else if(RxState == 1)//接收数据{if(RxData == '\r'){RxState = 2;}else {Serial_RxPacket[pRxPacket] = RxData;pRxPacket ++;}}else if(RxState == 2)//等待第二个包尾{if(RxData == '\n'){RxState = 0;Serial_RxPacket[pRxPacket] = '\0';Serial_RxFlag = 1;}}USART_ClearITPendingBit(USART1,USART_IT_RXNE); }
}
注意要用else if,如果只用三个并列的if可能会在状态转移的时候出现问题。比如在状态0,需要转移到状态1,就置RxState=1,结果就会造成下面状态1的条件就立马满足了,这样会出现连续两个if都同时成立的情况,就不符合执行逻辑了。所以这里要使用else if,保证每次进状态机代码之后只能选择执行其中一个状态的代码。或者用switch case语句也可以保证只有一个条件满足。写好状态选择的部分,就可以依次写每个状态执行的操作逻辑和状态转移条件了。
重要变量:
USART1_IRQHandler中断函数是把数据进行了一次转存,最终还是要扫描查询Serial_RxFlag来接收数据。
RxState这个静态变量类似于全局变量,函数进入只会初始化一次为0,在函数退出后,数据仍然有效。与全局变量不同的是,静态变量只能在本函数使用。这里就用RxState当做状态变量S,根据状态转换图,三个状态S分别为0、1、2,所以在if语句里根据RxState的不同,需要进入不同的处理程序。
pRxPacket这个静态变量用于指示接收到数据包中哪一个数据了,最开始默认为0.
中断函数先判断标志位,如果RXNE确实置1了,就进入if 。
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET){...;}
在if代码框里首先获取一下RxData。
uint8_t RxData = USART_ReceiveData(USART1);
接着就是三个状态的条件判断和相应状态下的程序逻辑。
1.等待包头
if(RxState == 0)//等待包头{if(RxData == '@'){RxState = 1;pRxPacket = 0;}}
如果收到包头,那就可以转移状态RxState=1;如果没有收到@就不转移状态。
2.接收数据
因为载荷字符数量并不确定,所以每次接受之前必须先判断是不是包尾\r。
else if(RxState == 1)//接收数据{if(RxData == '\r'){RxState = 2;}else {Serial_RxPacket[pRxPacket] = RxData;pRxPacket ++;//接收数据后位置编码就自增,指示接收下一个位置的数据。}}
此代码逻辑是,每进一次接收状态数据就转存一次缓存数据,同时存的位置后移。当收到包尾\r就证明数据收完了,这时就可以转移到下一个状态。同时对pRxPacket清0,为下次接收准备,可以在状态0转移到状态1时提前清一个0.
3.等待第二个包尾
如果收到第二个包尾\n,那就可以回到最初的状态RxState=0,同时为了表示一个数据包接收到了,可以置一个接收标志位Serial_RxFlag=1;如果没有收到\n就时还没收到包尾,也不做处理,仍然在这个状态等待包尾。
else if(RxState == 2)//等待第二个包尾{if(RxData == '\n'){RxState = 0;Serial_RxPacket[pRxPacket] = '\0';Serial_RxFlag = 1;}}
接收到包尾之后还需要给字符数组的最后加一个字符串结束标志位\0,方便后续对字符串进行处理,不然这个字符数组没有结束标志位,就不知道这个字符串有多长了。
Serial_RxPacket[pRxPacket] = '\0';
这里调用USART_ClearITPendingBit函数,直接清除一下标志位:
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
以上中断接收和变量的封装就完成了!
四、代码问题改进
当然这样还是存在一个问题,如果连续发送数据包,程序处理不及时,可能导致数据包错位。一般文本数据包是独立的,不存在连续,错位的话问题就比较大。所以程序可以稍作修改来解决这个问题,等每次处理完成之后,再开始接收下一个数据包。
可以利用设计的获取标志位Serial_GetRxFlag函数,程序逻辑不使用原来读取Flag之后立刻清除的策略。在中断函数里,等待包头的时候再加一个条件。
if(RxData == '@' && Serial_RxFlag == 0)
如果收到包头,并且Serial_RxFlag=0时,才执行接收,若Serial_RxFlag不等于0,就是发太快了,还没处理完呢,就跳过这个数据包。
之前读取标志位之后立刻清零的函数Serial_GetRxFlag先删除。
把Serial_RxFlag也申明为外部可调用,暂时不封装了。
extern uint8_t Serial_RxFlag;
主函数里当Serial_RxFlag标志位为1时,就代表接收到数据包了,可以执行相应的操作。等操作完成之后,再把Serial_RxFlag标志位清0,在中断函数USART1_IRQHandler里只有Serial_RxFlag标志位为0了,才会继续接收下一个数据包。这样写数据和读数据就是严格分开的,不会同时进行,就可以避免数据包错位的现象了。但是这样发送数据包的频率就不能太快了,太快会丢弃部分数据包。
总代码示例:
Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>char Serial_RxPacket[100];//接收缓存用于接收字符
uint8_t Serial_RxFlag;//自定义的标志位,如果收到一个数据包,就置Serial_RxFlagvoid Serial_Init(void)
{//第一步开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIO的时钟//第二步初始化GPIO引脚GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;//引脚模式GPIO_InitStruct.GPIO_Pin= GPIO_Pin_9;//引脚选择Pin_9GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOAGPIO_InitStruct.GPIO_Mode= GPIO_Mode_IPU;//引脚模式GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;//引脚选择Pin_10GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOA//第三步初始化USARTUSART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 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;//停止位USART_InitStructure.USART_WordLength =USART_WordLength_8b; //字长USART_Init(USART1,&USART_InitStructure);//配置USART1的接收中断USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;NVIC_Init(&NVIC_InitStructure);USART_Cmd(USART1,ENABLE);}//发送数据的函数
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)
{uint8_t i;for(i = 0;String[i] != '\0';i++){Serial_SendByte(String[i]);/}
}//这个函数的返回值是X的Y次方
uint32_t Serial_Pow(uint32_t X,uint32_t 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');}
}//printf函数重定向到串口
int fputc(int ch,FILE *f)
{Serial_SendByte(ch);return ch;
}//函数实现一个Serial_RxData变量读后自动清除标志位Serial_RxFlag的功能
//uint8_t Serial_GetRxFlag(void)
//{
// if(Serial_RxFlag == 1)
// {
// Serial_RxFlag = 0;
// return 1;
// }
// return 0;
//}//中断函数
void USART1_IRQHandler(void)
{static uint8_t RxState = 0;static uint8_t pRxPacket = 0;//判断标志位if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET){uint8_t RxData = USART_ReceiveData(USART1);if(RxState == 0)//等待包头{if(RxData == '@' && Serial_RxFlag == 0){RxState = 1;pRxPacket = 0;}}else if(RxState == 1)//接收数据{if(RxData == '\r'){RxState = 2;}else {Serial_RxPacket[pRxPacket] = RxData;pRxPacket ++;}}else if(RxState == 2)//等待第二个包尾{if(RxData == '\n'){RxState = 0;Serial_RxPacket[pRxPacket] = '\0';Serial_RxFlag = 1;}}USART_ClearITPendingBit(USART1,USART_IT_RXNE); }
}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H#include <stdio.h>extern char Serial_RxPacket[];
extern uint8_t Serial_RxFlag;void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array,uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number,uint8_t Length);#endif
总结
以上就是今天要讲的内容,本文仅仅简单介绍了串口收发文本数据包程序设计的思路并详解了一些设计代码的细节,最后对于代码可能产生的问题做了改进。
相关文章:
STM32 串口收发文本数据包
单片机学习! 目录 前言 一、文本数据包格式 二、串口收发文本数据包代码 三、代码解析 3.1 标志位清除 3.2 数据包接收 四、代码问题改进 总结 前言 本文介绍了串口收发文本数据包程序设计的思路并详解代码作用。 一、文本数据包格式 文本数据包的格式的定义…...
铭记一次项目重大事故
在程序的世界里,bug 就像隐藏在暗处的小怪兽,时不时跳出来捣乱。而职业生涯中,总有那么一个或几个 bug 让我们刻骨铭心。它或许让项目差点夭折,或许让你熬了无数个通宵,或许有着离奇的出现方式和曲折的解决过程。无论是…...
AUTOSAR 汽车开放系统架构
AUTOSAR 官网 AUTOMOTIVE OPEN SYSTEM ARCHITECTURE AUTOSAR (AUTomotive Open System ARchitecture) is a global partnership of leading companies in the automotive and software industry to develop and establish the standardized software framework and open E/E …...
从零用java实现 小红书 springboot vue uniapp (2)主页优化
前言 移动端演示 http://8.146.211.120:8081/#/ 前面的文章我们基本完成了主页的布局 今天我们具体的去进行实现 并且分享我开发时遇到的问题 首先先看效果 java仿小红书主页 实现效果为 1.顶端全屏切换 2.上划加载更多 3.下拉当前页整体刷新 顶端全屏切换我们选择 gui-switch…...
打电话玩手机识别-支持YOLO,COCO,VOC格式的标记,超高识别率可检测到手持打电话, 非接触式打电话,玩手机自拍等
打电话玩手机识别-支持YOLO,COCO,VOC格式的标记,超高识别率可检测到手持打电话, 非接触式打电话,玩手机自拍等1275个图片。 手持打电话: 非接触打电话 玩手机 数据集下载 yolov11:https://download.csdn…...
黑马程序员Java项目实战《苍穹外卖》Day12
苍穹外卖-day12 课程内容 工作台Apache POI导出运营数据Excel报表 功能实现:工作台、数据导出 工作台效果图: 数据导出效果图: 在数据统计页面点击数据导出:生成Excel报表 1. 工作台 1.1 需求分析和设计 1.1.1 产品原…...
实现SpringBoot项目嵌入其他项目
很多时候我们需要在项目里面嵌入其他项目或者被其他项目嵌入,如我们开发一个开源项目b,用户需要在自己的项目a嵌入b项目,使用b项目的功能,而且要实现a项目工作最小化,最好实现引入即用。 1.定义b项目的自定义配置 …...
海康威视摄像头RTSP使用nginx推流到服务器直播教程
思路: 之前2020年在本科的时候,由于项目的需求需要将海康威视的摄像头使用推流服务器到网页进行直播。这里将自己半个月琢磨出来的步骤给大家发一些。切勿转载!!!! 使用网络摄像头中的rtsp协议---------通…...
【自动化】requirements.txt
1.是什么? 用于列出项目依赖的所有Python包及其版本。这使得其他开发者可以轻松地安装与你的项目兼容的环境,或者在不同的机器上复制相同的开发环境。 2.如何编写requirements.txt 手动创建 格式: 包名版本号 在终端home命令自动生成 创建r…...
C++ 中多态性在实际项目中的应用场景
背景与需求 在一个图形绘制软件中,需要绘制多种不同的图形,如圆形、矩形、三角形等。每个图形都有自己的绘制方法,但是它们都可以被看作是一个抽象的 “图形” 概念,并且都有一个共同的操作,比如绘制(draw&…...
手机租赁系统开发指南一站式服务流程解析
内容概要 手机租赁系统的开发是一个复杂但有趣的过程,像搭建乐高一样,只要找到合适的模块,就能打造出一个宾至如归的租赁平台。在这部分,我们将对开发流程的整体结构进行简要概述,并指出每个环节的重要性。 首先&…...
基于Couchbase的数据构建方案:数仓分层
初步方案是将公共层和报表层分别放在不同的bucket中,这种设计从存储和访问优化的角度是合理的,但仍有以下细节需要考虑: 1. 数仓公共层设计(origin bucket) 合理性分析: 将ODS、DWD、DWS层的数据放在一个b…...
Linux中的线程
目录 线程的概念 进程与线程的关系 线程创建 线程终止 线程等待 线程分离 原生线程库 线程局部存储 自己实现线程封装 线程的优缺点 多线程共享与独占资源 线程互斥 互斥锁 自己实现锁的封装 加锁实现互斥的原理 死锁 线程同步 线程的概念 回顾进程相关概念 …...
【保姆级】Mac如何安装+切换Java环境
本文从如何下载不同版本的JDK,到如何丝滑的切换JDK,以及常见坑坑的处理方法,应有尽有,各位看官走过路过不要错过~~~ 下载⏬ 首先上官网: https://www.oracle.com/ 打不开的话可以使用下面👇这个中文的 https://www.oracle.com/cn/java/technologies/downloads/a…...
leetcode 31 Next Permutation
题意 找到下一个permutation是什么,对于一个数组[1,2,3],下一个排列就是[1, 3, 2] 链接 https://leetcode.com/problems/next-permutation/ 思考 首先任何一个permutation满足一个性质,从某个位置往后一定是降序。…...
从RocketMQ到Dubbo:自研中间件技术的崛起
目录 一. 宝兰德 BES 中间件(BES - Business Enterprise Server) 二. TongWeb 中间件 三. 阿里巴巴 RocketMQ 四. 京东 JStorm 五. 华为 GaussDB 六. 华为 FusionInsight 七. 百度 Tinker 八. 中科曙光 Kylin 九. 滴滴 Druid 一. 宝兰德 BES 中间…...
计算机键盘简史 | 键盘按键功能和指法
注:本篇为 “计算机键盘简史 | 键盘按键功能和指法” 相关文章合辑。 英文部分机翻未校。 The Evolution of Keyboards: From Typewriters to Tech Marvels 键盘的演变:从打字机到技术奇迹 Introduction 介绍 The keyboard has journeyed from a humb…...
考研两三题
283. 移动零 - 力扣(LeetCode) 本题解法:使用双指针 解法一: 如上图,在一开始slow指向-1,fast指向数组起始位,进入while循环,保证fast不越界,因为代码中fast会自增&#…...
CentOS8或docker镜像centos8更换镜像源
因为 CentOS 8 已经结束生命周期,原来的镜像源不可用了。我们需要将镜像源改为 CentOS 8 的替代源。 在容器中运行以下命令: 首先备份原有的源 cd /etc/yum.repos.d/ mkdir backup mv *.repo backup/ 创建新的源文件 cat > /etc/yum.repos.d/Cent…...
集成方案 | Docusign + 泛微,实现全流程电子化签署!
本文将详细介绍 Docusign 与泛微的集成步骤及其效果,并通过实际应用场景来展示 Docusign 的强大集成能力,以证明 Docusign 集成功能的高效性和实用性。 在现代企业运营中,效率和合规性是至关重要的。泛微作为企业级办公自动化和流程管理的解决…...
知乎数据分析面试题及参考答案
请阐述大数定理和中心极限定理的内容及其在数据分析中的应用。 大数定理是指在随机试验中,每次出现的结果不同,但是大量重复试验出现的结果的平均值却几乎总是接近于某个确定的值。简单来说,当样本量足够大的时候,样本均值会趋近于总体均值。例如,抛硬币试验,当抛硬币的次…...
8.Vue------如何重新加载子组件key属性----vue知识积累
在子组件中使用key属性是一种强制重新渲染子组件的有效方法。通过改变key属性的值,Vue会 销毁旧组件并创建新组件,从而实现组件的重新加载。这种方法适用于需要完全重新渲染子组件, 包括其内部状态和生命周期钩子的场景,特别是…...
C语言动态内存管理
1.为什么要有动态内存分配 内存开辟方式: int val 20;//在栈空间上开辟四个字节 char arr[10] {0};//在栈空间上开辟10个字节的连续空间开辟空间的方式特点 空间开辟大小是固定德的数组在申明的时候,必须指明数组的长度,数组空间一旦确定了大小不能…...
【蓝桥杯备战】Day 1
1.基础题目 LCR 018.验证回文串 给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。 本题中,将空字符串定义为有效的 回文串 。 示例 1: 输入: s "A man, a plan, a canal: Panama…...
电子应用设计方案-51:智能镜子系统方案设计
智能镜子系统方案设计 一、引言 智能镜子作为一种新兴的智能家居设备,将传统镜子与现代科技相结合,为用户提供了丰富的功能和便捷的体验。本方案旨在设计一款功能强大、用户友好、外观美观的智能镜子系统。 二、系统概述 1. 系统目标 - 提供清晰的镜面…...
[2015~2024]SmartMediaKit音视频直播技术演进之路
技术背景 2015年,因应急指挥项目需求,我们实现了RTMP推送音视频采集推送(采集摄像头和麦克风数据)模块,在我们做好了RTMP推送模块后,苦于没有一个满足我们毫秒级延迟诉求的RTMP播放器,于是第一…...
图片添加水印的实验原理,实验代码,实验现象
一、图片添加水印的实验原理 1. 选择水印类型 可见水印:这些水印可以直接被肉眼看到,通常用于防止未经授权的使用。它们可以是文字、标志或图案等形式,并且放置在图像的显著位置。不可见水印:这类水印不容易被察觉,但…...
Python Web 开发案例解析
一、Flask 基础应用案例 (一)项目搭建与初始化 环境准备 安装 Python 解释器,确保版本符合 Flask 要求,如 Python 3.6 及以上。使用pip安装 Flask 库,命令为pip install flask。可以创建虚拟环境,如python…...
JPG 转 PDF:免费好用的在线图片转 PDF 工具
JPG 转 PDF:免费好用的在线图片转 PDF 工具 在日常工作和生活中,我们经常需要将图片转换为 PDF 格式。无论是制作电子文档、准备演示材料,还是整理照片集,将图片转换为 PDF 都是一个常见的需求。今天为大家介绍一款完全免费、无需…...
代码随想录44天
1143.最长公共子序列 class Solution:def longestCommonSubsequence(self, s: str, t: str) -> int:n, m len(s), len(t)cache # 缓存装饰器,避免重复计算 dfs 的结果(记忆化)def dfs(i: int, j: int) -> int:if i < 0 or j <…...
C语言-详细解答-重组字符串并16进制转10进制
1.题目要求 2.代码实现 #include <stdio.h> #include <ctype.h> #include <string.h>int hexToDec(char hex[]) {int len strlen(hex);int base 1;int dec 0;for (int i len - 1; i > 0; i--) {if (isdigit(hex[i])) {dec (hex[i] - 0) * base;} e…...
scala列表
1 不可变 List 说明 (1)List 默认为不可变集合 (2)创建一个 List(数据有顺序,可重复) (3)遍历 List (4)List 增加数据 (5&#…...
医疗急救技能大赛理论题库
医疗急救技能大赛理论题库 题型包括:A1 型题 400题,A2 型题40题,A3/A4 型40 题, B 型题 80 题 ,X 型题 160 题,判断题 80 题。 一、A1 型题:共计400 题 (即最佳回答题,它由一个叙…...
深入浅出WebRTC—ULPFEC
FEC 通过在发送端添加额外的冗余信息,使接收端即使在部分数据包丢失的情况下也能恢复原始数据,从而减轻网络丢包的影响。在 WebRTC 中,FEC 主要有两种实现方式:ULPFEC 和 FlexFEC,FlexFEC 是 ULPFEC 的扩展和升级&…...
SQL 在线格式化 - 加菲工具
SQL 在线格式化 打开网站 加菲工具 选择“SQL 在线格式化” 或者直接访问 https://www.orcc.online/tools/sql 输入sql,点击上方的格式化按钮即可 输入框得到格式化后的sql结果...
到达率的变化动态调整服务器的服务率,实现负载均衡,提高资源利用效率
中心可以根据任务到达率的变化动态调整服务器的服务率,实现负载均衡,提高资源利用效率 服务率和到达率 中心可以根据任务到达率的变化动态调整服务器的服务率,实现负载均衡,提高资源利用效率服务率(Service Rate)到达率(Arrival Rate)控制参数实现负载均衡的方法在云计…...
【Golang】Go语言编程思想(六):Channel,第四节,Select
使用 Select 如果此时我们有多个 channel,我们想从多个 channel 接收数据,谁来的快先输出谁,此时应该怎么做呢?答案是使用 select: package mainimport "fmt"func main() {var c1, c2 chan int // c1 and …...
认证插件介绍
本文档是针对 UOS 登录器插件给出开发指南,目的是为了让开发人员了解如何在 UOS 登录器上增加一种自定义认证方式,对插件接口做了详细说明以及实战练习。 文章目录 一、认证插件可以做什么?二、认证流程三、术语说明四、安全性五、可靠性六、…...
MindSearch深度解析实践
1. 课程内容 1.1 MindSearch 简介 MindSearch 是一个开源的 AI 搜索引擎框架,具有与 Perplexity.ai Pro 相同的性能。我们可以轻松部署它来构建自己的专属搜索引擎,可以基于闭源的LLM(如GPT、Claude系列),也可以使用…...
[oeasy]python052_[系统开发语言为什么默认是c语言
[系统开发语言为什么默认是c语言 [趣味拓展]c语言发祥史_c是如何成为第一系统语言的 上次我们了解了 标识符 要求 首字符 为 字母和下划线 后面字符 为 字母和下划线 外加 数字 添加图片注释,不超过 140 字(可选) 使用isidentifier函数 可…...
【java常用算法和应用场景】
java常用算法和应用场景 Java中常用的算法涵盖多个领域,包括排序算法、查找算法、字符串匹配算法、图论算法、动态规划算法、贪心算法、分治算法等。以下是Java中一些常用算法及其应用场景和示例代码: 一、排序算法 排序算法是计算机科学中的一种基本…...
D96【python 接口自动化学习】- pytest进阶之fixture用法
day96 pytest的fixture详解(三) 学习日期:20241211 学习目标:pytest基础用法 -- pytest的fixture详解(三) 学习笔记: fixture(scop"class") (scop"class") 每一个类调…...
知从科技总经理受邀参加上海临港新片区商会“湖畔TECS”技术分享沙龙(第五期)
11月26日,上海知从科技有限公司创始人陈荣波先生受邀出席临港新片区商会 “湖畔TECS”技术分享沙龙(第五期)活动,并在活动上为参会嘉宾们做了主题分享。本次活动由临港新片区商会主办,智能网联汽车创新联盟协办&#x…...
scala的泛型应用场景
用代码表示泛型类://泛型:类型参数化 //Pair约定一对数据 class Pair[T](var x:T,var y:T) //class 类名(成员名:数据类型)--->class 类名[泛型名](成员名:泛型名)/.参考代码&…...
三种策略改进的沙猫群优化算法(MSCSO)
三种策略改进的沙猫群优化算法(MSCSO) 目录 三种策略改进的沙猫群优化算法(MSCSO)效果一览基本介绍程序设计参考资料效果一览 基本介绍 改进点: 三角形游走策略Levy飞行游走策略透镜成像反向学习SCSO是 2022年提出的元启发式优化算法,该算法灵感来源于沙猫的捕食行为,沙猫群…...
Google Cloud Database Option(数据库选项说明)
关系数据库 在关系数据库中,信息存储在表、行和列中,这通常最适合结构化数据。因此,它们用于数据结构不经常更改的应用程序。与大多数关系数据库交互时使用 SQL(结构化查询语言)。它们为数据提供 ACID 一致性模式&am…...
java抽奖系统登录下(四)
6.4 关于登录 最简单的登录: 1、web登录页填写登录信息,前端发送登录信息到后端; 2、后端接受登录信息,并校验。校验成功,返回成功结果。 这种登录会出现一个问题,用户1成功登录之后,获取到后台…...
Linux入门攻坚——41、Linux集群系统入门-lvs(2)
lvs-dr:GATEWAY Director只负责请求报文,响应报文不经过Director,直接由RS返回给Client。 lvs-dr的报文路线如上图,基本思路就是报文不会回送Director,第①种情况是VIP、DIP、RIP位于同一个网段,这样&…...
Android Freezer
Freezer原理 Android按照优先级将一般的APP从高到低分为: 前台进程 --> 可感知进程–> 服务进程 --> Cached进程。 Freezer通过冻住cached进程,来迫使这些进程让出CPU,以达到优化系统资源使用的目的。 Cached进程是怎么判定的呢? 由于andro…...
GeeCache-单体并发缓存
实现LRU中value接口的缓存类 使用互斥锁封装LRU缓存类,实现并发访问 实现Group组,用名称对缓存分类 Getter为缓存击穿时调用的回调函数 若缓存击穿则调用回调函数,并把读取到的值加载到缓存中...