蓝桥杯备赛,第十届蓝桥杯省赛试题解析。
题目分析
原题
原题参见蓝桥杯大赛=>第十届真题.zip
另:官方试题汇总链接=>蓝桥杯大赛历届真题
硬件分析
本题中使用到的硬件设备除了MCU外主要有:LCD(液晶显示器)、电位器R37、按键(B1、B2、B3、B4)、LED
硬件上与第十一届的试题基本相同,因此,硬件分析请参照上篇博文:
软件分析
在本题中我们需要实现如下功能:
- LED闪烁(200ms)
- 使用LCD显示用户界面
- 按键短按检测及处理
- ADC读取并转换R37的电阻值
功能实现
STM32CubeMX引脚配置
与十一届赛题配置基本相同,但不需要使用PA6与PA7用作PWM输出。
LED部分
代码逻辑为:
- 通过全局变量
LEDCount
以及Systick中断来判断是否到达200ms - LED每间隔200ms翻转一次电平,以实现闪烁效果
- 通过变量
SettingItem
来保存需要闪烁的LED编号 - 通过R37的值判断是否超出上限或下限,使对应LED闪烁
#define LED_REFRESH_DELAY 190
volatile uint32_t LEDCount = 0;
void LED_Refresh(void)
{
if(LEDCount > 2 * LED_REFRESH_DELAY)
{
LEDCount = 0;
return;
}
uint8_t status = (LEDCount < LED_REFRESH_DELAY)? GPIO_PIN_RESET : GPIO_PIN_SET;
HAL_GPIO_WritePin(GPIOC, ALL_LED_PIN, GPIO_PIN_SET);
if (R37V > SettingItem[MAX_VOLT])
{
HAL_GPIO_WritePin(GPIOC, 0x0100 << ((uint8_t)SettingItem[UPPER_LD] - 1), status);
}
else if (R37V < SettingItem[MIN_VOLT])
{
HAL_GPIO_WritePin(GPIOC, 0x0100 << ((uint8_t)SettingItem[LOWER_LD] - 1), status);
}
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
LCD部分
代码逻辑为:
- 通过判断全局变量
UI
的值,来决定显示哪一个界面 - 通过标准库字符串格式化函数
sprintf
来格式化需要显示的字符串 - 在Setting界面通过设置背景颜色及文本颜色实现部分文本高亮
#define UI_MAIN 0x00
#define UI_SETTING 0x01
char ui_str[2][20] = {
" Main ",
" Setting "};
const char status_str[3][8] = {
"Normal",
"Upper",
"Lower"};
const char temp_str[SETTING_ITEM_SUM][22] = {
" Max Volt:%.2fV ",
" Min Volt:%.2fV ",
" Upper:LD%.0f ",
" Lower:LD%.0f "};
void LCD_Show(void)
{
char str[20] = " ";
LCD_DisplayStringLine(Line1, (u8 *)ui_str[UI]);
if (UI == UI_MAIN)
{
LCD_DisplayStringLine(Line4, (u8*)str);
LCD_DisplayStringLine(Line6, (u8*)str);
sprintf(str, " Volt: %.2fV ", R37V);
LCD_DisplayStringLine(Line3, (u8 *)str);
sprintf(str, " Status: %s ", status_str[Status]);
LCD_DisplayStringLine(Line5, (u8 *)str);
return;
}
// Setting User Interface
for (size_t i = 0; i < SETTING_ITEM_SUM; i++)
{
sprintf(str, temp_str[i], SettingItem[i]);
u32 j = 0;
u16 refcolumn = 319; //319;
char *ptr = str;
while ((*ptr != 0) && (j < 20)) // 20
{
if (i == ChooseItem && j == 4)
{
LCD_SetBackColor(White);
LCD_SetTextColor(Black);
}
LCD_DisplayChar(Line3 + Line1 * i, refcolumn, *ptr);
if (i == ChooseItem && j == 17)
{
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
}
refcolumn -= 16;
ptr++;
j++;
}
}
}
按键部分
该部分代码逻辑与十一届试题基本一致,参见上篇博文:
按键处理函数代码如下
void B1_Short(void)
{
UI = (UI == UI_MAIN) ? UI_SETTING : UI_MAIN;
ChooseItem = 0;
}
void B2_Short(void)
{
if (UI == UI_MAIN)
return;
ChooseItem = (ChooseItem + 1) % 4;
}
void B3_Short(void)
{
if (UI == UI_MAIN)
return;
if (ChooseItem / 2)
{
// Upper LD and Lower LD
SettingItem[ChooseItem] = (SettingItem[ChooseItem] >= 8.0f) ? 1.0f : SettingItem[ChooseItem] + 1.0f;
if (SettingItem[UPPER_LD] == SettingItem[LOWER_LD])
{
SettingItem[ChooseItem] = (SettingItem[ChooseItem] >= 8.0f) ? 1.0f : SettingItem[ChooseItem] + 1.0f;
}
}
else
{
// Max Volt and Min Volt
if (SettingItem[ChooseItem] < 3.30f)
{
if (ChooseItem == MIN_VOLT)
{
if (SettingItem[MIN_VOLT] + 0.3f >= SettingItem[MAX_VOLT])
{
return;
}
}
SettingItem[ChooseItem] += 0.3f;
}
}
}
void B4_Short(void)
{
if (UI == UI_MAIN)
return;
if (ChooseItem / 2)
{
// Upper LD and Lower LD
SettingItem[ChooseItem] = (SettingItem[ChooseItem] <= 1.0f) ? 8.0f : SettingItem[ChooseItem] - 1.0f;
if (SettingItem[UPPER_LD] == SettingItem[LOWER_LD])
{
SettingItem[ChooseItem] = (SettingItem[ChooseItem] <= 1.0f) ? 8.0f : SettingItem[ChooseItem] - 1.0f;
}
}
else
{
// Max Volt and Min Volt
if (SettingItem[ChooseItem] > 0.0f)
{
if (ChooseItem == MAX_VOLT)
{
if (SettingItem[MAX_VOLT] - 0.3f <= SettingItem[MIN_VOLT])
{
return;
}
}
SettingItem[ChooseItem] -= 0.3f;
}
}
}
ADC部分
代码逻辑:
- 同样与十一届试题基本相同,在获取到ADC的值后,将该值转换成电压值
- 通过将采集到的电压值与上下限的设定值比较,设置当前的状态
void Status_Update(void)
{
HAL_ADC_Start(&hadc2);
R37V = HAL_ADC_GetValue(&hadc2) * 3.3 / 4095.0;
HAL_ADC_Stop(&hadc2);
if (R37V >= SettingItem[MAX_VOLT])
Status = STATUS_UPPER;
else if (R37V <= SettingItem[MIN_VOLT])
Status = STATUS_LOWER;
else
Status = STATUS_NORMAL;
}
完整代码
参见Github链接蓝桥杯第十届省赛