蓝桥杯嵌入式第15届第二套省赛满分工程——附工程链接
本文分享蓝桥杯第15届嵌入式省赛题目解析与实现方案,重点分析了PWM波形控制系统的设计思路。通过CubeMX配置定时器2/15生成PWM信号,详细说明参数计算方法(如ARR=80M/8/200=50000)。代码部分展示了LCD显示界面切换逻辑与按键处理功能,包括电压阈值设定、模式切换及锁定机制。文章强调竞赛题目思维深度的提升趋势,建议通过借鉴解题思路来提升备赛效率,同时提供了可复用的嵌入式开发框
前言
近年来,蓝桥杯嵌入式竞赛的题目设计逐渐向思维深度和创新性倾斜,传统“刷题式”备考的效果正在减弱。面对这一趋势,借鉴他人的解题思路和分析方法,往往能帮助参赛者更快抓住问题核心,实现事半功倍。
本次分享聚焦蓝桥杯第15届第二套省赛的这道典型题目,这些题目有一定难度。通过拆解题目背景、关键思路和优化技巧,希望能为读者提供一种可参考的思考范式。需说明的是,所有观点仅为个人经验总结,未必完全适用所有场景,欢迎交流指正。
(注:若需具体题目解析或进一步讨论,可随时补充细节。)
————————————————
题目

Cubemx配置


定时器2的配置
定时器15的配置
定时器6的配置
定时器6,我作为按键消抖用的,定时器2和15,你们可以在PA1、PA2引脚上任选其中可以配置的定时器。
预分配我习惯设为8-1,这样有时候频率比较大的时候的精确度可以大一些,看你个人习惯。
ARR:一般选设为题目的初始条件来设定;

题目初始条件为低速,也就是200Hz。
所以ARR=时钟频率/预分配系数/频率
ARR=80M/8/200=50000,我这里设成5000了,你们修改一下就好了。
或者你们可以像我一样在程序里面改。
TIM2->ARR = 50000; // 设置TIM2自动重装载值(决定PWM频率)
TIM15->ARR = 50000; // 设置TIM15自动重装载值
代码实现
LCD显示
void lcd_proc()
{
char text[30];
if (view == 0)// 数据视图
{
sprintf(text, " DATA ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text, " A=%0.1fV ", adc_f);// 显示ADC电压
LCD_DisplayStringLine(Line3, (uint8_t *)text);
sprintf(text, " P=%d%%-%d%% ", duty1, duty2);// 显示双路占空比
LCD_DisplayStringLine(Line4, (uint8_t *)text);
if (mode == 0)sprintf(text, " MODE=L ");
else sprintf(text, " MODE=H ");// 模式指示
LCD_DisplayStringLine(Line5, (uint8_t *)text);
if (lock == 0)sprintf(text, " LOCK=N ");
else if (lock == 1)sprintf(text, " LOCK=Y "); // 锁定状态
LCD_DisplayStringLine(Line6, (uint8_t *)text);
}
if (view == 1) // 参数设置视图
{
sprintf(text, " PARA ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text, " PU=%0.1fV ", Pu);
LCD_DisplayStringLine(Line3, (uint8_t *)text);
sprintf(text, " PD=%0.1fV ", Pd);
LCD_DisplayStringLine(Line4, (uint8_t *)text);
sprintf(text, " ");
LCD_DisplayStringLine(Line5, (uint8_t *)text);
sprintf(text, " ");
LCD_DisplayStringLine(Line6, (uint8_t *)text);
}
if (view == 2)// 错误视图
{
sprintf(text, " ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text, " ");
LCD_DisplayStringLine(Line3, (uint8_t *)text);
sprintf(text, " ERROR ");
LCD_DisplayStringLine(Line4, (uint8_t *)text);
sprintf(text, " ");
LCD_DisplayStringLine(Line5, (uint8_t *)text);
sprintf(text, " ");
LCD_DisplayStringLine(Line6, (uint8_t *)text);
}
}
按键操作
void key_proc()
{
if (bkey[1].flag == 1) {
view = (view + 1) % 2; // 在0/1视图间切换
choose_ud = 0; // 重置参数选择
/* 检查阈值有效性 */
if ((int)(Pu * 10) <= (int)(Pd * 10)) {
error = 1; // 触发错误状态
view = 2; // 跳转到错误视图
}
if (error == 0) { // 有效时更新阈值
Pu_r = Pu;
Pd_r = Pd;
}
bkey[1].flag = 0; // 清除按键标志
}
// 按键2短按:模式切换/参数选择
if (bkey[2].flag == 1) {
if (view == 1) choose_ud = !choose_ud; // 在参数视图切换选择对象
if (lock == 0) { // 未锁定时可改模式
if (view == 0) mode = !mode; // 在数据视图切换工作模式
// 根据模式调整PWM频率
if (mode == 0) { // 低频模式
TIM2->ARR = 50000;
TIM15->ARR = 50000;
} else if (mode == 1) { // 高频模式
TIM2->ARR = 5000;
TIM15->ARR = 5000;
}
}
bkey[2].flag = 0;
}
// 按键3短按:增加选定参数值
if (bkey[3].flag == 1) {
if (view == 1) {
if (choose_ud == 0) { // 增加上限
Pu += 0.3f;
if (Pu > 3.4f) Pu = 3.3f; // 限制最大值
} else { // 增加下限
Pd += 0.3f;
if (Pd > 3.4f) Pd = 3.3f;
}
}
bkey[3].flag = 0;
}
// 按键4短按:减小选定参数值/解锁
if (bkey[4].flag == 1) {
if (view == 1) {
if (choose_ud == 0) { // 减小上限
Pu -= 0.3f;
if (Pu < -0.1f) Pu = 0.0f;
} else { // 减小下限
Pd -= 0.3f;
if (Pd < -0.1f) Pd = 0.0f;
}
}
if (lock == 1 && view == 0) lock = 0; // 在数据视图解锁
bkey[4].flag = 0;
}
// 按键4长按:锁定参数
if (bkey[4].long_flag == 1) {
if(view == 0) lock = 1; // 在数据视图锁定
bkey[4].long_flag = 0;
}
//给其他长按清零,不清掉的话有时候测评点会出问题
if (bkey[3].long_flag == 1)
{
bkey[3].long_flag = 0;
}
if (bkey[2].long_flag == 1)
{
bkey[2].long_flag = 0;
}
if (bkey[1].long_flag == 1)
{
bkey[1].long_flag = 0;
}
}
定时器中断
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6) { // 处理TIM6中断(10ms周期)
key_serv(); // 按键扫描服务
if (error == 1) { // 错误状态处理
time_3s++; // 递增计数器
if (time_3s > 299) { // 3秒超时(300 * 10ms)
time_3s = 0;
error = 0; // 清除错误
Pu = Pu_r; // 恢复原阈值
Pd = Pd_r;
view = 1; // 返回参数视图
}
}
}
}
4t测试满分:
总结
此文仅代表个人愚见。
15届第二套省赛满分工程链接
视频讲解
其他届满分工程及解析
更多推荐



所有评论(0)