【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解

这篇具有很好参考价值的文章主要介绍了【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

原题展示

【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解

【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解
【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解

【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解

【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解
【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解
【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解

原题分析

  本套试题较为常规,试题主要需要使用的模块有:LCD、LED、按键、定时器输入捕获功能、采集光照传感器的值以及串口,其中最重要的是串口收发数据以及定时器的输入捕获功能,其余的各个部分还算比较常规、比较简单。下面咱就一起来看看这届赛题的题解吧!🤤🤤🤤

详细题解

LED相关

    通过查询产品手册知,LED的引脚为PC8~PC15,外加锁存器74HC573需要用到的引脚PD2。(由于题目要求除LED1、LED2、LED3、LED4外的其他LED都处于熄灭状态,此处特意将所有的LED都初始化以便于管理其他的LED灯)
CubeMX配置:
【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解
代码样例
    由于G431的所有LED都跟锁存器74HC573连接,因此每次更改LED状态时都需要先打开锁存器,写入数据后再关闭锁存器。

/*****************************************************
* 函数功能:改变所有LED的状态
* 函数参数:
*			char LEDSTATE: 0-表示关闭 1-表示打开
* 函数返回值:无
******************************************************/
void changeAllLedByStateNumber(char LEDSTATE)
{
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
					|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
	//打开锁存器    准备写入数据
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	//关闭锁存器 锁存器的作用为 使得锁存器输出端的电平一直维持在一个固定的状态
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

/*****************************************************
* 函数功能:根据LED的位置打开或者是关闭LED
* 函数参数:
*			uint16_t LEDLOCATION:需要操作LED的位置
*			char LEDSTATE: 0-表示关闭 1-表示打开
* 函数返回值:无
******************************************************/
void changeLedStateByLocation(uint16_t LEDLOCATION,char LEDSTATE)
{
	HAL_GPIO_WritePin(GPIOC,LEDLOCATION,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

  试题要求的LED显示其条件都比较单一,在满足点亮条件时直接点亮,否则,就直接熄灭即可:

/**************************************************
* 函数功能:LED工作函数
* 函数参数:无
* 函数返回值:无
***************************************************/
void ledPro(void)
{
	// a角度变化大于其参数
	if(data.ax > para.Pax)
		changeLedStateByLocation(LED1,1);
	else
		changeLedStateByLocation(LED1,0);
	// b角度变化大于其参数
	if(data.bx > para.Pbx)
		changeLedStateByLocation(LED2,1);
	else
		changeLedStateByLocation(LED2,0);
	// f频率大于其参数
	if(data.f > para.Pf)
		changeLedStateByLocation(LED3,1);
	else
		changeLedStateByLocation(LED3,0);
	// 处于模式A
	if(data.mod == 'A')
		changeLedStateByLocation(LED4,1);
	else
		changeLedStateByLocation(LED4,0);
	// 角度a-b>10
	if(data.a - data.b > 10)
		changeLedStateByLocation(LED5,1);
	else
		changeLedStateByLocation(LED5,0);
}

LCD相关

样例代码
  由于LCD的相关代码在官方给的比赛资源数据包中存在,因此,可以直接调用资源包中的.c、.h文件来完成LCD的相关初始化以及显示。这是一个简单的LCD初始化函数,其功能是将LCD显示屏初始化为一个背景色为黑色、字体颜色为白色的屏幕,具体代码如下:

/******************************************************************************
* 函数功能:LCD初始化
* 函数参数:无
* 函数返回值:无
*******************************************************************************/
void lcdInit(void)
{
	//HAL库的初始化
	LCD_Init();
	//设置LCD的背景色
	LCD_Clear(Black);
	//设置LCD字体颜色
	LCD_SetTextColor(White);
	//设置LCD字体的背景色
	LCD_SetBackColor(Black);
}

  在显示时,可以借助于sprintf()函数将需要显示的数据格式成一个字符串,再在LCD上显示这个字符串。

	char temp[20];
	sprintf(temp,"    mode:%c       ",data.mod);
	LCD_DisplayStringLine(Line8,(u8*)temp);

  为了操作LED与LCD显示方便,不让其相互干扰,小编这里对LCD进行了部分源码改写,使得每次LCD显示时不改变LED的显示状态,具体的方法各位可以点击查看【蓝桥杯】一文解决蓝桥杯嵌入式开发板(STM32G431RBT6)LCD与LED显示冲突问题,并讲述LCD翻转显示。

按键相关

    通过查询产品手册知,开发板上的四个按键引脚为PB0~PB2、PA0
CubeMX配置
【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解
样例代码
  由于主板上的按键数量较少,因此小编这里的按键读取操作相对简单粗暴,其实现步骤为:

  • 步骤一:判断按键是否按下以及按键锁是否打开,在两者同时满足的情况下进入下一步;
  • 步骤二:关闭按键锁并且延时10ms,实现按键的延时消抖;
  • 步骤三:再次读取每个按键的值,判断按键按下的位置;
  • 步骤四:读取每个按键的状态,如果都处于松开状态就打开按键锁;

  具体代码实现:

/*********************************************
 * 函数功能:按键扫描 含按键消抖 无长按短按设计
 * 函数参数:无
 * 函数返回值:按键的位置
 *            返回值说明:B1-1 B2-2 B3-3 B4-4
*********************************************/
unsigned char scanKey(void)
{
	//按键锁
	static unsigned char keyLock = 1;
    //记录按键消抖时间
    // static uint16_t keyCount = 0;

	//按键按下
    if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET
      || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET || HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET) 
      && keyLock == 1){
        //给按键上锁 避免多次触发按键
        keyLock = 0;
        
        //按键消抖 这里最好不要使用延时函数进行消抖 会影响系统的实时性
        // if(++keyCount % 10 < 5) return 0;
        // if(HAL_GetTick()%15 < 10) return 0;
        HAL_Delay(10);

        //按键B1
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET){
            return 1;
        }
        //按键B2
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET){
            return 2;
        }
        //按键B3
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET){
            return 3;
        }
        //按键B4
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET){
            return 4;
        }
    }
    //按键松开
    if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == SET && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == SET
      && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == SET && HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == SET) 
      && keyLock == 0){
        //开锁
        keyLock = 1;
    }
    return 0;
}

  当然了,在本届试题中按键逻辑不仅仅是判断按键是否按下这么简单,主板上的每个按键都具有其特有的功能,下面就一起来看看吧!😉😉😉
  每个按键的功能:

  • 按键B1:具有切换显示界面的功能;
  • 按键B2:具有改变角度变化参数的功能;
  • 按键B3:具有改变频率参数以及频率采集触发模式的功能;
  • 按键B4:具有在按键触发模式下刷新角度数据采集的功能;

  样例代码实现:

