0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何用STM32让相对编码器说话?

Q4MP_gh_c472c21 来源:21ic 作者:grhr 2021-03-17 10:24 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

编码器的由来和原理

若要对伺服系统中的电机进行高精度控制,需要准确的转子角度位置,这时候自然会想到,如果能张江转子每一圈进行细分,这样每次转多少角度便能精确知道。在这样的背景下,相对编码器就诞生了。

在网上找到下文这个图,很形象的表征了相对编码器的原理。

如图所示,在码盘上平均开出很多个等间距的槽,一段是LED灯发出信号,另一端是接收器接收信号。如果信号能穿过码盘,则接收信号为高电平,反之则为低电平。这样当转子转起来以后,就不断的处高低电平。这就是编码器基本原理。

可以看到这里有三个信号,A/B/Z,这时候就要想为什么要3个信号呢?如果仅仅对一圈做细分,命名一个信号就可以了。这就涉及到下面两个问题。

(1)如果是1个信号channel A,电机是正转还是反转就不知道了。需要一个相对的参考信号channel B,A和B相互呈一个角度,这样通过A和B的相对位置就能知道电机是顺时钟转还是逆时针转了。

(2)如果是2个信号,其中一旦有码盘有损坏,就可能出现检测结果无法校验的情况。举个例子,如果一圈开了16个槽,则每旋转一圈,正常情况下就有16个高低电平的信号出来。但如果一个槽坏了,实际上每转一圈只有15个信号出来,但这时如果仅仅通过channel A和channel B是无法判断的。在进行数据处理时还是认为16个信号为一圈,处理结果就有较大的偏差。为了避免这样的问题,补充z信号,一圈只出一个,这样就能相互交验了。一方面通过对A或者B计数,知道z是否有问题,反之对z信号计数就能知道A/B是否有问题。

所以就有了上图的z/A/B三个信号,共同组成了一个功能齐全的编码器。

在网上经常看到说A/B之间相互差90°,这个90°是认为360°为一个周期而言的。如下图所示。通过看A/B相对位置就知道电机是正转还是反转了。

b8e1be74-86b9-11eb-8b86-12bb97331649.png

实测波形,如下图所示(示波器不太好,有点毛刺)

b90de332-86b9-11eb-8b86-12bb97331649.png

正转

反转

▍使用STM32,让编码器说话

背景

STM32中提供了编码器接口,比较适用于相对编码器的应用场景。在手册中可以看到:

可以看到这里使用专用的模块就能完成相应的计数,通过数据的变化就能测出电机的转速。

所以,我想让编码器说话。在家翻箱倒柜以后,我准备了如下几个东西:

(1)带编码器的直流电机:这是作为编码器的载体使用,电机编码器的分辨率较低,每圈只有16个脉冲。但不影响测试。

(2)直流电源:用来直观的调电机的转速和正反转。

为了避免打广告的嫌疑,就不贴电源和电机图片了。

(3)STM32开发板:在家翻箱倒柜,找出2015年在21ic获得的STM32072 discovery板

(4)LED数码管。用来通过编码器的数据处理,显示电机的转速。

试验第一步,让LED数码管显示起来。

因为显示数据是最终目的。使用的这个板子,是集成了HC595锁存器的板子。相比于网上买的大部分51开发板数码管电机设计,使用两个HC595,可以大大减少pin脚的数量。网上使用的4位数码管,需要8个pin作为段选或者位选,非常麻烦。

根据HC595的手册,具有锁存加移位的特性(图中我标注所示)

bc24263a-86b9-11eb-8b86-12bb97331649.png

最上面的3个SH-CP/DS/ST-CP,像极了SPI通信波形,只要合理配置,只需要3个信号线即可完成4数码管的轮流显示。

于是在开发板的pin做了如下硬件配置

Pin(数码管) 74HC595SPIPin

SCLKPin11(shift)SPICLKPB13

RCLKPin12(Storage)NSSPB12

DIOPin14(datainput)SPIMOSIPC3

QHPin9(dataoutput)SPIMISOPC2

SPI配置代码如下(配置了SPI几个pin脚的定义,时钟,SPI模式等):

void SPI_Digital_Tube_Config(void){ SPI_InitTypeDef SPI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure;

/* Disable the SPI peripheral */ SPI_Cmd(SPI2, DISABLE); /* Enable SCK, MOSI, MISO and NSS GPIO clocks */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); RCC_AHBPeriphClockCmd(SPI_Digital_Tube_SCK_GPIO_CLK | SPI_Digital_Tube_MOSI_GPIO_CLK| SPI_Digital_Tube_NSS_GPIO_CLK, ENABLE);

