一、分析
本届的风格又变了一番,但是难度也降低了些。
又是考察了 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
这块不赘述了,背就完了