/* -------------------------------- begin  -------------------------------- */
/**
  * @Name    sysInit
  * @brief   按键逻辑函数
  * @param   None
  * @retval  None
  * @author  黑心萝卜三条杠
  * @Data    2023-03-24
 **/
/* -------------------------------- end -------------------------------- */
static void keyPro(void)
{
	static char mod[2] = {0,0};
	unsigned char keyValue = scanKey();
	switch(keyValue){
		case 1: 
			// 切换LCD显示界面
			mod[0] ^= 1;
		    break;
		case 2: 
			// 增加角度变化参数
			if(mod[0] == 1)
			{
				para.Pax += 10;
				para.Pbx += 10;
				if(para.Pax == 70)
					para.Pax = 10;
				if(para.Pbx == 70)
					para.Pbx = 10;
			}
			break;
		case 3: 
			// 增加频率参数
			if(mod[0] == 1)
			{
				para.Pf += 1000;
				if(para.Pf == 11000)
					para.Pf = 1000;
			}
			// 切换触发模式
			else if(mod[0] == 0)
			{
				mod[1] ^= 1;
				data.mod = 'A' + mod[1];
			}
			break;
		case 4: 
			// 数据刷新
			if(data.mod == 'A')
				falg = 1;
			break;
		default: break;
	}
	
	// LCD显示
	lcdDisplay(mod[0]);
}

串口相关

