1. 项目概述
这个里程速度测试表的设计源于我在参加大学生方程式赛车项目时的实际需求。当时我们需要一个轻量化、低功耗且成本可控的车载仪表,用于实时监测赛车的行驶速度和累计里程。市面上的商业产品要么功能过剩价格昂贵,要么精度不足难以满足比赛要求。
基于STM32F103C8T6单片机的解决方案完美平衡了性能和成本。整个系统包含霍尔传感器信号采集、速度计算算法、里程累计逻辑和OLED显示模块,代码量控制在8KB以内,硬件成本不到50元。在实际测试中,速度测量误差小于0.5km/h,完全满足赛事技术要求。
2. 硬件设计解析
2.1 核心器件选型
主控芯片选用STM32F103C8T6(蓝色pill开发板),主要考虑以下因素:
- 72MHz主频足够处理速度计算任务
- 内置12位ADC可用于扩展模拟量采集
- 丰富的定时器资源(8个16位定时器)
- 市场价格仅10元左右
速度传感器采用A3144霍尔效应开关,相比光电编码器具有以下优势:
- 无接触式测量,寿命更长
- 防水防尘性能好
- 输出信号干净,无需额外整形电路
- 单价不到2元
显示模块选用0.96寸OLED(SSD1306驱动),选择依据:
- 128x64分辨率足够显示关键数据
- 自发光特性,阳光下可视性好
- 功耗仅0.08W(LCD屏的1/5)
- 支持I2C接口节省IO资源
2.2 电路设计要点
信号采集电路设计特别注意了以下细节:
- 霍尔传感器供电增加100μF电解电容滤波
- 信号线串联1kΩ电阻防止过冲
- 并联104瓷片电容滤除高频干扰
- 使用施密特触发器整形波形
电源管理部分采用LM2940稳压芯片,关键设计:
- 输入输出端各加470μF+104电容组合
- 续流二极管选用1N5819
- 预留测试点便于电压测量
实际调试中发现,未加磁珠时发动机点火会导致系统复位,后在电源输入端增加FB2012磁珠后问题解决。
3. 软件实现细节
3.1 速度计算算法
采用定时器输入捕获模式测量脉冲周期,关键代码逻辑:
c复制// 定时器2通道1配置为输入捕获
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
// 中断服务程序中计算实时速度
void TIM2_IRQHandler(void)
{
static uint16_t lastCapture = 0;
uint16_t currentCapture = TIM_GetCapture1(TIM2);
// 计算脉冲周期(单位:定时器时钟周期)
uint32_t pulsePeriod = currentCapture - lastCapture;
// 转换为速度(km/h)
float speed = (WHEEL_CIRCUMFERENCE * 3600) / (PULSES_PER_REV * pulsePeriod * TIMER_CLOCK_PERIOD);
lastCapture = currentCapture;
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
}
速度滤波采用滑动平均算法:
c复制#define FILTER_WINDOW 5
float speedBuffer[FILTER_WINDOW];
uint8_t bufferIndex = 0;
float filteredSpeed(float newSpeed)
{
speedBuffer[bufferIndex] = newSpeed;
bufferIndex = (bufferIndex + 1) % FILTER_WINDOW;
float sum = 0;
for(int i=0; i<FILTER_WINDOW; i++){
sum += speedBuffer[i];
}
return sum / FILTER_WINDOW;
}
3.2 里程累计逻辑
里程计算需要考虑的数据类型选择:
- 使用32位无符号整数记录脉冲计数(单位:次)
- 浮点数存储累计里程(单位:公里)
- EEPROM存储总里程防止掉电丢失
里程更新策略:
c复制void updateOdometer(uint32_t newPulseCount)
{
static uint32_t lastPulseCount = 0;
uint32_t delta = newPulseCount - lastPulseCount;
totalPulseCount += delta;
totalDistance += (delta * WHEEL_CIRCUMFERENCE) / PULSES_PER_REV;
lastPulseCount = newPulseCount;
// 每1000次脉冲保存一次EEPROM
if(totalPulseCount % 1000 == 0){
saveToEEPROM();
}
}
4. 系统校准与测试
4.1 参数校准方法
-
轮周长校准:
- 车辆直线行驶10米距离
- 记录脉冲计数N
- 计算实际周长 = 10000mm / (N/PULSES_PER_REV)
-
速度验证:
- 使用GPS设备作为基准
- 在20/40/60km/h三个速度点对比
- 修正定时器时钟分频系数
-
里程验证:
- 标准400米跑道测试
- 误差应小于0.5%
4.2 实测数据对比
| 速度基准(km/h) | 测量值(km/h) | 误差(%) |
|---|---|---|
| 20 | 20.1 | +0.5 |
| 40 | 39.8 | -0.5 |
| 60 | 60.3 | +0.5 |
| 80 | 79.6 | -0.5 |
测试环境:轮胎压力2.0bar,环境温度25℃,干燥沥青路面
5. 常见问题排查
5.1 速度显示跳变
可能原因及解决方案:
-
传感器安装间隙过大
- 调整间隙至0.5-1.0mm
- 使用非磁性垫片固定
-
电源干扰
- 检查稳压器输出电压纹波
- 增加电源滤波电容
-
软件滤波不足
- 增大滑动平均窗口
- 添加中值滤波预处理
5.2 里程计数不准
典型故障模式:
-
脉冲丢失:表现为里程偏少
- 检查传感器信号幅值(应>3V)
- 降低定时器输入捕获滤波器设置
-
误触发:表现为里程偏多
- 检查电路板接地
- 增加施密特触发器滞后电压
5.3 显示异常处理
OLED显示问题排查流程:
- 检查I2C地址配置(通常0x78或0x7A)
- 测量SCL/SDA线上拉电阻(推荐4.7kΩ)
- 确认初始化序列正确发送
- 检查对比度设置(建议0xCF)
6. 优化与扩展方向
6.1 低功耗优化
实测电流消耗:
- 正常运行:12mA
- 休眠模式:0.5mA
优化措施:
-
采用中断唤醒机制
- 无速度信号5分钟后进入STOP模式
- 霍尔信号触发EXTI唤醒
-
动态刷新率控制
- 高速时100ms刷新
- 低速时500ms刷新
- 静止时1s刷新
6.2 功能扩展
已实现的扩展功能:
- 峰值速度记录
- 行程里程分段统计
- 超速报警功能
预留扩展接口:
- CAN总线接口(PB8/PB9)
- 蓝牙模块串口(PA2/PA3)
- 模拟量输入(PA0-PA7)
在三个月实际使用中,这个自制仪表经历了高温、振动和潮湿环境考验,累计记录里程超过2000公里,证明其稳定性和可靠性完全达到实用要求。相比商业产品,最大的优势是可以完全自定义功能和显示内容,比如我们特别添加了单圈时间记录功能,对赛车调试非常有帮助。
