基于STM32的出租车计价器实验例程
一、实验概述
出租车计价器是嵌入式系统经典应用,核心功能是里程计量、费用计算、时间计费及人机交互。本实验基于STM32F103C8T6,通过霍尔传感器测里程、定时器计时、LCD显示费用,实现“起步价+里程费+等候费”的计费逻辑,支持按键设置和参数存储。
二、系统架构
graph TD
A[霍尔传感器] -->|里程脉冲| B(STM32外部中断)
C[按键模块] -->|设置参数| B
B -->|数据处理| D[计费算法]
D -->|费用/里程| E[LCD1602显示]
F[定时器] -->|计时/低速检测| D
G[EEPROM] -->|存储参数| D
- 传感器:霍尔传感器(安装在车轮,每转1圈输出1个脉冲,车轮周长≈2m);
- 主控:STM32F103C8T6(72MHz,定时器、外部中断、GPIO控制);
- 显示:LCD1602(并行接口,显示里程、费用、时间);
- 存储:AT24C02 EEPROM(存储起步价、单价等参数);
- 按键:4个独立按键(启动/停止、设置、加、减)。
三、硬件设计
1. 核心组件选型
| 组件 | 型号/规格 | 功能说明 |
|---|---|---|
| 主控芯片 | STM32F103C8T6 | 32位ARM Cortex-M3,72MHz |
| 里程传感器 | AH3144霍尔传感器 | 开关型,输出脉冲(5V TTL) |
| 显示模块 | LCD1602(并行) | 16×2字符显示(里程、费用) |
| 存储芯片 | AT24C02(I2C) | 256字节EEPROM,存储参数 |
| 按键 | 4×轻触开关 | 启动/停止、设置、加、减 |
| 电源 | 5V DC | 供电(STM32+LCD+传感器) |
2. 硬件连接
| 模块 | 引脚 | STM32引脚 | 说明 |
|---|---|---|---|
| 霍尔传感器 | OUT | PA0(EXTI0) | 外部中断0,上升沿触发 |
| LCD1602 | RS | PB0 | 寄存器选择 |
| RW | PB1 | 读写控制(接地=只写) | |
| EN | PB2 | 使能信号 | |
| D4-D7 | PB4-PB7 | 4位数据线(简化驱动) | |
| AT24C02 | SDA | PB10(I2C2_SDA) | I2C数据线 |
| SCL | PB11(I2C2_SCL) | I2C时钟线 | |
| 按键 | 启动/停止 | PC0 | 低电平有效 |
| 设置 | PC1 | 低电平有效 | |
| 加 | PC2 | 低电平有效 | |
| 减 | PC3 | 低电平有效 |
四、STM32CubeMX配置
1. 时钟配置
- 系统时钟树:HSE=8MHz → PLL×9 → 72MHz SYSCLK;
- APB1/APB2:36MHz/72MHz(定时器时钟源为APB1/APB2)。
2. 外设配置
(1)外部中断(霍尔传感器)
- 引脚:PA0(EXTI0),上升沿触发;
- NVIC:启用EXTI0中断(优先级中等)。
(2)定时器(计时/低速检测)
- TIM2:通用定时器,1ms中断(用于计时和脉冲计数);
- 预分频器:72-1(72MHz/72=1MHz,1μs计数);
- 自动重装载值:1000-1(1ms中断)。
(3)I2C(AT24C02)
- I2C2:标准模式(100kHz),PB10(SDA)、PB11(SCL),开漏输出+4.7kΩ上拉。
(4)GPIO(按键与LCD)
- 按键:PC0-PC3,输入模式,上拉电阻;
- LCD:PB0(RS)、PB1(RW)、PB2(EN)、PB4-PB7(D4-D7),推挽输出。
五、软件设计
1. 核心模块实现
(1)霍尔传感器脉冲计数(外部中断)
// 全局变量
volatile uint32_t pulse_count = 0; // 脉冲总数(1脉冲=2m)
volatile float total_distance = 0.0f; // 总里程(km)
// 外部中断0回调函数(PA0,霍尔传感器)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GPIO_PIN_0) {
pulse_count++; // 每脉冲计数+1
total_distance = (pulse_count * 2.0f) / 1000.0f; // 2m/脉冲 → km
}
}
(2)定时器计时(低速检测与等候费)
// 全局变量
uint32_t timer_count = 0; // 1ms计数
uint32_t wait_time = 0; // 等候时间(秒)
uint8_t is_moving = 0; // 行驶状态(1=行驶,0=等候)
// TIM2中断回调函数(1ms)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
timer_count++;
if (timer_count % 1000 == 0) { // 1秒
if (is_moving) {
// 行驶中:检测速度(假设车速<12km/h为低速)
float speed = (pulse_count * 2.0f) / (timer_count * 0.001f) * 3.6f; // km/h
if (speed < 12.0f) {
wait_time++; // 低速→等候时间+1秒
}
} else {
wait_time++; // 停车等候→时间+1秒
}
timer_count = 0;
}
}
}
(3)计费算法(核心逻辑)
// 计费参数(存储在EEPROM)
typedef struct {
float start_price; // 起步价(3km内,元)
float per_km; // 超起步里程单价(元/km)
float per_minute; // 等候费(元/分钟)
float start_km; // 起步里程(km)
} PriceParam;
PriceParam price = {10.0f, 2.0f, 0.5f, 3.0f}; // 默认参数
float total_fee = 0.0f; // 总费用
// 计算费用
void CalculateFee(void) {
if (total_distance <= price.start_km) {
total_fee = price.start_price; // 未超起步里程
} else {
// 超起步里程费用 + 等候费
float extra_km = total_distance - price.start_km;
total_fee = price.start_price + extra_km * price.per_km;
}
// 等候费(每分钟0.5元)
total_fee += (wait_time / 60.0f) * price.per_minute;
}
(4)LCD1602显示驱动
// LCD写命令/数据
void LCD_WriteCmd(uint8_t cmd) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // RS=0(命令)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); // RW=0(写)
GPIOB->ODR = (GPIOB->ODR & 0x0F) | (cmd & 0xF0); // 高4位
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET); // EN=1
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET); // EN=0
// 低4位
GPIOB->ODR = (GPIOB->ODR & 0x0F) | ((cmd << 4) & 0xF0);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
}
// 显示里程和费用
void LCD_ShowData(void) {
char buf[16];
// 第一行:里程
sprintf(buf, "Dist:%.2fkm", total_distance);
LCD_WriteCmd(0x80); // 第一行首地址
for (uint8_t i=0; i<16; i++) LCD_WriteData(buf[i]);
// 第二行:费用
sprintf(buf, "Fee:%.2f Yuan", total_fee);
LCD_WriteCmd(0xC0); // 第二行首地址
for (uint8_t i=0; i<16; i++) LCD_WriteData(buf[i]);
}
(5)按键处理(设置参数)
// 按键扫描(消抖)
uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) {
if (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) {
HAL_Delay(20); // 消抖
if (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) {
while (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET); // 等待释放
return 1;
}
}
return 0;
}
// 主循环按键处理
void Key_Process(void) {
if (Key_Scan(GPIOC, GPIO_PIN_0)) { // 启动/停止
is_moving = !is_moving;
}
if (Key_Scan(GPIOC, GPIO_PIN_1)) { // 设置
// 进入参数设置模式(略,可切换显示参数并调整)
}
if (Key_Scan(GPIOC, GPIO_PIN_2)) { // 加
price.start_price += 1.0f;
}
if (Key_Scan(GPIOC, GPIO_PIN_3)) { // 减
price.start_price -= 1.0f;
}
}
参考代码 基于STM32出租车计价器实验例程 www.youwenfan.com/contentcss/180355.html
六、实验步骤
1. 硬件连接
- 霍尔传感器OUT接PA0,VCC接5V,GND接GND;
- LCD1602按并行接口连接PB0-PB7;
- AT24C02的SDA/SCL接PB10/PB11,VCC接3.3V,GND接GND;
- 按键接PC0-PC3,另一端接地。
2. STM32CubeMX配置
- 按“四、STM32CubeMX配置”步骤配置时钟、中断、I2C、GPIO;
- 生成MDK-ARM工程(Keil5)。
3. 代码烧录与调试
- 编写各模块驱动(中断、定时器、LCD、按键);
- 烧录程序,通过LCD观察里程和费用变化;
- 测试按键功能(启动/停止、设置参数)。
七、实验结果
- 里程显示:车轮转动时,LCD实时显示里程(如0.5km、1.0km);
- 费用计算:3km内显示10元,超过3km后每公里+2元,等候时间每分钟+0.5元;
- 按键功能:按“启动/停止”键切换行驶状态,按“设置”键进入参数调整模式。
八、扩展功能
- 语音播报:添加SYN6288语音模块,播报费用;
- GPS定位:通过NEO-6M模块获取位置,显示在LCD;
- 夜间模式:添加光敏电阻,夜间自动加收20%费用;
- 数据存储:用AT24C02存储历史行程记录。
九、注意事项
- 霍尔传感器安装:确保与车轮磁铁对齐,每转1圈输出1个脉冲;
- LCD对比度调节:通过10kΩ电位器调节V0引脚电压;
- 参数存储:修改参数后需写入AT24C02,避免掉电丢失;
- 低功耗设计:停车时STM32进入停止模式,通过按键唤醒。
十、总结
本实验基于STM32实现了出租车计价器核心功能,涵盖传感器数据采集、定时器中断、LCD显示、按键交互和计费算法,通过模块化设计可快速扩展功能