目录

17.02_QEP-符号加脉冲信号计数

测试步骤

设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13(QEP0_Z);

实测数据

CCWSIGN_UpDown

qep_cnt = 0

timeabzcnt
00100
11101
20102
31103
40104
51105
60106
71107
80108
91109
1001010
1111011
1201012
1311013
1401014
1511015
1610015
1700014
1810013
1900012
2010011
2100010
221009
230008
241007
250006
261005
270004
281003
290002
301001
310000

参考代码

17.02_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_CCWSIGN_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);
    }
}

使用到的库函数

库函数部分代码

#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;
    }
}