电机应用开发-编码器的使用

news2024/9/28 17:32:13

目录

编码器

增量式编码器倍频技术

常用编码器测速方法:M法、T法和M/T法

STM32的编码器接口

编码器接口结构体

减速电机编码器测速实验

硬件设计

TIM3配置编码器

测速环节

步进电机编码器测速实验


编码器

增量式编码器倍频技术

增量式编码器输出的常见脉冲波形信号形式:

占空比为50%的方波,通道A和通道B相位差为90°。

正弦波的模拟信号,通道A和通道B相位差为90°。

对于占空比为50%的方波,通道A和通道B相位差为90°。先以下图为例子。

如果只在某一通道的上升沿计数,则计数频率 = 该通道频率。

如果在某一通道的上升沿和下降沿都计数,则计数频率 = 该通道频率 * 2,即2倍频。

如果在双通道的上升沿和下降沿都计数,则计数频率 = 该通道频率 * 4,即4倍频。

因此,至少在1/2个原始方波周期内就可以计数一次,最多在1/4个原始方波周期内就可以计数一次。这样计数频率就是原始方波信号的2或4倍,即编码器的分辨率提高了2倍到4倍。

假设有个增量式编码器的分辨率是600PPR,能分辨最小角度是0.6°,对它进行4倍频后就相当于把分辨率提高到600*4=2400PPR,此时编码器能够分辨的最小角度为0.15°。

编码器倍频计数还可以用来扩展一些测速方法的速度适用范围。例如电机测试通常使用M法测量,编码器4倍频后可以扩展M法的速度下限。

常用编码器测速方法:M法、T法和M/T法

对于电机转速的测试,可以把增量式编码器安装到电机上,用控制器对编码器脉冲进行计数,然后通过特定的方法求出电机转速。

M法(频率测量法):在一个固定的定时时间内(以秒为单位),统计这段时间的编码器脉冲数,计算速度值。

设编码器单圈脉冲数为C,在时间T内统计到的编码器脉冲数为M,则转速n = M / CT。编码器单圈脉冲数C是一个常数,所以转速n和M成正比,使得在高速测量时M变大可以获得较好的测量精度和平稳度。但如果速度很低,低到每个T只有几个脉冲,此时算出的转速误差就会比较大,并且不稳定。

有一些方法可以改善M法在低速测量的准确性,比如增量式编码器倍频技术。当原本捕获到的脉冲M只有4个,经过4倍频后,相同电机状态M变成了16个,也就提升了低速下的测量精度。

T法(周期测量法):建立在一个已知频率的高频脉冲并对其计数,计数时间由捕获到的编码器相邻两个脉冲的间隔时间T决定,计数值为M。设编码器单圈总脉冲数为C,高频脉冲的频率为F,则转速n = 1/CT=F/CM。C和F是常数,所以转速n和M成反比。在电机高转速时,编码器脉冲间隔时间T很小,使得测量周期内的高频脉冲计数值M也变得很少,导致测量误差变大。而在低转速时,T足够大,测量周期内的M也足够多。所以T法和M法刚好相反,更适合测量低速

M/T法综合了M法和T法各自的优势,即测量编码器脉冲数又测量一定时间内的高频脉冲数。

在一个相对固定的时间内,计数编码器脉冲数为M0,并计数一个已知频率为F的高频脉冲,计数值为M1,设编码器单圈总脉冲数为C,则转速n = FM0/CM1。由于F和C是常数,所以转速n只受M0和M1的影响。电机高速时,M0增大,M1减小,相当于M法;电机低速时,M1增大,M0减小,相当于T法

STM32的编码器接口

STM32芯片内部有专门用来采集增量式编码器方波信号的接口,这些接口实际上是STM32定时器的其中一种功能。编码器接口功能只有高级定时器TIM1/8和通用定时器TIM2~TIM5才有。

编码器接口用到了定时器的输入捕获部分。

这个表格将编码器接口所有可能出现的工作情况全都列了出来,包括它是如何实现方向检测和倍频的。

