# 17.01_QEP-正交编码

## 测试步骤

设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13（QEP0_Z）；

## 实测数据

### T1_T2

qep_cnt = 0

time | a | b | z | cnt
--- | --- | --- | --- | ---
0 | 0 | 0 | 0 | 0
1 | 1 | 0 | 0 | 1
2 | 1 | 1 | 0 | 2
3 | 0 | 1 | 0 | 3
4 | 0 | 0 | 0 | 4
5 | 1 | 0 | 0 | 5
6 | 1 | 1 | 0 | 6
7 | 0 | 1 | 0 | 7
8 | 0 | 0 | 0 | 8
9 | 1 | 0 | 0 | 9
10 | 1 | 1 | 0 | 10
11 | 0 | 1 | 0 | 11
12 | 0 | 0 | 0 | 12
13 | 1 | 0 | 0 | 13
14 | 1 | 1 | 0 | 14
15 | 0 | 1 | 0 | 15
16 | 0 | 1 | 0 | 15
17 | 1 | 1 | 0 | 14
18 | 1 | 0 | 0 | 13
19 | 0 | 0 | 0 | 12
20 | 0 | 1 | 0 | 11
21 | 1 | 1 | 0 | 10
22 | 1 | 0 | 0 | 9
23 | 0 | 0 | 0 | 8
24 | 0 | 1 | 0 | 7
25 | 1 | 1 | 0 | 6
26 | 1 | 0 | 0 | 5
27 | 0 | 0 | 0 | 4
28 | 0 | 1 | 0 | 3
29 | 1 | 1 | 0 | 2
30 | 1 | 0 | 0 | 1
31 | 0 | 0 | 0 | 0

## 参考代码

17.01_QEP_Count.c

    /**
     * @brief QEP-正交编码
     * @details
     * 设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13（QEP0_Z）；
     */
    #include "lks32mc09x_gpio.h"
    #include "lks32mc09x_qep.h"

    volatile uint32_t qep_cnt;
    /**
     * @brief 主函数
     */
    int main(void)
    {
        // 配置QEP模块
        QEP_InitTypeDef QEP_InitStruct;
        QEP_StructInit(&QEP_InitStruct); // 初始化QEP结构体为默认值

        QEP_InitStruct.Mode = QEP_Mode_T1_T2; ///< 编码器模式选择
        QEP_InitStruct.FE_CNT_EN = DISABLE;   ///< 下降沿计数使能
        QEP_InitStruct.ZC = QEP_ZC_NONE;      ///< Z信号配置
        QEP_InitStruct.TH = 65535;            ///< QEP计数门限寄存器
        QEP_InitStruct.ClockDiv = 0;          ///< 时钟分频
        QEP_InitStruct.Filter = 0;            ///< 信号输入滤波
        QEP_InitStruct.IRQEna = 0;            ///< 中断使能
        QEP_Init(QEP0, &QEP_InitStruct);      // 初始化QEP0模块

        // 配置需要使用的gpio
        GPIO_Config(GPIO2, GPIO_PinSource_11, GPIO_Mode_IN, GPIO_AF_QEP0);
        GPIO_Config(GPIO2, GPIO_PinSource_12, GPIO_Mode_IN, GPIO_AF_QEP0);
        GPIO_Config(GPIO0, GPIO_PinSource_13, GPIO_Mode_IN, GPIO_AF_QEP0);

        // 主循环
        while (1)
        {
            qep_cnt = QEP_GetCount(QEP0);
        }
    }

## 使用到的库函数

