第十一届 蓝桥杯 嵌入式 省赛

一、分析

本届的风格又变了一番,但是难度也降低了些。

又是考察了 PWM 和 ADC。

第八、九届也考察了 PWM。建议先复习这两届,再回来模拟。

LCD的显示也提了额外的要求。

1. 功能概述

  • 电位器 R37 输出的模拟电压信号
  • PA6输出频率固定,占空比可调节
  • PA7输出频率固定,占空比可调节
  • 完成 B1、B2、B3、B4 按键动作
  • 完成 LCD 显示数据和参数

2. 性能要求

  • 数据显示界面下电压值更新时间:<= 0.1 秒
  • PA6、PA7输出信号占空比跟随相应时间:<=1秒
  • 按键相应时间:<=0.1 秒
  • 输出频率精度:<=5%
  • 输出占空比精度:<=5%

本届把性能要求单独列出来,好看多了。

3. 运行模式

这部分是关键,和往届考察的PWM不同

1)自动模式:PA6 和 PA7 输出信号占空比相同,

当 R37_Volt = 0,PA6 和 PA7 持续输出低电平。

当 R37_Volt = 3.3,PA6 和 PA7 持续输出高电平。

2)手动模式:PA6、PA7 输出信号占空比通过按键控制,与 R37_Volt 无关

这部分的伪代码,先把下面的先搞定,再来写这里的。

伪代码

cpp 复制代码
PWM_state_manu = 0;
PWM_state_auto = 0;
void Running_mode(){
    if(D_state == Data_Disp):
    if(mode == AUTO){
        if(Key_Down == 3){
            if(PWM_state_auto == 0)
            {
            // 80M/(80*10K)=100
            TIM_AUTORELOAD(&htim3, 9999);
            // 80M/(80*5K) =200
            TIM_AUTORELOAD(&htim17, 4999);

            TIM_COMPARE(&htim3, Channel_1, (unsigned int)(10000*((R37_Volt)/3.3)));
            TIM_COMPARE(&htim17, Channel_1, (unsigned int)(5000*((R37_Volt)/3.3)));
            PWM_state_auto = 1;
            }
            else if(PWM_state_auto == 1){
            // 80M/(80*10K)=100
            TIM_AUTORELOAD(&htim17, 9999);
            // 80M/(80*5K) =200
            TIM_AUTORELOAD(&htim3, 4999);

            TIM_COMPARE(&htim17, Channel_1, (unsigned int)(10000*((R37_Volt)/3.3)));
            TIM_COMPARE(&htim3, Channel_1, (unsigned int)(5000*((R37_Volt)/3.3)));
            PWM_state_auto = 0;
            }
        }
    }
    else if(mode == MANU)
    {
        //切换PA6和PA7的频率
        if(Key_Down == 3){
            if (PWM_state_manu == 0)
            {
                // 80M/(80*10K)=100
                TIM_AUTORELOAD(&htim3, 9999);
                // 80M/(80*5K) =200
                TIM_AUTORELOAD(&htim17, 4999);

                //频率改变后,占空比也要跟随,毕竟跟周期有关
                TIM_COMPARE(&htim3, Channel_1, (100*PA6_Duty));
                TIM_COMPARE(&htim17, Channel_1, (50*PA6_Duty));

                PWM_state_manu = 1;
            }
            else if(PWM_state_manu == 1)
            {
                // 80M/(80*10K)=100
                TIM_AUTORELOAD(&htim17, 9999);
                // 80M/(80*5K) =200
                TIM_AUTORELOAD(&htim3, 4999);

                //频率改变后,占空比也要跟随,毕竟跟周期有关
                TIM_COMPARE(&htim17, Channel_1, (100*PA6_Duty));
                TIM_COMPARE(&htim3, Channel_1, (50*PA6_Duty));

                 PWM_state_manu = 0;
            }
        }
    }

}

4. LCD 显示界面

本届直接把像素位置给标出来了,严格要求显示的细节。

1)数据显示界面

  • Data
  • 电压
  • 模式:自动或手动