STM32的编码器接口在计数时并不是单纯地采集某一通道信号地上升沿或下降沿,而是需要综合另一个通道信号的电平。

表中“相反信号的电平”指的是在计数时所参考的另一个通道信号的电平,这些电平决定了计数器的计数方向。

仅在TI1处计数:TI1比TI2提前1/4个周期,以TI1的信号边沿作为有效边沿。当检测到TI1的上升沿时,TI2为低电平,此时计数器向上计数一次,下一时刻检测到TI1的下降沿时,TI2为高电平,此时计数器仍然向上计数一次,以此类推。这样就能把TI1的上升沿和下降沿都用来计数,即实现了对原始信号的2倍频。

仅在TI2处计数:TI1比TI2滞后1/4个周期,以TI1的信号边沿作为有效边沿。当检测到TI1的上升沿时,TI2为高电平,此时计数器向下计数一次,下一时刻检测到TI1的下降沿时,TI2为低电平,此时计数器仍然向下计数一次,以此类推。这样就能把TI1的上升沿和下降沿都用来计数,即实现了对原始信号的2倍频。

在TI1和TI2处均计数:把两个通道的上升沿和下降沿都用来计数,计数方向也是两个通道同时参考,相当于仅在一个通道处计数的2倍,所以实现了对原始信号的4倍频。

编码器接口结构体

主要有时基初始化结构体TIM_Base_InitTypeDef和编码器初始化配置结构体TIM_Encoder_InitTypeDef。

编码器初始化配置结构体TIM_Encoder_InitTypeDef用于定时器的编码器接口模式,与HAL_TIM_Encoder_Init函数配合使用完成初始化配置工作。

高级定时器TIM1和TIM8以及通用定时器TIM2~TIM5都带有编码器接口,使用时都必须单独设置。

typedef struct
{
	uint32_t EncoderMode;    // 编码器模式。通道A计数/通道B计数/双通道计数。设定TIMx_SMCR:SMS[2:0]
	uint32_t IC1Polarity;    // 输入信号极性。设置定时器通道在编码器模式下的输入信号是否反相。设定TIMx_CCER:CCxNP、CCxP
	uint32_t IC1Selection;   // 输入通道。TIM_ICSELECTION_DIRECTTI/TIM_ICSELECTION_INDIRECTTI/TIM_ICSELECTION_TRC。设定TIMx_CCMRx:CCxS[1:0]
	uint32_t IC1Prescaler;   // 输入捕获预分频器。1/2/4/8,设定TIMx_CCMRx:ICxPSC[1:0]
	uint32_t IC1Filter;      // 输入捕获滤波器,0x0~0xf,设定TIMx_CCMRx:ICxF[3:0]
	uint32_t IC2Polarity;    // 输入信号极性。设置定时器通道在编码器模式下的输入信号是否反相。设定TIMx_CCER:CCxNP、CCxP
	uint32_t IC2Selection;   // 输入通道。TIM_ICSELECTION_DIRECTTI/TIM_ICSELECTION_INDIRECTTI/TIM_ICSELECTION_TRC。设定TIMx_CCMRx:CCxS[1:0]
	uint32_t IC2Prescaler;   // 输入捕获预分频器。1/2/4/8,设定TIMx_CCMRx:ICxPSC[1:0]
	uint32_t IC2Filter;      // 输入捕获滤波器,0x0~0xf,设定TIMx_CCMRx:ICxF[3:0]
 }TIM_Encoder_InitTypeDef;

单通道计数设置编码器接口为2倍频,双通道计数设置编码器接口为4倍频。

编码器接口模式下只能使用这个TIM_ICSELECTION_DIRECTTI。

减速电机编码器测速实验

硬件设计

用到的减速电机和之前的减速电机按键控制例程相同,所以电机、开发板和驱动板的硬件连接也完全相同,只加上了编码器的连线。

TIM3配置编码器

/* 定时器溢出次数 */
__IO int16_t Encoder_Overflow_Count = 0;
TIM_HandleTypeDef TIM_EncoderHandle;