/* SPI pin mappings */ GPIO_PinAFConfig(SPI_Digital_Tube_SCK_GPIO_PORT, SPI_Digital_Tube_SCK_SOURCE, SPI_Digital_Tube_SCK_AF); GPIO_PinAFConfig(SPI_Digital_Tube_MOSI_GPIO_PORT, SPI_Digital_Tube_MOSI_SOURCE, SPI_Digital_Tube_MOSI_AF); GPIO_PinAFConfig(SPI_Digital_Tube_MISO_GPIO_PORT, SPI_Digital_Tube_MISO_SOURCE, SPI_Digital_Tube_MISO_AF); GPIO_PinAFConfig(SPI_Digital_Tube_NSS_GPIO_PORT, SPI_Digital_Tube_NSS_SOURCE, SPI_Digital_Tube_NSS_AF);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;

/* SPI SCK pin configuration */ GPIO_InitStructure.GPIO_Pin = SPI_Digital_Tube_SCK_PIN; GPIO_Init(SPI_Digital_Tube_SCK_GPIO_PORT, &GPIO_InitStructure);

/* SPI MOSI pin configuration */ GPIO_InitStructure.GPIO_Pin = SPI_Digital_Tube_MOSI_PIN; GPIO_Init(SPI_Digital_Tube_MOSI_GPIO_PORT, &GPIO_InitStructure);

/* SPI MISO pin configuration */ GPIO_InitStructure.GPIO_Pin = SPI_Digital_Tube_MISO_PIN; GPIO_Init(SPI_Digital_Tube_MISO_GPIO_PORT, &GPIO_InitStructure);

/* SPI NSS pin configuration */ GPIO_InitStructure.GPIO_Pin = SPI_Digital_Tube_NSS_PIN; GPIO_Init(SPI_Digital_Tube_NSS_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Pin = SPI_Digital_Tube_NSS_PIN; GPIO_Init(SPI_Digital_Tube_NSS_GPIO_PORT, &GPIO_InitStructure);

/* SPI configuration -------------------------------------------------------*/ SPI_I2S_DeInit(SPI2); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;// SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_Init(SPI2, &SPI_InitStructure);

/* Initialize the FIFO threshold */ SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF);

/* Enable the SPI peripheral */ SPI_Cmd(SPI2, ENABLE);

// /* Enable NSS output for master mode */// SPI_SSOutputCmd(SPI2, ENABLE);}

使用TIM6作为定时器,配置代码如下(1ms定时周期):

static void BASIC_TIM_Mode_Config(void){ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE); TIM_TimeBaseStructure.TIM_Period = BASIC_TIM_Period;//1ms TIM_TimeBaseStructure.TIM_Prescaler= BASIC_TIM_Prescaler;//47 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter=0; TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure); TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update); TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE); TIM_Cmd(BASIC_TIM, ENABLE); }

实际上每次只会有一个数码管亮,为了较好的视觉体验,将数码管进行千位百位十位个位循环显示,这样做的好处是4个数码管轮流显示,其亮度相同,避免出现一个数码管过亮的情形,影响视觉体验。数码管代码如下:

void DisplayNumber(uint16_t num){ uint8_t mythousandNum,myhundredNum,mytenNum,myunitNum=0; if(num》9999)num=9999; mythousandNum=num/1000%10; myhundredNum=num/100%10; mytenNum=num/10%10; myunitNum=num%10; switch(mydisplaybit) { case thousaud: Display16(mythousandNum,4); mydisplaybit=hundred; break; case hundred: Display16(myhundredNum,3); mydisplaybit=ten; break; case ten: Display16(mytenNum,2); mydisplaybit=unit; break; case unit: Display16(myunitNum,1); mydisplaybit=thousaud; break; default: Display16(mythousandNum,4); mydisplaybit=hundred; break; }}

static void Display16(uint8_t num,uint8_t place){ GPIO_ResetBits(SPI_Digital_Tube_NSS_GPIO_PORT, SPI_Digital_Tube_NSS_PIN); uint16_t Temp=((Num[num])《《8)+((0x01)《《(place-1)); SPI2_Send_Byte16(Temp); GPIO_SetBits(SPI_Digital_Tube_NSS_GPIO_PORT, SPI_Digital_Tube_NSS_PIN);}

