蓝桥杯嵌入式第15届真题-个人理解+解析
个人吐槽
#因为最近蓝桥杯快要开始了,我舍不得米白费了,所以就认真刷刷模拟题,但是我感觉真题会更好,所以就看了一下上届的真题。不过它是真的长,我看着就头晕,但是还是把几个模块认真分析了一下就还是很容易写的。
#分析的话,频率超限和频率突变,这位博主就写的很好,所以我就直接使用他的来了,很清晰,所以这里更多的是以我的视角一起来分析一下想法。题目的话,就不截取了。第十五届蓝桥杯嵌入式组省赛题目分析及代码_蓝桥杯嵌入式15届赛题-CSDN博客
个人心得部分
对于题目来说,每一个题目的要求都是有用的。 写题目的时候一定要注意。 比如说性能要求那里,频率的测量范围,真的会影响到后面的测评结果。所以一定要大致的扫描完题目后,细致地针对题目要求来进行代码编写。
STM32CUBEMX 配置部分
所用到的外设有
- LED1-7 默认配置 LD1-LD7
- 四个按键 B1,B2,B3,B4 --- 这个后续会在我的代码里出现的,注意
- 这里我用到了差不多4个Timer 四个定时器
Timer2,Timer16 都是通道1,配置为输入捕获,且时钟源为内部时钟。 psc为80 - 1其他保持默认。
Timer6 我是用于按键状态机 定时器中断处理 按键状态机 它的频率为100hz 。 psc,arr你怎么给取决于自己。只要保证定时器为100hz即可。其他默认
Timer4 我是用于处理频率数据 10hz,psc,arr由你决定。只要保证为10hz即可。其他保持默认。
模块分割(先从简单的部分开始)
1.变量部分
/*
主要的变量是以下部分,之前我写过按键状态机,
那个变量的话,我单独拿出来讲。由于是第一次写这个省赛题目, 我会标出这个代码所在文件main.c
*/
typedef enum
{LCD_SHOW_DATA_PAGE_FREQUENCE, /* 数据界面(频率) */LCD_SHOW_DATA_PAGE_TIMES , /* 数据界面(周期) */LCD_SHOW_PARA_PAGE , /* 参数界面*/LCD_SHOW_COUNT_PAGE , /* 统计界面*/
}LCD_Page;typedef enum
{PARA_NONE = 0,PARA_PD,PARA_PH,PARA_PX,
}Para; //参数/* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
char text[20] ;
int ccl_value_1 = 0, ccl_value_2 = 0; // 获取输入捕获值/* 对于频率部分 fA / fB = FA / FB + PX */
int fA = 0,fB = 0; // 频率值
int PD = 1000,PH = 5000,PX = 0;
float fre_A = 0,fre_B = 0;
float A_times = 0,B_times = 0;int NDA = 0,NDB = 0; /* A/B通道频率突变次数 */
int NHA = 0,NHB = 0; /* A/B通道频率超限次数 */
int PHA[2],PHB[2];
uint16_t A_us = 0,B_us = 0; /* A,B的时间 (us)*/
float A_ms = 0, B_ms = 0; /* A,B的时间 (ms)*/
LCD_Page mypage = LCD_SHOW_DATA_PAGE_FREQUENCE; /* 初始化为 数据界面(频率)*/
LCD_Page Key3page = LCD_SHOW_DATA_PAGE_FREQUENCE;
Para mypara = PARA_NONE; //用于参数选择/* --- 频率突变部分 --- */
int PDA[30] = {0},PDB[30] = {0};
int Max_A,Max_B,Min_A,Min_B;
uint16_t timerA = 0,timerB = 0; // 3s的时间窗口标志位/* --- 最终频率--- */
int FreA = 0,FreB = 0;
上面是main.c 函数中运用到的变量值
/*fun.h
*/#ifndef _fun_h
#define _fun_h
#include "headfile.h"#define KEY_NUM 4
#define DEBOUNCE_CNT 2
#define LONG_PRESS_THRESH 900 // 长按判断阈值(ms)#define LED_CloseAll() do{GPIOC->ODR = 0xFFFFU;}while(0)/* 枚举按键状态类型 */
typedef enum
{KEY_STATE_IDLE,KEY_STATE_DEBOUNCE,KEY_STATE_PRESSED,
// KEY_STATE_RELEASE_DEBOUNCE
}KeyState;typedef enum
{ KEY_EVT_NONE, //没有按下? KEY_EVT_PRESS, //按下事件KEY_EVT_RELEASE, //释放事件KEY_EVT_LONG_PRESS // 长按事件
}KeyEventType;typedef struct
{KeyEventType type;uint32_t duration; //事件持续时间(ms)}KeyEvent;// 增强按键结构体
typedef struct {GPIO_TypeDef* GPIOx;uint16_t GPIO_Pin;KeyState state;uint32_t pressStart; // 按下开始时间uint32_t pressDuration; // 当前按压持续时间uint8_t eventFlag; // 事件标志KeyEvent event; // 事件详情
} KeyHandle;void Keys_StateMachine_Handler(void);/* 获取当前按压持续时间 */
uint32_t Get_KeyPressDuration(uint8_t KeyID);// 事件轮询函数
KeyEvent Poll_KeyEvent(uint8_t keyID);/* 获取按键事件 */
uint8_t Get_Key(uint8_t KeyID);void LED_Ctrl(uint8_t num,uint8_t mode);
void LED_CLOSEALL(void);
#endif
这里是我自己定义的fun.h文件 里面包括我自己定义的按键状态机 + LED灯控制。
/*headfile.h
*/#ifndef _headfile_h
#define _headfile_h#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "stm32g4xx.h" // Device header#include "string.h"
#include "stdint.h"
#include "main.h"
#include "gpio.h"
#include "TIM.h"#include "fun.h"
#include "lcd.h"#endif
这个是headfile.h文件 里面集成了大部分所要用到的头文件,就无需我们总是在不同的头文件中重复引入相同的头文件。
1.5 频率捕获部分
/*
main.c输入捕获部分
*/// 变量部分
int ccl_value_1 = 0, ccl_value_2 = 0; // 获取输入捕获值/* 对于频率部分 fA / fB = FA / FB + PX */
int fA = 0,fB = 0; // 频率值
int PD = 1000,PH = 5000,PX = 0;
float fre_A = 0,fre_B = 0;
////代码部分/* 频率获取部分
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{/* r40 输出捕获 通道 A */if(htim->Instance == TIM2){fA = 1000000 / (TIM2->CCR1+1);TIM2->CNT = 0;// 这里我们根据 性能要求部分推出if(fA > 20000) fA = 20000;else if(fA < 400) fA = 400;FreA = fA + PX;ccl_value_1 = 1000000 / FreA;/*这里我根据测评出的结果 推出来,它其实周期的计算是根据最终的频率来计算的而不是我最先测出的fA来进行计算。*/}/* R39 输出捕获 通道 B */if(htim->Instance == TIM16){fB = 1000000 / (TIM16->CCR1+1);TIM16->CNT = 0;if(fB > 20000) fB = 20000;else if(fB < 400) fB = 400;FreB = fB + PX;ccl_value_2 = 1000000 / FreB;}
}
为什么我们在输入捕获那里 还写一个if..else。 因为我们可以具体去看性能要求部分。
400 hz --- 20000hz
// 这里我们根据 性能要求部分推出
if(fA > 20000) fA = 20000;
else if(fA < 400) fA = 400;
2.按键部分
按键部分的话,我就不细说了,它在我写的博客里,就是最近的博客。直接上代码了。 头文件是fun.h 就是上面那个代码。
/*此为 fun.c 里面包括了
1.按键状态机 + 状态轮询函数(我进行封装了)
2.LED控制
*/
#include "fun.h"// 初始化 按键
static KeyHandle Keys[KEY_NUM] =
{{B1_GPIO_Port,B1_Pin,KEY_STATE_IDLE,0,0,0,{KEY_EVT_PRESS, 0}},{B2_GPIO_Port,B2_Pin,KEY_STATE_IDLE,0,0,0,{KEY_EVT_PRESS, 0}},{B3_GPIO_Port,B3_Pin,KEY_STATE_IDLE,0,0,0,{KEY_EVT_PRESS, 0}},{B4_GPIO_Port,B4_Pin,KEY_STATE_IDLE,0,0,0,{KEY_EVT_PRESS, 0}},
};void Keys_StateMachine_Handler(void)
{static uint8_t debounceCnt[KEY_NUM] = {0};uint32_t now = HAL_GetTick(); // 获取tick时间uint8_t i;for(i = 0;i<KEY_NUM;i++){uint8_t pinState = HAL_GPIO_ReadPin(Keys[i].GPIOx,Keys[i].GPIO_Pin); //读取按键状态switch(Keys[i].state){case KEY_STATE_IDLE://如果按下if(pinState == GPIO_PIN_RESET){Keys[i].state = KEY_STATE_DEBOUNCE; //第一次按下的时侯debounceCnt[i] = 0;Keys[i].pressStart = now; //记录下初始按下时间}break;case KEY_STATE_DEBOUNCE:if (pinState == GPIO_PIN_RESET) { // 持续检测按下状态if (++debounceCnt[i] >= DEBOUNCE_CNT) {Keys[i].state = KEY_STATE_PRESSED;Keys[i].event.type = KEY_EVT_PRESS;Keys[i].event.duration = 0;Keys[i].eventFlag = 1;debounceCnt[i] = 0;}} else { // 中途释放则回到IDLEKeys[i].state = KEY_STATE_IDLE;debounceCnt[i] = 0;}break; case KEY_STATE_PRESSED:/* 持续更新按压时间 */Keys[i].pressDuration = now - Keys[i].pressStart; // now 不断在更新if(Keys[i].pressDuration >= LONG_PRESS_THRESH){Keys[i].event.type = KEY_EVT_LONG_PRESS;Keys[i].event.duration = Keys[i].pressDuration; //获取按下时间Keys[i].eventFlag = 1;Keys[i].pressStart = now; // 重置计时避免重复触发}if(pinState == GPIO_PIN_SET){Keys[i].state = KEY_STATE_IDLE;Keys[i].event.type = KEY_EVT_RELEASE;Keys[i].event.duration = now - Keys[i].pressDuration;Keys[i].eventFlag = 1;}break;} }
}/* 获取当前按压持续时间 */
uint32_t Get_KeyPressDuration(uint8_t KeyID)
{if(KeyID >= KEY_NUM) return 0; // 超出范围return Keys[KeyID].pressDuration; // 返回持续时间
}// 事件轮询函数
KeyEvent Poll_KeyEvent(uint8_t keyID)
{KeyEvent evt = {KEY_EVT_NONE, 0};if(keyID >= KEY_NUM) return evt;if(Keys[keyID].eventFlag){evt = Keys[keyID].event;Keys[keyID].eventFlag = 0;// 重置持续时间(释放事件后)if(evt.type == KEY_EVT_RELEASE){Keys[keyID].pressDuration = 0;}}return evt;
}/* 获取按键事件 B1 --- KeyID - 0 */
uint8_t Get_Key(uint8_t KeyID)
{if(KeyID >= KEY_NUM ) return 0;if(Keys[KeyID].eventFlag){Keys[KeyID].eventFlag = 0;return 1;}else{return 0;}
}/*led亮灭控制*/
void LED_Ctrl(uint8_t num,uint8_t mode)
{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8 << (num - 1),mode ? GPIO_PIN_RESET:GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);}void LED_CLOSEALL(void)
{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);}
接下来就是直接放到定时器里面,每10ms触发一次检测,通过定时器来消除抖动。
/*
main.c 部分
*/
/* TIM定时器处理部分 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{/* 处理按键 100hz */if(htim->Instance == TIM6){Keys_StateMachine_Handler(); //此处每10ms进入一次按键状态机}
}
3.按键轮询
/*main.c
前言,在前面,我们已经完成了按键状态机,所以现在可以正式
开启,按键检测(按键轮询 都可以)
*/// 所用到的变量 (这里是我进行再一次重复拿出来,加深你的印象)
typedef enum
{LCD_SHOW_DATA_PAGE_FREQUENCE, /* 数据界面(频率) */LCD_SHOW_DATA_PAGE_TIMES , /* 数据界面(周期) */LCD_SHOW_PARA_PAGE , /* 参数界面*/LCD_SHOW_COUNT_PAGE , /* 统计界面*/
}LCD_Page;typedef enum
{PARA_NONE = 0,PARA_PD,PARA_PH,PARA_PX,
}Para; //参数 针对于参数界面,我用的是switch + enumint ccl_value_1 = 0, ccl_value_2 = 0; // 获取输入捕获值
int fA = 0,fB = 0; // 频率值
int PD = 1000,PH = 5000,PX = 0;
float fre_A = 0,fre_B = 0; //转换为float值
float A_times = 0,B_times = 0; // 周期值转为float值LCD_Page mypage = LCD_SHOW_DATA_PAGE_FREQUENCE; /* 初始化为 数据界面(频率)*/
LCD_Page Key3page = LCD_SHOW_DATA_PAGE_FREQUENCE;
Para mypara = PARA_NONE; //用于参数选择int FreA = 0,FreB = 0; //最终频率
//// 代码部分/* 按键轮询处理及逻辑优化 */
void Key_While()
{/*------ 页面控制变量 ------*/static uint8_t DataSubPage = 0; // 数据子页面 0-频率 1-次数static uint8_t ChoosePara = 0; // 参数选择 0-PD 1-PH 2-PXstatic uint8_t ChoosePage = 0; // 页面选择 0-数据 1-参数 2-统计/*------ 按键事件处理 ------*/KeyEvent KEYS[4];for(uint8_t i = 0; i < 4; i++) {KEYS[i] = Poll_KeyEvent(i);}/*------ 按键1: 参数加 ------*/if(KEYS[0].type == KEY_EVT_PRESS) {LCD_Clear(Black);if(mypage == LCD_SHOW_PARA_PAGE) // 加入了页面限制{switch(mypara) {case PARA_PD: PD = (PD >= 1000) ? 1000 : (PD + 100); break;case PARA_PH: PH = (PH >= 10000) ? 10000 : (PH + 100); break;case PARA_PX: PX = (PX >= 1000) ? 1000 : (PX + 100); break;}}}/*------ 按键2: 参数减 ------*/else if(KEYS[1].type == KEY_EVT_PRESS) {LCD_Clear(Black);if(mypage == LCD_SHOW_PARA_PAGE) //将按键只在参数界面才可以作用{switch(mypara) {case PARA_PD: PD = (PD <= 100) ? 100 : (PD - 100); break;case PARA_PH: PH = (PH <= 1000) ? 1000 : (PH - 100); break;case PARA_PX: PX = (PX <= -1000) ? -1000 : (PX - 100); break;}}}/*------ 按键3: 参数切换/子页面切换 ------*/else if(KEYS[2].type == KEY_EVT_PRESS) {LCD_Clear(Black);switch(Key3page) {/* 参数页切换 */case LCD_SHOW_PARA_PAGE:ChoosePara = (ChoosePara + 1) % 3; // 优化为循环切换switch(ChoosePara) {case 0: mypara = PARA_PD; break;case 1: mypara = PARA_PH; break;case 2: mypara = PARA_PX; break;break;}break;/* 数据页子页面切换 */case LCD_SHOW_DATA_PAGE_FREQUENCE:case LCD_SHOW_DATA_PAGE_TIMES:DataSubPage = (DataSubPage + 1) % 2; // 使用常量更佳LCD_Clear(Black);Key3page = (DataSubPage == 0) ? LCD_SHOW_DATA_PAGE_FREQUENCE : LCD_SHOW_DATA_PAGE_TIMES;mypage = Key3page; // 保持页面同步break;}}/*------ 按键3: 参数切换/子页面切换/统计页长按清零 ------*/else if(KEYS[2].type == KEY_EVT_LONG_PRESS) { // 新增长按处理if(Key3page == LCD_SHOW_COUNT_PAGE) {NDA = NDB = NHA = NHB = 0;LCD_Clear(Black);// 可以添加统计页刷新函数}}/*------ 按键4: 主页面切换 ------*/else if(KEYS[3].type == KEY_EVT_PRESS) {ChoosePage = (ChoosePage + 1) % 3; // 循环切换LCD_Clear(Black);switch(ChoosePage) {case 0: // 数据页DataSubPage = 0;Key3page = LCD_SHOW_DATA_PAGE_FREQUENCE;mypage = LCD_SHOW_DATA_PAGE_FREQUENCE;break;case 1: // 参数页ChoosePara = 0; // 重置参数选择Key3page = LCD_SHOW_PARA_PAGE;mypage = LCD_SHOW_PARA_PAGE;mypara = PARA_PD; // 默认选中PDbreak;case 2: // 统计页Key3page = LCD_SHOW_COUNT_PAGE;mypage = LCD_SHOW_COUNT_PAGE;break;}}
}
对于按键3部分的处理,其实我处理的很粗超,其实用if...else if...反而更快,更方便。 对于那个数据子页面的转换,我是以一种互斥量的想法编写的,我拿了这个值,那你就不能拿。对于这个转换也是 所以 key3page 是用来控制现在是哪个页面 ---> 对于按键来说。 mypage是用来决定lcd显示的。 其他的就比较清晰了。
4.LCD显示
/*main.c
这里是LCD显示部分
*/// 需要的变量 这个只是用于提示各位接下来会出现的变量,这些之前已经定义过了
/* USER CODE BEGIN PM */
typedef enum
{LCD_SHOW_DATA_PAGE_FREQUENCE, /* 数据界面(频率) */LCD_SHOW_DATA_PAGE_TIMES , /* 数据界面(周期) */LCD_SHOW_PARA_PAGE , /* 参数界面*/LCD_SHOW_COUNT_PAGE , /* 统计界面*/
}LCD_Page;typedef enum
{PARA_NONE = 0,PARA_PD,PARA_PH,PARA_PX,
}Para; //参数int ccl_value_1 = 0, ccl_value_2 = 0; // 获取输入捕获值/* 对于频率部分 fA / fB = FA / FB + PX */
int fA = 0,fB = 0; // 频率值
int PD = 1000,PH = 5000,PX = 0;
float fre_A = 0,fre_B = 0;
float A_times = 0,B_times = 0;int NDA = 0,NDB = 0; /* A/B通道频率突变次数 */
int NHA = 0,NHB = 0; /* A/B通道频率超限次数 */
int PHA[2],PHB[2];
uint16_t A_us = 0,B_us = 0; /* A,B的时间 (us)*/
float A_ms = 0, B_ms = 0; /* A,B的时间 (ms)*/
LCD_Page mypage = LCD_SHOW_DATA_PAGE_FREQUENCE; /* 初始化为 数据界面(频率)*/
LCD_Page Key3page = LCD_SHOW_DATA_PAGE_FREQUENCE;
Para mypara = PARA_NONE; //用于参数选择
////代码部分
void LCD_SHOWPAGE(void)
{switch(mypage){case LCD_SHOW_DATA_PAGE_FREQUENCE:LCD_DisplayStringLine(Line1,(uint8_t * )" DATA ");if(FreA<0){LCD_DisplayStringLine(Line3,(uint8_t *)" A=NULL ");}else if(FreA > 1000){fre_A = (float)FreA / 1000.0f;sprintf(text," A=%.2fKHz ",fre_A);LCD_DisplayStringLine(Line3,(uint8_t *)text);}else{sprintf(text," A=%dHz ",FreA);LCD_DisplayStringLine(Line3,(uint8_t *)text); }if(FreB < 0){LCD_DisplayStringLine(Line4,(uint8_t *)" B=NULL");}else if(FreB > 1000){fre_B = (float)FreB / 1000.0f;sprintf(text," B=%.2fKHz ",fre_B);LCD_DisplayStringLine(Line4,(uint8_t *)text); }else{sprintf(text," B=%dHz ",FreB);LCD_DisplayStringLine(Line4,(uint8_t *)text); }break;case LCD_SHOW_DATA_PAGE_TIMES:LCD_DisplayStringLine(Line1,(uint8_t * )" DATA "); /* 对于通道A 这里我进行了修改 通过判断频率是否为负,进而判断是否为NULL */if(FreA < 0){LCD_DisplayStringLine(Line3,(uint8_t *)" A=NULL");}else if(ccl_value_1 > 1000){A_times = (float)ccl_value_1 / 1000.0f;sprintf(text," A=%.2fmS ",A_times);LCD_DisplayStringLine(Line3,(uint8_t *)text);}else{sprintf(text," A=%duS ",ccl_value_1);LCD_DisplayStringLine(Line3,(uint8_t *)text);}// 这里我进行了修改 通过判断频率是否为负,进而判断是否为NULLif(FreB < 0){LCD_DisplayStringLine(Line4,(uint8_t *)" B=NULL");}else if(ccl_value_2 > 1000){B_times = (float)ccl_value_2 / 1000.0f;sprintf(text," B=%.2fmS ",B_times);LCD_DisplayStringLine(Line4,(uint8_t *)text);}else{sprintf(text," B=%duS ",ccl_value_2);LCD_DisplayStringLine(Line4,(uint8_t *)text);}break;case LCD_SHOW_PARA_PAGE:LED_Ctrl(1,0);LCD_DisplayStringLine(Line1,(uint8_t *)" PARA ");sprintf(text," PD=%dHz ",PD);LCD_DisplayStringLine(Line3,(uint8_t *)text);sprintf(text," PH=%dHz ",PH);LCD_DisplayStringLine(Line4,(uint8_t *)text);sprintf(text," PX=%dHz ",PX);LCD_DisplayStringLine(Line5,(uint8_t *)text); break;case LCD_SHOW_COUNT_PAGE:LED_Ctrl(1,0);LCD_DisplayStringLine(Line1,(uint8_t *)" RECD ");sprintf(text," NDA=%d ",NDA);LCD_DisplayStringLine(Line3,(uint8_t *)text);sprintf(text," NDB=%d ",NDB);LCD_DisplayStringLine(Line4,(uint8_t *)text);sprintf(text," NHA=%d ",NHA);LCD_DisplayStringLine(Line5,(uint8_t *)text);sprintf(text," NHB=%d ",NHB);LCD_DisplayStringLine(Line6,(uint8_t *)text);break;default:break;}if(NDA >= 3 || NDB >= 3) {LED_Ctrl(8,1);}else{LED_Ctrl(8,0);}if(mypage == LCD_SHOW_DATA_PAGE_FREQUENCE || mypage == LCD_SHOW_DATA_PAGE_TIMES){LED_Ctrl(1,1);}else{LED_Ctrl(1,0);}
}
5.频率处理部分
5.1频率超限部分
/*main.c处理频率超限
*/
//变量部分
int NDA = 0,NDB = 0; /* A/B通道频率突变次数 */
int NHA = 0,NHB = 0; /* A/B通道频率超限次数 */
int PHA[2],PHB[2]; // 为什么是数组,PHA[0] --- 上一次的值 PHA[1] --- 这一次的值//代码部分
void PH_Beyond(void)
{/*获取最新的值*/PHA[1] = FreA;PHB[1] = FreB;if(PHA[1] > PH && PHA[0] < PH){NHA++;}if(PHB[1] > PH && PHB[0] < PH){NHB++;}if(PHA[1] > PH) {LED_Ctrl(2,1);}else{LED_Ctrl(2,0);}if(PHB[1] > PH){LED_Ctrl(3,1);}else{LED_Ctrl(3,0);}PHA[0] = PHA[1];PHB[0] = PHB[1];
}
5.2频率突变部分
/*main.c 频率突变部分 用到TIM4定时器 频率为10hz
*///变量部分int PDA[30] = {0},PDB[30] = {0};
int Max_A,Max_B,Min_A,Min_B;//
/* TIM定时器处理部分 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{/* 处理按键 100hz */if(htim->Instance == TIM6){Keys_StateMachine_Handler(); }/* 处理获取的频率数据 10hz */if(htim->Instance == TIM4){if(timerA == 0){Max_A = FreA;Min_A = FreA;}else{if(Max_A < FreA){Max_A = FreA;}if(Min_A > FreA){Min_A = FreA;}}timerA++;if(timerA >= 30){timerA = 0;if((Max_A - Min_A) > PD){NDA++;}}if(timerB == 0){Max_B = FreB;Min_B = FreB;}else{if(Max_B < FreB){Max_B = FreB;}if(Min_B > FreB){Min_B = FreB;}}timerB++;if(timerB >= 30){timerB = 0;if((Max_B - Min_B) > PD){NDB++;}}}
}
这里的原理就类似于时间窗口,一个大小为3s的时间窗口,不断检测进入窗口的值,一边进入,一边释放,类似于队列的效果。这样就可以实时的测出最大值,最小值。注意最小值的初始状态一定要和最大值相同。不然就会一直保持那个值(无法达到检测最小值的效果了)
总体逻辑部分
/*main.c
*//* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
typedef enum
{LCD_SHOW_DATA_PAGE_FREQUENCE, /* 数据界面(频率) */LCD_SHOW_DATA_PAGE_TIMES , /* 数据界面(周期) */LCD_SHOW_PARA_PAGE , /* 参数界面*/LCD_SHOW_COUNT_PAGE , /* 统计界面*/
}LCD_Page;typedef enum
{PARA_NONE = 0,PARA_PD,PARA_PH,PARA_PX,
}Para; //参数/* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
char text[20] ;
int ccl_value_1 = 0, ccl_value_2 = 0; // 获取输入捕获值/* 对于频率部分 fA / fB = FA / FB + PX */
int fA = 0,fB = 0; // 频率值
int PD = 1000,PH = 5000,PX = 0;
float fre_A = 0,fre_B = 0;
float A_times = 0,B_times = 0;int NDA = 0,NDB = 0; /* A/B通道频率突变次数 */
int NHA = 0,NHB = 0; /* A/B通道频率超限次数 */
int PHA[2],PHB[2];
uint16_t A_us = 0,B_us = 0; /* A,B的时间 (us)*/
float A_ms = 0, B_ms = 0; /* A,B的时间 (ms)*/
LCD_Page mypage = LCD_SHOW_DATA_PAGE_FREQUENCE; /* 初始化为 数据界面(频率)*/
LCD_Page Key3page = LCD_SHOW_DATA_PAGE_FREQUENCE;
Para mypara = PARA_NONE; //用于参数选择int PDA[30] = {0},PDB[30] = {0};
//uint8_t i;
int Max_A,Max_B,Min_A,Min_B;int FreA = 0,FreB = 0;
uint16_t timerA = 0,timerB = 0;
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* 频率获取部分
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{/* r40 输出捕获 通道 A */if(htim->Instance == TIM2){fA = 1000000 / (TIM2->CCR1+1);TIM2->CNT = 0;if(fA > 20000) fA = 20000;else if(fA < 400) fA = 400;FreA = fA + PX;ccl_value_1 = 1000000 / FreA;/*这里我根据测评出的结果 推出来,它其实周期的计算是根据最终的频率来计算的而不是我最先测出的fA来进行计算。*/}/* R39 输出捕获 通道 B */if(htim->Instance == TIM16){fB = 1000000 / (TIM16->CCR1+1);TIM16->CNT = 0;if(fB > 20000) fB = 20000;else if(fB < 400) fB = 400;FreB = fB + PX;ccl_value_2 = 1000000 / FreB;}
}/* TIM定时器处理部分 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{/* 处理按键 100hz */if(htim->Instance == TIM6){Keys_StateMachine_Handler(); }/* 处理获取的频率数据 10hz */if(htim->Instance == TIM4){if(timerA == 0){Max_A = FreA;Min_A = FreA;}else{if(Max_A < FreA){Max_A = FreA;}if(Min_A > FreA){Min_A = FreA;}}timerA++;if(timerA >= 30){timerA = 0;if((Max_A - Min_A) > PD){NDA++;}}if(timerB == 0){Max_B = FreB;Min_B = FreB;}else{if(Max_B < FreB){Max_B = FreB;}if(Min_B > FreB){Min_B = FreB;}}timerB++;if(timerB >= 30){timerB = 0;if((Max_B - Min_B) > PD){NDB++;}}}
}/* 按键轮询处理及逻辑优化 */
void Key_While()
{/*------ 页面控制变量 ------*/static uint8_t DataSubPage = 0; // 数据子页面 0-频率 1-次数static uint8_t ChoosePara = 0; // 参数选择 0-PD 1-PH 2-PXstatic uint8_t ChoosePage = 0; // 页面选择 0-数据 1-参数 2-统计/*------ 按键事件处理 ------*/KeyEvent KEYS[4];for(uint8_t i = 0; i < 4; i++) {KEYS[i] = Poll_KeyEvent(i);}/*------ 按键1: 参数加 ------*/if(KEYS[0].type == KEY_EVT_PRESS) {LCD_Clear(Black);if(mypage == LCD_SHOW_PARA_PAGE) // 加入了页面限制{switch(mypara) {case PARA_PD: PD = (PD >= 1000) ? 1000 : (PD + 100); break;case PARA_PH: PH = (PH >= 10000) ? 10000 : (PH + 100); break;case PARA_PX: PX = (PX >= 1000) ? 1000 : (PX + 100); break;}}}/*------ 按键2: 参数减 ------*/else if(KEYS[1].type == KEY_EVT_PRESS) {LCD_Clear(Black);if(mypage == LCD_SHOW_PARA_PAGE){switch(mypara) {case PARA_PD: PD = (PD <= 100) ? 100 : (PD - 100); break;case PARA_PH: PH = (PH <= 1000) ? 1000 : (PH - 100); break;case PARA_PX: PX = (PX <= -1000) ? -1000 : (PX - 100); break;}}}/*------ 按键3: 参数切换/子页面切换 ------*/else if(KEYS[2].type == KEY_EVT_PRESS) {LCD_Clear(Black);switch(Key3page) {/* 参数页切换 */case LCD_SHOW_PARA_PAGE:ChoosePara = (ChoosePara + 1) % 3; // 优化为循环切换switch(ChoosePara) {case 0: mypara = PARA_PD; break;case 1: mypara = PARA_PH; break;case 2: mypara = PARA_PX; break;break;}break;/* 数据页子页面切换 */case LCD_SHOW_DATA_PAGE_FREQUENCE:case LCD_SHOW_DATA_PAGE_TIMES:DataSubPage = (DataSubPage + 1) % 2; // 使用常量更佳LCD_Clear(Black);Key3page = (DataSubPage == 0) ? LCD_SHOW_DATA_PAGE_FREQUENCE : LCD_SHOW_DATA_PAGE_TIMES;mypage = Key3page; // 保持页面同步break;}}/*------ 按键3: 参数切换/子页面切换/统计页长按清零 ------*/else if(KEYS[2].type == KEY_EVT_LONG_PRESS) { // 新增长按处理if(Key3page == LCD_SHOW_COUNT_PAGE) {NDA = NDB = NHA = NHB = 0;LCD_Clear(Black);// 可以添加统计页刷新函数}}/*------ 按键4: 主页面切换 ------*/else if(KEYS[3].type == KEY_EVT_PRESS) {ChoosePage = (ChoosePage + 1) % 3; // 循环切换LCD_Clear(Black);switch(ChoosePage) {case 0: // 数据页DataSubPage = 0;Key3page = LCD_SHOW_DATA_PAGE_FREQUENCE;mypage = LCD_SHOW_DATA_PAGE_FREQUENCE;break;case 1: // 参数页ChoosePara = 0; // 重置参数选择Key3page = LCD_SHOW_PARA_PAGE;mypage = LCD_SHOW_PARA_PAGE;mypara = PARA_PD; // 默认选中PDbreak;case 2: // 统计页Key3page = LCD_SHOW_COUNT_PAGE;mypage = LCD_SHOW_COUNT_PAGE;break;}}
}/*
Log: 显示部分 数据的频率和周期显示会有冲突*/
void LCD_SHOWPAGE(void)
{switch(mypage){case LCD_SHOW_DATA_PAGE_FREQUENCE:LCD_DisplayStringLine(Line1,(uint8_t * )" DATA ");if(FreA<0){LCD_DisplayStringLine(Line3,(uint8_t *)" A=NULL ");}else if(FreA > 1000){fre_A = (float)FreA / 1000.0f;sprintf(text," A=%.2fKHz ",fre_A);LCD_DisplayStringLine(Line3,(uint8_t *)text);}else{sprintf(text," A=%dHz ",FreA);LCD_DisplayStringLine(Line3,(uint8_t *)text); }if(FreB < 0){LCD_DisplayStringLine(Line4,(uint8_t *)" B=NULL");}else if(FreB > 1000){fre_B = (float)FreB / 1000.0f;sprintf(text," B=%.2fKHz ",fre_B);LCD_DisplayStringLine(Line4,(uint8_t *)text); }else{sprintf(text," B=%dHz ",FreB);LCD_DisplayStringLine(Line4,(uint8_t *)text); }break;case LCD_SHOW_DATA_PAGE_TIMES:LCD_DisplayStringLine(Line1,(uint8_t * )" DATA "); /* 对于通道A 这里我进行了修改 通过判断频率是否为负,进而判断是否为NULL */if(FreA < 0){LCD_DisplayStringLine(Line3,(uint8_t *)" A=NULL");}else if(ccl_value_1 > 1000){A_times = (float)ccl_value_1 / 1000.0f;sprintf(text," A=%.2fmS ",A_times);LCD_DisplayStringLine(Line3,(uint8_t *)text);}else{sprintf(text," A=%duS ",ccl_value_1);LCD_DisplayStringLine(Line3,(uint8_t *)text);}// 这里我进行了修改 通过判断频率是否为负,进而判断是否为NULLif(FreB < 0){LCD_DisplayStringLine(Line4,(uint8_t *)" B=NULL");}else if(ccl_value_2 > 1000){B_times = (float)ccl_value_2 / 1000.0f;sprintf(text," B=%.2fmS ",B_times);LCD_DisplayStringLine(Line4,(uint8_t *)text);}else{sprintf(text," B=%duS ",ccl_value_2);LCD_DisplayStringLine(Line4,(uint8_t *)text);}break;case LCD_SHOW_PARA_PAGE:LED_Ctrl(1,0);LCD_DisplayStringLine(Line1,(uint8_t *)" PARA ");sprintf(text," PD=%dHz ",PD);LCD_DisplayStringLine(Line3,(uint8_t *)text);sprintf(text," PH=%dHz ",PH);LCD_DisplayStringLine(Line4,(uint8_t *)text);sprintf(text," PX=%dHz ",PX);LCD_DisplayStringLine(Line5,(uint8_t *)text); break;case LCD_SHOW_COUNT_PAGE:LED_Ctrl(1,0);LCD_DisplayStringLine(Line1,(uint8_t *)" RECD ");sprintf(text," NDA=%d ",NDA);LCD_DisplayStringLine(Line3,(uint8_t *)text);sprintf(text," NDB=%d ",NDB);LCD_DisplayStringLine(Line4,(uint8_t *)text);sprintf(text," NHA=%d ",NHA);LCD_DisplayStringLine(Line5,(uint8_t *)text);sprintf(text," NHB=%d ",NHB);LCD_DisplayStringLine(Line6,(uint8_t *)text);break;default:break;}if(NDA >= 3 || NDB >= 3) {LED_Ctrl(8,1);}else{LED_Ctrl(8,0);}if(mypage == LCD_SHOW_DATA_PAGE_FREQUENCE || mypage == LCD_SHOW_DATA_PAGE_TIMES){LED_Ctrl(1,1);}else{LED_Ctrl(1,0);}
}void PH_Beyond(void)
{/*获取最新的值*/PHA[1] = FreA;PHB[1] = FreB;if(PHA[1] > PH && PHA[0] < PH){NHA++;}if(PHB[1] > PH && PHB[0] < PH){NHB++;}if(PHA[1] > PH) {LED_Ctrl(2,1);}else{LED_Ctrl(2,0);}if(PHB[1] > PH){LED_Ctrl(3,1);}else{LED_Ctrl(3,0);}PHA[0] = PHA[1];PHB[0] = PHB[1];
}/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM2_Init();MX_TIM4_Init();MX_TIM6_Init();MX_TIM3_Init();MX_TIM8_Init();MX_TIM16_Init();/* USER CODE BEGIN 2 */LCD_Init();LCD_Clear(Black);LCD_SetBackColor(Black);LCD_SetTextColor(White);HAL_TIM_Base_Start_IT(&htim6);HAL_TIM_Base_Start_IT(&htim4);HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);HAL_TIM_IC_Start_IT(&htim16,TIM_CHANNEL_1);LED_CLOSEALL(); /* 关闭所有的LED *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){Key_While();LCD_SHOWPAGE();PH_Beyond();/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
以上就是我的全部内容了,若有疑问请评论区提出。
我写的比较潦草,最近要去刷一下题了,所以大体写的很快。
相关文章:
蓝桥杯嵌入式第15届真题-个人理解+解析
个人吐槽 #因为最近蓝桥杯快要开始了,我舍不得米白费了,所以就认真刷刷模拟题,但是我感觉真题会更好,所以就看了一下上届的真题。不过它是真的长,我看着就头晕,但是还是把几个模块认真分析了一下就还是很容…...
数据库系统概述 | 第二章课后习题答案
本文为数据库系统概论(第五版)【高等教育出版社】部分课后答案 如有错误,望指正 👻 习题 👻 答案...
深入解析CPU主要参数:选购与性能评估指南
引言 中央处理器(CPU)作为计算机的"大脑",其性能直接决定了整机的运算能力和响应速度。无论是组装新电脑、升级旧系统还是选购笔记本电脑,理解CPU的关键参数都至关重要。本文将从技术角度全面解析CPU的各项主要参数&am…...
Lettuce与Springboot集成使用
一、Lettuce核心优势与Spring Boot集成背景 Lettuce特性 基于Netty的非阻塞I/O模型,支持同步/异步/响应式编程线程安全:共享单连接实现多线程并发操作,性能衰减低原生支持Redis集群、哨兵、主从架构,自动重连机制保障高可用Spring…...
【Kafka基础】ZooKeeper在Kafka中的核心作用:分布式系统中枢神经系统
在分布式系统的世界里,协调和管理多个节点间的状态是一项复杂而关键的任务。Apache Kafka作为一款高性能的分布式消息系统,其设计哲学是"专为单一目的而优化"——即高效处理消息流。为了实现这一目标,Kafka选择将集群协调管理的重任…...
专业的情商测评工具:EQ-i在线测评系统
专业的情商测评工具:EQ-i在线测评系统 基于巴昂情商量表的专业情商评估工具,帮助您更好地了解自己的情商水平。 什么是EQ-i? EQ-i(Emotional Quotient Inventory)是由Reuven Bar-On开发的情商量表,是国际上…...
Ubuntu安装Podman教程
1、先修改apt源为阿里源加速 备份原文件: sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup 修改源配置: vim sources.list删除里面全部内容后,粘贴阿里源: deb http://mirrors.aliyun.com/ubuntu/ focal main re…...
7.训练篇5-毕设
使用23w张数据集-vit-打算30轮-内存崩了-改为batch_size 8 我准备用23w张数据集,太大了,这个用不了,所以 是否保留 .stack() 加载所有图片?情况建议✅ 小数据集(<2w张,图像小)想加快速度可…...
java数据结构-哈希表
什么是哈希表 最理想的搜索方法 , 即就是在查找某元素时 , 不进行任何比较的操作 , 一次直接查找到需要搜索的元素 , 可以达到这种要求的方法就是哈希表. 哈希表就是通过构造一种存储结构 , 通过某种函数使元素存储的位置与其关键码位形成一 一映射的关系 , 这样在查找元素的时…...
Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV
Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV Author: Once Day Date: 2025年4月4日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可参考专栏: Linux实…...
SpringBoot配置文件多环境开发
目录 一、设置临时属性的几种方法 1.启动jar包时,设置临时属性 2.idea配置临时属性 3.启动类中创建数组指定临时属性 二、多环境开发 1.包含模式 2.分组模式 三、配置文件的优先级 1.bootstrap 文件优先: 2.特定配置文件优先 3.文件夹位置优…...
解锁健康密码:拥抱活力养生生活
在追求高品质生活的今天,健康养生成为了人们关注的焦点。它不仅关乎当下的生活质量,更是对未来的有力投资。 合理的饮食是健康养生的基石。一日三餐,应遵循 “五谷为养,五果为助,五畜为益,五菜为充” 的原则…...
手动将ModelScope的模型下载到本地
一、ModelScope 介绍 ModelScope 官网地址: https://www.modelscope.cn/home 模型库地址:https://www.modelscope.cn/models 文档中心:https://www.modelscope.cn/docs/home ModelScope旨在打造下一代开源的模型即服务共享平台,为…...
【Git】“warning: LF will be replaced by CRLF”的解决办法
一、原因分析 不同操作系统的换行符标准不同: • Windows:使用 CRLF(\r\n)表示换行; • Linux/Mac:使用 LF(\n)表示换行 Git 检测到本地文件的换行符与仓库设置或目标平台不兼容时…...
Linux常用基础命令应用
目录 一、文件与目录管理 1. 基础导航与查看 2. 文件操作核心命令 二、文本处理与日志分析 1. 查看与过滤 2. 组合命令与管道 三、系统管理与权限控制 1. 进程与资源监控 2. 权限与用户管理 四、网络与远程操作 1. …...
C++11可变参数模板单例模式
单例模式 该示例代码采用C11标准,解决以下问题: 通过类模板函数实现不同类型单例;单例类构造函数支持不同的个数;消除代码重复 示例代码 .h文件如下: //C11Singleton.h文件 #pragma oncetemplate <typename T&…...
JVM 有哪些垃圾回收器
垃圾收集算法 标记-复制算法(Copying): 将可用内存按容量划分为两个区域,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面, 然后再把已使用过的内存空间一次清理掉。 标记-清除算法(Mark-Sweep): 算法分为“标记” 和“清除”两个…...
6. 链式结构(Chain)的奥秘:从LLMChain到RouterChain
引言:从“单兵作战”到“流水线革命” 2023年某电商平台客服系统因处理复杂咨询需手动串联多个AI模块,平均响应时间长达12秒。引入LangChain链式架构后,工单处理速度提升8倍,错误率下降45%。本文将深入解析链式编程范式ÿ…...
TypeScript语言的操作系统原理
TypeScript语言的操作系统原理 引言 操作系统是计算机系统中最重要的组成部分之一,它为应用程序提供了一个运行环境,并管理着计算机硬件和软件资源。随着编程语言的发展,特别是TypeScript的流行,许多开发者开始探索将这种强类型…...
时间序列入门
时间序列入门 第一章 时间序列概述1.1 时间序列简介1.1.1 时间序列定义1.1.2 时间序列分量1.1.3 时间序列分类 第二章 时间序列绘图2.1 单变量时序绘制2.2 多变量时序绘制 第一章 时间序列概述 1.1 时间序列简介 1.1.1 时间序列定义 在进行时间序列之前,需要学习…...
VirtualBox安装FnOS
1.下载FnOS镜像 下载网址: https://www.fnnas.com/2.创建虚拟机 虚拟机配置如图所示(注意操作系统类型和网卡配置) (注意启动顺序) 3.启动虚拟机 网卡类型选择桥接的Virtual Adapter 如果没有IP地址或者IP地址无法…...
函数栈帧的创建与销毁
函数栈帧的创建与销毁 函数栈帧简介认识寄存器解析函数栈帧的创建与销毁 函数栈帧简介 我们在编程的过程中经常会听见函数栈帧这个词汇,那到底什么是函数栈帧呢?接下来就为大家解答一下,我们都知道,一个函数的创建是需要去开辟空…...
Scheme语言的算法
Scheme语言的算法探索 引言 Scheme是一种以表达式为基础的编程语言,属于Lisp家族,因其简洁、灵活的语法而受到广泛关注。Scheme不仅适合教学,还被用于实际应用开发和研究。本文将深入探讨Scheme语言的算法,包括其基本特性、常用…...
[C++面试] new、delete相关面试点
一、入门 1、说说new与malloc的基本用途 int* p1 (int*)malloc(sizeof(int)); // C风格 int* p2 new int(10); // C风格,初始化为10 new 是 C 中的运算符,用于在堆上动态分配内存并调用对象的构造函数,会自动计算所需内存…...
(回滚莫队)洛谷 P10268 符卡对决 题解
居然还没调出来?感觉是数据类型的问题,真是吓人。先把思路写一下吧。 题意 灵梦一共有 n n n 张符卡,每张卡都有一个能力值,对于第 i i i 张卡,它的能力值为 a i a_i ai,现在她想从中选出两张符卡并…...
C语言复习笔记--指针(3)
接上篇文章C语言复习笔记--指针(2)-CSDN博客我们继续进行指针的复习. 二级指针 指针变量也是变量,是变量就有地址,那指针变量的地址取出来后要存在在什么变量中呢?这就是⼆级指针. ⼆级指针的运算见下: 指针数组 指针数组概念 既然要联系数组和指针就涉…...
Fastjson 处理 JSON 生成与解析指南
Fastjson 是阿里巴巴开源的高性能 JSON 库,适用于 Java 对象的序列化(生成 JSON)和反序列化(解析 JSON)。以下是详细使用指南: 1. 添加依赖 <dependency><groupId>com.alibaba</groupId>…...
深度学习数据集划分比例多少合适
在机器学习和深度学习中,测试集的划分比例需要根据数据量、任务类型和领域需求灵活调整。 1. 常规划分比例 通用场景 训练集 : 验证集 : 测试集 60% : 20% : 20% 适用于大多数中等规模数据集(如数万到数十万样本),平衡了训练数…...
查询当前用户的购物车和清空购物车
业务需求: 在小程序用户端购物车页面能查到当前用户的所有菜品或者套餐 代码实现 controller层 GetMapping("/list")public Result<List<ShoppingCart>> list(){List<ShoppingCart> list shoppingCartService.shopShoppingCart();r…...
大模型如何引爆餐饮与电商行业变革
大模型如何引爆餐饮与电商行业变革? 一、时代背景:大模型重构产业逻辑的底层动力 1. 技术跃迁催生效率革命 2025年,大模型技术迎来"普惠临界点"。李开复在中关村论坛指出,大模型推理成本每年降低10倍,使得…...
【MySQL】01.MySQL环境安装
注意:在MYSQL的安装与卸载中,需要使用root用户进行。 一、卸载不必要的环境 • 查看是否有运行的服务 [rootVM-24-10-centos etc]# ps axj |grep mysql1 22030 22029 22029 ? -1 Sl 27 0:00 /usr/sbin/mysqld --daemonize --pid-fi…...
java 匿名内部类 和 Lambda 表达式
java 匿名内部类 和 Lambda 表达式 一、匿名内部类1.1说明1.2 匿名内部类的作用1.3 特点1.4 接口的正常使用情况(抽象类同理)1.5 通过局部内部类使用接口(抽象类同理)1.6 通过匿名内部类使用接口(抽象类同理࿰…...
Linux系统调用编程
进程和线程 进程是操作系统资源分配的基本单位,拥有独立的地址空间、内存、文件描述符等资源,进程间相互隔离。每个进程由程序代码、数据段和进程控制块(PCB)组成,PCB记录了进程状态、资源分配等信息。 线程是…...
Redis 数据类型详解
Redis 数据类型详解 Redis 是一个高性能的键值存储系统,支持多种数据类型,每种类型都有其特定的使用场景和操作命令。以下是 Redis 主要数据类型的详细介绍: 一、基本数据类型 1. String(字符串) 特点:…...
orangepi zero烧录及SSH联网
下载对应版本的armbian镜像 armbian的默认用户root,默认密码:1234 下载烧录工具win32diskimager https://sourceforge.net/projects/win32diskimager/files/Archive/ 插入16G以上TF卡,使用win32diskimager烧录armbian镜像 烧录完毕后用l…...
七均线策略思路
一种基于移动平均线的交易策略,具体如下: 1. 移动平均线计算: 计算了六个不同周期的收盘价移动平均值,分别为MA5、MA10、MA20、MA30、MA40和MA60。 2. 买入条件(BK): 当满足以下所有条件时执行买…...
【python脚本】基于pyautogui的python脚本
一、什么是自动化 自动化是指使用技术手段模拟人工,执行重复性任务。准确率100%,高于人工。 自动化应用场景: 自动化测试自动化运维自动化办公自动化游戏 二、pyautogui的使用 先使用 pip install pyautogui 指令安装这个第三方库 2.1 …...
人工智能时代人才培养的变革路径:模式创新、能力重塑与认证赋能
在科技日新月异的今天,人工智能(AI)已成为推动社会进步与经济发展的核心力量。从自动驾驶到医疗诊断,从金融分析到教育创新,AI的触角已延伸至人类生活的每一个角落。这一变革不仅重塑了产业格局,更对人才培养提出了前所未有的挑战与机遇。在人工智能时代,如何培养适应未…...
xpath定位
一、路径符号核心区别(表格速查) 符号名称作用范围典型使用场景性能影响/单斜杠./ 相对路径直接子级, /绝对路劲-根路径精确层级定位高效//双斜杠//当前元素下开始查找,可以跨嵌套层模糊层级/跨嵌套定位较低效 一、XPath基础定位类型&#…...
Python列表(List)深度解析
列表(List)是Python中最基础且强大的数据结构之一,但它的底层实现和特性远比表面看起来复杂。本文将深入探讨列表的各个方面。 1. 列表基础特性 1.1 可变序列类型 lst [1, 2, 3] lst[1] 20 # 可变性1.2 异构容器 mixed [1, "hello", 3.14, [1, 2]…...
Mybatis---入门
1. 什么是MyBatis? MyBatis是⼀款优秀的 持久层 框架,⽤于简化JDBC的开发。 MyBatis本是 Apache的⼀个开源项⽬iBatis,2010年这个项⽬由apache迁移到了google code,并且改名为MyBatis 。2013年11⽉迁移到Github. 官⽹:MyBa…...
FPGA--HDLBits网站练习
目录 用状态机编写一个 LED流水灯代码 CPLD和FPGA芯片 CPLD(复杂可编程逻辑器件) FPGA(现场可编程门阵列) Verilog练习 基本 向量 用状态机编写一个 LED流水灯代码 往期作业已完成,博客地址: FPGA…...
《Linux内存管理:实验驱动的深度探索》【附录】【实验环境搭建 4】【Qemu 如何模拟numa架构】
我们在学习 linux 内核时,会涉及到很多 numa 的知识,那我们该如何在 qemu 中模拟这种情况,来配合我们的学习呢? 我们该如何模拟 如下的 numa 架构 Qemu 模拟 NUMA 架构 -M virt,gic-version3,virtualizationon,typevirt \ -cp…...
如何分析 jstat 统计来定位 GC?
全文目录: 开篇语前言摘要概述jstat 的核心命令与参数详解基本命令格式示例 jstat 输出解读主要字段含义 典型 GC 问题分析案例案例 1:年轻代 GC 过于频繁案例 2:老年代发生频繁 Full GC案例 3:元空间(Metaspace&#…...
Day51 | 3. 无重复字符的最长子串、12. 整数转罗马数字、49. 字母异位词分组、73. 矩阵置零
3. 无重复字符的最长子串 题目链接:3. 无重复字符的最长子串 - 力扣(LeetCode) 题目难度:中等 代码: class Solution {public int lengthOfLongestSubstring(String s) {Set<Character> setnew HashSet<&…...
【Linux系统编程】进程概念,进程状态
目录 一,操作系统(Operator System) 1-1概念 1-2设计操作系统的目的 1-3核心功能 1-4系统调用和库函数概念 二,进程(Process) 2-1进程概念与基本操作 2-2task_struct结构体内容 2-3查看进程 2-4通…...
第二十八章:Python可视化图表扩展-和弦图、旭日图、六边形箱图、桑基图和主题流图
一、引言 在数据可视化领域,除了常见的折线图、柱状图和散点图,还有一些高级图表类型可以帮助我们更直观地展示复杂数据关系。本文将介绍五种扩展图表:和弦图、旭日图、六边形箱图、桑基图和主题流图。这些图表在展示数据关系、层次结构和流量…...
深入理解C++引用:从基础到现代编程实践
一、引用的本质与基本特性 1.1 引用定义 引用是为现有变量创建的别名,通过&符号声明。其核心特点: 必须初始化且不能重新绑定 与被引用变量共享内存地址 无独立存储空间(编译器实现) 类型必须严格匹配 int value 42; in…...
OpenVLA-OFT——微调VLA的三大关键设计:支持动作分块的并行解码、连续动作表示以及L1回归目标
前言 25年3.26日,这是一个值得纪念的日子,这一天,我司「七月在线」的定位正式升级为了:具身智能的场景落地与定制开发商 ,后续则从定制开发 逐步过渡到 标准产品化 比如25年q2起,在定制开发之外࿰…...
linux3 mkdir rmdir rm cp touch ls -d /*/
Linux 系统的初始目录结构遵循 FHS(Filesystem Hierarchy Standard,文件系统层次标准),定义了每个目录的核心功能和存储内容。以下是 Linux 系统初始安装后的主要目录及其作用: 1. 核心系统目录 目录用途典型内容示例…...