ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《正点原子BEE BLOCK模块资料》ATK-MB013 模块使用说明

《正点原子BEE BLOCK模块资料》ATK-MB013 模块使用说明

原创 嵌入式/内核开发 作者:正点原子 时间:2025-03-20 09:48:39 0 删除 编辑

ATK-MB013 模块使用说明


        1,硬件连接

       这里以正点原子M48Z-M3最小系统板STM32F103版为例,给大家介绍一下模块和板卡的连接方法。其它板卡与模块的硬件连接方法,请大家在“ATK-MB013 EC11编码器模块\3,程序源码\相应板卡例程文件夹\readme.txt”路径下查看。

       ATK-MB013 EC11编码器模块可通过杜邦线与正点原子M48Z-M3最小系统板STM32F103版进行连接,具体的连接关系,如下表所示:


表1.1 EC11编码器模块与M48Z-M3最小系统板STM32F103版连接关系


        2,实验功能


       2.1 EC11编码器模块测试实验


       2.1.1 功能说明

       在本实验中,当编码器被旋转后,串口会打印编码器的累计计数值和累计旋转圈数;当编码器按键被按下(按压旋钮),串口会打印按键状态。需要查看这部分实验信息的用户,可用杜邦线将最小系统板STM32F103的PA9引脚和GND连接至外部的USB转串口设备,这样就可以通过XCOM上位机查看串口打印的信息了。

       用户可以通过旋转编码器的旋钮,改变编码器累计计数值和累计旋转圈数。

       开发板的LED0闪烁,提示程序运行。


       2.1.2 源码解读

       打开本实验的工程文件夹,能够在./Drivers/BSP目录下看到ATK_ENCODER文件夹,其包含了ATK-MB013 EC11编码器模块的驱动文件,如下图所示:


图2.1.2.1 ATK-MB013 EC11编码器模块驱动代码


       2.1.2.1 ATK-MB013 EC11编码器模块驱动

       下面简要介绍atk_encoder.c中几个重要的API函数。


       1. 函数atk_encoder_timx_init()

       该函数用于初始化ATK-MB013 EC11编码器模块相关的定时器,具体代码如下所示:

TIM_HandleTypeDef g_timx_encode_chy_handle;         /* 定时器x句柄 */
 
/**
 * @brief       编码器定时器初始化
 * @param       arr: 自动重装值
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void atk_encoder_timx_init(uint16_t arr, uint16_t psc)
{
    GPIO_InitTypeDef gpio_init_struct;
    TIM_Encoder_InitTypeDef timx_encoder_chy_handle;
    
    /************************** 时钟使能 ***************************/
    ATK_ENCODER_TIMX_CH1_GPIO_CLK_ENABLE();             /* 开启通道1的IO时钟 */
    ATK_ENCODER_TIMX_CH2_GPIO_CLK_ENABLE();             /* 开启通道2的IO时钟 */
    ATK_ENCODER_TIMX_CH1_CLK_ENABLE();                   /* 开启通道1定时器时钟 */
    ATK_ENCODER_TIMX_CH2_CLK_ENABLE();                   /* 开启通道2定时器时钟 */
    
/**************************** GPIO配置 *******************************/
/* 通道1的IO口,即编码器的A相 */
    gpio_init_struct.Pin = ATK_ENCODER_TIMX_CH1_GPIO_PIN; 
    gpio_init_struct.Mode = GPIO_MODE_INPUT;            /* 输入模式 */
    gpio_init_struct.Pull = GPIO_PULLUP;                 /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;     /* 高速 */
    HAL_GPIO_Init(ATK_ENCODER_TIMX_CH1_GPIO_PORT, &gpio_init_struct);  
    
/* 通道2的IO口,即编码器的B相 */
    gpio_init_struct.Pin = ATK_ENCODER_TIMX_CH2_GPIO_PIN;   
    HAL_GPIO_Init(ATK_ENCODER_TIMX_CH2_GPIO_PORT, &gpio_init_struct);  
    
    /************************ 定时器基本配置 **************************/
    g_timx_encode_chy_handle.Instance = ATK_ENCODER_TIMX;   /* 定时器3 */
    g_timx_encode_chy_handle.Init.Prescaler = psc;           /* 定时器分频 */
g_timx_encode_chy_handle.Init.Period = arr;               /* 自动重装载值 */
/* 递增计数模式 */
g_timx_encode_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  
/* 不分频 */
    g_timx_encode_chy_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;   
    
