找回密码
 立即注册
查看: 2472|回复: 9

STC32 + 旋转编码器的测试, 附件中有程序

[复制链接]

5

主题

18

回帖

139

积分

注册会员

积分
139
发表于 2023-6-26 21:36:26 | 显示全部楼层 |阅读模式
简单测试,不考虑编码器卡死等特殊情的处理。



编码器.zip (46.43 KB, 下载次数: 397)




1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

已绑定手机

16

主题

276

回帖

2168

积分

金牌会员

积分
2168
QQ
发表于 2023-6-27 08:35:39 | 显示全部楼层
感谢楼主的分享
QQ:1638975601
手机:19952583876(微信同号)
回复

使用道具 举报 送花

5

主题

18

回帖

139

积分

注册会员

积分
139
 楼主| 发表于 2023-6-28 09:44:00 | 显示全部楼层
本帖最后由 BG1MGD 于 2023-6-30 22:57 编辑

O1CN01OVq0mN1w30aenzjay_!!2201532596251.jpg
回复

使用道具 举报 送花

已绑定手机

1

主题

844

回帖

2441

积分

金牌会员

积分
2441
发表于 2024-6-15 15:02:54 | 显示全部楼层
有没有消抖处理?
靡不有初,鲜克有终
回复

使用道具 举报 送花

0

主题

29

回帖

158

积分

注册会员

积分
158
发表于 2024-11-4 20:44:36 | 显示全部楼层
谢谢分享,是不是接到任意两个IO口都可以实现左右旋转计数啊
回复

使用道具 举报 送花

已绑定手机

20

主题

1456

回帖

4428

积分

论坛元老

积分
4428
发表于 2024-11-4 21:15:04 | 显示全部楼层
zwf3*** 发表于 2024-11-4 20:44
谢谢分享,是不是接到任意两个IO口都可以实现左右旋转计数啊

可以
但要完美的没有,因为完美的方法需要硬件支持才行。
回复

使用道具 举报 送花

已绑定手机

0

主题

1

回帖

6

积分

新手上路

积分
6
发表于 2025-1-4 01:45:40 来自手机 | 显示全部楼层
你好,想问一下,是否使用PWM输入捕获会占用一整路PWM(比如PWMA或者PWMB)此时如果用的PWMA中的一个通道采用输入捕获采集AB相,那么是不是在采集的同时PWMA的别的通道处于无法使用状态(既无法正常输出PWM波形)
回复

使用道具 举报 送花

已绑定手机

0

主题

10

回帖

598

积分

高级会员

积分
598
发表于 2026-7-22 21:57:20 | 显示全部楼层
谢谢,到时用STC8H8K64U试一下
回复

使用道具 举报 送花

已绑定手机

11

主题

484

回帖

1362

积分

金牌会员

积分
1362
发表于 2026-7-23 07:08:36 | 显示全部楼层
Forev*** 发表于 2026-7-22 21:57
谢谢,到时用STC8H8K64U试一下

程序有缺陷,引用需谨慎。
回复

使用道具 举报 送花

14

主题

1431

回帖

7982

积分

论坛元老

积分
7982
发表于 2026-7-23 09:37:31 | 显示全部楼层
原代码的主要缺陷分析

1. 缺少消抖处理
机械编码器在旋转时,触点会产生抖动(bounce),通常持续 5~20ms。代码中仅用 `while(P33==0);` 等待电平变化,但抖动会导致:
- 一次旋转被误判为多次
- 方向判断错误(因为 P32 在抖动期间可能不稳定)

2. 方向判断逻辑不严谨

  1. if(P33==0 && P32==1)          // 顺时针
  2. if(P33==0 && P32==0)          // 逆时针
复制代码

这种判断方式依赖 P33 下降沿瞬间 P32 的电平,但:
- 如果 P33 下降沿时 P32 刚好也在抖动,方向会判反
- 如果旋转速度很快,主循环轮询可能漏掉边沿,导致计数丢失

3. 阻塞式等待导致系统卡死
  1. while(P33==0);
复制代码

这种阻塞等待会让 CPU 完全卡住,无法响应其他中断或任务,在实时系统中不可接受。

4. 未使用硬件资源
STC32G 系列(如 STC32G12K128)内置了16位高级PWM定时器,支持正交编码器接口(QEI),可以硬件自动完成编码器计数和方向判断,无需软件轮询。