2)参数显示界面

  • Para
  • PA6占空比
  • PA7占空比

3)显示颜色

  • 背景色:黑
  • 前景色:白

伪代码

cpp 复制代码
typedef enum{
    Data_Disp,
    Para_Disp
} Disp_State;

typedef enum{
    AUTO,
    MANU
} Mode;

const char* ModeStrings[] = {
    "AUTO",
    "MANU"
};

Disp_State D_state = Data_Disp;
Mode mode = AUTO;
float PA6_Duty, PA7_Duty;
float R37_Volt;
void Led_Proc(){
    //数据显示界面
    if(D_state == Data_Disp){
        LCD_display("Data");
        LCD_display("V:%f",R37_Volt);
        LCD_display("Mode",ModeStrings[mode]);
    }
    else if(D_state == Para_Disp){
        LCD_Clear(Black);
        LCD_display("Para");
        LCD_display("PA6:%f%%", PA6_Duty);
        LCD_display("PA6:%f%%", PA7_Duty);
    }
}

5. 按键功能

1)B1:界面切换

2)B2:参数设置下,手动模式下,PA6占空比加10%,数值边界(10%-90%),需要设置参数循环。

3)B3:参数设置下,手动模式下,PA7占空比加10%,数值边界(10%-90%),需要设置参数循环。

4)B4:切换模式

5)B3:数据显示下,互换PA6和PA7输出频率

伪代码

cpp 复制代码
unsigned int Key_Down;
Key_Proc(){
    //消抖不写了
    //按键模版不写了

    switch (Key_Down)
    {
    case 1:
        if(D_state == Data_Disp){
            LCD_Clear(Black);
            D_state = Para_Disp;
        }
        else if(D_state == Para_Disp){
            LCD_Clear(Black);
            D_state = Data_Disp;
        }
        break;
    case 2:
        if(D_state == Para_Disp){
            if(mode == MANU){
                if(PA6_Duty < 90){
                    PA6_Duty += 10;
                }
                else if(PA6_Duty = 90)
                    PA6_Duty = 10;
            }
        }
        break;
    case 3:
        if(D_state == Para_Disp){
            if(mode == MANU){
                if(PA7_Duty < 90){
                    PA7_Duty += 10;
                }
                else if(PA7_Duty = 90)
                    PA7_Duty = 10;
            }
        }
        else if (D_state == Data_Disp)
        {
            //切换频率这块单独写个函数
        }
        break;
    case 4:
        if(mode == AUTO)
            mode = MANU;
        else 
            mode == AUTO;
        break;
    }
}

6. LED 指示灯

1)自动模式,LD1亮;手动模式,LD1灭。

2)数据界面,LD2亮;参数界面,LD2灭。

cpp 复制代码
void Led_Proc(){
    if(mode == AUTO){
        ucled |= 0x01;
    }
    else if(mode == MANU){
        ucled &= ~0x01;
    }

    if(D_state == Data_Disp)
        ucled |= 0x02;
    else if(D_state == Para_Disp)
        ucled &= ~0x02;
}

7. 初始状态说明

1)自动模式

2)数据界面

3)10%占空比

4)PA6_f = 100Hz,PA7_f = 200Hz

占空比和频率在 CubeMx 调整。

二、CubeMx

Key 、LED 和 ADC 就不重复写了,重点是 PWM。

PWM

按图中配置的话,

那么此时改动 Pulse = 100,那么Duty = 100/999+1 = 10%

当然这些都是初始值,除了时钟频率和 PSC保持不变,其它都要在代码中改动。

当然 TIM17 和 TIM3 也一样。

三、完整代码编写

1. 全局变量

cpp 复制代码
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef enum{
	Data_Disp,
	Para_Disp
} Disp_State;

typedef enum{
	AUTO,
	MANU
} MODE;

const char* ModeString[] = {
	"AUTO",
	"MANU"
};

Disp_State D_state = Data_Disp;
MODE mode = AUTO;
/* USER CODE END PTD */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