/************************* 编码器接口配置 ***************************/
/* TI1,TI2都检测 */
timx_encoder_chy_handle.EncoderMode = TIM_ENCODERMODE_TI12; 
/* 边沿检测器设置,非反向 */
timx_encoder_chy_handle.IC1Polarity = TIM_ICPOLARITY_RISING;       
/* 不分频 */        
timx_encoder_chy_handle.IC1Prescaler = TIM_ICPSC_DIV1; 
/* 滤波器设置 */   
timx_encoder_chy_handle.IC1Filter = 10;                  
/* 边沿检测器设置,非反向 */
timx_encoder_chy_handle.IC2Polarity = TIM_ICPOLARITY_RISING; 
/* 不分频 */              
timx_encoder_chy_handle.IC2Prescaler = TIM_ICPSC_DIV1;     
/* 滤波器设置 */     
    timx_encoder_chy_handle.IC2Filter = 10;  
    HAL_TIM_Encoder_Init(&g_timx_encode_chy_handle, &timx_encoder_chy_handle);
 
/* 开启编码器通道1 */
HAL_TIM_Encoder_Start(&g_timx_encode_chy_handle, ATK_ENCODER_TIMX_CH1); 
/* 开启编码器通道2 */ 
    HAL_TIM_Encoder_Start(&g_timx_encode_chy_handle, ATK_ENCODER_TIMX_CH2);  
    
/************************* 定时器中断配置 ***************************/
 
/* 抢占优先级2,响应优先级0 */
HAL_NVIC_SetPriority(ATK_ENCODER_TIMX_INT_IRQn, 2, 0);  
/* 开启定时器3中断 */
HAL_NVIC_EnableIRQ(ATK_ENCODER_TIMX_INT_IRQn);   
/* 清除更新中断 */
__HAL_TIM_CLEAR_FLAG(&g_timx_encode_chy_handle,TIM_IT_UPDATE);     
/* 开启更新中断 */
    __HAL_TIM_ENABLE_IT(&g_timx_encode_chy_handle,TIM_IT_UPDATE);      
/* 清空计数 */
    __HAL_TIM_SET_COUNTER(&g_timx_encode_chy_handle, 0);
}

       atk_encoder_timx_init()函数主要用于初始化编码器接口相关的IO及定时器,其中A相连接的是PA6(通道1),B相连接的是PA7(通道2)。这里用到的定时器为定时器3,配置为双边沿检测,即4倍频,如果接入的编码器为20线,则旋转一圈会计数80次。


       2. 计数值计算相关函数

       该系列函数用于获取编码器的计数值,具体代码如下所示:

/**
 * @brief       定时器3中断服务函数
 * @param       无
 * @retval      无
 */
void ATK_ENCODER_TIMX_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_timx_encode_chy_handle);
}
 
/* 用于记录溢出次数,向下溢出就-1,向上溢出就+1 */
volatile int g_timx_encode_count = 0;  
 
/**
 * @brief       定时器中断回调函数
 * @param        htim:定时器句柄指针
 * @note        此函数会被定时器中断函数共同调用的
 * @retval      无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
 /* 判断CR1的DIR位 */
    if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   
    {
        g_timx_encode_count--;       /* DIR位为1,也就是递减计数 */
    }
    else
    {
        g_timx_encode_count++;     /* DIR位为0,也就是递增计数 */
    }
}
 
/**
 * @brief       编码器计数
 * @param       无
 * @retval      当前总计数值
 */
int32_t atk_encoder_get_count(void)
{
    /* 计算当前总计数值,当前总计数值 = 计数器当前值 + 溢出次数*65536  */
return ( int32_t )(__HAL_TIM_GET_COUNTER(&g_timx_encode_chy_handle) 
+ g_timx_encode_count * 65536);
}

       上述代码中存在3个函数,前两者用于记录编码器计数溢出的次数,当定时器计数溢出时,会进入到定时器中断回调函数中,然后根据CR1寄存器的DIR位来判断是向上溢出还是向下溢出,依此来修改g_timx_encode_count变量的值。atk_encoder_get_count()函数则用于计算编码器的当前总计数值,具体计算公式见上述代码注释。


       3. 编码器按键相关函数

       该系列函数用于初始化编码器的按键以及扫描按键状态,具体代码如下所示:

/**
 * @brief       编码器按键初始化函数
 * @param       无
 * @retval      无
 */