完整示例代码(硬件正交编码器方案)
  1. /******************************************************************************
  2. * STC32G 硬件正交编码器(QEI)读取旋转编码器示例
  3. *
  4. * 硬件连接:
  5. *   - 编码器 A 相 -> P2.0 (PWMA_ETR/ENC_A)
  6. *   - 编码器 B 相 -> P2.1 (PWMA_IS/ENC_B)
  7. *   - 编码器按键 -> P3.4 (按下为低电平)
  8. *   - LED 显示   -> P0 端口 (8位LED,低电平点亮)
  9. *
  10. * 功能:
  11. *   - 正转:计数增加,LED 流水灯右移
  12. *   - 反转:计数减少,LED 流水灯左移
  13. *   - 按键按下:计数清零
  14. *
  15. * 使用 STC32G12K128,Keil C251 编译
  16. ******************************************************************************/
  17. #include <STC32G.H>
  18. #define MAIN_Fosc       24000000UL      // 定义主时钟频率(24MHz)
  19. // LED 显示编码(低电平点亮)
  20. unsigned char code LedCode[] = {
  21.     0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F
  22. };
  23. // 全局变量
  24. volatile unsigned int EncoderCount;     // 编码器计数值(硬件自动更新)
  25. unsigned char DisplayIndex;             // LED 显示索引
  26. // 函数声明
  27. void PWM_Encoder_Init(void);
  28. void GPIO_Init(void);
  29. void Delay20ms(void);
  30. /******************************************************************************
  31. * 主函数
  32. ******************************************************************************/
  33. void main(void)
  34. {
  35.     // 1. 允许访问扩展 RAM 区(XFR)
  36.     EAXFR = 1;
  37.     // 2. 设置等待周期(0等待,最高性能)
  38.     CKCON = 0;
  39.     WTST = 0;
  40.     // 3. GPIO 初始化
  41.     GPIO_Init();
  42.     // 4. 初始化 PWM 正交编码器模式
  43.     PWM_Encoder_Init();
  44.     // 5. 开启总中断
  45.     EA = 1;
  46.     // 6. 主循环
  47.     while(1)
  48.     {
  49.         // 读取硬件编码器计数值(16位)
  50.         // 注意:ENCx_CNT 是 16 位寄存器,需分两次读取
  51.         unsigned char temp_low, temp_high;
  52.         temp_low  = ENC1_CNTL;          // 先读低字节
  53.         temp_high = ENC1_CNTH;          // 再读高字节
  54.         EncoderCount = ((unsigned int)temp_high << 8) | temp_low;
  55.         // 将计数值映射到 LED 显示(取低3位,0~7)
  56.         DisplayIndex = (unsigned char)(EncoderCount & 0x07);
  57.         P0 = LedCode[DisplayIndex];
  58.         // 检测按键按下(P3.4 低电平有效)
  59.         if(P34 == 0)
  60.         {
  61.             Delay20ms();                // 消抖
  62.             if(P34 == 0)
  63.             {
  64.                 // 清零硬件编码器计数器
  65.                 ENC1_CNTL = 0;
  66.                 ENC1_CNTH = 0;
  67.                 EncoderCount = 0;
  68.                 DisplayIndex = 0;
  69.                 P0 = LedCode[0];
  70.                 // 等待按键释放
  71.                 while(P34 == 0);
  72.             }
  73.         }
  74.     }
  75. }
  76. /******************************************************************************
  77. * GPIO 初始化
  78. ******************************************************************************/
  79. void GPIO_Init(void)
  80. {
  81.     // P0 推挽输出(驱动 LED)
  82.     P0M1 = 0x00;
  83.     P0M0 = 0xFF;
  84.     // P2.0、P2.1 设置为高阻输入(编码器输入)
  85.     P2M1 = (1 << 0) | (1 << 1);         // P2.0, P2.1 设置为高阻
  86.     P2M0 = 0x00;
  87.     // P3.4 设置为高阻输入(按键输入)
  88.     P3M1 = (1 << 4);
  89.     P3M0 = 0x00;
  90.     // 初始 LED 全灭
  91.     P0 = 0xFF;
  92. }
  93. /******************************************************************************
  94. * PWM 正交编码器模式初始化
  95. *
  96. * 使用 PWMA 的编码器模式,A 相 -> P2.0,B 相 -> P2.1
  97. * 配置为 4 倍频模式(上升沿和下降沿都计数)
  98. ******************************************************************************/
  99. void PWM_Encoder_Init(void)
  100. {
  101.     // 1. 使能 PWMA 时钟
  102.     PWMA_CR1 = 0x00;                    // 先关闭计数器
  103.     PWMA_IER = 0x00;                    // 关闭中断
  104.     // 2. 配置编码器模式
  105.     //    PWMA_SMCR: SMS[2:0] = 011(编码器模式1,根据 A/B 相计数)
  106.     //               SMS[2:0] = 100(编码器模式2,仅根据 A 相计数)
  107.     //               SMS[2:0] = 101(编码器模式3,A/B 双沿计数,4倍频)
  108.     PWMA_SMCR = 0x05;                   // 编码器模式3,4倍频
  109.     // 3. 设置捕获/比较通道为输入
  110.     //    PWMA_CCMR1: CC1S[1:0] = 01(CC1 映射到 TI1,即 A 相)
  111.     PWMA_CCMR1 = 0x01;
  112.     //    PWMA_CCMR2: CC2S[1:0] = 01(CC2 映射到 TI2,即 B 相)
  113.     PWMA_CCMR2 = 0x01;
  114.     // 4. 使能捕获
  115.     PWMA_CCER1 = 0x00;                  // 先清除
  116.     PWMA_CCER1 |= (1 << 0);             // CC1E = 1,使能 CC1 捕获
  117.     PWMA_CCER1 |= (1 << 4);             // CC2E = 1,使能 CC2 捕获
  118.     // 5. 设置输入滤波器(可选,用于硬件消抖)
  119.     //    IC1F[3:0] = 1111(最大滤波深度)
  120.     PWMA_CCMR1 |= (0x0F << 4);          // IC1F = 1111
  121.     PWMA_CCMR2 |= (0x0F << 4);          // IC2F = 1111
  122.     // 6. 设置自动重装载值(16位最大计数范围)
  123.     PWMA_ARRH = 0xFF;                   // 高字节
  124.     PWMA_ARRL = 0xFF;                   // 低字节(65535)
  125.     // 7. 清除计数器
  126.     ENC1_CNTL = 0;
  127.     ENC1_CNTH = 0;
  128.     // 8. 使能编码器接口
  129.     PWMA_ENCR = 0x01;                   // ENC_EN = 1
  130.     // 9. 启动计数器
  131.     PWMA_CR1 |= (1 << 0);               // CEN = 1,使能计数
  132. }
  133. /******************************************************************************
  134. * 简单延时函数(约20ms @ 24MHz)
  135. ******************************************************************************/
  136. void Delay20ms(void)
  137. {
  138.     unsigned int i;
  139.     for(i = 0; i < 60000; i++);
  140. }
复制代码




回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-11-7 14:34 , Processed in 0.080960 second(s), 95 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表