蓝桥杯嵌入式STM32G431RBT6保姆级外设配置指南(HAL库版,含LCD、ADC、PWM)
蓝桥杯嵌入式STM32G431RBT6实战开发全攻略:从HAL库配置到竞赛项目搭建
第一次拿到DK117E-G4开发板时,面对密密麻麻的引脚和陌生的HAL库,大多数嵌入式初学者都会感到无从下手。本文将带你从零开始,用最直观的方式掌握STM32G431RBT6的核心外设配置技巧。不同于零散的教程,我们采用项目驱动式学习——从点亮第一个LED开始,逐步实现LCD显示、ADC采样、PWM输出等竞赛必备功能,最终搭建完整的嵌入式系统框架。
1. 开发环境搭建与基础工程创建
在开始外设配置前,需要准备好开发环境。STM32CubeMX是ST官方提供的图形化配置工具,能自动生成初始化代码,大幅降低开发门槛。
软件安装:
- 下载并安装STM32CubeMX(最新版本)
- 安装Keil MDK-ARM或STM32CubeIDE开发环境
- 安装对应版本的STM32G4 HAL库
新建工程步骤:
# 在STM32CubeMX中选择MCU型号 STM32G431RBT6 # 配置时钟源为外部晶振8MHz RCC → HSE → Crystal/Ceramic Resonator # 生成代码时选择MDK-ARM工具链
注意:蓝桥杯竞赛中通常要求使用指定版本的开发工具,建议提前确认竞赛规则。
时钟树配置是许多初学者容易忽略的关键步骤。STM32G431的最高主频可达170MHz,但需要正确配置PLL参数:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| HSE频率 | 8MHz | 外部晶振频率 |
| PLLM分频 | 1 | 输入时钟预分频 |
| PLLN倍频 | 85 | 主PLL倍频系数 |
| PLLP分频 | 2 | 系统时钟分频 |
| SYSCLK | 170MHz | 最终系统时钟频率 |
2. GPIO配置与LED控制实战
LED控制是嵌入式开发的"Hello World",也是调试其他功能的基础。DK117E-G4开发板上提供了8个用户LED,对应PC8-PC15引脚。
2.1 CubeMX图形化配置
- 在Pinout视图中找到PC8-PC15引脚
- 将引脚模式设置为GPIO_Output
- 为每个引脚设置用户标签(如LED1、LED2等)
生成代码后,HAL库会自动创建初始化函数。我们可以在main.c中添加控制代码:
// 点亮所有LED void LED_AllOn(void) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10| GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13| GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET); } // LED流水灯效果 void LED_Flow(uint16_t delay) { for(int i=8; i<=15; i++) { HAL_GPIO_WritePin(GPIOC, 1<<i, GPIO_PIN_RESET); HAL_Delay(delay); HAL_GPIO_WritePin(GPIOC, 1<<i, GPIO_PIN_SET); } }2.2 按键输入检测
开发板上的用户按键连接在PB0(B1)和PB1(B2)引脚。配置步骤:
- 将PB0和PB1配置为GPIO_Input模式
- 启用上拉电阻(GPIO Pull-up/Pull-down选择Pull-up)
按键检测代码示例:
// 检测按键按下 uint8_t KEY_Read(uint8_t key) { if(key == 1) return (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET); else if(key == 2) return (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == GPIO_PIN_RESET); return 0; } // 在主循环中使用 if(KEY_Read(1)) { LED_AllOn(); } else { LED_AllOff(); }3. LCD显示屏驱动与图形界面开发
蓝桥杯竞赛中,LCD常用于显示系统状态、采集数据等信息。DK117E-G4采用128x64分辨率的LCD模块,通过FSMC接口连接。
3.1 FSMC接口配置
- 在CubeMX中启用FSMC控制器
- 选择FSMC_NE1片选信号(对应PG9引脚)
- 配置为LCD接口模式:
- 数据宽度:8位
- 地址建立时间:2个HCLK周期
- 数据建立时间:4个HCLK周期
关键引脚对应关系:
| LCD信号 | STM32引脚 | 说明 |
|---|---|---|
| CS | PG9 | 片选信号 |
| RS | PD11 | 命令/数据选择 |
| WR | PD5 | 写使能 |
| RD | PD4 | 读使能 |
| D0-D7 | PD14-PD15,PD0-PD5 | 数据总线 |
3.2 LCD驱动实现
基于HAL库的LCD初始化代码框架:
void LCD_Init(void) { // 硬件复位 HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET); HAL_Delay(20); HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET); HAL_Delay(20); // 发送初始化命令序列 LCD_WriteCmd(0xAE); // 关闭显示 LCD_WriteCmd(0xD5); // 设置显示时钟分频 LCD_WriteCmd(0x80); // 更多初始化命令... LCD_Clear(); // 清屏 LCD_WriteCmd(0xAF); // 开启显示 } // 显示字符串函数 void LCD_ShowString(uint8_t x, uint8_t y, char *str) { uint8_t i = 0; while(str[i] != '\0') { LCD_ShowChar(x + i*6, y, str[i]); i++; } }4. ADC采样与数据处理技巧
开发板上的电位器连接在PA1引脚,通过ADC1通道2进行采样。蓝桥杯竞赛中常要求实时显示电压值或进行阈值判断。
4.1 ADC配置步骤
- 在CubeMX中启用ADC1
- 配置通道2为单端输入
- 设置参数:
- 分辨率:12位(0-4095)
- 数据对齐:右对齐
- 扫描模式:禁用
- 连续转换模式:启用
ADC采样代码示例:
// 初始化ADC void ADC_Init(void) { hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.ScanConvMode = DISABLE; hadc1.Init.ContinuousConvMode = ENABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; HAL_ADC_Init(&hadc1); // 配置通道 ADC_ChannelConfTypeDef sConfig = {0}; sConfig.Channel = ADC_CHANNEL_2; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5; HAL_ADC_ConfigChannel(&hadc1, &sConfig); } // 获取ADC值并转换为电压 float Get_Voltage(void) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 10); uint32_t adcValue = HAL_ADC_GetValue(&hadc1); return (adcValue * 3.3f / 4095.0f); }4.2 软件滤波算法
实际应用中,ADC采样值常伴有噪声。以下是几种常用的滤波方法:
- 移动平均滤波:取最近N次采样的平均值
- 中值滤波:取N次采样的中间值
- 一阶滞后滤波:Y(n) = αX(n) + (1-α)Y(n-1)
移动平均滤波实现示例:
#define FILTER_LEN 10 float ADC_Filter(void) { static float buf[FILTER_LEN] = {0}; static uint8_t index = 0; float sum = 0; buf[index] = Get_Voltage(); index = (index + 1) % FILTER_LEN; for(uint8_t i=0; i<FILTER_LEN; i++) { sum += buf[i]; } return sum / FILTER_LEN; }5. PWM输出与电机控制应用
定时器的PWM功能在控制LED亮度、驱动电机等场景中广泛应用。我们以TIM1通道1(PA8引脚)为例,配置PWM输出。
5.1 PWM基础配置
- 在CubeMX中启用TIM1
- 配置通道1为PWM Generation CH1
- 设置参数:
- 预分频器(Prescaler):0
- 计数器周期(Counter Period):999
- 脉冲宽度(Pulse):初始值500
- 时钟分频(Clock Division):无分频
- 计数模式(Counter Mode):向上计数
PWM初始化代码:
void PWM_Init(void) { htim1.Instance = TIM1; htim1.Init.Prescaler = 0; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 999; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&htim1); TIM_OC_InitTypeDef sConfigOC = {0}; sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 500; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); } // 调整PWM占空比 void PWM_SetDuty(uint16_t duty) { __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty); }5.2 呼吸灯效果实现
结合PWM和定时器中断,可以创建平滑的呼吸灯效果:
// 在main.c中添加全局变量 uint16_t pwmVal = 0; uint8_t dir = 1; // 1:增加, 0:减少 // 在定时器中断回调函数中 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM2) { // 假设TIM2用于产生中断 if(dir) { pwmVal += 5; if(pwmVal >= 1000) dir = 0; } else { pwmVal -= 5; if(pwmVal <= 0) dir = 1; } PWM_SetDuty(pwmVal); } }6. 外设综合应用:完整竞赛项目框架
将前面学到的外设整合起来,我们可以构建一个典型的蓝桥杯竞赛项目框架,包含以下功能:
- 系统状态显示:在LCD上实时显示ADC采样值、PWM占空比等信息
- 用户交互:通过按键调整PWM参数或切换显示模式
- 数据处理:对ADC采样值进行滤波和算法处理
- 视觉反馈:用LED指示系统工作状态
主程序框架示例:
int main(void) { HAL_Init(); SystemClock_Config(); LED_Init(); KEY_Init(); LCD_Init(); ADC_Init(); PWM_Init(); char dispBuf[20]; float voltage = 0; uint16_t pwmDuty = 500; while(1) { // 读取按键并调整PWM if(KEY_Read(1) && pwmDuty < 990) pwmDuty += 10; if(KEY_Read(2) && pwmDuty > 10) pwmDuty -= 10; PWM_SetDuty(pwmDuty); // 读取并滤波ADC值 voltage = ADC_Filter(); // LCD显示 sprintf(dispBuf, "Voltage: %.2fV", voltage); LCD_ShowString(0, 0, dispBuf); sprintf(dispBuf, "PWM Duty: %d", pwmDuty/10); LCD_ShowString(0, 1, dispBuf); // LED状态指示 if(voltage > 2.5) LED_On(1); else LED_Off(1); HAL_Delay(100); } }在实际竞赛中,还需要考虑以下优化点:
- 降低功耗:合理配置外设时钟,不使用时关闭
- 提高实时性:使用中断代替轮询
- 增强鲁棒性:添加异常处理机制
- 模块化设计:将不同功能封装成独立模块
