2. 嵌入式系统基础:STM32微控制器介绍、GPIO、定时器、中断系统
各位同学,欢迎来到第二章。这一章我们聊聊嵌入式系统的核心——STM32微控制器。说实话,我当年刚接触STM32时,也被它那几百页的参考手册吓到过。但别担心,咱们今天只讲最实用的三个模块:GPIO、定时器和中断系统。掌握了这些,你就能让电梯的按钮亮起来、让电机转起来、让系统响应各种突发状况。
2.1 STM32微控制器概览
STM32是意法半导体推出的32位ARM Cortex-M系列微控制器。为什么电梯系统选它?因为它的性价比高、外设丰富、生态成熟。我在项目中用过F1、F4、G0系列,各有千秋。
| 系列 | 内核 | 主频 | 适合场景 |
|---|---|---|---|
| STM32F1 | Cortex-M3 | 72MHz | 通用控制,成本敏感 |
| STM32F4 | Cortex-M4 | 168MHz | 需要DSP或浮点运算 |
| STM32G0 | Cortex-M0+ | 64MHz | 低功耗、小封装 |
我个人习惯在电梯控制板上用STM32F103系列。为什么?因为它的定时器资源丰富,而且市面上能找到大量参考设计。你想想看,电梯系统需要同时处理多个楼层呼叫、门控信号、安全回路,外设不够用可不行。
2.2 GPIO:让芯片与外界对话
GPIO(通用输入输出引脚)是芯片最基础的接口。说白了,它就是芯片的“手脚”——用来读取按钮状态、控制指示灯、驱动继电器。
2.2.1 GPIO的工作模式
STM32的GPIO有8种模式,但咱们做电梯系统,常用的就这几种:
- 推挽输出:驱动LED、继电器。输出高电平时拉高,低电平时拉低。
- 浮空输入:读取按钮状态。注意,这种模式下引脚电平不确定,需要外部上拉或下拉。
- 上拉/下拉输入:内部集成电阻,省去外部元件。我建议在电梯门锁检测上用内部上拉,省一个电阻就是省一分钱。
- 复用功能:比如把引脚配置为定时器输出或串口通信。
重点:配置GPIO时,别忘了开启对应的时钟!我见过太多新手调了半天GPIO没反应,结果发现是RCC时钟没开。嗯,这个坑我踩过。
2.2.2 代码示例:点亮电梯楼层指示灯
// 初始化PB0为推挽输出,用于控制1楼指示灯
void LED_Init(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 开启时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
// 点亮1楼指示灯
void Floor1_LED_On(void) {
GPIO_SetBits(GPIOB, GPIO_Pin_0);
}
// 熄灭1楼指示灯
void Floor1_LED_Off(void) {
GPIO_ResetBits(GPIOB, GPIO_Pin_0);
}
小技巧:我习惯用位带操作来快速翻转GPIO。比如 PBout(0) = 1; 比调用函数快得多。在电梯高速运行时,这点时间差可能决定系统能否及时响应。
2.3 定时器:精确控制时间
电梯系统里,定时器无处不在。电机加速/减速需要PWM、门控延时需要计时、故障检测需要超时判断。STM32的定时器分为三类:基本定时器、通用定时器、高级定时器。
2.3.1 定时器的工作原理
定时器的核心是一个不断递增(或递减)的计数器。当计数值达到预设的自动重装载值时,就会产生更新事件或中断。你可以把它想象成一个秒表——到了设定时间就“叮”一声。
举个例子:系统时钟72MHz,预分频器设为7200-1,那么定时器时钟就是72MHz/7200 = 10kHz。如果自动重装载值设为10000-1,那么定时器每1秒溢出一次。
注意:预分频器和自动重装载值都要减1!因为计数器是从0开始计数的。我曾经在调试电梯门控延时程序时,忘了减1,结果门关了2秒就自动打开,差点夹到人。
2.3.2 代码示例:定时器中断实现1秒闪烁
// 定时器2初始化,产生1秒中断
void TIM2_Init(void) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 72MHz/7200 = 10kHz
TIM_TimeBaseStructure.TIM_Period = 10000 - 1; // 10kHz/10000 = 1Hz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能更新中断
TIM_Cmd(TIM2, ENABLE); // 启动定时器
}
// 定时器2中断服务函数
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// 在这里翻转LED,实现1秒闪烁
PBout(0) = !PBout(0);
}
}
2.4 中断系统:让CPU及时响应紧急事件
中断是嵌入式系统的灵魂。没有中断,CPU就得轮询所有外设,效率极低。在电梯系统里,中断用来处理最紧急的事情——比如安全回路断开、急停按钮按下、平层传感器触发。
2.4.1 NVIC与中断优先级
STM32使用嵌套向量中断控制器(NVIC)来管理中断。每个中断源都可以设置抢占优先级和子优先级。抢占优先级高的中断可以打断优先级低的中断——这就是“嵌套”的含义。
我个人建议:在电梯系统中,安全相关的中断(如门锁检测、超速保护)设为最高优先级。为什么?因为人命关天。我曾经在一个项目中,把串口中断优先级设得比安全回路中断还高,结果串口数据量一大,安全回路响应延迟了十几毫秒。嗯,这个教训让我记住了:优先级分配要按紧急程度,而不是按方便程度。
2.4.2 外部中断配置步骤
- 配置GPIO为输入模式(上拉或下拉)
- 将GPIO连接到EXTI(外部中断/事件控制器)
- 配置EXTI的触发方式(上升沿、下降沿或双边沿)
- 配置NVIC,使能中断并设置优先级
- 编写中断服务函数
2.4.3 代码示例:急停按钮中断
// 配置PA0为外部中断,下降沿触发(按钮按下时)
void EXTI0_Init(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 内部上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 最高优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
// 急停中断服务函数
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
// 急停按钮被按下!立即停止电梯
Motor_Stop(); // 停止电机
Brake_Engage(); // 抱闸
Door_Unlock(); // 开门
SetAlarm(); // 发出警报
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
避坑指南:中断服务函数里不要做复杂操作!我曾经在中断里调用printf打印调试信息,结果串口还没发完,下一个中断又来了,系统直接卡死。记住:中断里只做最紧急的事,比如置标志位、清中断标志。具体的处理逻辑放到主循环里做。
2.5 本章小结
这一章我们聊了STM32的三大基础模块:GPIO是芯片的“手脚”,定时器是“时钟”,中断是“警报系统”。在电梯故障诊断与自检系统中,这三者缺一不可。下一章,我们将把这些知识应用到实际的电梯控制逻辑中。
最后送大家一句话:嵌入式开发没有捷径,多动手、多踩坑、多总结。我在这个行业干了十几年,最深的体会就是——纸上得来终觉浅,绝知此事要躬行。