新闻详情

新闻详情

首页 / 资讯中心 / 详情

STM32F103C8T6智能小车测速与距离计算:编码器、定时器与单位换算调参指南

发布时间:2026/9/10 10:33:15来源:尧图网络
STM32F103C8T6智能小车测速与距离计算:编码器、定时器与单位换算调参指南
简介STM32F103C8T6智能小车测速与行驶距离显示实验的完整程序源码包适合嵌入式入门学习者与小车DIY爱好者参考。程序基于Keil4环境编写适配STM32F103C8T6主控配合L293D电机驱动、TT直流减速电机、测速模块及OLED屏实现了实时测速和里程显示功能作者在实车上验证可用。包内共42个文件除工程必备的h头文件、c源文件、uvproj工程文件及hex烧录文件外还包含L293D电机驱动电路图jpg和程序说明txt便于理解硬件连接与代码逻辑。资源压缩包仅372KB结构清晰适合直接编译学习或在此基础上二次开发。目前已有1762人学习下载对正在做智能小车课设或竞赛项目的读者有较高参考价值。1. stm32f103c8t6 智能小车测速先想清楚距离是怎么算出来的把 stm32f103c8t6 智能小车测速并显示行驶距离这个实验拆开看很多人以为难点在“读编码器”真正翻车的往往在单位换算OLED 上距离从 1.2m 跳到 18m或者小车后退时里程还在涨。这里的关键不是传感器灵敏度而是定时器资源怎么分配、计数增量怎么读、轮径按什么值算。这篇文章从 stm32f103c8t6 最小系统板的引脚配置讲起把编码器电机测速、正交解码、定时器中断和 OLED/串口显示串成一条能直接调试的路线适合刚跑通点亮 LED、准备做完整闭环的开发者也适合想从裸机搬到 FreeRTOS 的人。2. 测速与距离计算的底层原理编码器、定时器与单位换算2.1 编码器电机测速的两种实现方式外部脉冲计数与正交解码常见的小车套件里电机驱动板后面那个带三根线的电机就是编码器减速电机。编码器输出 A、B 两路方波相位差固定为 90 度轮子正转时 A 相超前反转时 A 相落后。测速有两种常见做法只接 A 相做外部脉冲计数或者把 A/B 两相接进定时器的编码器模式做正交解码。外部脉冲计数最简单把 A 相接在定时器外部时钟或普通 GPIO 中断上固定时间内统计脉冲数。它的问题也很明显无法判断旋转方向小车倒退时脉冲数只会继续增加后续做原地转弯或避障返回时累计距离会完全失真。我一般会直接选正交解码。stm32f103c8t6 的 TIM1~TIM4 都支持编码器模式A/B 相分别接定时器通道 CH1/CH2计数器会同时捕获两路信号的上升沿和下降沿等效实现 4 倍频。也就是说一个电机轴转一圈产生 N 个原始脉冲计数器的增量会是 4N。这样低速时脉冲密度更高而且通过读取计数器增量的正负就能知道方向。测速方式接线方向判断分辨率适合场景外部脉冲计数只用 A 相接定时器外部时钟或 EXTI不知道方向1 倍频只统计总里程不需要方向定时器正交解码A/B 接 CH1/CH2配置 Encoder Mode增量正负即方向4 倍频智能小车测速、速度闭环、里程累计2.2 stm32f103c8t6 引脚功能与定时器资源分配选定时器之前先看引脚。C8T6 是 TQFP48 封装引脚不算富裕编码器至少占用两个轮子的 4 个输入脚。以最常见的双轮差速小车为例左轮用 TIM3右轮用 TIM4这样两个轮子的计数完全独立不需要在中断里切换通道。TIM3 的编码器输入默认映射到 PA6/PA7TIM4 映射到 PB6/PB7。PA6/PA7 在很多最小系统板上直接以排针引出干扰少适合接左轮编码器。PB6/PB7 虽然也够用但如果你的 OLED 走硬件 I2C 恰好选了 PB8/PB9那么 PB6/PB7 不会被占用可以放心给右轮。注意 PB4/PB5、PA15/PB3 这几个脚默认是 JTAG 调试脚如果要用它们做编码器输入CubeMX 里需要把 SYS 的 Debug 改成 Serial Wire否则引脚电平不变化。还有一个容易忽略的电路细节编码器输出如果是开漏类型需要在编码器板或者主控板上加上拉电阻STM32 内部上拉大约 30~50kΩ抗干扰能力一般建议在编码器 VCC 和输出引脚之间并联 10kΩ 外部上拉。如果编码器模块是推挽输出则 GPIO 配置成 Input Floating 即可不用开上拉。2.3 行驶距离的累计逻辑轮径、减速比与脉冲量纲单位换算是这个实验最值得手算清楚的部分。编码器装在电机轴上经过减速齿轮后才带动车轮因此电机轴转一圈车轮只转 1/减速比 圈。设编码器线数为 PPR正交解码 4 倍频则电机轴每转一圈计数器变化 4 × PPR换算到车轮每转一圈需要再乘以减速比 G。每脉冲距离的计算公式为每脉冲距离 车轮周长 / (PPR × 4 × G)以常见 13 线编码器、减速比 1:30、车轮直径 65mm 为例PPR×4×G 13×4×30 1560车轮周长约为 3.14159×65 204.2mm因此一个脉冲对应约 0.131mm。行驶距离就是累计脉冲数乘以这个 0.131mm。参数符号示例值含义编码器线数PPR13电机轴每转一圈 A 相输出脉冲数正交解码倍频44上升沿和下降沿都计数减速比G30电机轴转 30 圈车轮转 1 圈车轮直径D65 mm实际与地面接触的外径每脉冲距离mm/pulse约 0.131 mm距离累计的“刻度”实际调试时13 线 4 倍频不一定等于 52因为有些编码器手册把两个通道的脉冲总数算成 PPR买到模组后最好拿手转动车轮用调试器观察 TIMx-CNT 变化反向确认每圈脉冲数。左右轮要分别累计转弯时内外轮转速不同如果只测一个轮子小车原地转一圈后显示的距离也会跟着涨。3. 用 HAL 库搭最小可运行代码GPIO 初始化、定时器计数与中断回调3.1 工程初始化从最小系统板到 CubeMX 引脚配置用 STM32CubeMX 初始化最省事。芯片选择 STM32F103C8T6时钟源看最小系统板上有没有 8MHz 晶振有晶振就选 HSE外部晶振输入PLL 倍频到 72MHz没有晶振就选 HSI直接把系统时钟拉到 64MHz 或 72MHz 都行测速对系统时钟精度不那么敏感。引脚分配按左轮 TIM3、右轮 TIM4 来做PA6 设为 TIM3_CH1PA7 设为 TIM3_CH2PB6 设为 TIM4_CH1PB7 设为 TIM4_CH2。接下来在 TIM3 和 TIM4 的 Parameter Settings 里把 Clock Source 改成 Encoder ModeEncoder Mode 选 TI1 and TI2表示两个通道都参与计数这才会有 4 倍频效果。Counter Period 用默认 65535不需要改小。还要再开一个 TIM6 做固定 100ms 的测速节拍。TIM6 是基础定时器没有输入输出通道专门用来产生周期中断不会和编码器复用冲突。72MHz 下Prescaler 设为 7199Counter Period 设为 999这样定时器时钟是 10kHz计满 1000 次正好 100ms。在 NVIC 设置里使能 TIM6 全局中断TIM3/TIM4 的 NVIC 不用勾选编码器计数不需要更新中断。这里有个坑CubeMX 生成工程后PA6/PA7/PB6/PB7 可能被默认设置成 GPIO_Output需要手动在 GPIO 配置里改成 Input Mode并选择上拉或浮空。如果编码器模块上已经有 10k 上拉选 Floating如果没有外部上拉选 Pull-up。否则编码器信号高电平会被钳住计数器永远不动。3.2 关键代码编码器模式配置与测速中断CubeMX 生成代码后需要手动添加编码器启动和测速中断处理。编码器启动放在 main 初始化完成后HAL_TIM_Encoder_Start(htim3, TIM_CHANNEL_ALL); HAL_TIM_Encoder_Start(htim4, TIM_CHANNEL_ALL); HAL_TIM_Base_Start_IT(htim6); // 100ms 周期中断开始然后编写速度与距离计算。测速的核心不是每次读到一个脉冲就去刷新显示而是等固定 100ms 窗口到读取当前编码器计数值与上一次值做差。这样速度单位是“100ms内的脉冲数”再换算成 m/s。/* 宏定义根据自己小车实际参数修改 */ #define PPR 13 #define GEAR_RATIO 30 #define WHEEL_DIAMETER_MM 65.0f #define PULSE_PER_REV (PPR * 4 * GEAR_RATIO) #define MM_PER_PULSE (3.14159f * WHEEL_DIAMETER_MM / PULSE_PER_REV) #define SAMPLE_SEC 0.1f static int16_t last_left_cnt 0; static int16_t last_right_cnt 0; volatile int32_t left_total_pulse 0; volatile int32_t right_total_pulse 0; volatile float left_speed_ms 0.0f; volatile float right_speed_ms 0.0f; volatile float left_distance_m 0.0f; volatile float right_distance_m 0.0f; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM6) { /* 用 int16_t 截断计数值计数回绕时差值仍然正确 */ int16_t now_left (int16_t)(TIM3-CNT); int16_t now_right (int16_t)(TIM4-CNT); int16_t left_delta now_left - last_left_cnt; int16_t right_delta now_right - last_right_cnt; last_left_cnt now_left; last_right_cnt now_right; left_total_pulse left_delta; right_total_pulse right_delta; left_speed_ms (float)left_delta * MM_PER_PULSE / SAMPLE_SEC / 1000.0f; right_speed_ms (float)right_delta * MM_PER_PULSE / SAMPLE_SEC / 1000.0f; left_distance_m (float)left_total_pulse * MM_PER_PULSE / 1000.0f; right_distance_m (float)right_total_pulse * MM_PER_PULSE / 1000.0f; } }代码里的关键点在(int16_t)(TIM3-CNT)。TIM3-CNT 是一个 16 位无符号计数器范围 0~65535正转超过 65535 会回绕到 0反转会从 0 回绕到 65535。直接做减法会得到错误的正负号但先强制转换成 int16_t 后65535 变成了 -1计数器从 65530 变到 3 时差值为 3 - (-6) 9正好是正转 9 个脉冲完全不需要在更新中断里手动处理溢出。速度换算里left_delta * MM_PER_PULSE得到 100ms 内走过的距离 mm除以 SAMPLE_SEC 得到每秒多少毫米再除以 1000 变成 m/s。距离则始终用原始脉冲数乘以每脉冲距离不要反复累加“本次距离”的浮点结果否则每次四舍五入误差会被持续放大。3.3 显示行驶距离的几种输出方式OLED、串口与屏幕刷新显示部分常见做法是 0.96 寸 OLEDI2C 接口一般接 PB8/PB9和编码器的 PB6/PB7 不冲突。OLED 驱动可以直接搜 SSD1306 软件 I2C 版本主循环里把距离和速度格式化成字符串显示while (1) { char line[24]; float total_dist (left_distance_m right_distance_m) * 0.5f; float avg_speed (left_speed_ms right_speed_ms) * 0.5f; sprintf(line, D:%.2fm V:%.2fm/s, total_dist, avg_speed); OLED_ShowString(0, 0, line); HAL_Delay(50); }OLED 刷新一次要几毫秒如果写在定时器中断里测速窗口会被拉长且不稳定所以中断里只算数据主循环只做显示。如果你更想用串口通信调数据用 USART1PA9 为 TXPA10 为 RX重定向 fputc 后就能直接 printfprintf(s%.2f,%.2f d%.2f\r\n, left_speed_ms, right_speed_ms, total_dist);注意 C8T6 的 Flash 是 64KB默认启用浮点 printf 会增加十几 KB 代码量基本放得下但如果还要塞 FreeRTOS 和 OLED 字库就要小心。省空间的办法是把浮点放大成整数再输出例如(int)(total_dist * 100)输出厘米数上位机再自行还原。4. 实战调参从“数字乱跳”到“距离准”的 3 个必调参数4.1 速度计算窗口与毛刺过滤第一个参数是测速窗口。TIM6 用 100ms 属于兼顾响应和平滑的默认值。小车速度很低时100ms 内的脉冲数其实很少0.05m/s 的速度对应每秒约 380 个脉冲100ms 也有 38 个还可以。如果采样窗口改成 20ms同样低速下只有约 8 个脉冲速度显示会一帧一个值没法看。建议低速小车用 100ms中高速车用 50ms。第二个参数是毛刺过滤。编码器信号质量不好时停车后计数器会在一个值附近抖动原因可能是电机 PWM 辐射干扰也可能是上拉电阻没接好。在中断里加个死区判断if (left_delta -2 left_delta 2) { left_delta 0; }死区设为 2 个脉冲对应约 0.26mm对正常行驶的里程统计几乎没有影响但能避免停车时距离缓慢爬升。如果小车行驶过程中速度仍然跳动先别急着改软件用示波器或逻辑分析仪看编码器 A/B 相波形优先查悬空引脚和地线。4.2 轮径和减速比的标定方法第三个参数也是最重要的参数每脉冲距离。理论值用“铭牌参数”算出来往往不准原因是轮子受压后有效直径变小减速比实际值也有误差。常见的标定方法是“跑固定距离反推”。在地上画一条 1m 直线让小车以固定低速度从起点直行到终点通过串口读出整个过程左右轮平均脉冲数 N然后重新计算每脉冲距离 1000mm / N把算出来的值写回MM_PER_PULSE宏。如果第一次显示距离是 0.93m真实距离是 1.00m也可以用一个修正系数直接缩放修正后 MM_PER_PULSE 当前 MM_PER_PULSE × (1.00 / 0.93)标定时小车必须走直线左右轮如果分别计数优先取两个轮子的平均值。如果左右轮显示距离差异超过 5%说明两边轮径或编码器参数不一致这种偏差靠软件平均修不回来要检查轮胎气压和左右电机负载。4.3 定时器溢出与溢出中断处理编码器模式下计数器是 16 位频繁在 65535 和 0 之间回绕这是正常现象。前面代码里用int16_t截断法处理了回绕但前提是没有把 TIM3/TIM4 的更新中断也交给HAL_TIM_PeriodElapsedCallback。如果不小心在 CubeMX 里勾选了 TIM3 全局中断编码器每溢出一次就会进入回调函数回调又被 TIM6 的 100ms 节拍干扰会出现两个问题一是速度被重复计算二是回调函数里同时读 TIM3-CNT 和 TIM4-CNT寄存器值可能被打断导致左右轮脉冲错位。解决方法是 TIM3/TIM4 的 NVIC 不勾选只保留 TIM6 中断。还有一种情况是硬件上 A/B 相接反而导致计数器反向速度显示为负值。这个不影响距离绝对值但会影响方向判断。如果程序里要判断正反转直接看 delta 的正负即可不需要额外换线。必调参数修改位置调试目标TIM6 中断周期CubeMX 中 TIM6 Prescaler/Period速度跳动和响应速度的平衡MM_PER_PULSE代码最上方宏定义距离显示与真实地面距离一致死区阈值中断中 delta 判断停车时距离不自己涨5. 进阶串口里程核对与 FreeRTOS 下的任务划分5.1 里程清零与多圈行驶的累计逻辑累计脉冲用的是 int32_t理论上要累计数百公里才会溢出日常实验不用考虑。但主循环里执行“清零”操作时要避免和中断冲突中断可能刚好在left_total_pulse 0这条语句前后执行加法导致清不完。正确做法是短暂关中断再清__disable_irq(); left_total_pulse 0; right_total_pulse 0; __enable_irq();关中断时间极短不会影响串口接收。如果是无操作系统环境这样处理就够了。多圈行驶不需要额外记录“圈数”距离本身足够表达。5.2 用串口输出实时速度和里程并在上位机核对MCU 端已经通过 printf 输出s...,... d...上位机可以直接用 Python 的 pyserial 读取并画曲线验证速度是否随 PWM 占空比线性变化import serial ser serial.Serial(COM3, 115200, timeout1) while True: line ser.readline().decode(errorsignore).strip() if line.startswith(s): _, data line.split( , 1) print(data)这个脚本只做打印实际调试时还能把数据写入 csv 文件再用 matplotlib 对比左右轮速度差能很快发现某一侧编码器丢脉冲的问题。验证方法是把小车架起来让轮子空转串口读到的左右轮速度应当非常接近偏差超过 10% 就检查机械阻力。5.3 FreeRTOS 下测速任务与显示任务的分工裸机工程加上 FreeRTOS 后不要把测速计算搬进某个任务轮询因为任务可能被其他高优先级任务抢占导致采样窗口忽长忽短。正确做法是保留 TIM6 中断里的测量和累计中断里不做 FreeRTOS API 调用只更新全局变量显示和控制各开一个任务。显示任务 200ms 循环读一次left_distance_m并刷 OLED控制任务按 10ms 周期读速度做 PID。两个任务之间不需要加锁因为它们读的是同一个floatCrotex-M3 上 32 位对齐读写是原子的。唯一的原则是中断负责写任务负责读任务不要反过来去修改脉冲计数。这样即使显示任务卡顿测速节拍也不会被拖慢。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

