# 17.03_QEP-双向计数模式  

## 测试步骤  
设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13（QEP0_Z）
## 实测数据  

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

## 参考代码  
17.03_QEP_Count.c
```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_CCWCW_UpDown; ///< 编码器模式选择
    QEP_InitStruct.FE_CNT_EN = ENABLE;             ///< 下降沿计数使能
    QEP_InitStruct.ZC = QEP_ZC_NONE;               ///< 上升沿清零
    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);
    }
}

```

## 使用到的库函数  
库函数部分代码
```c
#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;
    }
}
```