uint16_t Lcd_Disp_Str[21];
float R37_Volt;
uint8_t PA6_Duty=10, PA7_Duty=10;
uint8_t Key_val, Key_up, Key_down, Key_old;
uint8_t ucled;

uint8_t PWM_state_manu = 0;
uint8_t PWM_state_auto = 0;
__IO uint16_t uwTick_Set_Lcd = 0;
__IO uint16_t uwTick_Set_Key = 0;
__IO uint16_t uwTick_Set_Led = 0;
__IO uint16_t uwTick_Set_PWM = 0;

/* USER CODE END PV */

2. 主函数

cpp 复制代码
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();
  MX_ADC2_Init();
  MX_TIM3_Init();
  MX_TIM17_Init();
  /* USER CODE BEGIN 2 */
	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 */
		Lcd_Proc();
		Key_Proc();
		Led_Proc();
		Running_mode();
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

3. 显示

cpp 复制代码
void Lcd_Proc(){
	if((uwTick_Set_Lcd - uwTick) < 100)
		return;
	uwTick = uwTick_Set_Lcd;

	R37_Volt = (((float)(Get_ADC2())/4096)*3.3);
	if(D_state == Data_Disp){
		
		sprintf((char*) Lcd_Disp_Str, "      Data");
		LCD_DisplayStringLine(Line0, Lcd_Disp_Str);
		
		sprintf((char*) Lcd_Disp_Str, "    V:%4.2fV", R37_Volt);
		LCD_DisplayStringLine(Line2, Lcd_Disp_Str);
		
		sprintf((char*) Lcd_Disp_Str, "    Mode:%s", ModeString[mode]);
		LCD_DisplayStringLine(Line4, Lcd_Disp_Str);
		
	}
	else if(D_state == Para_Disp){
		
		sprintf((char*) Lcd_Disp_Str, "      Para");
		LCD_DisplayStringLine(Line0, Lcd_Disp_Str);
		
		sprintf((char*) Lcd_Disp_Str, "    PA6:%02d%%", (unsigned int)PA6_Duty);
		LCD_DisplayStringLine(Line2, Lcd_Disp_Str);
		
		sprintf((char*) Lcd_Disp_Str, "    PA7:%02d%%", (unsigned int)PA7_Duty);
		LCD_DisplayStringLine(Line4, Lcd_Disp_Str);
		
	}	
}

4. 按键

cpp 复制代码
void Key_Proc(){
	if((uwTick_Set_Key - uwTick) < 100)
		return;
	uwTick = uwTick_Set_Key;

	Key_val = Key_Scan();
	Key_up = Key_val & (Key_val ^ Key_old);
	Key_down = ~Key_val & (Key_val ^ Key_old);
	Key_old = Key_val;
	
	switch(Key_down)
	{
		case 1:
			if(D_state == Data_Disp){
				LCD_Clear(Black);
				D_state = Para_Disp;
			}
			else if(D_state == Para_Disp){
				LCD_Clear(Black);
				D_state = Data_Disp;
			}
		break;
			
		case 2:
			if(D_state == Para_Disp){
				if(mode == MANU){
					if(PA6_Duty < 90){
						PA6_Duty += 10;
					}
					else if(PA6_Duty == 90)
						PA6_Duty = 10;
				}
			}
		break;
			
		case 3:
			if(D_state == Para_Disp){
				if(mode == MANU){
					if(PA7_Duty < 90){
						PA7_Duty += 10;
					}
					else if(PA7_Duty == 90)
						PA7_Duty = 10;
				}
			}
			
			break;
		
		case 4:
			if(mode == AUTO)
          mode = MANU;
			else 
					mode = AUTO;
			break;
	}
}

5. LED

cpp 复制代码
void Led_Proc(){
	if((uwTick_Set_Led - uwTick) < 100)
		return;
	uwTick = uwTick_Set_Led;
	
	if(mode == AUTO){
			ucled |= 0x01;
	}
	else if(mode == MANU){
			ucled &= ~(0x01);
	}

	if(D_state == Data_Disp)
			ucled |= 0x02;
	else if(D_state == Para_Disp)
			ucled &= ~(0x02);
	
	Led_Disp(ucled);
}