void atk_encoder_key_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    ATK_ENCODER_KEY_GPIO_CLK_ENABLE();            /* 时钟使能 */
 
    gpio_init_struct.Pin = ATK_ENCODER_KEY_GPIO_PIN;  /* 编码器引脚 */
    gpio_init_struct.Mode = GPIO_MODE_INPUT;            /* 输入 */
    gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速 */
/* 编码器引脚模式设置,上拉输入 */
    HAL_GPIO_Init(ATK_ENCODER_KEY_GPIO_PORT, &gpio_init_struct);    
}
 
/**
 * @brief       编码器按键扫描函数
 * @param       无
 * @retval      0:无按键按下
 *              ENCODER_KEY_PRESSED:按键被按下
 *              ENCODER_KEY_LOOSE:按键被松开
 */
uint8_t atk_encoder_key_scan(void)
{
    static uint8_t key_up = 1;                     /* 按键松开标志 */
    uint8_t key_val = 0;
    
    if ((key_up == 1) && (ATK_ENCODER_KEY == 0))  /* 按键松开标志为1,且有按键按下 */
    {
        key_up = 0;                                   /* 标记按键被按下 */
        delay_ms(10);                                /* 去抖动 */
        
        if (ATK_ENCODER_KEY == 0)
        {
            key_val = ENCODER_KEY_PRESSED;      /* 返回按键被按下状态 */
        }
    }
    else if ((key_up == 0) && (ATK_ENCODER_KEY == 1))   /* 检测到按键被松开 */
    {
        key_up = 1;                                  /* 标记按键被松开 */
        key_val = ENCODER_KEY_LOOSE;             /* 返回按键被松开状态 */
    }
    
    return key_val;                                 /* 返回键值 */
}

       上述代码中,atk_encoder_key_init()函数用于初始化编码器按键相关的IO,这里使用的是PA5,配置为输入上拉模式。atk_encoder_key_scan()函数用于扫描编码器按键的状态,当按键被按下时,会返回ENCODER_KEY_PRESSED;当按键被松开时,会返回ENCODER_KEY_LOOSE。


       2.1.2.2 实验测试代码

     实验的测试代码在demo.c文件中,该文件在工程根目录下的 User文件夹。测试代码的入口函数为demo_run(),具体的代码,如下所示:

/**
 * @brief       例程演示入口函数
 * @param       无
 * @retval      无
 */
void demo_run(void)
{
    uint8_t key, t = 0;
    int32_t last_count = 0, new_count = 0;
 
    atk_encoder_timx_init(0XFFFF, 0);  /* 初始化编码器相关定时器,不分频 */
    atk_encoder_key_init();                   /* 初始化编码器按键 */
    
    while (1)
    {
        key = atk_encoder_key_scan();
 
        if (key == ENCODER_KEY_PRESSED)    /* 编码器按键被按下 */
        {
            printf("按键被按下\r\n");
        }
        else if(key == ENCODER_KEY_LOOSE)   /* 编码器按键被松开 */
        {
            printf("按键被释放\r\n");
        }
        
        new_count = atk_encoder_get_count(); /* 读取编码器计数值 */
        
        if(new_count != last_count)            /* 当计数值与上一次不同才打印到串口 */
        {
            last_count = new_count;             /* 记录当前计数值 */
            printf("编码器当前的计数值为:%d\r\n", new_count);
            printf("编码器当前旋转的圈数为:%0.2f\r\n", (float)new_count / 80);
        }
 
        t++;
        delay_ms(10);
 
        if (t == 20)
        {
            LED0_TOGGLE();                        /* LED0闪烁, 提示系统正在运行 */
            t = 0;
        }
    }
}

       从上面代码可以看出,整个测试代码的逻辑相对简单。初始化编码器模块的相关定时器和按键后,在while循环中调用相关函数不断地扫描按键状态和读取编码器的计数值,并通过串口打印,LED0闪烁表示程序正常运行。

       注意:为了更好地观察编码器的计数值与按键状态,仅当编码器的计数值发生变化时才会打印串口。


       2.1.3 实验现象

       将ATK-MB013 EC11编码器模块按照第一节“硬件连接”中介绍的连接方式与开发板连接,并将实验代码编译烧录至开发板中。本实验使用串口输出调试信息,因此需将开发板的PA9连接至DAP虚拟串口(或USB转TTL模块)的RX引脚。完成连接后,可通过串口调试助手XCOM查看实验信息输出,如下图所示:


图2.1.3.1串口调试助手显示内容


       注:其它现象请看2.1.1功能说明。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70040860/viewspace-3078162/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    765088