CubeMX配置
    在写本届试题的串口接收PC数据时需要注意:PC发送不定长的数据。这时就需要按位储存PC发送过来的数据,每次处理完成后就清空历史数据。
CubeMX配置
    配置时一定一定记得改引脚!!!【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解
样例代码
    本程序中小编使用的是中断接收PC发送的数据其函数原型为:

// 函数原型:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
// 参数解析:
UART_HandleTypeDef *huart:串口通道;
uint8_t *pData:存放数据的buff;
uint16_t Size:一次接收数据的长度

    在使用时还需要使用该函数“中断初始化”,否则不能够进入中断接收数据;
  下面就是一个串口接收不定长数据的demo:

/**********************************************串口相关************************************/

//定义一个串口信息的结构
uint8_t ucRxbuff[4];
uint8_t _ucRxbuff[1],lenBuff = 0;

/***使用HAL_UART_Receive_IT中断接收数据 每次接收完成数据后就会执行该函数***/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{	
	if(huart->Instance == USART1){
		ucRxbuff[lenBuff++%3] = _ucRxbuff[0];
		// 重新使能中断		
		HAL_UART_Receive_IT(huart,(uint8_t *)&_ucRxbuff,sizeof(_ucRxbuff)); 
	}
}

  虽然说是不定长数据,但是其能够接收的数据最长为3个字节。由于题中串口接收的有效数据最长为3字节,因此此处小编也将其设置成3字节。
  题中要求串口功能不仅仅是接收数据这么简单,其还需要能够解析串口接收的数据,并且以此为指令将合适的结果发送给PC。下面就是小编写的一个简单的数据处理demo:

/* -------------------------------- begin  -------------------------------- */
/**
  * @Name    usartPro
  * @brief   串口处理函数
  * @param   None
  * @retval  None
  * @author  黑心萝卜三条杠
  * @Data    2023-03-24
 **/
/* -------------------------------- end -------------------------------- */
static void usartPro(void)
{
	char temp [40];
	
	// 去除一些不非正常情况的结果
	if(strlen((char*)ucRxbuff) == 0)
		return ;
	
	// 查询当前数据
	if(strcmp((char*)ucRxbuff,"a?") == 0)
		sprintf(temp,"a:%1.f\r\n",data.a);
	else if(strcmp((char*)ucRxbuff,"b?") == 0)
		sprintf(temp,"b:%1.f\r\n",data.b);
	// 查询历史数据(按时间顺序)
	else if(ucRxbuff[0]=='a' && ucRxbuff[1]=='a' && ucRxbuff[2]=='?')
		sprintf(temp,"aa:%1.f-%1.f-%1.f-%1.f-%1.f\r\n",oldData[0][0],oldData[0][1],oldData[0][2],oldData[0][3],oldData[0][4]);
	else if(ucRxbuff[0]=='b' && ucRxbuff[1]=='b' && ucRxbuff[2]=='?')
		sprintf(temp,"bb:%1.f-%1.f-%1.f-%1.f-%1.f\r\n",oldData[1][0],oldData[1][1],oldData[1][2],oldData[1][3],oldData[1][4]);
	// 查询历史数据(按大小)
	else if(ucRxbuff[0]=='q' && ucRxbuff[1]=='a' && ucRxbuff[2]=='?')
	{
		sort(oldData[0],5,sortData);
		sprintf(temp,"aa:%1.f-%1.f-%1.f-%1.f-%1.f\r\n",sortData[0],sortData[1],sortData[2],sortData[3],sortData[4]);
	}
	else if(ucRxbuff[0]=='q' && ucRxbuff[1]=='b' && ucRxbuff[2]=='?')
	{
		sort(oldData[1],5,sortData);
		sprintf(temp,"bb:%1.f-%1.f-%1.f-%1.f-%1.f\r\n",sortData[0],sortData[1],sortData[2],sortData[3],sortData[4]);
	}
	// 接收到其他数据
	else
		sprintf(temp,"error\t\n");
	// 串口数据发送
	HAL_UART_Transmit(&huart1,(uint8_t*)temp,sizeof(char)*strlen(temp),20);
	memset(ucRxbuff,0,sizeof(ucRxbuff));
	lenBuff = 0;                                                     
}

