欢迎光临
我们一直在努力

蓝桥杯嵌入式16届省赛真题(❤️附源文件❤️和超详细代码级注释❤️)

此蓝桥杯嵌入式系列将加班加点的更新下去并且完全免费,这几天代码调试花了点时间,更新慢了点。

希望大家给一个免费的赞和收藏。

❤️❤️❤️❤️❤️❤️祝大家在比赛中取得好成绩❤️❤️❤️❤️❤️❤️

另外本次代码,加入了更多宏定义,以及命名方式 。

使代码更加方便阅读  和   后期参数修改

大家如果有什么 好的建议 或者 不懂的 都可以一起在评论区讨论,看到了会尽快回答的;

本套源文件在文末有链接,想直接的看的可以直接到文末下载




1.cubemx配置

2.文件配置

3.题目分析

一、核心考点(按分值权重排序,重点突破)

结合真题85分程序设计要求,考点分模块,优先掌握高权重模块(ADC、PWM、LCD、按键),再突破次要模块,避免丢基础分。

1. 高权重考点(60%+,必拿分)

  • ADC两路模拟采样:对应R37(模拟A)、R38(模拟B),要求0~3.3V采集,配合中断/定时器触发,需加滤波(满足LCD无噪点要求)。

  • PWM输出(PA7):频率、占空比按模拟信号阶梯式调节,需精准计算PSC、CCR2,满足频率偏差±3%、占空比偏差±2%。

  • LCD显示:3个界面(监控、统计、参数),严格按格式显示(背景黑、前景白,参数=值,单位规范),刷新频率0.1秒。

  • 按键处理:4个按键(B1~B4),实现界面切换、锁定/解锁、参数加减、长按重置,需防抖(响应≤0.2秒)。

2. 中权重考点(20%,易丢分)

  • 定时器应用:TIM6触发ADC采样(固定100ms)、TIM3输出PWM、PA15脉冲捕获(计算DF)。

  • 参数校验与锁定:参数(DS/DR/FS/FR)合法性校验,锁定功能(锁定后PWM参数不变)。

  • LED指示:LD1~LD3对应界面、锁定、异常状态,LD4~LD7熄灭。

3. 低权重考点(5%,不丢基础分)

  • 系统运行时长:计时格式xxHyyMzzS,支持长按B2重置。

  • 频率异常判定:PA15捕获频率(DF)与PA7输出频率(CF)偏差>1000Hz,触发异常(LD3点亮)。

  • 文件提交规范:仅提交自行编写的.c/.h和.hex(准考证号命名),压缩包≤30MB。

二、做题思路(按开发流程,分步落地,避免混乱)

核心原则:先搭框架,再填细节;先实现基础功能,再优化性能;先调试单个模块,再整合联调。全程贴合真题初始状态和功能要求。