6. PWM

cpp 复制代码
void Running_mode(){
	if((uwTick_Set_PWM - uwTick) < 100)
		return;
	uwTick = uwTick_Set_PWM;
	if(D_state == Data_Disp)
	{
	if(Key_down == 3){
		if(mode == AUTO){
			if(PWM_state_auto == 0)
			{
				//80M/(80*10K)=100
				__HAL_TIM_SET_AUTORELOAD(&htim3, 9999);
				// 80M/(80*5K) =200
				__HAL_TIM_SET_AUTORELOAD(&htim17, 4999);
				
				__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (unsigned int)(10000*((R37_Volt)/3.3)));
				__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, (unsigned int)(5000*((R37_Volt)/3.3)));
				PWM_state_auto = 1;
			}
			if(PWM_state_auto == 1)
			{
				//80M/(80*10K)=100
				__HAL_TIM_SET_AUTORELOAD(&htim17, 9999);
				// 80M/(80*5K) =200
				__HAL_TIM_SET_AUTORELOAD(&htim3, 4999);
				
				__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, (unsigned int)(10000*((R37_Volt)/3.3)));
				__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (unsigned int)(5000*((R37_Volt)/3.3)));
				PWM_state_auto = 0;
			}
			
		}
		切换PA6和PA7的频率
		else if(mode == MANU){
			if(PWM_state_manu == 0)
			{
				__HAL_TIM_SET_AUTORELOAD(&htim3, 9999);
				__HAL_TIM_SET_AUTORELOAD(&htim17, 4999);
				
				//频率改变后,占空比也要跟随,毕竟跟周期有关
				__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (100*PA6_Duty));
				__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, (50*PA6_Duty));
				
				PWM_state_manu = 1;				
			}
			else if(PWM_state_manu == 1){
				__HAL_TIM_SET_AUTORELOAD(&htim17, 9999);
				__HAL_TIM_SET_AUTORELOAD(&htim3, 4999);
				
				//频率改变后,占空比也要跟随,毕竟跟周期有关
				__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, (100*PA6_Duty));
				__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (50*PA6_Duty));
				
				PWM_state_manu = 0;	
			}
		}
	}
}
}

7. BSP

这块不赘述了,背就完了

相关推荐
wuqingshun3141599 小时前
蓝桥杯 9. 九宫幻方
数据结构·c++·算法·职场和发展·蓝桥杯·深度优先
ChoSeitaku11 小时前
NO.95十六届蓝桥杯备战|图论基础-单源最短路|负环|BF判断负环|SPFA判断负环|邮递员送信|采购特价产品|拉近距离|最短路计数(C++)
c++·蓝桥杯·图论
SuperW21 小时前
蓝桥杯嵌入式十五届模拟三(串口、双ADC)
单片机·职场和发展·蓝桥杯
夏天的阳光吖1 天前
C++蓝桥杯实训篇(四)
开发语言·c++·蓝桥杯
小乐xiaole1 天前
蓝桥杯 2025 C++组 省 B 题解
c++·蓝桥杯·深度优先
写不出bug的小李1 天前
首次打蓝桥杯总结(c/c++B组)
职场和发展·蓝桥杯
SuperW2 天前
蓝桥杯嵌入式十六届赛前复习总结与准备
职场和发展·蓝桥杯
wuqingshun3141592 天前
经典算法 判断一个图中是否有环
java·开发语言·数据结构·c++·算法·蓝桥杯·深度优先
ChoSeitaku2 天前
NO.93十六届蓝桥杯备战|图论基础-拓扑排序|有向无环图|AOV网|摄像头|最大食物链计数|杂物(C++)
c++·蓝桥杯·图论
想成为配环境大佬2 天前
P8739 [蓝桥杯 2020 国 C] 重复字符串
算法·蓝桥杯·贪心