1.0题目来源
可直接上网下载或者在线搜索蓝桥杯历年真题,这有一个宝藏的刷题网站(其他博主推荐)
2.0题目考点
这一届真题并不难,在考试内容上考了“固定的三件套”(led,lcd,按键),除此之外就考了一个ADC2测量R37电阻,而且串口,pwm波均不涉及。
3.0题目底层环境搭建
![](https://i-blog.csdnimg.cn/blog_migrate/6c10d716c5bd9471de1f6d2f415bd3ed.png)
图3-1 总体配置
![](https://i-blog.csdnimg.cn/blog_migrate/50570af12e0d1cab7b9d423d7d45c964.png)
图3-2时钟配置
![](https://i-blog.csdnimg.cn/blog_migrate/43a5e8996e3de1564aea7f0304badb97.png)
图3-3ADC2的配置
![](https://i-blog.csdnimg.cn/blog_migrate/7a0e6292b7adf1b291fac19a92d050c0.png)
图3-4ADC2的配置
4.0题目分析:
4.1解题步骤
一、状态分解
状态1:初始状态,刚上电的主界面(Main),实现ADC实时电压信号的采集
状态2:切换到参数设置界面(Setting)
状态3:在参数设置界面按键选择参数实现高亮显示
状态4:在参数设置界面进行上下限值的加减以及灯的序号的加减
二、状态的实现
①状态1:设置Main 页面为page=1,然后用LCD实现显示ADC实时采集到的电压值
②状态2:将初始值进行设置,将Setting页面设置为page=2,通过按键B1实现它的切换过程,设置完之后通过LCD显示出来
③状态3:要实现高亮显示,首先将所想要的颜色放在if判断语句后的执行语句,然后在显示完之后再将所选行恢复原来的颜色。
④状态4: 通过按键三和四实现上下限值进行设置,在实现LED灯顺序切换的时候将每一个LED灯亮度的数值存储到数组中。这个数组可以是指针数组,也可以是普通的数组,虽然指针数组可以提高运行效率,避免拷贝构造,通过传地址即可实现灯的顺序切换,但是为了方便理解,本题用了普通的数组,直接传值来改变LED灯的顺序
三、关键部分代码展示
①主函数:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "led.h"
#include "key.h"
#include "stdio.h"
#include "string.h"
/* 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 */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
//函数声明区
void led_proc(void);
void key_proc(void);
void lcd_proc(void);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//变量定义区
uint8_t page=1;//界面变量
//按键专用变量
__IO uint32_t uwtick_key=0;//按键减速变量
unsigned char uckey_val,//用于保存当前键值
uckey_up,//保存按键放开后的值
uckey_down,//保存按键按下的键值
uckey_old;//保存上一次的键值
//记录按键按下的次数
uint8_t counter=0;
//参数值
double data1=2.4;
double data2=1.2;
//LCD显示变量
__IO uint32_t uwtick_lcd=0;//LCD减速变量
unsigned char Lcd_disp_string[21];
uint8_t i=3;//实现选行和高亮显示变量
//adc变量
double adc=0.0;
//led显示变量数组
__IO uint32_t uwtick_led=0;//led灯减速变量
unsigned char led[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
int j=0;//当做数组的下标(led1的递增或递减)
int k=1;//数组的下标(LED2的递增或递减)
unsigned char temp1=0x01;//临时变量储存数组值
unsigned char temp2=0x02;//临时变量储存数组值
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
LCD_Init();//LCD的初始化
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC2_Init();
/* USER CODE BEGIN 2 */
//LCD初始化
// LCD_Init();
LCD_Clear(Black);//执行清屏操作
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
led_proc();
key_proc();//调用函数
lcd_proc();
}
}
//LED灯,用数组来实现
//led
//LED部分超过上限LD1显示,低于下限LD2闪烁
void led_proc()
{
if((uwTick-uwtick_led)<50) return;
uwtick_led=uwTick;
adc=get_adc2()*3.3/4096;//将读取到的电压值存放到adc中
//实现功能:LED的闪烁
if(adc>data1)
{
led_disp(temp1);
HAL_Delay(100);
led_disp(0x00);
HAL_Delay(100);
}
if(adc<data2)
{
led_disp(temp2);
HAL_Delay(100);
led_disp(0x00);
HAL_Delay(100);
}
}
//通过按键设置各个界面
void key_proc()
{
if((uwTick-uwtick_key)<10) return;
uwtick_key=uwTick;
uckey_val=Key_Scan();
uckey_down=uckey_val&(uckey_old^uckey_val);
uckey_up=~uckey_val&(uckey_old^uckey_val);
uckey_old=uckey_val;
//B0按下
if(uckey_down==1)
{
//切换界面
if(page==1)
{
page=2;
}
else page=1;
LCD_Clear(Black);
}
//B2按键按下,功能:切换,高亮显示
if(uckey_down==2&&page==2)
{
i+=2;
if(i>9)
{
i=3;
}
}
//按键三 实现+0.3V,每按下一次,所选参数+0.3,实现LED序号的加功能
if((uckey_down==3)&&(page==2))
{
if(i==3&&(data1<3.0))
{
data1+=0.30;
}
if(i==5&&((data1-data2)>=0.3))
{
data2+=0.30;
}
//此时选择第七行 led1灯的切换
if((i==7)&&(adc>data1))
{
temp1=led[j+1];//j的初值是0,当按键按下时,led的序号会发生加一的改变,所以对应数组下标要+1
j++;
if(j==7)//当数组下标j=7,j=6,表示来到LED1灯序号加到了第八个灯,无法再加,下面同理
j=6;
}
//此时选择第九行 led2灯的切换
if((i==9)&&(adc<data2))
{
temp2=led[k+1];
k++;
if(k==7)
k=6;
}
}
//进行减操作:减上下限值,减LED灯的序号
if(uckey_down==4&&page==2)
{
if(i==3&&((data1-data2)>=0.3)&&(data1>0.3))
{
data1-=0.30;
}
if(i==5&&(data2>0))
{
data2-=0.30;
}
if((i==7)&&(adc>data1)&&(j>(-1)))//对LED灯的序号进行减操作
{
temp1=led[j];
j--; //当上面的j=0时候,执行完这条语句直接变成了-1
if(j==-1)
{
temp1=led[0];
}
}
if((i==9)&&(adc<data2)&&(k>0))//对LED灯的序号进行减操作
{
temp2=led[k];
k--;
if(k==0)
{
temp2=led[1];
}
}
}
}
//LCD显示部分
void lcd_proc()
{
if((uwTick-uwtick_lcd)<50) return;
uwtick_lcd=uwTick;
//数据显示界面
if(page==1)
{
sprintf((char*)Lcd_disp_string," Main");
LCD_DisplayStringLine(Line1,Lcd_disp_string);
//将采集到的电压值进行打印
sprintf((char*)Lcd_disp_string," Volt:%3.2fV",adc);
LCD_DisplayStringLine(Line3,Lcd_disp_string);
//随着实时参数的变化,这个显示的内容也不同
if((adc>data1)&&(adc<3.3)) //当采集的ADC值大于上限值
{
//Upper后面一定要打一个空格(实现清屏),下面也一样,normal就不用
sprintf((char*)Lcd_disp_string," Status:Upper ");
}
if((adc<data2)&&(adc>=0))//当采集的ADC值小于下限值
{
sprintf((char*)Lcd_disp_string," Status:Lower ");
}
if((adc<data1)&&(adc>data2))//当处于中间时为正常值
{
sprintf((char*)Lcd_disp_string," Status:Normal");
}
LCD_DisplayStringLine(Line6,Lcd_disp_string);
}
//数据设置界面 并且实现参数选择功能:选到的参数进行高亮显示
else
{
sprintf((char*)Lcd_disp_string," Setting ");
LCD_DisplayStringLine(Line1,Lcd_disp_string);
//实现高亮显示
sprintf((char*)Lcd_disp_string," MaxVolt:%3.2fV ",data1);
if(i==3)
LCD_SetBackColor(Green);//高亮显示时的背景色
LCD_DisplayStringLine(Line3,Lcd_disp_string);
LCD_SetBackColor(Black);//执行完后恢复原色
//实现高亮显示
sprintf((char*)Lcd_disp_string," MinVolt:%3.2fV ",data2);
if(i==5)
LCD_SetBackColor(Green);//高亮显示时的背景色
LCD_DisplayStringLine(Line5,Lcd_disp_string);
LCD_SetBackColor(Black);//执行完后恢复原色
//实现高亮显示
sprintf((char*)Lcd_disp_string," Upper:LD1 ");
if(i==7)
LCD_SetBackColor(Green); //高亮显示时的背景色
LCD_DisplayStringLine(Line7,Lcd_disp_string);
LCD_SetBackColor(Black);//执行完后恢复原色
//实现高亮显示
sprintf((char*)Lcd_disp_string," Lower:LD2 ");
if(i==9)
LCD_SetBackColor(Green);//高亮显示时的背景色
LCD_DisplayStringLine(Line9,Lcd_disp_string);
LCD_SetBackColor(Black);//执行完后恢复原色
}
}
/* USER CODE END 3 */
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
②ADC代码:
![](https://i-blog.csdnimg.cn/blog_migrate/cd3d20b30152f030b81915b753a8dced.png)
注:只需要在adc.c上添加142~148行的代码即可。
③:其它LED显示函数和按键扫描函数
![](https://i-blog.csdnimg.cn/blog_migrate/a9badad721af578b6137cef34b4cab5a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/317ce98839da253866e9d2a46cfdc6dc.png)
声明:
本人自己编写代码,可能由于时间匆忙会有不正之处,希望有大佬能指正一下,提高代码的运行效率
非常感谢阅读,希望能对你有所帮助,如有什么不正确的或者Bug,请留言,不喜勿喷,照顾一下新人,谢谢!!!!!!!!😃