然后,每隔0.5s累加一次。在定时器中累计

void TIM6_DAC_IRQHandler(){ static uint16_t counter=0; static uint16_t num_buffer=0; if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) { counter++; if(counter》499) { num_buffer++; counter=0; } DisplayNumber(num_buffer); TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update); } }

所以,初试成功。

试验第二步,让编码器说话。

首先,在STM32中配置编码器。

使用PA6和PA7作为定时器3的通道1和通道2,进行下图模式的计数。

be704acc-86b9-11eb-8b86-12bb97331649.png

即效果如下:

代码如下

void TIM3_EncoderConfig(void){ TIM_ICInitTypeDef TIM_ICInitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;

HALL_TIM_APBxClock_FUN(ENCODER_TIM_CLK, ENABLE);

/* GPIOA clock enable */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //PA6 & PA7 RCC_AHBPeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); /* phase A & B*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_1);//TIM3_CH1 GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_1);//TIM3_CH2

TIM_DeInit(TIM3); TIM_TimeBaseStructure.TIM_Period =0xffff; TIM_TimeBaseStructure.TIM_Prescaler =0; TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode =TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);

TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge);

TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter = 0; TIM_ICInit(TIM3, &TIM_ICInitStructure);

// Clear all pending interrupts TIM_ClearFlag(TIM3, TIM_FLAG_Update); TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

//Reset counter TIM_SetCounter(TIM3,0); TIM_Cmd(TIM3, ENABLE);

