12 QDEC简介

QDEC全称Quadrature Decoder,即正交解码器。

其作用是用来解码来自旋转编码器的脉冲序列,以提供外部设备运动的步长和方向。

12.1 功能描述

12.1.1 特点

  • 可配置时钟

  • 支持过滤器

  • 支持APB总线

  • 支持DMA

12.1.2 正转和反转

QDEC是通过采集到phase_a、phase_b相邻两次的数值变化来判断外设的运动方向。

顺时针采集数据如图所示:
QDEC顺时针采集数据

图 12.1: QDEC顺时针采集数据

逆时针采集数据如图所示:
QDEC逆时针采集数据

图 12.2: QDEC逆时针采集数据

在QDEC数据上,如果引脚配置和连接正确,顺时针转动则采集到的数据逐渐增大,逆时针则数据逐渐减小。

12.2 使用方法

12.2.1 方法概述

方法概述为:GPIO选配,时钟配置,QDEC参数配置以及数据处理。

12.2.1.1 GPIO选择

驱动接口:PINCTRL_SelQDECIn

QDEC的GPIO选择,请参考 PINCTRL

对phase_a、phase_b选定要配置的GPIO口,并调用PINCTRL_SelQDECIn接口进行配置。

12.2.1.2 时钟配置

驱动接口:SYSCTRL_SelectQDECClk

当前QDEC可以选择使用的时钟源为HCLK时钟或者sclk_slow时钟

出于实际效果和硬件资源等因素考虑,我们推荐开发者选择sclk_slow作为时钟源

对所选用的时钟源还需要进行一次分频,分频系数范围为1-1023,默认值为2

这里需要特别注意的是:qdec_pclk的时钟速度不能大于qdec选择的时钟源 请务必配置qdec_pclk时钟频率不大于qdec时钟源频率

qdec_pclk的时钟可以通过接口配置,分频系数范围为1-15:

/**
 * @brief Set Qdec pclk divider
 * @param[in]   div             divider
 */
void SYSCTRL_UpdateQdecClk(uint32_t div);

注意: 如果配置qdec_pclk频率大于qdec时钟源频率,否则可能会出现qdec配置寄存器无法正确读写的问题。

例如:使用sclk_slow时钟为24Mhz,pclk默认为128Mhz,需要调用SYSCTRL_UpdateQdecClk(6)设置qdec_pclk约22M。

另外:当通过SYSCTRL_UpdateQdecClk配置qdec_pclk相较于hclk较慢时。由于存在总线等待,可以调整接口内部宏NOP_NUME值,增加读写nop时间延缓读写时间,保证寄存器写入完成。

12.2.1.3 QDEC参数配置

驱动接口:QDEC_EnableQdecDiv、QDEC_QdecCfg

共有3个参数需要配置:qdec_clk_div、filter和miss

其含义分别如下:

  • qdec_clk_div:用于控制qdec结果上报频率。即多少个时钟周期上报一次采样结果

  • filter:用于过滤filter×时钟周期时长以内的毛刺

  • miss:用于控制qdec可以自动补偿的最大miss结果数。例如由于滚轮转动过快,导致两次采样中变换了不止一个结果,则此时会自动补偿最多miss个结果。

对于miss值配置此处建议先加入较小的miss值(如miss=1)测试效果,如果效果良好则可以尝试继续加大miss值。在保证性能的基础上,理论上miss值越大越好。

注意: 对于miss值的配置需要格外注意,miss值的设置主要考虑到可能由于转动速度过快导致有数据丢失的情况,但此补偿机制容易受设备信号质量影响。对于信号质量很差的设备,如信号很多毛刺,如果加入miss则可能出现“采样数据跳变”“换向迟钝”的问题。对于此类设备,建议进行以下几方面尝试:

1. 如选用sclk_slow作为时钟源,检查是否有配置pclk频率小于qdec工作频率

2. 改用较小工作时钟

3. 采用较小的miss值(如miss=1)和较大的filter值

4. 采用较大工作时钟(如HCLK时钟),不加miss进行采样

如果偶尔有较小的数据跳变,如5以内,则需判断其可能属于正常情况。

12.2.2 注意点

  • phase_a、phase_b引脚配置注意区分正反,交换引脚则得到相反的转向

  • 配置pclk时钟可能会影响其他外设,建议配好qdec之后将pclk及时恢复

  • qdec采样快慢和时钟正相关,和qdec_clk_div大小无关,qdec_clk_div只控制对结果的上报频率

  • qdec上报结果会同时触发qdec中断或者DMA_REQ,qdec_clk_div设置过低会占用较多CPU资源

  • filter建议选择较大值,受毛刺影响较小,稳定性较好

12.3 编程指南

12.3.1 驱动接口

  • QDEC_QdecCfg:qdec标准配置接口

  • QDEC_EnableQdecDiv:qdec_clk_div设置使能接口

  • QDEC_ChannelEnable:qdec通道使能接口

  • QDEC_GetData:qdec获取数据接口

  • QDEC_GetDirection:qdec获取转向接口

  • QDEC_Reset:qdec复位接口

12.3.2 代码示例

下面一段代码展示了qdec全部配置并循环读数:

static void QDEC_PclkCfg(void)
{
    if ((APB_SYSCTRL->QdecCfg >> 15) & 1)
        return;
    uint32_t hclk = SYSCTRL_GetHClk();
    uint32_t slowClk = SYSCTRL_GetSlowClk();
    uint8_t div = hclk / slowClk;
    if (hclk % slowClk)
        div++;
    if (!(div >> 4))
        SYSCTRL_SetPClkDiv(div);
}
void test(void)
{
    // setup qdec
    SYSCTRL_ClearClkGateMulti((1 << SYSCTRL_ITEM_APB_PinCtrl) |
                              (1 << SYSCTRL_ITEM_APB_QDEC));
    SYSCTRL_ReleaseBlock(SYSCTRL_ITEM_APB_QDEC);
    PINCTRL_SelQDECIn(16, 17);    // set GPIO16=phase_a, GPIO17=phase_b

    SYSCTRL_SelectQDECClk(SYSCTRL_CLK_SLOW, 100);
    QDEC_PclkCfg();    // set pclk not bigger than sclk_slow
    QDEC_EnableQdecDiv(QDEC_DIV_1024);
    QDEC_QdecCfg(50, 1);
    QDEC_ChannelEnable(1);

    // print qdec data and direction when rotate the mouse wheel manually
    uint16_t preData = 0;
    uint16_t data = 0;
    uint8_t dir;
    while(1) {
        data = QDEC_GetData();
        dir = QDEC_GetDirection();
        if (data != preData) {
            if (dir) {
                printf("data: %d, %s\n", data, "anticlockwise");
            } else {
                printf("data: %d, %s\n", data, "clockwise");
            }
        }
        preData = data;
    }
}

当手动转动鼠标滚轮时,会打印出收到的qdec数据和转向。

推荐开发者采用timer定时轮询的方式读取qdec数据,并进行数据处理和上报。具体的qdec详细使用实例请参考SDK中HID mouse例程。