光照传感器相关

CubeMX配置
【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解

  光照传感器的数据获取基于ADC数据获取,因此这里只要合理初始化ADC及其通道即可。这里小编在启用ADC通道后只修改了一个地方——采样周期。

样例代码
  这里的ADC获取函数没有什么高大上的,与寻常的ADC获取函数一样:

// 外部声明的ADC2
extern ADC_HandleTypeDef hadc2;
/* -------------------------------- begin  -------------------------------- */
/**
  * @Name    getADC
  * @brief   获取光敏电阻的阻值
  * @param   None
  * @retval  float:转换后的值
  * @author  黑心萝卜三条杠
  * @Data    2023-03-25
 **/
/* -------------------------------- end -------------------------------- */
float getR47ADC(void)
{
	uint16_t temp = 0;
	
    HAL_ADC_Start(&hadc2);
    temp = HAL_ADC_GetValue(&hadc2);
	
	return temp/(4096 - temp) * 10;
}

定时器相关

CubeMX配置
  本届试题定时器的主要功能为输入捕获,要不就是捕获频率,要不就是捕获其占空比。
  捕获频率时的配置:
【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解

  利用输入捕获功能计算占空比时的配置:
【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解
  这里需要注意:利用输入捕获功能计算频率与占空比其触发方式上是不一样的,前者是单沿触发,后者是双沿触发。由于PA6、PA7两个引脚的捕获功能配置上是一样的,因此此处只放置一个配置示图。

样例代码

  小编在网上看到了某些大佬测量占空比时只测量了两次,即一次上升沿与一次下降沿,然后就可以得到PWM的占空比。但是这里小编使用的是测量三次,因为小编认为测量两次并不能够计算出占空比,详细的解析各位可以查看这篇文章使用定时器输入捕获功能测量占空比及频率。(当然了这是我个人的观点,如果大家认为有啥不对之处,欢迎大家留言或私信😜😜😜)

  那么这里小编就不过多赘述啦,上面的文章写的确实很清楚了。😁😁😁哦对了,定时器的输入捕获需要自己使用函数HAL_TIM_IC_Start_IT(&htim16,TIM_CHANNEL_1);打开的嗷。😉😉😉

/**********************************************定时器输入捕获相关************************************/

//记录定时器上升沿 下降沿计数值
struct date
{
	//记录计数值  偶数为上升沿 计数的下降沿
	uint32_t count[3];
	//用于记录当前应该是上升沿触发还是下将沿触发
	char edge_flag;
	//记录数据采集数量
	int number;
};

// 保存定时器输入捕获时需要的相关变量以及最终结果
struct date time16Data,time3Data;
// 保存PWM的数据值 RP[0]-PWM1的值  RP[1]-PWM2的值
uint32_t  RP[4] = {0,0,0,0};

