STM32 串口收发文本数据包

单片机学习!

目录

前言

一、文本数据包格式

二、串口收发文本数据包代码

三、代码解析

 3.1 标志位清除

3.2 数据包接收

四、代码问题改进

总结


前言

        本文介绍了串口收发文本数据包程序设计的思路并详解代码作用。


一、文本数据包格式

        文本数据包的格式的定义如下图所示:可变包长,含包头包尾,其中包头为@,包尾为换行的两个符号\r和\n,中间的载荷字符数量不固定。

b16afb066c18430aa8d89c5115d051e4.png

二、串口收发文本数据包代码

        程序就只写接收的部分,因为发送不像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博客icon-default.png?t=O83Ahttps://blog.csdn.net/Echo_cy_/article/details/142794600?spm=1001.2014.3001.5501

STM32 USART串口接收_stm32 uart发送数据-CSDN博客icon-default.png?t=O83Ahttps://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


总结

        以上就是今天要讲的内容,本文仅仅简单介绍了串口收发文本数据包程序设计的思路并详解了一些设计代码的细节,最后对于代码可能产生的问题做了改进。

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

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

相关文章

预言机调研

预言机 1. 概述 预言机主要承担两个工作&#xff0c;一是验证信息可靠性&#xff0c;二是传递信息。 如果没有预言机&#xff0c;区块链的信息来源将仅限于其内部数据&#xff0c;其广泛使用的潜力和可能性将会大大降低。 区块链预言机是区块链与外部世界之间的桥梁。它们使区…

【1211更新】腾讯混元Hunyuan3D-1文/图生3D模型云端镜像一键运行

目录 项目介绍 显存占用 11月21 新增纹理烘焙模块Dust3R 烘焙相关参数&#xff1a; AutoDL云端镜像 启动说明 标准模型下载 项目介绍 https://github.com/Tencent/Hunyuan3D-1 腾讯混元 3D 生成模型,支持文本和图像条件生成(对于文生3D&#xff0c;支持中/英双语生成)…

【前端】HTML标签汇总

目录 展示用户信息的标签 1.文本标签 span 2.标题标签 h1~h6 3.竖着布局的标签 div 4.段落标签 p 5.超链接标签 a 5.1跳转至网上的资源 5.2锚点 6.列表标签 6.1有序列表 ol 6.2无序列表 ul 7.图片标签 img 7.1相对路径 7.1.1兄弟关系 7.1.2叔侄关系 7.1.3表兄弟…

今天你学C++了吗?——C++中的类与对象(日期类的实现)——实践与知识的碰撞❤

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…

负载均衡和tomcat

一、负载均衡 1.相关概念 nginx的反向代理<-->负载均衡 负载均衡 将四层或者是七层的请求分配到多台后端的服务器上&#xff0c;从而分担整个业务的负载。提高系统的稳定性&#xff0c;也可以提供高可用&#xff08;备灾&#xff0c;其中的一台后端服务器如果发生故障…

【蓝桥杯每日一题】推导部分和——带权并查集

推导部分和 2024-12-11 蓝桥杯每日一题 推导部分和 带权并查集 题目大意 对于一个长度为 ( N ) 的整数数列 ( A 1 , A 2 , ⋯ , A N A_1, A_2, \cdots, A_N A1​,A2​,⋯,AN​ )&#xff0c;小蓝想知道下标 ( l ) 到 ( r ) 的部分和 ∑ i l r A i A l A l 1 ⋯ A r \su…

【C++】判断能否被 3, 5, 7 整除问题解析与优化

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述&#x1f4af;老师代码实现与分析老师代码逻辑分析优点缺点 &#x1f4af;学生代码实现与分析学生代码逻辑分析优点缺点 &#x1f4af;改进与优化优化代码实现优化…

[计算机网络]IP地址推行的“书同文,车同轨”

硬件地址无法直接转换的故事 在很久很久以前&#xff0c;网络世界就像一个庞大的帝国&#xff0c;各个村落&#xff08;网络&#xff09;都有自己的语言&#xff08;硬件地址&#xff09;。每个村落都有自己的规则和习惯&#xff0c;村里的每户人家&#xff08;设备&#xff0…

深度优先搜索(DFS)与回溯法:从全排列到子集问题的决策树与剪枝优化

文章目录 前言&#x1f384;一、全排列✨核心思路✨实现步骤✨代码✨时间和空间复杂度&#x1f381;1. 时间复杂度&#x1f381;2. 空间复杂度 &#x1f384;二、子集✨解法一&#xff1a;逐位置决策法&#x1f381;步骤分析&#x1f381;运行示例&#x1f381;代码 ✨解法二&a…

