一、实验前准备
1.1利用STM32Cube创建工程并且打开工程

注释:配置好所用引脚
1.2ST-Link驱动




1.3下载CH340驱动以及程序下载软件(烧录)



1.4硬件连接

二、LED点亮原理

寿命与亮度有关系,亮度越大导致发热过大会减少LED寿命
为什么:核心原理:LED不是理想的光源
理想的光源会将所有电能100%转换成光能。但LED(以及几乎所有电光源)在实际工作中,其电光转换效率达不到100%。
根据情况对PA8,推挽输出,开漏输出都可以;PA3仅推挽输出
设置指定GPIO高低电平 由于下图文件使用STM32Cube建立无需使能时钟,引脚初始化因此可使用下述代码,如使用上图方法请参考3.4完整代码展示

由于上述文件使用STM32Cube建立无需使能时钟,引脚初始化因此可使用上述代码,如使用
三、设置指定GPIO高低电平的几种方法
3.1 使用HAL库函数(推荐)
// 设置指定GPIO引脚为低电平
HAL_GPIO_WritePin(GPIOx, GPIO_PIN_x, GPIO_PIN_RESET);
// 示例:设置GPIOA的PIN5为低电平
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
3.2 使用LL库(低层库)
// 设置引脚为低电平
LL_GPIO_ResetOutputPin(GPIOx, GPIO_PIN_x);
// 示例:设置GPIOB的PIN3为低电平
LL_GPIO_ResetOutputPin(GPIOB, GPIO_PIN_3);
3. 3直接操作寄存器
// 使用BSRR寄存器(推荐方式)
GPIOx->BSRR = (GPIO_PIN_x << 16); // 低16位用于置位,高16位用于复位
// 或使用ODR寄存器
GPIOx->ODR &= ~GPIO_PIN_x;
// 示例:设置GPIOC的PIN8为低电平
GPIOC->BSRR = (GPIO_PIN_8 << 16);
// 或
GPIOC->ODR &= ~GPIO_PIN_8;
3.4 完整示例代码
#include "main.h"
#include "stm32f1xx_hal.h" // 根据你的芯片系列选择
int main(void)
{
HAL_Init();
// 配置GPIO
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
// 配置PA5为推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 设置PA5为低电平
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
while (1)
{
// 主循环
}
}
注释:STM32 GPIO输出低电平四种方案优缺点对比
| 方案 | 优点 | 缺点 | 适用场景 | 性能等级 |
|---|---|---|---|---|
| HAL库函数 | • 代码可读性高 • 跨STM32系列兼容性好 • 易于维护和调试 • 提供完善的错误处理 • 适合初学者 | • 执行效率相对较低 • 代码体积较大 • 有函数调用开销 • 实时性稍差 | • 快速原型开发 • 跨平台项目 • 团队协作开发 • 对实时性要求不高的应用 | ★★★☆☆ |
| LL库(低层库) | • 执行效率高 • 代码体积小 • 接近寄存器操作的性能 • 保持一定的可移植性 • 较好的实时性 | • 可读性不如HAL • 需要更多硬件知识 • 兼容性稍差于HAL | • 对性能有要求的应用 • 资源受限环境 • 有经验的开发者 • 特定芯片优化 | ★★★★☆ |
| 直接操作寄存器 | • 最高执行效率 • 最小代码体积 • 最佳实时性 • 完全控制硬件 • 无函数调用开销 | • 可读性差 • 可移植性差 • 容易出错 • 调试困难 • 需要深入了解硬件 | • 极端性能要求 • 实时性关键应用 • 资深嵌入式工程师 • 特定优化场景 | ★★★★★ |
| 完整示例代码 | • 包含完整初始化流程 • 适合学习和参考 • 减少配置错误 • 提供完整上下文 | • 代码冗长 • 包含不必要部分 • 需要根据实际情况修改 | • 学习阶段 • 项目模板 • 调试参考 • 新手入门 | ★★☆☆☆ |
性能对比数据表
| 操作方式 | 执行时间 | 代码大小 | 可维护性 | 学习曲线 |
|---|---|---|---|---|
| HAL库 | 100% (基准) | 100% (基准) | 优秀 | 简单 |
| LL库 | 60-70% | 70-80% | 良好 | 中等 |
| 寄存器 | 30-50% | 50-60% | 一般 | 困难 |
选择指南表
| 项目阶段 | 推荐方案 | 替代方案 | 关键考量 |
|---|---|---|---|
| 学习入门 | HAL库 + 完整示例 | - | 易于理解,减少错误 |
| 快速原型 | HAL库 | LL库 | 开发速度快,调试方便 |
| 产品开发 | LL库 | HAL库 | 平衡性能和开发效率 |
| 性能优化 | 寄存器操作 | LL库 | 极致性能,资源利用最大化 |
| 团队项目 | HAL库/LL库 | - | 代码可维护性强,易于协作 |
3.5 重要注意事项
-
时钟使能:在使用GPIO前必须使能对应GPIO端口的时钟
-
GPIO配置:需要先将GPIO配置为输出模式
-
引脚定义:根据实际使用的引脚修改
GPIOx和GPIO_PIN_x
3.6 常用GPIO输出模式
-
GPIO_MODE_OUTPUT_PP:推挽输出 -
GPIO_MODE_OUTPUT_OD:开漏输出
对于大多数应用场景,推荐使用HAL库函数,因为代码可读性好且移植性强。
四、拓展
问题:PA8和PA5两个一起的时候亮,而注释掉PA8代码,只留PA5是不亮或者微亮
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
HAL_Delay(500);
1. 最可能的原因:PA5被复用为其他功能
STM32的PA5通常默认用作:
-
ADC输入通道5
-
SPI1 SCK引脚
-
调试接口
当PA8也在使用时,可能系统配置正确,但单独使用PA5时配置被覆盖。
改用其他引脚
2. 解决方案
方案1:检查并重新配置PA5
c
// 在初始化部分确保PA5正确配置
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 明确配置PA5为输出模式
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 重要:推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 初始状态
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
方案2:禁用PA5的复用功能
// 如果使用CubeMX,检查这些配置:
// - PA5不应用作ADC
// - PA5不应用于SPI1
// - 调试接口不影响PA5
// 或者在代码中强制禁用ADC
__HAL_RCC_ADC1_CLK_DISABLE(); // 禁用ADC时钟
方案3:完整的初始化修复
c
void GPIO_Init_Fixed(void)
{
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 先配置PA8
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 重新配置PA5,确保模式正确
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 确保是输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 设置初始状态
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}
3. 调试步骤
步骤1:检查GPIO配置寄存器
c
// 在调试时查看PA5的配置
uint32_t mode = GPIOA->MODER; // 模式寄存器
uint32_t otype = GPIOA->OTYPER; // 输出类型寄存器
// PA5应该配置为输出模式 (01)
// 第10-11位应该是01: 通用输出模式
printf("GPIOA MODER: 0x%08lX\n", mode);
printf("PA5 mode bits: %lu\n", (mode >> 10) & 0x3);
步骤2:使用不同的GPIO引脚测试
c
// 暂时改用其他引脚测试,如PA6、PA7
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
步骤3:电压测量
-
用万用表测量PA5单独工作时的电压
-
正常情况:
GPIO_PIN_RESET时应该是0V,GPIO_PIN_SET时应该是3.3V -
如果电压异常(如1.5V左右),说明配置错误
4. 如果使用CubeMX配置
在CubeMX中检查:
-
PA5的Pinout配置:应该是GPIO_Output
-
System Core → GPIO:PA5配置为Output Push Pull
-
Analog → ADC:确保PA5没有被用作ADC输入
-
Connectivity → SPI1:确保PA5没有被用作SPI SCK
5. 最终解决方案
c
#include "main.h"
// 修复PA5配置的函数
void Fix_PA5_Configuration(void)
{
// 禁用可能影响PA5的外设
__HAL_RCC_ADC1_CLK_DISABLE();
__HAL_RCC_SPI1_CLK_DISABLE();
// 重新配置PA5为输出
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
// 修复PA5配置
Fix_PA5_Configuration();
while (1)
{
// 现在PA5应该能正常工作了
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
HAL_Delay(500);
}
}
这个问题90%的可能性是PA5被配置为模拟输入模式(ADC默认模式),导致无法输出数字信号。按照上述方法重新配置即可解决!
五、成品展示


2115

被折叠的 条评论
为什么被折叠?