库函数部分代码

    #define GPIO_AF_I2C 6
    #define QEP0 ((QEP_TypeDef *)QEP0_BASE)
    #define REG_RESET(reg,mask) reg &= ~(mask)
    #define REG_SET(reg,mask) reg |= (mask)
    #define REG_WRITE(reg,mask) reg = (mask)
    #define SYS0 ((SYS_TypeDef *)(SYS_BASE))
    #define SYS_MODULE_GPIO BIT11
    #define SYS_MODULE_QEP0 BIT8
    #define SYS_MODULE_TIMER0 BIT4
    #define SYS_MODULE_TIMER1 BIT5
    #define SYS_MODULE_TIMER2 BIT6
    #define TIMER0 ((TIMER_TypeDef *)TIMER0_BASE)
    #define TIMER1 ((TIMER_TypeDef *)TIMER1_BASE)
    #define TIMER2 ((TIMER_TypeDef *)TIMER2_BASE)
    /**
     * @brief GPIO功能配置结构体句柄
     */
    typedef struct GPIO_InitTypeDef {
        uint32_t GPIO_Pin;
        GPIO_Mode_TypeDef GPIO_Mode;
        GPIO_PuPd_TypeDef GPIO_PuPd;
        uint32_t GPIO_PODEna;
        uint32_t GPIO_PFLT;
    };
    /**
     * @brief GPIO模式选择枚举
     */
    typedef enum GPIO_Mode_TypeDef {
        GPIO_Mode_IN = 0,
        GPIO_Mode_OUT = 1,
        GPIO_Mode_ANA = 2,
        GPIO_Mode_IO = 3,
    }} {enum_name};
    /**
     * @brief QEP初始化结构体定义
     */
    typedef struct QEP_InitTypeDef {
        uint32_t Mode;
        uint32_t FE_CNT_EN;
        uint16_t TH;
        uint32_t ClockDiv;
        uint32_t Filter;
        uint32_t IRQEna;
        uint32_t ZC;
    };
    /**
     * @brief  GPIO配置函数
     * @param  GPIOx: GPIO端口
     * @param  GPIO_PinSource: GPIO引脚
     * @param  mode: GPIO模式
     * @param  GPIO_AF_x: GPIO复用功能选择
     */
    void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
    {
        GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_StructInit(&GPIO_InitStructure);
        GPIO_InitStructure.GPIO_Pin  = 1 << GPIO_PinSource;
        GPIO_InitStructure.GPIO_Mode = mode;
        if (GPIO_AF_x == GPIO_AF_I2C)
        {
            GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        }
        else
        {
            __NOP();
        }
        GPIO_Init(GPIOx, &GPIO_InitStructure);
    }
    /**
     * @brief QEP初始化函数（初始化完成后会自动使能，并使能timer2）
     * @param QEPx QEP模块指针
     * @param TIM_QEPInitStruct 初始化结构体指针
     */
    void QEP_Init(QEP_TypeDef *QEPx, QEP_InitTypeDef *TIM_QEPInitStruct)
    {
        // 使能QEP模块时钟
        if (QEPx == QEP0)
        {
            SYS_ModuleClockCmd(SYS_MODULE_QEP0, ENABLE);
            TIMER_Enable(TIMER2);
        }
        else
        {
            __NOP();
        }
        // QEP配置寄存器
        QEPx->CFG = BIT15 |
                    (TIM_QEPInitStruct->Filter << 16) |
                    (TIM_QEPInitStruct->FE_CNT_EN << 10) |
                    (TIM_QEPInitStruct->Mode << 8) |
                    (TIM_QEPInitStruct->ZC);

        // 设置计数门限
        QEPx->TH = TIM_QEPInitStruct->TH;

        // 设置中断使能
        QEPx->IE = TIM_QEPInitStruct->IRQEna;
    }
    /**
     * @brief QEP结构体初始化函数
     * @param TIM_QEPInitStruct 初始化结构体指针
     */
    void QEP_StructInit(QEP_InitTypeDef *TIM_QEPInitStruct)
    {
        for (int i = 0; i < sizeof(QEP_InitTypeDef) / sizeof(uint32_t); i++)
        {
            ((uint32_t *)TIM_QEPInitStruct)[i] = 0;
        }
    }
    /**
     * @brief 使能Timer
     * @param TIMERx Timer实例指针
     */
    void TIMER_Enable(TIMER_TypeDef *TIMERx)
    {
        if (TIMERx == TIMER0)
        {
            SYS_ModuleClockCmd(SYS_MODULE_TIMER0, ENABLE);
        }
        else if (TIMERx == TIMER1)
        {
            SYS_ModuleClockCmd(SYS_MODULE_TIMER1, ENABLE);
        }
        else if (TIMERx == TIMER2)
        {
            SYS_ModuleClockCmd(SYS_MODULE_TIMER2, ENABLE);
        }
        else
        {
            __NOP();
        }
    }
    /**
     * @brief 初始化GPIO
     * @param GPIOx GPIO模块指针
     * @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
     */
    void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
    {
        // 使能GPIO时钟
        SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);

        // 配置引脚模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
        {
            GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin;  // 打开输入使能
            GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
            GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
        }
        else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
        {
            GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
            GPIOx->POE |= GPIO_InitStruct->GPIO_Pin;  // 使能输出
        }
        else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
        {
            GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
            GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
        }
        else // GPIO_Mode_ANA
        {
            GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
            GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
            GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
        }

        if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
        {
            GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
        }
        else
        {
            GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
        }
        // 配置开漏使能
        if (GPIO_InitStruct->GPIO_PODEna)
        {
            GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
        }
        else
        {
            GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
        }

        // 配置滤波使能
        if (GPIO_InitStruct->GPIO_PFLT)
        {
            GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
        }
        else
        {
            GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
        }
    }
    /**
     * @brief 初始化GPIO结构体为默认值
     * @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
     */
    void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
    {
        for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
        {
            ((uint8_t *)GPIO_InitStruct)[i] = 0;
        }
    }
    /**
     * @brief 数字模块时钟使能
     * @param nModule 模块编号
     * @param state 使能或禁用状态
     */
    void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
    {
        REG_WRITE(SYS0->PROTECT, 0x7a83);
        if (state)
        {
            REG_SET(SYS0->CLK_FEN, nModule);
        }
        else
        {
            REG_RESET(SYS0->CLK_FEN, nModule);
        }
        REG_WRITE(SYS0->PROTECT, 0);
    }
    /**
     * @brief 获取QEP计数器当前值
     * @param QEPx QEP模块指针
     * @return 计数器当前值
     */
    uint16_t QEP_GetCount(QEP_TypeDef *QEPx)
    {
        return QEPx->CNT;
    }
    /**
     * @brief 配置GPIO引脚复用功能
     * @param GPIOx GPIO模块指针
     * @param GPIO_PinSource 引脚源
     * @param GPIO_AF 复用功能选择
     */
    void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
    {
        uint8_t offset;
        uint8_t pins = GPIO_PinSource >> 2;
        offset       = ((GPIO_PinSource & 0x3) * 4);
        switch (pins)
        {
            case 0:
                GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
                break;
            case 1:
                GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
                break;
            case 2:
                GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
                break;
            case 3:
                GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
                break;
            default:
                break;
        }
    }