蓝桥杯备赛,第十届蓝桥杯省赛试题解析。

题目分析

原题

原题参见蓝桥杯大赛=>第十届真题.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链接蓝桥杯第十届省赛

Last modification:April 9th, 2021 at 07:36 pm