湖南单招避坑:很多家长都踩过这些雷 2026/9/10 11:12:23

湖南单招避坑:很多家长都踩过这些雷

每年湖南高职单招季,都有大批家长和考生陷入升学误区。家长一心想让孩子稳上公办全日制大专,不懂官方政策、不了解行业乱象、分不清机构真假套路,仅凭片面宣传和主观判断做选择,最后大概率踩坑:要么花高价报无用集训、…

阅读更多 →
CANN/ge SetStreamId API 2026/9/10 11:12:23

CANN/ge SetStreamId API

SetStreamId 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前…

阅读更多 →
Flipper Zero Amiibo模拟实战:3种文件来源与1个.bin转换脚本 2026/9/10 11:12:23

Flipper Zero Amiibo模拟实战:3种文件来源与1个.bin转换脚本

Flipper Zero Amiibo模拟实战:3种文件来源与1个.bin转换脚本 【免费下载链接】Flipper Playground (and dump) of stuff I make or modify for the Flipper Zero 项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper 用 Flipper Zero 的 NFC 模块模拟…

阅读更多 →
Nx Cloud CI Watcher:基于 MCP 轮询与自愈状态机的 Subagent 设计深度解析 2026/9/10 11:12:23

Nx Cloud CI Watcher:基于 MCP 轮询与自愈状态机的 Subagent 设计深度解析

Nx Cloud CI Watcher:基于 MCP 轮询与自愈状态机的 Subagent 设计深度解析 【免费下载链接】nx The Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in ha…

阅读更多 →
RuView 工业安全传感:基于 WiFi CSI 的工厂车间、仓储物流与工地施工监测技术方案 2026/9/10 11:12:23

RuView 工业安全传感:基于 WiFi CSI 的工厂车间、仓储物流与工地施工监测技术方案

RuView 工业安全传感:基于 WiFi CSI 的工厂车间、仓储物流与工地施工监测技术方案 【免费下载链接】RuView π RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection — all without a single…

阅读更多 →
云优CMS开发指南:ASP.NET设备监控网站定制化实践 2026/9/10 11:09:22

云优CMS开发指南:ASP.NET设备监控网站定制化实践

简介:本资源是一款专为智能监控系统设备生产企业定制的云优CMS网站模板,面向中小企业技术负责人、前端开发人员及建站运维人员,解决企业快速搭建专业官网、统一展示产品方案与技术实力的痛点。压缩包共1189个文件,含336个PHP核心逻…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