锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

嵌入式课程 之 霍尔传感器(编码器)实验

时间:2022-11-19 07:30:01 如何不启用windows传感器两线传感器分正负极吗c1623转向传感器cnt传感器250cn光电传感器100cn光电传感器

订阅专栏
版权属于以下公司,禁止非授权转载:

嵌入式课程 之 霍尔传感器(编码器)实验_真相很简单-CSDN博客_霍尔编码器

北京西普阳光教育科技有限公司(https://www.simpleware.com.cn)
维周机器人科技有限公司(http://www.vejoe.com)
文章目录
实验目的
【实验原理】
【实验环境】
硬件设备:
软件环境:
【实验步骤】
第一步 工程环境的配置
第二步 编写定时器函数,完成定时器的配置和启用
第三步 编写main.c文件
第四步 编译下载,观察实验现象
【思考题】
1、选择题
2、简答题
实验目的
熟悉编码器的类型及其工作原理;
熟悉编码器的输出形式,熟悉单片机如何通过编码器的输出获得电机的旋转方向和速率;
掌握STM32F10xx编码器在系列微控制器上的接口配置和数据采集过程;
【实验原理】
编码器(encoder)是指通过收集和准备某种形式的信号或数据(通常是指电机转速)转换为可用于传输和存储的信号形式的设备。编码器可分为接触式编码器和非接触式编码器;根据工作原理,可分为光电、磁电和触点刷编码器。光电编码器通过收集光通过码盘孔产生的脉冲信号的频率进行转换,磁电编码器通过霍尔元件感应电机转动产生的磁场变化进行转换,触点刷编码器直接收集接触点在码盘上的位置;也可分为增量和绝对编码器。增量式编码器记录通过采集位移信息产生的脉冲信号,近似于“速率”的概念,绝对式编码器记录位置信息,与过程无关,近似于“路程”的概念。

本实验使用的编码器是增量编码器,通常有两个输出信号,A相和B相(有些编码器有A、B、Z三相输出,只有A相输出有编码器)。每个信号都是由方波组成的脉冲序列,A相与B相的相位差为90度。接收单片机AB相输入后,电机的转向可以根据收相位的顺序确定,电机的转速可根据脉冲序列的频率确定。这种解码方法称为正交解码。值得注意的是,脉冲频率与电机转速成正比,其具体比例系数与代码盘孔数、减速箱齿数有关,速度与驾驶速率也有比例系数,因此通常根据现场测试结果直接确定脉冲频率与驾驶速率之间的经验系数。

STM32芯片集成了强大的定时器模块,其工作状态主要包括输入捕获模式、PWM输入模式、强制输出模式、输出比较模式PWM模式、单脉冲模式、编码器接口模式、调试模式等。定时器模块的编码器接口模式应用于本实验。当定时器在编码器模式下工作时,定时器会收集两个指定的引脚输入信号,然后通过检测输入信号的跳转边缘来确定输入脉冲的频率和相位差,从而计算电机的旋转方向和速率。编码器接口模式基本上相当于使用方向选择的外部时钟。这意味着计数器只有0到0TIMx_ARR连续计数寄存器的自动装载值(根据方向,或0到0ARR计数,或是ARR到0计数)。因此,在开始计数之前必须配置它TIMx_ARR。


图1 编码器模式下的计数器操作示意图

值得注意的是,跳变沿检测功能只完成,普通IO嘴可以做到。与普通相比,定时器模块的编码器接口模式。IO口的优点是能抵抗一定的噪音,如图1所示,计数器在输入信号有毛刺时仍能正常工作,普通IO要实现类似的功能,需要人工添加一系列代码。
如图2所示:


图2 程序流程示意图

【实验环境】
硬件设备:
1.双轮自平衡机器人。如图3所示,安装在平衡车背面中间HC-SR超声波测距模块04。
2、ST-Link下载器(包括USB如图4所示。
操作系统:
Windows7/8/10,32bit/64bit


图3 双轮自平衡机器人

图4 ST-Link下载器与下载线

软件环境:
Keil 5

【实验步骤】
第一步 工程环境的配置
(1)打开已建立的工程模板,在新建立的工程模板中加入五个文件夹,分别命名为USER、HARDWARE、SYSTEM、CORE、FWLib如图5所示,文件夹。其中USER主函数存储在文件夹中,HARDWARE本实验对应的硬件设备函数存储在文件夹中,SYSTEM存储本课程所有实验通用函数,CORE启动文件存储在文件夹中,FWLib底层驱动函数存储在文件夹中。

图5工程模板对应的文件夹

(2)在HARDWARE文件夹下有两个新文件,分别是encoder.c和encoder.h。存储编码器函数文件和头文件,如图6所示。

图6 在HARDWARE建立文件夹encoder.c与encoder.h文件

第二步 编写定时器函数,完成定时器的配置和启用
(3)打开程序encoder.c文件,先将encoder.h文件包含在内。Encoder_Init_TIM2 编写函数,TIM2采集左码盘返回值,选择要使用的时钟,配置引脚,启用编码器模式,配置参数。

#include "encoder.h"
#include "stm32f10x_gpio.h"
/****************************************************************
函数功能:把TIM2初始化为编码器接口模式
入口参数:无
返回值:无
****************************************************************/
void Encoder_Init_TIM2(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
///使定时器4的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//使能PA端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; ///端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; ///浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; ///预分频器
///设定计数器自动重载值
TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD;
///选择时钟分频:不分频
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//TIM向上计数
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM更新标志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//Reset counter
TIM_SetCounter(TIM2,0);
TIM_Cmd(TIM2, ENABLE);
}
(1) (4)同样,写作Encoder_Init_TIM4函数,TIM4定时器采集右侧编码器返回值。

/****************************************************************
函数功能:把TIM4初始化为编码器接口模式
入口参数:无
返回值:无
****************************************************************/
void Encoder_Init_TIM4(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; &bsp;
TIM_ICInitTypeDef TIM_ICInitStructure;  
GPIO_InitTypeDef GPIO_InitStructure;
//使能定时器4的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
//使能PB端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;    //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);        //初始化GPIOA
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //预分频器
//设定计数器自动重载值
TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; 
//选择时钟分频:不分频
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//TIM向上计数
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,
TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_ClearFlag(TIM4, TIM_FLAG_Update);//清除TIM的更新标志位
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
TIM_SetCounter(TIM4,0);//Reset counter
TIM_Cmd(TIM4, ENABLE); 
}
(5)编写编码器计数器读取函数

/*****************************************************************
函数功能:单位时间读取编码器计数
入口参数:定时器
返回值:速度值
*****************************************************************/
int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;    
    switch(TIMX)
    {
        case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;
        case 4:  Encoder_TIM= (short)TIM4 -> CNT;  TIM4 -> CNT=0;break;    
        default:  Encoder_TIM=0;
    }
    return Encoder_TIM;
}
(2) 打开encoder.h,编写函数声明

#ifndef __ENCODER_H
#define __ENCODER_H
#include     
#define ENCODER_TIM_PERIOD (u16)(65535)   //不可大于65535 因为F103的定时器是16位的
void Encoder_Init_TIM2(void);
void Encoder_Init_TIM4(void);
int Read_Encoder(u8 TIMX);
#endif
第三步 编写main.c文件
(3) (6)将工程编译需要用到的头文件包含进来,并预定义显示函数和全局变量。

#include "encoder.h" //包含编码器函数头文件
#include "sys.h"   //包含系统头文件
#include "stm32f10x.h" //包含系统寄存器定义声明的头文件

void oled_show(void);
int Encoder_Left,Encoder_Right;   //左右编码器的脉冲计数
(4) (7)在主函数中调用延时函数、显示函数和编码器函数的初始化函数。

int main(void)

delay_init();       //延时函数初始化
    OLED_Init();     //OLED初始化
        Encoder_Init_TIM2();            //编码器接口初始化
        Encoder_Init_TIM4();            //编码器接口初始化
(5) (8)定义主循环,在主循环中调用超声波读取函数和显示函数。

    while(1)
    {
        Encoder_Left=Read_Encoder(2);       //读取左侧编码器值
        Encoder_Right=Read_Encoder(4);      //读取右侧编码器值
        oled_show();          //显示屏打开
        delay_ms(50); 
    }
(6) (9)编写OLED显示函数

void oled_show(void)
{
//显示右侧编码器返回值
    OLED_ShowString(0,10,"Encoder_Right");
    if(Encoder_Right >=0)OLED_ShowString(20,20,""),
        OLED_ShowNumber(45,20,Encoder_Right,4,12);    
    else OLED_ShowString(20,20,"-"),
        OLED_ShowNumber(45,20,4-Encoder_Right,4,12);
//显示左侧编码器返回值
    OLED_ShowString(0,40,"Encoder_Left");
    if(Encoder_Left>=0)OLED_ShowString(10,50,""),
        OLED_ShowNumber(45,50,Encoder_Left,4,12);
    else OLED_ShowString(10,50,"-"),
        OLED_ShowNumber(45,50,-Encoder_Left,4,12);
    //=============刷新======================//
    OLED_Refresh_Gram();    
}
第四步 编译并下载,观察实验现象
(10)本实验采用仿真器为STLink V2,将仿真器与小车相连,注意正负极不要接反,如图7所示。

图7仿真器与下载线连接图

(11)编译程序:点击如图8所示的编译按键。

图8Keil编译环境下的编译按键

(12)当编译完成后,如果没有问题,Build Output栏会出现无错误、无警告的提示,如图9所示。

图9编译通过后Build Output栏提示信息

(13)下载程序:点击如图所示的下载按键,程序就会下载到STM32的芯片中。下载按键如图10所示。

图10Keil编译环境下的下载按键

(14)观察实验现象,OLED显示屏上显示出当前电机的转速,用手从不同方向转动电机,观察数值的变化,结果如图11所示。

图11平衡车上的编码器测量数据

【思考题】
1、选择题
题目1:编码器传回的正交编码中,两个信号的相位差是多少(B)
A:60度
B:90度
C:120度
D:180度

题目2:若要配置一个8位的编码器,在encoder.h中的自动重载值应该设为多少(B)
A:8
B:255
C:256
D:65535

2、简答题
题目1:简要说明如何将编码器的输出转换为方向信息和速率信息。
编码器输出为AB两相脉冲信号,A相和B相之间的相位差为90度。单片机在接收到AB相输入后,可以根据收到相位的先后顺序确定电机的转向,根据脉冲序列的频率确定电机的转速。

题目2:结合相关资料,试说明如何通过IO和中断对编码器的输出数据进行解码。

(开放性题目,答案不唯一)配置两个IO口为上升下降沿触发中断,设置全局变量表示转速、方向,设置表示跳变的标志位,指定其中一个输入,若该输入触发中断,则转速变量加1,检测相应的寄存器,若为上升沿触发则跳变标志位置1,若为下降沿触发则跳变标志位置0;当另一输入进入中断时,可以根据自身是上升沿还是下降沿触发结合方向标志位,确定自身是超前于另一信号,还是滞后于另一信号,从而确定方向。


————————————————
版权声明:本文为CSDN博主「放羊郎」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/suoxd123/article/details/100694019

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章