1. 项目概述
最近用STM32F103C8T6做了一个基于矩阵键盘的密码锁系统,通过4×4矩阵键盘输入密码,验证成功后驱动舵机动作。整个项目使用STM32CubeMX进行硬件配置,采用HAL库开发。系统还集成了OLED显示屏用于交互反馈,通过串口进行调试信息输出。
这个项目非常适合嵌入式初学者练手,涵盖了GPIO控制、定时器PWM输出、矩阵键盘扫描、OLED驱动等多个常用外设的使用。下面我会详细分享整个开发过程,包括硬件设计、软件实现和调试经验。
2. 硬件设计与CubeMX配置
2.1 核心硬件选型
主控选用STM32F103C8T6,这是一款性价比极高的Cortex-M3内核MCU,具有丰富的外设资源:
- 72MHz主频
- 64KB Flash
- 20KB RAM
- 多个定时器
- USART、I2C等通信接口
矩阵键盘采用常见的4×4薄膜键盘,16个按键可以满足数字输入需求。舵机使用SG90,工作电压4.8-6V,PWM控制信号周期20ms。
2.2 CubeMX引脚配置
在CubeMX中完成以下引脚配置:
-
矩阵键盘接口:
- 行线(PB4-PB7):推挽输出,无上下拉
- 列线(PB8-PB11):上拉输入
-
PWM输出:
- TIM2 Channel1(PA0)输出PWM信号控制舵机
- 频率50Hz(周期20ms),脉宽0.5-2.5ms对应0-180度
-
OLED显示:
- I2C接口(PA6-SCL, PA7-SDA)
-
调试接口:
- USART2用于调试信息输出
2.3 定时器配置
配置TIM2产生PWM信号:
- 时钟源:内部时钟
- Prescaler:71 (72MHz/(71+1)=1MHz)
- Counter Period:19999 (1MHz/20000=50Hz)
- Pulse:初始值设为5 (对应0.5ms脉宽)
3. 软件实现详解
3.1 矩阵键盘扫描实现
矩阵键盘扫描采用行列反转法,核心代码如下:
c复制uint16_t Key_scan(void)
{
uint16_t Key_val=0;
uint16_t temp;
// 扫描第一行
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
if((GPIOB->IDR & 0x780) != 0x780) {
HAL_Delay(100); // 消抖
if((GPIOB->IDR & 0xF00) != 0xF00) {
temp = (GPIOB->IDR & 0xF70);
switch(temp) {
case 0xE70 : Key_val = 1; break;
case 0xD70 : Key_val = 2; break;
// 其他按键处理...
}
}
}
// 其他行扫描类似...
return Key_val;
}
3.2 密码验证逻辑
设置6位密码"223132",通过状态机实现密码验证:
c复制while(1) {
KeyBord_Num = Key_scan();
if(KeyBord_Num!=0) {
switch(input_index) {
case 0: KeyBord_Num1=KeyBord_Num; break;
// 其他位处理...
case 5:
KeyBord_Num6=KeyBord_Num;
if(KeyBord_Num1==2 && KeyBord_Num2==2 &&
KeyBord_Num3==3 && KeyBord_Num4==1 &&
KeyBord_Num5==3 && KeyBord_Num6==2) {
Status = PasswordRight;
} else {
Status = PasswordWrong;
}
break;
}
if(input_index <5) input_index++;
HAL_Delay(100);
}
if(Status == PasswordRight) {
// 密码正确处理
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,25); // 舵机转动
} else if(Status == PasswordWrong) {
// 密码错误处理
}
}
3.3 OLED显示驱动
OLED通过软件模拟I2C驱动,实现了字符、字符串、数字等显示功能:
c复制void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line-1)*2, (Column-1)*8);
for(i=0;i<8;i++) {
OLED_WriteData(OLED_F8x16[Char-' '][i]);
}
OLED_SetCursor((Line-1)*2+1, (Column-1)*8);
for(i=0;i<8;i++) {
OLED_WriteData(OLED_F8x16[Char-' '][i+8]);
}
}
4. 关键问题与解决方案
4.1 矩阵键盘抖动问题
现象:按键按下时经常检测到多次触发
解决:
- 硬件消抖:在按键两端并联0.1uF电容
- 软件消抖:检测到按键后延时100ms再次确认
4.2 舵机控制不准确
现象:舵机角度与预期不符
解决:
- 确保PWM频率准确为50Hz
- 调整Pulse值范围,实测5-25对应0-180度
- 供电要充足,建议单独电源供电
4.3 OLED显示异常
现象:显示乱码或无法显示
解决:
- 检查I2C地址是否正确(0x78)
- 确认初始化序列完整
- 检查电源电压是否稳定(3.3V)
5. 项目优化建议
-
密码存储安全:
- 当前密码硬编码在代码中,不安全
- 可改为EEPROM存储,支持密码修改
-
用户交互增强:
- 增加按键音反馈
- OLED显示输入掩码(*号代替数字)
-
低功耗设计:
- 无操作时进入低功耗模式
- 通过按键唤醒
-
多级权限:
- 设置用户密码和管理员密码
- 不同权限对应不同功能
6. 完整代码结构
项目主要包含以下文件:
main.c:主程序,包含密码验证逻辑oled.c:OLED显示驱动oled.h:OLED显示接口OLED_Font.h:字库数据stm32f1xx_hal_conf.h:HAL库配置
关键函数:
Key_scan():矩阵键盘扫描OLED_ShowString():OLED字符串显示HAL_TIM_PWM_Start():PWM输出启动__HAL_TIM_SET_COMPARE():设置PWM占空比
7. 实际测试效果
系统实现了以下功能:
- 通过矩阵键盘输入6位密码
- OLED实时显示输入状态
- 密码正确时舵机转动90度
- 密码错误时OLED显示错误信息
- 串口输出调试信息
测试中发现的问题及解决:
- 初始上电时舵机抖动 → 增加初始化时的延时
- 快速按键输入会漏检 → 优化扫描算法,减少延时时间
- OLED在低温下显示异常 → 增加初始化重试机制
8. 开发心得
-
HAL库使用:
- 相比标准库,HAL库初始化更简单
- 但执行效率稍低,关键部位可混合使用LL库
-
调试技巧:
- 善用串口打印调试信息
- 使用逻辑分析仪抓取PWM波形
- 分段测试各模块功能
-
性能优化:
- 矩阵键盘扫描放在定时器中断中
- OLED使用局部刷新代替全局刷新
- 关键代码使用寄存器操作
这个项目虽然不大,但涵盖了嵌入式开发的多个基础知识点。通过实践,我对STM32的外设使用、HAL库开发、硬件调试等都有了更深入的理解。特别是对状态机的应用,让我的编程思维有了很大提升。