步骤1:CubeMX基础配置(10分钟,奠定基础)

  • 系统时钟:配置为80MHz(真题强制要求),外部晶振。

  • ADC配置:ADC1(R38,模拟B)、ADC2(R37,模拟A),开启常规转换,选择TIM6 TRGO触发(100ms采样),开启EOC中断(转换完成中断)。

  • 定时器配置:

  • TIM3:输出PWM(PA7,CH2),ARR初始设999(配合PSC计算频率)。

  • TIM6:定时100ms(PSC=7999,ARR=999),开启TRGO触发ADC,不开启定时器中断(仅用于触发ADC)。

  • TIMx(PA15):输入捕获模式,捕获PA15脉冲信号(计算DF)。

  • GPIO配置:B1~B4(按键,上拉输入)、LD1~LD7(推挽输出,初始熄灭)。

  • LCD配置:复用官方驱动,配置对应GPIO(按资源包驱动要求)。

  • 步骤2:单个模块开发(30分钟,逐个调试)

  • ADC采样模块:

  • 编写ADC中断回调函数(HAL_ADC_ConvCpltCallback),读取采样值,转换为电压(3.3*采样值/4096)。

  • 添加滑动平均滤波(10次采样,满足LCD无噪点),存储到全局变量(R37_vol、R38_vol)。

  • 调试:旋转R37/R38,查看采样值是否在0~4095、电压0~3.3V平滑变化。

  • PWM输出模块:

  • 根据真题公式,计算VA(占空比档位电压跨度)、VB(频率档位电压跨度)。

  • 编写PWM参数更新函数,根据R37_vol确定占空比(CCR2),R38_vol确定频率(PSC)。

  • 调试:旋转电位器,查看PA7输出频率、占空比是否符合阶梯调节规则,偏差在要求范围内。

  • 按键处理模块:

  • 编写按键扫描函数(防抖处理,延时10ms),区分短按/长按(B2长按2秒重置时长)。

  • 实现界面切换(B1)、锁定/解锁(B2短按)、参数加减(B3/B4)、参数切换(B2在参数界面)。

  • 调试:按下按键,响应时间≤0.2秒,无误触发、重复触发。

  • LCD显示模块:

  • 编写3个界面的显示函数,严格按真题格式(位置、大小写、单位)。

  • 设置LCD背景色(黑)、前景色(白),0.1秒刷新一次(配合定时器或主循环延时)。

  • 调试:切换界面,显示内容清晰、无噪点,参数更新及时。

  • 步骤3:模块整合与功能完善(20分钟)

  • 整合模块:将ADC采样→PWM更新→LCD显示→按键处理串联,实现“采样→调节→显示”闭环。

  • 实现锁定功能:添加g_lock_flag全局变量,锁定时不更新PWM参数,LD2点亮。

  • 实现频率异常判定:计算CF(PA7频率)与DF(PA15捕获频率)的差值,偏差>1000Hz时,LD3点亮,记录异常参数到统计界面。

  • 实现系统时长计时:用定时器(如TIM7)定时1秒,累加时、分、秒,支持长按B2重置。

  • 步骤4:参数校验与优化(10分钟,避免扣分)

  • 参数校验:退出参数界面时,校验DS<DR、FS<FR,且至少能调节一个步长,非法则还原参数并跳转监控界面。

  • 性能优化:ADC滤波、按键防抖、LCD刷新频率,确保所有性能要求达标(响应时间、偏差、无噪点)。

  • 调试异常:排查ADC中断不触发、PWM参数错误、LCD显示错乱、按键误触发等问题。

  • 步骤5:提交准备(5分钟,避免零分)

  • 编译生成.hex文件,以准考证号命名。

  • 筛选需提交的文件:仅保留自行编写/修改的.c/.h(如main.c、fun.c、headfile.h),不包含资源包原有未修改文件。

  • 压缩文件,确保大小≤30MB,按考试系统要求提交。

  • 三、核心知识点(对应考点,直击真题,避免冗余)

    1. ADC采样知识点(真题核心)

    • STM32G431 ADC:12位分辨率,参考电压3.3V,采样范围0~4095(对应0~3.3V)。

    • 触发方式:定时器TRGO触发(真题推荐,固定100ms采样),替代轮询,解放CPU。

    • 中断配置:开启ADC全局中断(NVIC)、EOC中断(转换完成触发),回调函数中读取采样值并重启中断。

    • 滤波:滑动平均滤波(10次采样),解决采样波动,满足LCD无噪点要求(真题性能要求)。

    • 电压转换公式:电压值 = 3.3f × 采样值(uint32_t) / 4096.0f。

    2. PWM输出知识点(真题核心)

    • PWM核心寄存器:

      • PSC(预分频器):分频系统时钟(80MHz),计数频率 = 80000000 / (PSC+1)。

      • ARR(自动重载值):定时器计数上限,决定PWM周期,周期 = (PSC+1)×(ARR+1) / 80000000。

      • CCR2(比较寄存器):控制PWM占空比,占空比 = (CCR2 / (ARR+1)) × 100%(真题要求精准计算)。

    • 真题调节规则:

      • 占空比:10%(固定最小值)~DR,步长DS,VA = 3.3 / [(DR-10)/DS + 1](+1是计算档位总数)。

      • 频率:1000Hz(固定最小值)~FR,步长FS,VB = 3.3 / [(FR-1000)/FS + 1]。

    • 频率/占空比偏差控制:精准计算PSC和CCR2,避免整数运算误差(强制转换为float)。

    3. 定时器知识点

    • TIM6(触发ADC):仅用于产生TRGO信号,定时100ms,无需开启中断,配置PSC=7999、ARR=999。

    • TIM3(PWM输出):通道2(PA7),配置为PWM模式1,ARR初始999,根据频率需求修改PSC。

    • 输入捕获(PA15):配置为上升沿捕获,计算两次捕获的时间差,换算为频率(DF = 80000000 / 时间差)。

    • 计时功能:用TIM7定时1秒,累加时、分、秒,注意溢出处理(如秒满60进1,分满60进1)。

    4. LCD显示知识点

    • 显示规范:背景色黑色,前景色白色,参数与值用“=”间隔,单位规范(Hz小写,H大写)。

    • 界面要求:3个界面(监控、统计、参数),严格按真题图示的位置、内容、大小写显示。

    • 刷新频率:0.1秒一次,可通过定时器中断或主循环延时实现,避免显示卡顿、噪点。

    • 复用官方驱动:调用资源包中的LCD_Init()、LCD_ShowString()等函数,无需重新编写驱动。

    5. 按键处理知识点

    • 防抖处理:按键扫描时,延时10ms后再次检测,确认按键状态稳定(避免机械抖动导致误触发)。

    • 按键功能区分:

      • B1:界面切换(监控→统计→参数→监控)。

      • B2:监控界面(短按锁定/解锁,长按2秒重置时长);参数界面(切换参数)。

      • B3/B4:仅在参数界面有效,对应参数加减(按真题调节规则)。

    • 长按判定:记录按键按下时间,超过2秒视为长按,松开后执行对应功能(不触发短按)。

    6. 其他关键知识点

    • 全局变量:用于存储采样值、PWM参数、系统状态(g_lock_flag、运行时长、异常参数等),方便跨函数调用。

    • 参数校验:DS<DR、FS<FR,且(DR-10)能被DS整除、(FR-1000)能被FS整除(确保阶梯调节有效)。

    • LED控制:LD1(监控界面亮)、LD2(锁定亮)、LD3(异常亮),低电平点亮(按竞赛板硬件特性)。

    4.headfile.h

    #ifndef __HEADFILE_H__
    #define __HEADFILE_H__

    #include "main.h"
    #include "gpio.h"
    #include "tim.h"
    #include "adc.h"

    #include "stdio.h"
    #include "stdint.h"
    #include "string.h"

    #include "fun.h"
    #include "key.h"
    #include "lcd.h"
    #include "headfile.h"

    #define DEFAULT_DS_1 1
    #define DEFAULT_DR_8080
    #define DEFAULT_FS_100100
    #define DEFAULT_FR_20002000

    #define DEFAULT_BASE_DUTY_10 10
    #define DEFAULT_BASE_FREQ_1000 1000

    #define DEFAULT_SYSTEM_CLOCK_80MHZ 80000000
    #define DEFAULT_TIM2_CLOCK_1MHZ 1000000UL
    #define PWM_TIM3_ARR_100 100

    #define LOCK 1
    #define UNLOCK 0

    #define PWM 0
    #define RECD 1
    #define PARA 2

    // DS:占空比小步进(+1%)
    #define DEFAULT_DS_STEP_1 1 // 单位:%
    // DR:占空比大步进(+10%)
    #define DEFAULT_DR_STEP_10 10 // 单位:%

    // ———————- 频率相关步进 ———————-
    // FS:频率小步进(+100Hz)
    #define DEFAULT_FS_STEP_100 100 // 单位:Hz
    // FR:频率大步进(+1000Hz)
    #define DEFAULT_FR_STEP_1000 1000 // 单位:Hz

    // ———————- 参数类型标识(用于判断当前选择) ———————-
    // 用宏定义参数类型,方便代码中判断当前要调节的参数
    #define PARAM_TYPE_DS 0 // 当前调节:占空比小步进(DS)
    #define PARAM_TYPE_DR 1 // 当前调节:占空比大步进(DR)
    #define PARAM_TYPE_FS 2 // 当前调节:频率小步进(FS)
    #define PARAM_TYPE_FR 3 // 当前调节:频率大步进(FR)

    extern uint8_t g_lock_flag;

    extern uint8_t lcd_flag;

    extern uint8_t g_param_DS;
    extern uint8_t g_param_DR;
    extern uint32_t g_param_FS;
    extern uint32_t g_param_FR;
    extern uint8_t g_param_temp_DS;
    extern uint8_t g_param_temp_DR;
    extern uint32_t g_param_temp_FS;
    extern uint32_t g_param_temp_FR;

    extern uint32_t g_pa15_freq;
    extern uint8_t g_initial_flag;

    #endif

    5.fun.c

    #include "headfile.h"

    char text[21];

    uint32_t pa7_freq;
    uint32_t pa7_duty;

    uint8_t lcd_flag;
    uint8_t g_lock_flag;
    uint8_t abnormal_flag;

    uint16_t run_hour;
    uint16_t run_minute;
    uint16_t run_second;
    uint16_t run_abnormal_hour;
    uint16_t run_abnormal_minute;
    uint16_t run_abnormal_second;

    uint8_t g_param_DS = DEFAULT_DS_1 ;
    uint8_t g_param_DR = DEFAULT_DR_80 ;
    uint32_t g_param_FS = DEFAULT_FS_100 ;
    uint32_t g_param_FR = DEFAULT_FR_2000 ;

    uint8_t g_param_temp_DS;
    uint8_t g_param_temp_DR;
    uint32_t g_param_temp_FS;
    uint32_t g_param_temp_FR;

    uint8_t g_param_n_duty;
    uint8_t g_param_n_freq;

    uint32_t param_abnormal_CF;
    uint8_t param_abnormal_CD;
    uint32_t param_abnormal_DF;
    uint32_t param_abnormal_XF;

    int param_XF;

    float VA;
    float VB;
    double R37_vol;
    double R38_vol;

    /**
    * @brief 获取ADC采样值并转换为电压
    * @param hadc ADC句柄指针
    * @return 转换后的电压值(单位:伏特)
    */
    double get_vol(ADC_HandleTypeDef *hadc)
    {
    HAL_ADC_Start(hadc); // 启动ADC转换
    uint32_t adc_value = HAL_ADC_GetValue(hadc); // 读取ADC转换结果
    return 3.3 * adc_value / 4096; // 将ADC值转换为电压(参考电压3.3V,12位ADC)
    }

    /**
    * @brief 系统初始化函数
    * @note 检查系统启动时间,达到200ms后设置初始化完成标志
    */
    void Initial_System()
    {
    if(HAL_GetTick() >= 100) // 检查系统运行时间是否达到200ms
    {
    g_initial_flag = 1; // 设置初始化完成标志位
    }
    }

    /**
    * @brief 系统状态更新和控制函数
    * @note 负责读取ADC值、计算运行时间、调整PWM参数、检测异常和更新LED状态
    */
    void change(void)
    {
    /* 读取ADC电压值 */
    R37_vol = get_vol(&hadc2); // 读取ADC2通道电压(用于占空比控制)
    R38_vol = get_vol(&hadc1); // 读取ADC1通道电压(用于频率控制)

    /* 计算系统运行时间 */
    run_hour = HAL_GetTick() / 3600000; // 计算运行小时数
    run_minute = HAL_GetTick() / 60000 – run_hour * 60; // 计算运行分钟数
    run_second = HAL_GetTick() / 1000 – run_hour * 3600 – run_minute * 60; // 计算运行秒数

    /* PWM占空比控制 */
    if(g_lock_flag == UNLOCK) // 检查是否锁定(0=未锁定,允许调整)
    {
    /* 计算占空比调整步长 */
    VA = 3.3f / (float)((g_param_DR – DEFAULT_BASE_DUTY_10)/g_param_DS + 1);

    /* 根据R37电压值查找对应的占空比档位 */
    for(g_param_n_duty = 0;\\
    g_param_n_duty < ((g_param_DR – DEFAULT_BASE_DUTY_10)/g_param_DS + 1); \\
    g_param_n_duty++ \\
    )
    {
    if(R37_vol >= (g_param_n_duty * VA) \\
    && R37_vol < ((g_param_n_duty+1) * VA) \\
    )
    {
    TIM3->CCR2 = DEFAULT_BASE_DUTY_10 + (g_param_n_duty * g_param_DS); // 设置TIM3通道2的占空比
    }
    }

    /* PWM频率控制 */
    VB = 3.3f / (float)((g_param_FR – DEFAULT_BASE_FREQ_1000)/g_param_FS + 1);

    /* 根据R38电压值查找对应的频率档位 */
    for(g_param_n_freq = 0; \\
    g_param_n_freq < ((g_param_FR – DEFAULT_BASE_FREQ_1000)/g_param_FS + 1); \\
    g_param_n_freq++ \\
    )
    {
    if(R38_vol >= (g_param_n_freq * VB) \\
    && R38_vol < ((g_param_n_freq+1) * VB))
    {
    /* 计算并设置TIM3的预分频器,实现频率调整 */
    TIM3->PSC = DEFAULT_SYSTEM_CLOCK_80MHZ / ((DEFAULT_BASE_FREQ_1000 + g_param_n_freq * g_param_FS)*PWM_TIM3_ARR_100 ) – 1;
    }
    }

    /* 更新当前PWM参数 */
    pa7_freq = DEFAULT_SYSTEM_CLOCK_80MHZ/ PWM_TIM3_ARR_100 / (TIM3->PSC + 1); // 当前频率值
    pa7_duty = PWM_TIM3_ARR_100 * TIM3->CCR2 / (TIM3->ARR+1); // 当前占空比值
    }

    /* 频率异常检测 */
    if(g_initial_flag) // 系统初始化完成后才进行异常检测
    {
    /* 计算PA7输出频率与PA15参考频率的差值 */
    param_XF = g_pa15_freq – pa7_freq;
    if(param_XF < 0)
    {
    param_XF = pa7_freq – g_pa15_freq; // 取绝对值
    }

    /* 判断频率差是否超过阈值(1000Hz) */
    if(param_XF > 1000)
    {
    if(abnormal_flag == 0) // 首次检测到异常
    {
    abnormal_flag = 1; // 设置异常标志
    /* 记录异常发生时的各项参数 */
    param_abnormal_CF = pa7_freq; // 异常时的当前频率
    param_abnormal_CD = pa7_duty; // 异常时的当前占空比
    param_abnormal_DF = g_pa15_freq; // 异常时的参考频率
    param_abnormal_XF = param_XF; // 异常时的频率差值
    run_abnormal_hour = run_hour; // 异常发生的小时
    run_abnormal_minute = run_minute; // 异常发生的分钟
    run_abnormal_second = run_second; // 异常发生的秒数
    }
    }
    else
    {
    abnormal_flag = 0; // 频率正常,清除异常标志
    }
    }

    /* LED状态显示 */
    led_show(1, lcd_flag == 0); // LED1显示LCD状态
    led_show(2, g_lock_flag); // LED2显示锁定状态
    led_show(3, abnormal_flag); // LED3显示异常状态
    }

    /**
    * @brief 控制LED显示状态
    * @param led LED编号(1-3),对应PC8、PC9、PC10引脚
    * @param mode LED模式(1=点亮,0=熄灭)
    * @note 使用74HC595锁存器控制,通过PD2引脚触发锁存
    */
    void led_show(uint8_t led, uint8_t mode)
    {
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET); // PD2置高,准备锁存数据

    if(mode) // 模式为1,点亮LED
    {
    /* 根据led编号选择对应的GPIO引脚并置低(点亮) */
    /* led=1: PC8, led=2: PC9, led=3: PC10 */
    HAL_GPIO_WritePin(GPIOC, (GPIO_PIN_8 << (led – 1)), GPIO_PIN_RESET);
    }
    else // 模式为0,熄灭LED
    {
    /* 根据led编号选择对应的GPIO引脚并置高(熄灭) */
    /* led=1: PC8, led=2: PC9, led=3: PC10 */
    HAL_GPIO_WritePin(GPIOC, (GPIO_PIN_8 << (led – 1)), GPIO_PIN_SET);
    }

    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET); // PD2置低,触发锁存,更新LED状态
    }

    /**
    * @brief 在LCD屏幕上显示PWM参数和系统状态
    * @note 显示内容包括:标题、输出频率、占空比、参考频率、锁定状态和运行时间
    */
    void pwm_show(void)
    {
    /* 显示标题 */
    sprintf(text, " PWM "); // 格式化标题字符串
    LCD_DisplayStringLine(Line1, (uint8_t *)text); // 在LCD第1行显示标题

    /* 显示PA7输出频率 */
    sprintf(text, " CF=%dHz ", pa7_freq); // 格式化当前频率字符串(CF=Current Frequency)
    LCD_DisplayStringLine(Line3, (uint8_t *)text); // 在LCD第3行显示当前频率

    /* 显示PA7占空比 */
    sprintf(text, " CD=%d%% ", pa7_duty); // 格式化当前占空比字符串(CD=Current Duty)
    LCD_DisplayStringLine(Line4, (uint8_t *)text); // 在LCD第4行显示当前占空比

    /* 显示PA15参考频率 */
    sprintf(text, " DF=%dHz ", g_pa15_freq); // 格式化参考频率字符串(DF=Desired Frequency)
    LCD_DisplayStringLine(Line5, (uint8_t *)text); // 在LCD第5行显示参考频率

    /* 显示锁定状态 */
    if(g_lock_flag == UNLOCK) // 判断是否处于解锁状态
    {
    sprintf(text, " ST=UNLOCK "); // 格式化解锁状态字符串(ST=Status)
    }
    else if(g_lock_flag == LOCK) // 判断是否处于锁定状态
    {
    sprintf(text, " ST=LOCK "); // 格式化锁定状态字符串
    }
    LCD_DisplayStringLine(Line6, (uint8_t *)text); // 在LCD第6行显示锁定状态

    /* 显示系统运行时间 */
    sprintf(text, " %02dH%02dM%02dS ", run_hour, run_minute, run_second); // 格式化运行时间字符串(时:分:秒)
    LCD_DisplayStringLine(Line7, (uint8_t *)text); // 在LCD第7行显示运行时间
    }

    /**
    * @brief 在LCD屏幕上显示异常记录信息
    * @note 显示异常发生时的各项参数:频率、占空比、参考频率、频率差值和发生时间
    */
    void recd_show(void)
    {
    /* 显示标题 */
    sprintf(text, " RECD "); // 格式化标题字符串(RECD=Record,记录)
    LCD_DisplayStringLine(Line1, (uint8_t *)text); // 在LCD第1行显示标题

    /* 显示异常时的当前频率 */
    sprintf(text, " CF=%dHz ", param_abnormal_CF); // 格式化异常时的频率字符串(CF=Current Frequency)
    LCD_DisplayStringLine(Line3, (uint8_t *)text); // 在LCD第3行显示异常时的频率

    /* 显示异常时的当前占空比 */
    sprintf(text, " CD=%d%% ", param_abnormal_CD); // 格式化异常时的占空比字符串(CD=Current Duty)
    LCD_DisplayStringLine(Line4, (uint8_t *)text); // 在LCD第4行显示异常时的占空比

    /* 显示异常时的参考频率 */
    sprintf(text, " DF=%dHz ", param_abnormal_DF); // 格式化异常时的参考频率字符串(DF=Desired Frequency)
    LCD_DisplayStringLine(Line5, (uint8_t *)text); // 在LCD第5行显示异常时的参考频率

    /* 显示异常时的频率差值 */
    sprintf(text, " XF=%dHz ", param_abnormal_XF); // 格式化频率差值字符串(XF=X Frequency,差值频率)
    LCD_DisplayStringLine(Line6, (uint8_t *)text); // 在LCD第6行显示频率差值

    /* 显示异常发生的时间 */
    sprintf(text, " %02dH%02dM%02dS ", run_abnormal_hour, run_abnormal_minute, run_abnormal_second); // 格式化异常发生时间
    LCD_DisplayStringLine(Line7, (uint8_t *)text); // 在LCD第7行显示异常发生时间
    }

    /**
    * @brief 在LCD屏幕上显示系统配置参数
    * @note 显示内容包括:占空比步长、占空比范围、频率步长和频率范围
    */
    void para_show(void)
    {
    /* 显示标题 */
    sprintf(text, " PARA "); // 格式化标题字符串(PARA=Parameter,参数)
    LCD_DisplayStringLine(Line1, (uint8_t *)text); // 在LCD第1行显示标题

    /* 显示占空比步长 */
    sprintf(text, " DS=%d%% ", g_param_temp_DS); // 格式化占空比步长字符串(DS=Duty Step)
    LCD_DisplayStringLine(Line3, (uint8_t *)text); // 在LCD第3行显示占空比步长

    /* 显示占空比范围 */
    sprintf(text, " DR=%d%% ", g_param_temp_DR); // 格式化占空比范围字符串(DR=Duty Range)
    LCD_DisplayStringLine(Line4, (uint8_t *)text); // 在LCD第4行显示占空比范围

    /* 显示频率步长 */
    sprintf(text, " FS=%dHz ", g_param_temp_FS); // 格式化频率步长字符串(FS=Frequency Step)
    LCD_DisplayStringLine(Line5, (uint8_t *)text); // 在LCD第5行显示频率步长

    /* 显示频率范围 */
    sprintf(text, " FR=%dHz ", g_param_temp_FR); // 格式化频率范围字符串(FR=Frequency Range)
    LCD_DisplayStringLine(Line6, (uint8_t *)text); // 在LCD第6行显示频率范围

    /* 清空第7行 */
    sprintf(text, " "); // 格式化空字符串
    LCD_DisplayStringLine(Line7, (uint8_t *)text); // 在LCD第7行显示空行
    }

    /**
    * @brief LCD显示控制函数
    * @note 根据lcd_flag标志位选择不同的显示界面
    * lcd_flag=0: 显示PWM参数界面
    * lcd_flag=1: 显示异常记录界面
    * lcd_flag=2: 显示系统参数界面
    */
    void lcd_show(void)
    {
    switch(lcd_flag) // 根据LCD显示标志位选择显示界面
    {
    case 0: // 标志位为0,显示PWM参数界面
    pwm_show(); // 调用PWM参数显示函数
    break;
    case 1: // 标志位为1,显示异常记录界面
    recd_show(); // 调用异常记录显示函数
    break;
    case 2: // 标志位为2,显示系统参数界面
    para_show(); // 调用系统参数显示函数
    break;
    }
    }

    6.key.c

    #include "headfile.h"

    uint8_t B1_state;
    uint8_t B2_state;
    uint8_t B3_state;
    uint8_t B4_state;
    uint8_t B1_last_state = 1;
    uint8_t B2_last_state = 1;
    uint8_t B3_last_state = 1;
    uint8_t B4_last_state = 1;

    uint8_t para_select;

    /**
    * @brief 按键扫描函数
    * @note 扫描4个按键状态,处理按键事件,实现界面切换、参数调整、锁定/解锁、复位等功能
    * 按键去抖动时间:20ms
    */
    void key_scan(void)
    {
    static uint32_t B2_last_2s; // 记录B2按键按下的时间戳,用于判断长按

    static uint32_t last_tick; // 上次扫描的时间戳
    uint32_t current_tick = HAL_GetTick(); // 获取当前时间戳
    if(current_tick – last_tick < 20) return; // 去抖动处理,20ms内不重复扫描
    last_tick = current_tick; // 更新上次扫描时间

    /* 读取4个按键的当前状态 */
    B1_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); // 读取B1按键状态(PB0)
    B2_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); // 读取B2按键状态(PB1)
    B3_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2); // 读取B3按键状态(PB2)
    B4_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); // 读取B4按键状态(PA0)

    /* B1按键处理:切换LCD显示模式 */
    if(B1_last_state == 1 && B1_state == 0) // 检测B1按键下降沿(按下)
    {
    lcd_flag++; // 切换到下一个显示模式
    lcd_flag %= 3; // 循环切换(0-2)

    if(lcd_flag == PARA) // 进入参数设置界面
    {
    /* 保存当前参数到临时变量 */
    para_select = 0; // 重置参数选择索引
    g_param_temp_DS = g_param_DS; // 保存占空比步长
    g_param_temp_DR = g_param_DR; // 保存占空比范围
    g_param_temp_FS = g_param_FS; // 保存频率步长
    g_param_temp_FR = g_param_FR; // 保存频率范围
    }

    if(lcd_flag == PWM) // 离开参数设置界面,返回PWM显示界面
    {
    /* 验证参数有效性,确认后保存参数 */
    if(DEFAULT_BASE_DUTY_10 + g_param_temp_DS <= g_param_temp_DR &&
    DEFAULT_BASE_FREQ_1000 + g_param_temp_FS <= g_param_temp_FR)
    {
    g_param_DS = g_param_temp_DS; // 应用占空比步长
    g_param_DR = g_param_temp_DR; // 应用占空比范围
    g_param_FS = g_param_temp_FS; // 应用频率步长
    g_param_FR = g_param_temp_FR; // 应用频率范围
    }
    }
    }

    /* B2按键处理:参数选择/锁定解锁/复位 */
    if(B2_last_state == 1 && B2_state == 0) // 检测B2按键下降沿(按下)
    {
    if(lcd_flag == PARA) // 在参数设置界面
    {
    para_select++; // 切换到下一个参数
    para_select %= 4; // 循环切换(0-3,对应4个参数)
    }

    if(lcd_flag == PWM) // 在PWM显示界面
    {
    B2_last_2s = HAL_GetTick(); // 记录按键按下时间
    }
    }
    else if(B2_last_state == 0 && B2_state == 1) // 检测B2按键上升沿(释放)
    {
    if(lcd_flag == PWM) // 在PWM显示界面
    {
    if(HAL_GetTick() – B2_last_2s > 2000) // 按键按下超过2秒(长按)
    {
    uwTick = 0; // 复位系统运行时间计数器
    }
    else // 短按
    {
    g_lock_flag = !g_lock_flag; // 切换锁定/解锁状态
    }
    }
    }

    /* B3按键处理:增加参数值 */
    if(B3_last_state == 1 && B3_state == 0) // 检测B3按键下降沿(按下)
    {
    if(lcd_flag == PARA) // 在参数设置界面
    {
    switch(para_select) // 根据当前选择的参数类型进行处理
    {
    case PARAM_TYPE_DS: // 占空比步长
    g_param_temp_DS = (g_param_temp_DS < 100) ? (g_param_temp_DS + DEFAULT_DS_STEP_1) : 90;
    break;
    case PARAM_TYPE_DR: // 占空比范围
    g_param_temp_DR = (g_param_temp_DR < 100) ? (g_param_temp_DR + DEFAULT_DR_STEP_10) : 100;
    break;
    case PARAM_TYPE_FS: // 频率步长
    g_param_temp_FS += DEFAULT_FS_STEP_100;
    break;
    case PARAM_TYPE_FR: // 频率范围
    g_param_temp_FR += DEFAULT_FR_STEP_1000;
    break;
    }
    }
    }

    /* B4按键处理:减少参数值 */
    if(B4_last_state == 1 && B4_state == 0) // 检测B4按键下降沿(按下)
    {
    if(lcd_flag == PARA) // 在参数设置界面
    {
    switch(para_select) // 根据当前选择的参数类型进行处理
    {
    case PARAM_TYPE_DS: // 占空比步长
    g_param_temp_DS = (g_param_temp_DS > 0) ? (g_param_temp_DS – DEFAULT_DS_STEP_1) : 1;
    break;
    case PARAM_TYPE_DR: // 占空比范围
    g_param_temp_DR = (g_param_temp_DR > 0) ? (g_param_temp_DR – DEFAULT_DR_STEP_10) : 20;
    break;
    case PARAM_TYPE_FS: // 频率步长
    g_param_temp_FS = (g_param_temp_FS > 0) ? (g_param_temp_FS – DEFAULT_FS_STEP_100) : 100;
    break;
    case PARAM_TYPE_FR: // 频率范围
    g_param_temp_FR = (g_param_temp_FR > 0) ? (g_param_temp_FR – DEFAULT_FR_STEP_1000) : 1000;
    break;
    }
    }
    }

    /* 更新按键状态,用于下次检测边沿 */
    B1_last_state = B1_state; // 保存B1按键状态
    B2_last_state = B2_state; // 保存B2按键状态
    B3_last_state = B3_state; // 保存B3按键状态
    B4_last_state = B4_state; // 保存B4按键状态
    }

    7.handler.c

    #include "headfile.h"

    uint32_t g_pa15_freq;
    uint32_t capture;

    uint32_t initial_timer;
    uint8_t g_initial_flag;

    /**
    * @brief 定时器输入捕获回调函数
    * @param htim 定时器句柄指针
    * @note 用于测量PA15引脚输入的PWM信号频率
    * 使用TIM2的输入捕获模式,捕获信号周期并计算频率
    */
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    if(htim->Instance == TIM2) // 判断是否为TIM2触发中断
    {
    /* 读取输入捕获值 */
    capture = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1) + 1; // 读取TIM2通道1的捕获值(周期计数值),+1修正

    /* 计算输入信号频率 */
    g_pa15_freq = DEFAULT_TIM2_CLOCK_1MHZ / capture; // 频率 = 定时器时钟 / 捕获值(周期)
    }
    }

    8.lcd.c(修改部分)

    void LCD_Init(void)
    {
    uint16_t temp = GPIOC->ODR & 0xff00; //此处修改
    LCD_CtrlLinesConfig();
    dummy = LCD_ReadReg(0);

    if(dummy == 0x8230)
    {
    REG_8230_Init();
    }
    else
    {
    REG_932X_Init();
    }
    dummy = LCD_ReadReg(0);
    GPIOC->ODR = (GPIOC->ODR & 0x00ff) | temp; //此处修改
    }

    void LCD_Clear(u16 Color)
    {
    uint16_t temp = GPIOC->ODR & 0xff00; //此处修改
    u32 index = 0;
    LCD_SetCursor(0x00, 0x0000);
    LCD_WriteRAM_Prepare(); /* Prepare to write GRAM */
    for(index = 0; index < 76800; index++)
    {
    LCD_WriteRAM(Color);
    }
    GPIOC->ODR = (GPIOC->ODR & 0x00ff) | temp; //此处修改
    }

    void LCD_DisplayStringLine(u8 Line, u8 *ptr)
    {
    uint16_t temp = GPIOC->ODR & 0xff00; //此处修改
    u32 i = 0;
    u16 refcolumn = 319;//319;

    while ((*ptr != 0) && (i < 20)) //20
    {
    LCD_DisplayChar(Line, refcolumn, *ptr);
    refcolumn -= 16;
    ptr++;
    i++;
    }
    GPIOC->ODR = (GPIOC->ODR & 0x00ff) | temp; //此处修改
    }

    9.main.c

    #include "main.h"
    #include "adc.h"
    #include "tim.h"
    #include "gpio.h"

    /* Private includes ———————————————————-*/
    /* USER CODE BEGIN Includes */
    #include "headfile.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 */

    /* USER CODE END PFP */

    /* Private user code ———————————————————*/
    /* USER CODE BEGIN 0 */

    /* 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();//初始化GPIO
    MX_TIM2_Init();//初始化TIM2
    MX_ADC1_Init();//初始化ADC1
    MX_ADC2_Init();//初始化ADC2
    MX_TIM3_Init();//初始化TIM3
    /* USER CODE BEGIN 2 */
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);//打开锁存器
    LCD_Init(); //初始化LCD
    LCD_SetBackColor(Black); //设置背景颜色
    LCD_SetTextColor(White); //设置字体颜色
    LCD_Clear(Black); //清屏
    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); //开启TIM2通道1的中断
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); //开启TIM3通道2的PWM输出
    HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED); //校准ADC1
    HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED); //校准ADC2
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
    Initial_System();
    key_scan();//扫描按键

    lcd_show();//显示LCD

    change();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
    }

    10.源文件(含题目)

    通过网盘分享的文件:16届蓝桥杯嵌入式省赛真题
    链接: https://pan.baidu.com/s/1qzvEbgAE6wTdRKC5uCU2ug?pwd=thsi 提取码: thsi

    赞(0)
    未经允许不得转载:171主机测评 » 蓝桥杯嵌入式16届省赛真题(❤️附源文件❤️和超详细代码级注释❤️)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址