/* Enable the TIM1 global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}

然后在中断服务函数中,将编码器的相对值计算出来,并根据编码器计数的相对变化,计算出电机的转速。具体代码如下:

void TIM6_DAC_IRQHandler(){ static uint16_t counter=0; static uint16_t num_buffer=0; static uint16_t temp_now=0; static uint16_t temp_pre=0; static uint16_t speed=0; if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) { counter++; temp_now=(TIM_GetCounter(TIM3)&0xffff); if(counter》499) { num_buffer=(temp_now-temp_pre)》0?temp_now-temp_pre:temp_pre-temp_now; speed=100*num_buffer*60/64; counter=0; } DisplayNumber(speed); if(counter%10==0)temp_pre=temp_now; TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update); } }

同时,为了防止TIM3中断溢出,记得清除中断标志位

void TIM3_IRQHandler (){ if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); }}

实际效果如下图所示(东西太多,手机不好拍动图,只能静物显示),可知,当电机电压9.32V时,转速为843rpm。当电压为18.7V时,转速为1687rpm。编码器的波形也用示波器显示出来了。还不错哈,哈哈哈。

▍结论

本文使用STM32F0 discovery开发板,完成了编码器计数和电机转速的计算,并通过数码管将电机转速实时显示出来。

原文标题:手把手教你怎么用STM32让相对编码器说话

文章出处:【微信公众号:嵌入式ARM】欢迎添加关注!文章转载请注明出处。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编码器
    +关注

    关注

    45

    文章

    4011

    浏览量

    143325
  • STM32
    +关注

    关注

    2313

    文章

    11191

    浏览量

    374580

原文标题:手把手教你怎么用STM32让相对编码器说话

文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    伺服电机正余弦编码器的相位对齐方式

    伺服电机正余弦编码器的相位对齐,本质上是为了编码器反馈的位置信号与电机转子实际的磁极位置(电角度)建立精确的对应关系。这是实现矢量控制、确保电机平稳出力、避免飞车的基础。 根据操作原理和依赖的设备
    的头像 发表于 03-20 15:36 194次阅读
    伺服电机正余弦<b class='flag-5'>编码器</b>的相位对齐方式

    磁铁在编码器中的作用与应用

    编码器中,磁铁的作用不可小觑,常用的磁性材料主要是钕铁硼和铁氧体,今天这篇文章主要介绍下磁铁用于哪些编码器类型,以及其具体作用。磁铁在编码器中的作用(功能)是什么?在编码器中,磁铁主
    的头像 发表于 03-19 14:42 371次阅读
    磁铁在<b class='flag-5'>编码器</b>中的作用与应用

    相对编码器:工业自动化领域的“性价比之王”

    在工业自动化浪潮席卷全球的今天,编码器作为机械运动与数字信号的“翻译官”,正扮演着愈发关键的角色。而在众多编码器类型中,相对编码器凭借其独特的优势,成为众多工业场景中的“性价比之王”
    的头像 发表于 11-24 08:52 418次阅读
    <b class='flag-5'>相对</b>式<b class='flag-5'>编码器</b>:工业自动化领域的“性价比之王”

    Vishay USB编码器接口技术解析与应用指南

    Vishay/MCB Industrie RAMK/RAME USB编码器接口是一款设计用于Vishay MCB编码器(AMK和RAME系列,包括霍尔效应)的电子板。该接口板只需使用随附的USB
    的头像 发表于 11-12 11:51 1130次阅读

    雷恩PRECILEC重载式编码器:重载工况,精准稳定#编码器

    编码器
    开地电子
    发布于 :2025年10月31日 16:18:04

    雷恩 PRECILEC 重载编码器:重载不 “失准”,稳定超预期#编码器

    编码器
    开地电子
    发布于 :2025年10月31日 15:59:24

    舵机伺服编码器

    舵机伺服编码器是用于实时检测舵机输出轴位置、速度或角度的核心反馈元件,它能将机械运动转化为电信号,控制系统实现对舵机的精准闭环控制。 这个问题切得很准,它直接指向了舵机 “精准控制” 的关键所在
    的头像 发表于 10-30 10:05 479次阅读

    编码器为何能取代光电编码器

    在复杂工业场景下,磁编码器凭借其不怕油污粉尘、抗冲击振动、安装便捷及高速响应等优势,正有效解决光电编码器怕脏、怕震、难安装、怕高速的痛点,逐渐成为提升机器人性能的新选择。
    的头像 发表于 10-15 09:55 929次阅读
    磁<b class='flag-5'>编码器</b>为何能取代光电<b class='flag-5'>编码器</b>?

    国产编码器在人形机器人领域的进展

    电子发烧友网综合报道 编码器是测量旋转角度、位移及速度的传感,作为伺服系统的核心部件,在人形机器人领域,其数据反馈对实现机器人运动的精密控制与定位至关重要。   编码器种类丰富,按技术原理可分为磁
    的头像 发表于 09-24 09:41 1698次阅读

    Bourns发布全新增量式微型编码器

    Bourns 推出 PEC04 系列 4 mm 增量式微型编码器、PEC05 PEC05 系列 5 mm 增量式微型编码器,以及 PEC06 型号 6 mm 增量式微型编码器。Bourns 全新微型
    的头像 发表于 09-22 16:05 1446次阅读

    绝对值编码器与增量式编码器相比有哪些优势?

    绝对值编码器与增量式编码器相比有哪些优势?核心功能:断电后位置信息不丢失,绝对值编码器:通过机械结构或电子存储(如电池备份),能实时输出当前位置的唯一绝对值编码(如二进制、格雷码)。无
    的头像 发表于 08-11 13:57 2039次阅读
    绝对值<b class='flag-5'>编码器</b>与增量式<b class='flag-5'>编码器</b>相比有哪些优势?

    新品|Unit Step16,16 定位BCD旋转编码器控制单元

    UnitStep16是一款基于STM32G031G8U6微控制的16定位旋转编码器控制单元。其核心功能在于实时采集旋转编码器的BCD编码
    的头像 发表于 08-01 17:35 1531次阅读
    新品|Unit Step16,16 定位BCD旋转<b class='flag-5'>编码器</b>控制单元

    增量型编码器与绝对值型编码器怎么选择?

    在选择增量型编码器与绝对值型编码器时,需要考虑多个因素,包括应用需求、成本、精度、可靠性以及环境适应性等。以下是对两种编码器的详细比较及选择建议: 一、增量型编码器 1. 优点:  
    的头像 发表于 07-10 10:34 1801次阅读

    Transformer架构中编码器的工作流程

    编码器是Transformer体系结构的基本组件。编码器的主要功能是将输入标记转换为上下文表示。与早期独立处理token的模型不同,Transformer编码器根据整个序列捕获每个token的上下文。
    的头像 发表于 06-10 14:27 1201次阅读
    Transformer架构中<b class='flag-5'>编码器</b>的工作流程

    一文读懂什么是磁性编码器

    磁性编码器是一种用于测量角度和线性位置的传感。它使用磁性信号来监测旋转或线性位置的变化,并把这些变化转换成数字信号。磁性编码器可用于各种应用中,比如机器人、汽车、数控机床等领域
    的头像 发表于 04-27 17:18 1245次阅读