// 定时器的回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	// 保存TIMx_CCR的值
	uint32_t  cclValue = 0;
	// 定时器16时执行该段
	if(htim->Instance == TIM16)
	{		
		//本次为上升沿触发
		if(time16Data.edge_flag%2==0)
		{
			//获取本次上升沿计数值
			time16Data.count[time16Data.number++] = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
			//采集一次下降沿、两次上升沿数据完成
			if(time16Data.number == 3)
			{
				RP[0] = (((time16Data.count[1]-time16Data.count[0])*1.0)/(time16Data.count[2]-time16Data.count[0]))*100;
				//将定时器的计数值设置成0
				__HAL_TIM_SetCounter(htim,0);
				__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); 
				//重新开启定时器
				HAL_TIM_IC_Start_IT(htim,TIM_CHANNEL_1);
				time16Data.edge_flag = 0;
				time16Data.number = 0;
			}
			else
			{				
				__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); 
				//将下次触发方式设置为下降沿触发
				time16Data.edge_flag += 1;
			}
		}
		//下降沿触发
		else
		{
			//读取本轮的计数值
			time16Data.count[time16Data.number++] = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
			//修改触发方式为上升沿触发
			__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); 
			time16Data.edge_flag += 1;				
		}	
		
		HAL_TIM_IC_Start_IT(&htim16, TIM_CHANNEL_1);
	}
	// 定时器3时执行该段
	if(htim->Instance == TIM3)
	{		
		//本次为上升沿触发
		if(time3Data.edge_flag%2==0)
		{
			//获取本次上升沿计数值
			time3Data.count[time3Data.number++] = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);
			//采集一次下降沿、两次上升沿数据完成
			if(time3Data.number == 3)
			{
				RP[1] = (((time3Data.count[1]-time3Data.count[0])*1.0)/(time3Data.count[2]-time3Data.count[0]))*100;
				//将定时器的计数值设置成0
				__HAL_TIM_SetCounter(htim,0);
				__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING); 
				//重新开启定时器
				HAL_TIM_IC_Start_IT(htim,TIM_CHANNEL_2);
				time3Data.edge_flag = 0;
				time3Data.number = 0;
			}
			else
			{				
				__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_FALLING); 
				//将下次触发方式设置为下降沿触发
				time3Data.edge_flag += 1;
			}
		}
		//下降沿触发
		else
		{
			//读取本轮的计数值
			time3Data.count[time3Data.number++] = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);
			//修改触发方式为上升沿触发
			__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING); 
			time3Data.edge_flag += 1;				
		}	
		HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);
	}
	// 定时器2时执行该段
	if(htim->Instance == TIM2)
	{
		cclValue = __HAL_TIM_GET_COUNTER(&htim2);
		__HAL_TIM_SetCounter(&htim2, 0);
		RP[2] = 1000000 / cclValue;
		HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
	}
	
}

(大家看到这个中断处理函数是不是非常不爽,非常多的重复代码😂😂😂,主要是小编暂时还没想到更好的书写方式。)

角度数据刷新函数

  这里的刷新函数高度符合题意,并且代码逻辑简单,这里不做过多描述,大家可以直接看代码:

/* -------------------------------- begin  -------------------------------- */
/**
  * @Name    getData
  * @brief   数据更新函数
  * @param   None
  * @retval  None
  * @author  黑心萝卜三条杠
  * @Data    2023-03-24
 **/
/* -------------------------------- end -------------------------------- */
static void getData(void)
{
	int flagPro = 0;
	float temp = 0;
	// 更新频率
	data.f = RP[2];
	// 触发模式
	if(data.mod == 'A' && falg == 1)
	{
		falg = 0;
		flagPro = 1;
	}
	// 环境光照值触发
	else if(data.mod == 'B' )
	{
		lightData[0] = getR47ADC();
		if(lightData[0]-lightData[1] > 10)
			flagPro = 1;
		// 保存本次光照值
		lightData[1] = lightData[0];
	}
	// 采集了数据 需要更新角度值
	if(flagPro == 1)
	{
		// 根据折线图转换角度a
		if(0 < RP[0] && RP[0] <= 10)
			temp = 0;
		else if(10 < RP[0] && RP[0] < 90)
			temp = (RP[0]-10)*9*1.0/4;
		else
			temp = 180;
		// 求与上次角度的差值
		data.ax = myAbs(temp,data.a);
		// 保存本次数据
		data.a = temp;
		// 更新历史数据
		oldData[0][oldCount[0]] = temp;
		if(++oldCount[0] == 5) oldCount[0] = 0;
		// 根据折线图转换角度b
		if(0 < RP[1] && RP[1] <= 10)
			temp = 0;
		else if(10 < RP[1] && RP[1] < 90)
			temp = (RP[1]-10)*9*1.0/8;
		else
			temp = 90;
		// 求与上次角度的差值
		data.bx = myAbs(temp,data.b);
		// 保存本次数据
		data.b = temp;
		// 更新历史数据
		oldData[1][oldCount[1]] = temp;
		if(++oldCount[1] == 5) oldCount[1] = 0;
	}
}

(尽管文章中使用定时器测量PWM的占空比理论上小编非常自信,但是小编使用示波器看过PWM输出的波形,这玩意非常乱,小编也不知道其结果到底正不正确,如果大家有好的方法欢迎大家留言或私信嗷!🤤🤤🤤)

