- PWM输出:通过PA1引脚输出频率可调(4kHz或8kHz)的PWM信号,占空比通过电位器R37调节。
- 频率测量:通过PAY引脚捕获输入信号的频率,并转换为速度值。
- ADC检测:检测电位器R37的模拟电压信号。
- LCD显示:显示数据界面、参数界面和统计界面。
- 按键功能:通过按键切换界面、调整参数、锁定/解锁占空比等。
- LED指示灯:根据系统状态控制LED指示灯。
以下是基于STM32 HAL库的代码实现和详细解释。
CubeMX 配置
-
系统时钟配置:
- 配置系统时钟为最大频率(例如80MHz)。
-
配置TIM2为PWM输出:
- TIM2 → Channel 2 → PWM Generation CH2。
- Parameter Settings:
- Prescaler: 0
- Counter Mode: Up
- Period (ARR): 19999(初始频率4kHz)
- Pulse (CCR): 初始占空比(例如5000,对应25%)
- GPIO:确认PA1配置为复用推挽输出。
-
配置TIM3为输入捕获:
- TIM3 → Channel 1 → Input Capture Direct Mode。
- Parameter Settings:
- Prescaler: 79(得到1MHz时钟)
- Counter Mode: Up
- Period (ARR): 65535
- GPIO:确认PAY引脚配置为输入捕获。
-
配置ADC:
- ADC1 → Channel 0 → 选择电位器R37的引脚。
- Parameter Settings:
- Resolution: 12位
- Data Alignment: Right
- Continuous Conversion Mode: Enabled
- NVIC Settings:启用ADC中断。
-
配置按键和LED:
- GPIO:配置B1、B2、B3、B4为输入,LD1-LD8为输出。
-
配置LCD:
- 根据LCD型号配置SPI或I2C接口。
-
生成代码:
- 生成项目代码并打开工程。
代码实现
1. PWM输出和频率调整
// 定义全局变量
typedef enum {
PWM_LOW_FREQ_MODE,
PWM_HIGH_FREQ_MODE
} PWMMode;
PWMMode current_mode = PWM_LOW_FREQ_MODE;
float current_duty_cycle = 0.0f;
uint32_t target_freq = 0;
int32_t remaining_steps = 0;
int32_t freq_step = 0;
uint32_t timer_clock = 80000000; // 根据实际时钟调整
// 初始化PWM
void PWM_Init() {
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
current_duty_cycle = (float)__HAL_TIM_GET_COMPARE(&htim2, TIM_CHANNEL_2) / (__HAL_TIM_GET_AUTORELOAD(&htim2) + 1);
}
// 模式切换函数
void SwitchPWMMode(PWMMode new_mode) {
if(new_mode == current_mode) return;
target_freq = (new_mode == PWM_HIGH_FREQ_MODE) ? 8000 : 4000;
int32_t delta_f = (int32_t)target_freq - (int32_t)(current_mode == PWM_HIGH_FREQ_MODE ? 8000 : 4000);
freq_step = (delta_f > 0) ? 100 : -100;
remaining_steps = abs(delta_f) / 100;
HAL_TIM_Base_Start_IT(&htim6); // 启动TIM6中断
}
// TIM6中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if(htim == &htim6 && remaining_steps > 0) {
uint32_t new_freq = (current_mode == PWM_LOW_FREQ_MODE) ?
(4000 + (100 * (remaining_steps - (abs(target_freq - 4000)/100 - remaining_steps)))) :
(8000 - (100 * (remaining_steps - (abs(target_freq - 8000)/100 - remaining_steps))));
uint32_t new_arr = (timer_clock / new_freq) - 1;
uint32_t new_ccr = current_duty_cycle * (new_arr + 1);
__HAL_TIM_SET_AUTORELOAD(&htim2, new_arr);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, new_ccr);
TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_UPDATE);
remaining_steps--;
if(remaining_steps == 0) {
HAL_TIM_Base_Stop_IT(&htim6);
current_mode = (target_freq == 8000) ? PWM_HIGH_FREQ_MODE : PWM_LOW_FREQ_MODE;
}
}
}
2. 频率测量
// 定义全局变量
uint32_t captured_freq = 0;
float speed = 0.0f;
uint8_t R = 1, K = 1;
// TIM3输入捕获回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if(htim == &htim3) {
captured_freq = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);
speed = (captured_freq * 2 * 3.14 * R) / (100 * K); // 计算速度
}
}
3. ADC检测
// 定义全局变量
uint32_t adc_value = 0;
float voltage = 0.0f;
// ADC转换完成回调函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
if(hadc == &hadc1) {
adc_value = HAL_ADC_GetValue(&hadc1);
voltage = (adc_value * 3.3) / 4095; // 转换为电压值
current_duty_cycle = voltage / 3.3; // 更新占空比
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, current_duty_cycle * (__HAL_TIM_GET_AUTORELOAD(&htim2) + 1));
}
}
4. LCD显示
// 显示数据界面
void DisplayDataScreen() {
char buffer[50];
sprintf(buffer, "DATA M=%c P=%.1f V=%.1f", (current_mode == PWM_HIGH_FREQ_MODE) ? 'H' : 'L', current_duty_cycle * 100, speed);
LCD_DisplayString(0, 0, buffer);
}
// 显示参数界面
void DisplayParameterScreen() {
char buffer[50];
sprintf(buffer, "PARA R=%d K=%d", R, K);
LCD_DisplayString(0, 0, buffer);
}
// 显示统计界面
void DisplayStatisticsScreen() {
char buffer[50];
sprintf(buffer, "RECD N=%d MH=%.1f ML=%.1f", switch_count, max_speed_high, max_speed_low);
LCD_DisplayString(0, 0, buffer);
}
5. 按键处理
// 按键处理函数
void HandleButtons() {
if (HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) == GPIO_PIN_RESET) {
// 切换界面
current_screen = (current_screen + 1) % 3;
HAL_Delay(200); // 防抖
}
if (HAL_GPIO_ReadPin(B2_GPIO_Port, B2_Pin) == GPIO_PIN_RESET) {
// 切换模式或参数
if (current_screen == DATA_SCREEN) {
SwitchPWMMode((current_mode == PWM_LOW_FREQ_MODE) ? PWM_HIGH_FREQ_MODE : PWM_LOW_FREQ_MODE);
} else if (current_screen == PARA_SCREEN) {
selected_param = (selected_param == PARAM_R) ? PARAM_K : PARAM_R;
}
HAL_Delay(200); // 防抖
}
if (HAL_GPIO_ReadPin(B3_GPIO_Port, B3_Pin) == GPIO_PIN_RESET) {
// 增加参数
if (current_screen == PARA_SCREEN) {
if (selected_param == PARAM_R) R = (R % 10) + 1;
else K = (K % 10) + 1;
}
HAL_Delay(200); // 防抖
}
if (HAL_GPIO_ReadPin(B4_GPIO_Port, B4_Pin) == GPIO_PIN_RESET) {
// 长按锁定/解锁占空比
uint32_t start_time = HAL_GetTick();
while (HAL_GPIO_ReadPin(B4_GPIO_Port, B4_Pin) == GPIO_PIN_RESET) {
if (HAL_GetTick() - start_time > 2000) {
is_duty_locked = !is_duty_locked;
HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, is_duty_locked ? GPIO_PIN_SET : GPIO_PIN_RESET);
break;
}
}
HAL_Delay(200); // 防抖
}
}
6. 主函数
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_ADC1_Init();
MX_LCD_Init();
PWM_Init();
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
HAL_ADC_Start_IT(&hadc1);
while (1) {
HandleButtons();
switch (current_screen) {
case DATA_SCREEN: DisplayDataScreen(); break;
case PARA_SCREEN: DisplayParameterScreen(); break;
case STAT_SCREEN: DisplayStatisticsScreen(); break;
}
HAL_Delay(100);
}
}
代码解释
-
PWM输出:
- 使用TIM2生成PWM信号,通过调整ARR和CCR实现频率和占空比控制。
- 模式切换时,频率在5秒内均匀变化,步进值小于200Hz。
-
频率测量:
- 使用TIM3的输入捕获功能测量输入信号的频率,并转换为速度值。
-
ADC检测:
- 使用ADC1检测电位器R37的电压,实时更新PWM占空比。
-
LCD显示:
- 根据当前界面显示数据、参数或统计信息。
-
按键处理:
- 实现界面切换、模式切换、参数调整和占空比锁定/解锁功能。
-
主函数:
- 初始化硬件和外设,进入主循环处理按键和更新显示。
注意事项
- 时钟配置:确保系统时钟和定时器时钟正确。
- 中断优先级:合理配置中断优先级,避免冲突。
- 防抖处理:按键处理时加入防抖逻辑。
- 边界检查:参数调整时检查边界值,避免无效参数。