/**
  * @brief  编码器接口初始化
  * @param  无
  * @retval 无
  */
void Encoder_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
	TIM_Encoder_InitTypeDef Encoder_ConfigStructure;

    __HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_TIM3_CLK_ENABLE();

	// 编码器通道1
	GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

	// 编码器通道2
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	
	/* 定时器初始化设置 */
    TIM_EncoderHandle.Instance 					= TIM3;
    TIM_EncoderHandle.Init.Prescaler 			= 0;
    TIM_EncoderHandle.Init.CounterMode 			= TIM_COUNTERMODE_UP;
    TIM_EncoderHandle.Init.Period 				= 65535;
    TIM_EncoderHandle.Init.ClockDivision 		= TIM_CLOCKDIVISION_DIV1;
    TIM_EncoderHandle.Init.AutoReloadPreload	= TIM_AUTORELOAD_PRELOAD_DISABLE;

    /* 设置编码器倍频数 */
    Encoder_ConfigStructure.EncoderMode 		= TIM_ENCODERMODE_TI12;
    Encoder_ConfigStructure.IC1Polarity 		= TIM_ICPOLARITY_RISING;
    Encoder_ConfigStructure.IC1Selection 		= TIM_ICSELECTION_DIRECTTI;
    Encoder_ConfigStructure.IC1Prescaler 		= TIM_ICPSC_DIV1;
    Encoder_ConfigStructure.IC1Filter 			= 0;
    Encoder_ConfigStructure.IC2Polarity 		= TIM_ICPOLARITY_RISING;
    Encoder_ConfigStructure.IC2Selection 		= TIM_ICSELECTION_DIRECTTI;
    Encoder_ConfigStructure.IC2Prescaler 		= TIM_ICPSC_DIV1;
    Encoder_ConfigStructure.IC2Filter 			= 0;
    HAL_TIM_Encoder_Init(&TIM_EncoderHandle, &Encoder_ConfigStructure);
	
	/* 清零计数器 */
    __HAL_TIM_SET_COUNTER(&TIM_EncoderHandle, 0);

    /* 清零定时器的更新事件中断标志位 */
    __HAL_TIM_CLEAR_IT(&TIM_EncoderHandle, TIM_IT_UPDATE);
    /* 使能定时器的更新事件中断 */
    __HAL_TIM_ENABLE_IT(&TIM_EncoderHandle, TIM_IT_UPDATE);
    /* 设置更新事件请求源为:计数器溢出 */
    __HAL_TIM_URS_ENABLE(&TIM_EncoderHandle);

    /* 设置中断优先级 */
    HAL_NVIC_SetPriority(TIM3_IRQn, 5, 1);
    /* 使能定时器中断 */
    HAL_NVIC_EnableIRQ(TIM3_IRQn);

    /* 使能编码器接口 */
    HAL_TIM_Encoder_Start(&TIM_EncoderHandle, TIM_CHANNEL_ALL);
}

/**
  * @brief  定时器更新事件回调函数
  * @param  无
  * @retval 无
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    /* 判断当前计数器计数方向 */
    if (__HAL_TIM_IS_TIM_COUNTING_DOWN(&TIM_EncoderHandle))  
    {	/* 下溢 */
        Encoder_Overflow_Count--;
    }
    else
    {	/* 上溢 */
        Encoder_Overflow_Count++;
    }
}

测速环节

/* 电机旋转方向 */
__IO int8_t Motor_Direction = 0;
/* 当前时刻总计数值 */
__IO int32_t Capture_Count = 0;
/* 上一时刻总计数值 */
__IO int32_t Last_Count = 0;
/* 电机转轴转速 */
__IO float Shaft_Speed = 0.0f;

/* 配置1ms时基为SysTick */
HAL_InitTick(5);

/**
  * @brief  SysTick中断回调函数
  * @param  无
  * @retval 无
  */