egg初始搭建

前言 egg.js 是由阿里开源的面向企业级开发的 Node.js 服务端框架&#xff0c;它的底层是由 Koa2 搭建。 Github&#xff1a;https://github.com/eggjs/egg&#xff0c;目前 14.8K Star&#xff0c;egg 很适合做中台。 安装 首先&#xff0c;你要 确保 Node 已经配置环境变量…

Python中的all/any函数和操作符and/or

操作符直观易读适用简单逻辑&#xff0c;函数紧凑方便适宜多条件处理。 (笔记模板由python脚本于2024年12月12日 22:19:10创建&#xff0c;本篇笔记适合有一定编程基础的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff…

论文学习——多种变化环境下基于多种群进化的动态约束多目标优化

论文题目&#xff1a;Multipopulation Evolution-Based Dynamic Constrained Multiobjective Optimization Under Diverse Changing Environments 多种变化环境下基于多种群进化的动态约束多目标优化&#xff08;Qingda Chen , Member, IEEE, Jinliang Ding , Senior Member, …

2025年山东省职业院校技能大赛“信息安全管理与评估”(山东省) 任务书

2025年山东省职业院校技能大赛“信息安全管理与评估”(山东省 任务书 模块一网络平台搭建与设备安全防护任务1&#xff1a;网络平台搭建 &#xff08;50分&#xff09;任务2&#xff1a;网络安全设备配置与防护&#xff08;250分&#xff09; 模块二网络安全事件响应、数字取证…

国标GB28181-2022平台EasyGBS如何实现无插件也能让RTSP在网页端播放?

在流媒体技术日新月异的今天&#xff0c;实时流传输协议&#xff08;RTSP&#xff09;作为视频监控、在线直播等领域的重要支撑&#xff0c;正经历着前所未有的变革。曾经&#xff0c;RTSP在网页端播放面临着诸多挑战&#xff0c;如浏览器兼容性问题、安全性考量以及视频流处理…

EXCEL 关于plot 折线图--频度折线图的一些细节

目录 0 折线图有很多 1 频度折线图 1.1 直接用原始数据做的频度折线图 2 将原始数据生成数据透视表 3 这样可以做出了&#xff0c;频度plot 4 做按某字段汇总&#xff0c;成为累计plot分布 5 修改上面显示效果&#xff0c;做成百分比累计plot频度分布 0 折线图有很多 这…

MATLAB四种逻辑运算

MATLAB中的四种逻辑运算包括逻辑与用&或 a n d 表示 ( 全为 1 时才为 1 &#xff0c;否则为 0 ) and表示(全为1时才为1&#xff0c;否则为0) and表示(全为1时才为1&#xff0c;否则为0)&#xff0c;逻辑或用|或 o r 表示 ( 有 1 就为 1 &#xff0c;都为 0 才为 0 ) or表示…

jmeter CLI Mode 传参实现动态设置用户数

一.需求 CLI 运行模式下每次运行想要传入不同的用户数&#xff0c;比如寻找瓶颈值的场景&#xff0c;需要运行多次设置不同的用户数。 二.解决思路 查看官方API Apache JMeter - Users Manual: Getting Started api CLI Mode 一节中提到可以使用如下参数做属性的替换&#…

iPhone苹果相册视频怎么提取音频?

在数字时代&#xff0c;视频已成为我们记录生活、分享故事的重要方式。然而&#xff0c;有时候我们只想保留视频中的音频部分&#xff0c;比如一段动人的背景音乐或是一段珍贵的对话。那么&#xff0c;苹果相册视频怎么提取音频呢&#xff1f;本文将介绍三种简单且实用的方法&a…

阿里云数据库MongoDB版助力极致游戏高效开发

客户简介 成立于2010年的厦门极致互动网络技术股份有限公司&#xff08;以下简称“公司”或“极致游戏”&#xff09;&#xff0c;是一家集网络游戏产品研发与运营为一体的重点软件企业&#xff0c;公司专注于面向全球用户的网络游戏研发与运营。在整个产业链中&#xff0c;公…

前端成长之路:HTML(4)

前文提到&#xff0c;在HTML中&#xff0c;表格是为了展示数据&#xff0c;表单是为了提交数据。表单标签是十分重要的标签&#xff0c;在网页中&#xff0c;需要和用户进行交互&#xff0c;收集用户信息等&#xff0c;此时就需要使用表单。表单可以将前端收集到的用户输入的信…