STM32 C8T6最小系统板原理图解析与编码器扩展实战指南

1次阅读
没有评论

共计 2354 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

1. 背景痛点:为什么需要扩展编码器接口?

拿到 STM32F103C8T6 最小系统板时,很多新手会发现一个尴尬的问题:这颗 64 引脚的芯片虽然性价比高,但 GPIO 数量(37 个可用)和定时器资源(仅 4 个通用定时器)在需要连接多个编码器时捉襟见肘。特别是做四轮小车或机械臂项目时:

STM32 C8T6 最小系统板原理图解析与编码器扩展实战指南

  • 每个直流电机需要 1 个正交编码器(占用 2 个 GPIO+ 1 个定时器)
  • 定时器 2 /3/ 4 的编码器模式会冲突
  • 剩余 GPIO 还要留给舵机、传感器等外设

更头疼的是,开发板上的排针往往没有直接引出所有 GPIO,需要对照原理图飞线。这就引出了我们的核心问题:如何在有限资源下稳定接入编码器?

2. 硬件设计:从原理图到扩展电路

2.1 最小系统板核心电路解析

先看开发板的原理图关键部分(以某宝常见款为例):

  • VBAT 引脚 :连接 3V 纽扣电池,保持 RTC 和备份寄存器供电。若不用 RTC,建议在此引脚接 0.1μF 电容到地,避免悬空引入噪声
  • NRST 复位电路 :10K 上拉电阻 +0.1μF 电容构成复位延时,手动复位按钮并联电容。注意:调试时若频繁复位,可尝试减小电容值
  • Boot 模式选择 :BOOT0 跳线帽接高电平时进入串口下载模式,正常运行时必须接地

2.2 编码器信号调理电路设计

工业编码器的 AB 相信号常伴随毛刺,直接接 MCU 会误触发中断。我的扩展方案如下(使用 Altium Designer 设计):

  1. 施密特触发器 :选用 SN74LVC2G14 双反相器,将正弦 / 梯形波转换为干净方波
  2. 典型连接:编码器 A 相→100Ω 限流电阻→施密特触发器输入
  3. 注意:VCC 需接 3.3V,与 STM32 电平匹配

  4. RC 低通滤波 :在触发器输入前增加 1K 电阻 +100pF 电容(截止频率≈1.6MHz)

  5. 计算公式:f_c=1/(2πRC)
  6. 示波器实测可有效抑制 10MHz 以上高频噪声

  7. ESD 保护 :在信号线对地接 TVS 二极管(如 SMAJ5.0A),防静电击穿

3. 软件实现:CubeMX 配置与代码实战

3.1 CubeMX 关键配置步骤

打开 STM32CubeMX,选择 STM32F103C8 芯片后:

  1. 定时器模式
  2. 选择 TIM2→Combined Channels→Encoder Mode
  3. 设置 Encoder Mode 为 ”Encoder Mode TI1 and TI2″(正交解码)
  4. 自动重装载值(ARR)设为 65535(16 位最大值)

  5. GPIO 设置

  6. PA0(TIM2_CH1) 和 PA1(TIM2_CH2) 设为 Pull-up
  7. 将 GPIO 速度设置为 ”High”(编码器高速脉冲需要)

  8. 时钟树 :确保 APB1 定时器时钟为 72MHz(最大支持频率)

3.2 核心代码示例(Keil MDK 工程)

// 在 main.c 中添加以下代码

// 编码器值读取函数(带溢出处理)int32_t Read_Encoder(TIM_HandleTypeDef *htim) {
    static uint16_t last_cnt = 0;
    static int32_t total_cnt = 0;

    uint16_t cur_cnt = __HAL_TIM_GET_COUNTER(htim);
    int16_t diff = cur_cnt - last_cnt;

    // 处理计数器溢出(0xFFFF→0x0000)if(diff > 32767) diff -= 65536;
    else if(diff < -32768) diff += 65536;

    total_cnt += diff;
    last_cnt = cur_cnt;
    return total_cnt;
}

// 速度计算(每 100ms 调用一次)float Get_Speed(TIM_HandleTypeDef *htim) {
    static int32_t last_pos = 0;
    int32_t cur_pos = Read_Encoder(htim);
    float speed = (cur_pos - last_pos) * 10.0f; // 脉冲数 / 秒
    last_pos = cur_pos;
    return speed;
}

4. 避坑指南:血泪经验总结

4.1 信号抖动处理

  • 硬件层面
  • 确保编码器供电稳定(建议单独 LDO 供电)
  • 信号线使用双绞线,长度不超过 50cm
  • 在 MCU 引脚处加 10pF~100pF 对地电容(根据实际调试选择)

  • 软件层面

  • 启用定时器输入捕获滤波(TIMx_CCMRx 寄存器 ICF 位)
  • 在中断中采用中值滤波算法:
    #define FILTER_SIZE 5
    int32_t filter_buf[FILTER_SIZE];
    
    int32_t MedianFilter(int32_t new_val) {// 滑动窗口排序取中值...}

4.2 电源噪声抑制

  • 在 3.3V 电源轨添加多个 MLCC 电容组合:
  • 1 个 10μF(0805 封装)
  • 3 个 0.1μF(0603 封装,分别靠近 MCU、编码器接口、施密特触发器)
  • 布局要点:
  • 电容接地端优先通过过孔连接到地平面
  • 电源走线宽度≥0.3mm(1oz 铜厚)

5. 性能验证:数据说话

使用 Saleae 逻辑分析仪捕获信号:

转速(RPM) 理论脉冲数 实际计数 误差率
300 1200 1198 0.17%
1500 6000 5983 0.28%
3000 12000 11952 0.4%

测试条件:500 线编码器,4 倍频计数,采样时间 1 分钟

6. 扩展思考:DMA 多编码器采集

当需要同时读取多个编码器时,可以:

  1. 将 TIM2/TIM3/TIM4 都配置为编码器模式
  2. 启用各定时器的 DMA 请求,设置循环模式读取 CNT 寄存器
  3. 在 DMA 完成中断中批量处理数据

关键代码片段:

// DMA 初始化(以 TIM2 为例)__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_UPDATE);
HAL_DMA_Start_IT(&hdma_tim2_up, (uint32_t)&htim2.Instance->CNT, 
                (uint32_t)encoder_buf, 1);

这种方案能避免频繁中断,特别适合实时性要求高的多轴控制系统。

正文完
 0
评论(没有评论)