void HAL_SYSTICK_Callback(void)
{
    static uint16_t i = 0;

    i++;

	/* 100ms计算一次 */
    if (i == 100) 
    {
        /* 电机旋转方向 = 计数器计数方向 */
        Motor_Direction = __HAL_TIM_IS_TIM_COUNTING_DOWN(&TIM_EncoderHandle);

        /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * 65535  */
        Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * ENCODER_TIM_PERIOD);

        /* 转轴转速 = 单位时间内的计数值 / 编码器总分辨率(4*16,4倍的物理分辨率) * 时间系数  */
        Shaft_Speed = (float)(Capture_Count - Last_Count) / ENCODER_TOTAL_RESOLUTION * 10 ;

        printf("电机方向:%d\r\n", Motor_Direction);
		/* 单位时间计数值 = 当前时刻总计数值 - 上一时刻总计数值 */
        printf("单位时间内有效计数值:%d\r\n", Capture_Count - Last_Count);
        printf("电机转轴处转速:%.2f 转/秒 \r\n", Shaft_Speed);
		/* 输出轴转速 = 转轴转速 / 减速比 */
        printf("电机输出轴转速:%.2f 转/秒 \r\n", Shaft_Speed / REDUCTION_RATIO); 

        /* 记录当前总计数值,供下一时刻计算使用 */
        Last_Count = Capture_Count;
        i = 0;
    }
}

步进电机编码器测速实验

类似

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1235713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STC单片机选择外部晶振烧录程序无法切换回内部晶振导致单片机不能使用

STC单片机选择外部晶振烧录程序无法切换回内部晶振导致单片机不能使用 1.概述 在学习51单片机过程中,选择了STC的12C2052AD型号单片机作为入门芯片。前几个课题实验使用默认的内部晶振烧录程序,运行都没有问题。 选择一个LED亮度渐变的课题做实验&…

【C++】构造函数详解

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …

Vue3 源码解读系列(九)——依赖注入