排序数据函数

  这里的排序函数主要用于指令qa?qb?,只有接收到这两个指令才会调用这个函数排序。这里可能大家回想:为什么不每次一获取到新数据就进行排序,那就有可能不用写一个0(n2)的排序算法了?其实小编有想过这种情况,但是这样子写会占用更多的CPU资源。一种极端的情况:开机以后PC一直不发送这两个指令给主板,那么主板的排序是不是会没啥用呢?

/* -------------------------------- begin  -------------------------------- */
/**
  * @Name    sort
  * @brief   对长度为len的数组data进行不改变原数组顺序的排序 将结果保存在resData
  * @param   float*data:源数据 int len:源数据长度 float*resData:排序后结果
  * @retval  None
  * @author  黑心萝卜三条杠
  * @Data    2023-03-24
 **/
/* -------------------------------- end -------------------------------- */
static void sort(float*data,int len,float*resData)
{
	// 复制原数组到新数组中
    memcpy(resData, data, len * sizeof(float)); 
    // 冒泡排序 
    for (int i = 0; i < len - 1; ++i) {
        for (int j = i + 1; j < len; ++j) {
            if (resData[i] > resData[j]) {
                int temp = resData[i];
                resData[i] = resData[j];
                resData[j] = temp;
            }
        }
    }
}

求数据的绝对值函数

  求绝对值函数用于求最近两次角度的差值,这里的差值肯定是一个正数,因此小编就写一个求绝对值的玩意。

/* -------------------------------- begin  -------------------------------- */
/**
  * @Name    myAbs
  * @brief   自己写的求绝对值函数
  * @param   float a:参与求绝对值的数1 float b:参与求绝对值的数2
  * @retval  None
  * @author  黑心萝卜三条杠
  * @Data    2023-03-24
 **/
/* -------------------------------- end -------------------------------- */
static float myAbs(float a,float b)    
{                              
	float temp = a-b;  
	if(temp >0) return temp;
	else return -temp; 
}

文章福利

下边是小编个人整理出来免费的蓝桥杯嵌入式福利,有需要的童鞋可以自取哟!🤤🤤🤤文章来源地址https://www.toymoban.com/news/detail-460682.html

  • 【蓝桥杯嵌入式】第十一届蓝桥杯嵌入式省赛(第二场)程序设计试题及其题解
  • 【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式省赛程序设计试题以及详细题解
  • 【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式省赛程序设计试题及其详细题解
  • 【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式省赛(第二场)程序设计试题及其题解
  • 【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式省赛客观题以及详细题解
  • 【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式国赛客观题以及详细题解
  • 【蓝桥杯】一文解决蓝桥杯嵌入式开发板(STM32G431RBT6)LCD与LED显示冲突问题,并讲述LCD翻转显示
    也欢迎大家留言或私信交流,共同进步哟!😉😉😉

到了这里,关于【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式国赛程序设计试题以及详细题解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题,真题分析与代码讲解

    🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏 🎏【蓝桥杯嵌入式】蓝桥杯第十届省赛真题 🎏【蓝桥杯嵌入式】蓝桥杯第十三届省赛程序真题 🪔本系列专栏 -  

    2023年04月15日
    浏览(92)
  • 第十二届蓝桥杯嵌入式省赛第一场真题(基于HAL库的巨简代码+超级详解)

    相关说明: 开发板:CT117E-M4(STM32G431RBT6) 开发环境: CubeMX+Keil5 涉及题目:第十二届蓝桥杯嵌入式省赛第一场真题 技巧:字符串比较 、字符串数组转移提取、for和return搭配使用、goto语句、利用%c和%s打印 CubeMX配置、主要函数代码及说明: 1.使能外部高速时钟: 2.配置时钟树:

    2023年04月11日
    浏览(49)
  • 【蓝桥杯嵌入式】第十四届蓝桥杯嵌入式省赛[第一场]程序设计题以及详细题解

      今年的第一场比赛绝对np,官方将串口直接省掉了,将其替换成很多小功能,如:切换计时、频率均匀变化、锁机制等等,总的来说本届赛题的难度提升了不少。   本届试题需要用到的功能模块有 LCD 、 LED 、 按键 、 定时器输入捕获 、 定时器PWM输出 、 ADC获取 ,虽然这

    2023年04月17日
    浏览(82)
  • 【蓝桥杯嵌入式】第十四届蓝桥杯嵌入式[模拟赛2]程序设计试题及详细题解

    这次的模拟赛试题模块还是一些常见模块: LCD 、 LED 、 按键 、 定时器 以及 串口 ,相对比较常规,相比于真正的省赛也比较简单。但是它 适合刚刚学完各个模块需要做真题的同学 ,可以借此来巩固自己之前所学;对于已经能够掌握各个模块的同学也是有帮助的,就是平台

    2023年04月13日
    浏览(134)
  • 【蓝桥杯嵌入式】第十四届蓝桥杯嵌入式[模拟赛1]程序设计试题及详细题解

    模拟赛1的题目中需要的准备的知识点不多,其中只用到了 串口 、 LCD 、 LED 、 按键 、 定时器的PWM输出 、以及 ADC 等几个模块,题目要求也简单详细并且数量不多,非常适合入门比赛,以及整合自己比赛的模块。 与模拟赛2相比,当然是模拟赛2的试题比较难啦,虽然需要的模

    2023年04月13日
    浏览(147)
  • 蓝桥杯单片机 第十二届 国赛

    蓝桥杯第十二届,模块都比较常规,感觉都和省赛差不多,主要是麻烦在数据处理上,而我们处理的数据又要在界面中显示出来。 对比之前的第十届,其实题目差不多,并且少了串口收发这一比较难的模块。但是多了max,min,avg三个超声波数据的处理。所以按道理时间方面应该

    2023年04月08日
    浏览(51)
  • 第十三届蓝桥杯嵌入式国赛真题(基于HAL库的巨简代码+超级详解)

    相关说明: 开发板:CT117E-M4(STM32G431RBT6) 开发环境: CubeMX+Keil5 涉及题目:第十三届蓝桥杯嵌入式国赛真题 难点:双路AD测量电压、输入捕获测频率、LCD屏幕翻转、冒泡法、初始上电判断、按键长短按 CubeMX配置、主要函数代码及说明: 1.使能外部高速时钟: 2.配置时钟树:

    2023年04月09日
    浏览(72)
  • 蓝桥杯青少组python:第十二届国赛

    1、设 s=\\\"Hi LanQiao\\\" ,运行一下哪个选项代码可以输出 \\\"LanQiao\\\" 子串() A、 print(S[-7:]) B、 print(s[-6:-1]) C、 print(s[-7:0]) D、 print(s[-7:0]) 2、已知 a=2021.0529 ,运行一下代码选项可以输出 2021.05 () A、 print(\\\"{2f}\\\".format(a)) B、 print(\\\"{:.2f}\\\".format(a)) C、 print(\\\"{2}\\\".format(a)) D、 print(\\\"{.2f}\\\".for

    2024年02月07日
    浏览(46)
  • 第十二届蓝桥杯国赛试题及解析

    第一题 *选择题严禁使用程序验证设s =HiLanQiao\\\',运行以下哪个选项代码可以输出“LanQiao”子串( A )。 A、print(s[-7:]) B、print(s/-6:-11) C、print(s1-7:01) D、print(s[-7:-1]) 第二题 *选择题严禁使用程序验证已知a=2021.0529,运行以下哪个选项代码可以输出“2021.05” ( B )A、print( .2f1\\\'.format(a

    2024年02月08日
    浏览(46)
  • 【小黑嵌入式系统第十二课】μC/OS-III程序设计基础(二)——系统函数使用场合、时间管理、临界区管理、使用规则、互斥信号量

    上一课: 【小黑嵌入式系统第十一课】μC/OS-III程序设计基础(一)——任务设计、任务管理(创建基本状态内部任务)、任务调度、系统函数 下一课: 【小黑嵌入式系统第十三课】PSoC 5LP第二个实验——中断控制实验 1.1 时间管理 1.1.1 控制任务的执行周期 在任务函数的代码

    2024年02月04日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包