依赖注入 依赖注入用于祖先组件向后代组件传递数据。 特点: 祖先组件不需要知道哪些后代组件在使用它提供的数据。 后代组件也不需要知道注入的数据来自哪里。 /*** provide 的实现*/ function provide(key, value) {let provides currentInstance.provides // 当…

量子计算+物流!“最后一英里”配送难题Unisys成功实时决策

(图片来源:网络) 此前,供应链行业一直致力于手工操作,严重依赖于纸质系统。后来随着客户需求的不断变化,这种传统方法逐渐显出不足之处。供应链行业正在迅速转向现代化,采用自动化和数据驱动的…

【开源】基于微信小程序的音乐平台

项目编号: S 055 ,文末获取源码。 \color{red}{项目编号:S055,文末获取源码。} 项目编号:S055,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示 四、核心代码4.1 查询单首…

redis的一些操作

文章目录 清空当前缓存和所有缓存配置内存大小,防止内存饱满设置内存淘汰策略键过期机制 清空当前缓存和所有缓存 Windows环境下使用命令行进行redis缓存清理 redis安装目录下输入cmdredis-cli -p 端口号flushdb 清除当前数据库缓存flushall 清除整个redis所有缓存…

关于一些网络的概述

语义分割网络是一种基于深度学习的计算机视觉技术,它能够将图像中的每个像素分配给特定的类别,从而实现对图像中不同对象的精确识别和定位。近年来,随着深度学习技术的不断发展,语义分割网络在各个领域都取得了显著的进展。 早期的语义分割网络主要采用全卷积神经网络(FC…

大数据分析仓库Kylin

一、Kylin 定义 Apache Kylin 是一个开源的分布式分析引擎,提供 Hadoop/Spark 之上的 SQL 查询接口及多维分析能力以支持超大规模数据,最初由 eBay 开发并贡献至开源社区。它能在亚秒内查询巨大的 Hive 表。 二、Kylin 架构 A、REST Server 是应用程序…

操作系统基础操作

操作系统的启动 体系结构概念 CPU、I/O、内存-通过总线连接 操作系统一开始存放时没有放在内存里,而是当在DISK中,由BIOS提供相应支持 DISK:存放OSBIOS:基本I/O处理系统(计算机开机时可以让系统检测各种外设&#…

软文转化率如何提高,媒介盒子分享三大原则

决定软文推广的主要因素就在于软文内容,但是如何才能提高软文转化率呢?下面就让媒介盒子为你解答! 一、 软文类型 软文类型按照推广目标划分主要分为品牌软文、产品软文、营销软文。 ● 品牌软文 品牌软文的目的在于扩大品牌对群众的影响力…

基于Vue+SpringBoot的桃花峪滑雪场租赁系统

项目编号: S 036 ,文末获取源码。 \color{red}{项目编号:S036,文末获取源码。} 项目编号:S036,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 游客服务2.2 雪场管理 三、数据库设…

SpringCloud微服务 【实用篇】| Eureka注册中心、Ribbon负载均衡

目录 一:Eureka注册中心 1. Eureka原理 2. 动手实践 ①搭建EurekaServer ②服务注册 ③服务发现 二:Ribbon负载均衡 1. 负载均衡原理 2. 负载均衡策略 3. 懒加载 一:Eureka注册中心 前面已经分析了,无论是SpringCloud还…

从0开始学习JavaScript--JavaScript中的集合类

JavaScript中的集合类是处理数据的关键,涵盖了数组、Set、Map等多种数据结构。本文将深入研究这些集合类的创建、操作,以及实际应用场景,并通过丰富的示例代码,帮助大家更全面地了解和应用这些概念。 数组(Array&…

【Java】NIO概述

本文主要介绍Java的IO。 这里主要按类的操作方式和操作对象对JavaIO进行分类,方便理解,后续使用时可以方便地查询。 一、操作方式分类 首先介绍几组概念: 字节流和字符流: 字节流:以字节为单位,每次次读…

【高级网络程序设计】Week2-3 HTML

一、The Basics 1. HTML&HTML file HTMLMarkup languageHyper Text Markup LanguageHTML fileText file with markup tags.htm/.html extension Create an html file Open an editor Type: <html><head><titile><body> Save it as .html Open i…

计算机网络——网络可靠性及网络出口配置

1. 前言&#xff1a; 学习目标&#xff1a; 1.了解链路聚合的作用 2. 了解ACL的工作原理 3. 了解NAT的工作原理和配置 2. 网络可靠性方案 网络可靠性是指网络在面对各种异常情况或故障时&#xff0c;能够维持正常运行和提供服务的能力。这包括防止网络中断、减小数据丢失的可能…

vue3中使用全局自定义指令和组件自定义指令

这篇文章会教大家如何实现全局自定义指令和组件自定义指令 &#x1f4d3;全局自定义指令和组件自定义指令的区别&#xff0c;除了写法不同和作用不同&#xff0c;其他的包括生命周期的使用方法都是一致的&#xff0c;全局自定义指令在main.ts中注册后整个项目都可以使用&#x…

dvwa-command injection 代码审计(超详细逐行审计)

dvwa-command injection 代码审计 low <?phpif( isset( $_POST[ Submit ] ) ) {// Get input$target $_REQUEST[ ip ];// Determine OS and execute the ping command.if( stristr( php_uname( s ), Windows NT ) ) {// Windows$cmd shell_exec( ping . $target );}…

Parallel Diffusion Models of Operator and Image for Blind Inverse Problems

盲逆问题算子和图像的并行扩散模型 论文链接&#xff1a;https://arxiv.org/abs/2211.10656 项目链接&#xff1a;https://github.com/BlindDPS/blind-dps Abstract 在正向算子已知的情况下(即非盲)&#xff0c;基于扩散模型的逆问题求解器已经展示了最先进的性能。然而&…

linux md5sum计算hash指令

在soc启动&#xff0c;验证镜像签名时&#xff0c;会计算文件的hash值&#xff0c;确保文件未被修改&#xff0c;md5sum可以计算&#xff0c;有256,512位的的其他指令&#xff0c; 如下&#xff0c;计算文件hash值。