编码器差分转单端电路(含原理图),基于STM32编码器接口模式的程序设计及电路设计


这已经是两年前我的文章草稿了。今天无意间看到了,想了想还是打算补充完整吧。

前言

市场上常见的增量式编码器大多都是A,B,Z相,但例如A+,A-,B+,B-,Z+,Z-这类的编码器其实就是所谓的差分编码,一般出现在有伺服驱动器的场景上用到,或者需要远距离传输时。因为带有对称负信号连接,电流对于电缆贡献的电磁场为0,衰减最小,抗干扰最佳,可传输较远的距离。那么问题来了,在使用定时器编码器模式时,该怎样去接线。这时候就需要用到差分转单端外围电路的实现。


以三路编码接口AB相为实例(如果需要Z相,原理基本一样),附带原理图(亲测),需要封装库的话可以私信我

一、电路实现

在这里插入图片描述

IC芯片使用的是TLP2745高速隔离光耦,足以满足速率要求(理论可以满足1MHZ频率)。且支持不超过30V电压输入。
这里3脚接了个1K限流电阻,输入电压以是5V为例。如果需要其他电压的输入,要更改此处的电阻值。输出电压是3V3。附带LED指示灯。
注意:虽然是差分电路,但是频率并不高,阻抗可以不计算。在某些场景超过100MHZ的情况下才需要严格按照规定进行阻抗匹配设计。

二、代码实现

1.硬件设计

根据原理图可以得知
在这里插入图片描述

都是使用定时器通道1,2的编码器接口模式(STM32使用编码器模式必须接通道1和通道2)
知道A,B相相位都存在90度的极限位差,可以得到电机此时的正反转状态。
要实现代码,还需知道编码器的分辨率是多少,实际应用中,都要用到四倍频,也就是上升沿下降沿都计算,获得四倍精准度(编码器模式本身就有这个模式,不需要担心四倍频会增加CPU负担,如果不倍频,那就是输入捕获功能了)
如果使用伺服驱动器,在某个参数可以设置编码器线数,以台达伺服为例,如设置500,实际到程序中四倍频后就是2000脉冲,也就是旋转一圈的脉冲数为2000。
在这里插入图片描述

如果使用增量式编码器,一般上面都会标注编码器线数。
在这里插入图片描述
例如,标注1000P/R,一圈脉冲数为1000,经过四倍频后,得到4000P/R。
注意:脉冲数越高,控制精度越高,可以根据实际设备选择分辨率。

2.软件设计

使用HAL库,以STM32F407为例:

硬件:

编码器输入(3-5V输入):

PA15	→		A1	TIM2_CH1
PA1	    →		B1	TIM2_CH2

PC6		→		A2	TIM3_CH1
PC7		→		B2	TIM3_CH2

软件:

1. 初始化编码器接口
需要对每个定时器进行初始化,以便它们能够以编码器模式工作。以下是针对一个定时器(例如,TIM2)的示例初始化代码:

/* 宏定义 --------------------------------------------------------------------*/
//A1/B1
// 定义编码器接口使用的定时器,此处为 TIM2
#define ENCODERAB1_TIMx                       TIM2

// 使能定时器模块的时钟,这里指的是 TIM2
#define ENCODERAB1_TIM_RCC_CLK_ENABLE()       __HAL_RCC_TIM2_CLK_ENABLE()

// 禁用定时器模块的时钟,这里指的是 TIM2
#define ENCODERAB1_TIM_RCC_CLK_DISABLE()      __HAL_RCC_TIM2_CLK_DISABLE()

// 使能用于编码器信号的 GPIO 端口A的时钟
#define ENCODERAB1_TIM_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOA_CLK_ENABLE()

// 定义编码器通道1的引脚和端口,此处为 PA15
#define ENCODERA1_TIM_CH1_PIN                 GPIO_PIN_15
#define ENCODERA1_TIM_CH1_GPIO                GPIOA

// 定义编码器通道2的引脚和端口,此处为 PA1
#define ENCODERB1_TIM_CH2_PIN                 GPIO_PIN_1
#define ENCODERB1_TIM_CH2_GPIO                GPIOA

// 定义 GPIO 引脚的替代功能,这里为 TIM2 的 AF1
#define GPIO_AB1_AFx_TIMx                     GPIO_AF1_TIM2

// 定义编码器接口模式,这里配置为同时使用 TI1 和 TI2
#define TIM_ENCODERMODE_TIx                   TIM_ENCODERMODE_TI12

// 定义定时器的 IRQ 号,这里是 TIM2 的 IRQ 号
#define ENCODER1_TIM_IRQn                     TIM2_IRQn

// 定义定时器的 IRQ 处理函数,这里是 TIM2 的 IRQ 处理函数
#define ENCODER1_TIM_IRQHANDLER               TIM2_IRQHandler


//A2/B2
//类似方法
/* 函数体 --------------------------------------------------------------------*/
/**
  * @brief  通用定时器初始化并配置通道PWM输出
  * @param  无
  * @retval 无
  * @note   无
  */
void Encoder_TIMx_Init(void){
	  /* 定时器编码器配置结构声明 */
	  TIM_Encoder_InitTypeDef sConfig = {0};
	  TIM_MasterConfigTypeDef sMasterConfig = {0};
	
	  /* 定时器基本环境配置 */
	  //A1/B1相 
	  htim2_Encoder.Instance = ENCODERAB1_TIMx;                               //定时器选择 
	  htim2_Encoder.Init.Prescaler = ENCODER_TIM_PRESCALER;                   //定时器预分频
	  htim2_Encoder.Init.CounterMode = TIM_COUNTERMODE_UP;                    //计数模式模式
	  htim2_Encoder.Init.Period = ENCODER_TIM32_PERIOD;                       //定时器周期
	  htim2_Encoder.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;              //时钟分频因子
	  htim2_Encoder.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;  //预装载
	
	  sConfig.EncoderMode = TIM_ENCODERMODE_TIx;                      //信号捕获模式
	  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;                    //输入捕获极性
	  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;                //输入捕获选择
	  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;                          //输入捕获预分频
	  sConfig.IC1Filter = 0;                                          //输入捕获滤波器
	
	  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;                    //输入捕获极性
	  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;                //输入捕获选择
	  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;                          //输入捕获预分频
	  sConfig.IC2Filter = 0;                                          //输入捕获滤波器
	  if (HAL_TIM_Encoder_Init(&htim2_Encoder, &sConfig) != HAL_OK)
	  {
	    Error_Handler();
	  }
	  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
	  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
	  if (HAL_TIMEx_MasterConfigSynchronization(&htim2_Encoder, &sMasterConfig) != HAL_OK)
	  {
	    Error_Handler();
	  }
	  
	  //A2/B2相
	  //类似方法
}

对于其它定时器,需要重复类似的初始化过程,只是改变相应的定时器实例和相关引脚配置。

2. 启动 / 停止编码器接口
在主函数或者适当的初始化部分中,启动或停止每个定时器的编码器模式:

/**
  * @brief  启动编码器接口
  * @param  无
  * @retval 无
  * @note   无
  */
 void Start_Encoder(void){
     HAL_TIM_Encoder_Start(&htim2_Encoder,TIM_CHANNEL_ALL);
     // 重复此过程以启动TIM3
 }
 
/**
  * @brief  停止编码器接口
  * @param  无
  * @retval 无
  * @note   无
  */
 void Stop_Encoder(void){
     HAL_TIM_Encoder_Stop(&htim2_Encoder,TIM_CHANNEL_ALL);
    // 重复此过程以停止TIM3
}

3. 读取编码器值
要读取编码器的值,可以直接读取定时器的计数器:

// 定义定时器周期,当定时器开始计数到ENCODER_TIMx_PERIOD值是更新定时器并生成对应事件和中断
#define ENCODER_TIM16_PERIOD                  0xFFFF
#define ENCODER_TIM32_PERIOD                  0xFFFFFFFF
/**
  * @brief  获取X轴编码器计数值
  * @param  无
  * @retval Value:返回32位有符号计数值
  * @note   无
  */
int32_t XEncoder_GetCounting(void){
    #if ( ENCODER_TIM16_PERIOD > 0xFFFF  )
    	/* 32bits 计数器 */
    	int32_t Value = __HAL_TIM_GET_COUNTER(&htim2_Encoder);
    	/* 假定定时器不会溢出 */
    	return Value ;
    #else 
    	/* 16bits 定时器 */  
    	uint32_t Value = __HAL_TIM_GET_COUNTER(&htim2_Encoder);
    	int32_t Period = ENCODER_TIM16_PERIOD + 1;
    	/* 16bits 定时器容易溢出 */
    	return Value + XOverflowCount * Period;
    #endif
}
// 用类似的方式读取TIM3的值,例如y轴

4. 处理方向和速度
你可以通过比较连续的编码器读数来确定电机的方向和速度。这部分需要结合你的具体应用逻辑来实现。

5. 清零或处理溢出
你可能需要定期清零计数器或者处理溢出情况。

例如对定时器TIM2清零:

__HAL_TIM_SET_COUNTER(&htim2, 0);

在适当的位置调用此类代码,比如在初始化函数中,或者在需要重置计数器的事件处理中
清零操作应谨慎使用,特别是在动态监测编码器值的系统中。在不适当的时机重置计数器可能会导致控制逻辑的错误判断或系统状态的混乱

例如对定时器TIM2处理溢出:
确保在定时器初始化时启用了溢出(更新)中断:

__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE);

然后,在定时器的中断服务例程中,添加处理溢出的代码:

void TIM2_IRQHandler(void){
    if(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET)
    {
        if(__HAL_TIM_GET_IT_SOURCE(&htim2, TIM_IT_UPDATE) != RESET)
        {
            __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);
            // 溢出处理代码
        }
    }
}

注意事项

确保你的编码器输入信号电平与单片机的I/O口兼容(例如:3-5V输入)。
如果你的编码器信号频率非常高,你可能需要调整定时器预分频器和输入滤波器以获得稳定的读数。
对于伺服驱动器,还需要考虑与驱动器的通信和控制逻辑,例如位置控制、速度控制等。
这是一个基础的程序设计框架,具体细节需要根据你的实际应用和硬件环境进行调整。

文章总结

编码器接口初始化:在软件方面,关键步骤是正确初始化各个定时器的编码器接口模式。这包括设置定时器的计数模式、编码器模式、输入捕获的极性和滤波器设置。

软件设计灵活性:STM32提供的库函数使得编码器接口的实现变得相对简单,同时也为定制化的控制逻辑提供了便利。

可靠性和精确性:由于使用了差分信号,这种设计在抗干扰方面表现良好,特别是在电气噪声较多的工业环境中。


个人结语

在曾经的嵌入式龙卷风,我是那个寻找完美电路设计的猎人。世界上没有现成的解决方案,就像超市里永远买不到成熟的香蕉。所以,我拿起了我的科技魔杖,变身为“逆向科研攻城狮”,开始了我的神秘探索之旅。

在这里,我需要的是一双锐利的眼睛,一只放大镜,和一颗不怕失败的心。就像古老的炼金术士,用他们的烧瓶和坩埚转化铅为金,我用我的放大镜和万用表在电路板上舞动。

每一个电阻,每一个电容,都是我的舞伴。我跟随着电流的节奏,从一个接点跳到另一个接点。在这个过程中,我遇到了无数的挑战,但也正是这些挑战,让我的电路更加完美,更加强大。

有时,我感觉自己就像是一个硬币铸币厂,每一枚硬币都是我辛勤劳动的成果。而当我最终完成我的作品时,那一刻的喜悦,是无法用言语来形容的。我创造了一个不仅稳定性强,而且能抵抗各种干扰的编码器硬件电路。这不仅是一段电路,而是一段艺术,一段由放大镜、万用表和示波器构建的艺术。

工业需要开源,就像马斯克一样!

### 回答1: 基于STM32的半导体激光器驱动电路设计需要考虑到激光器的输入和输出特性,以确保其稳定和高效的工作。 首先,需要选择适合激光器的STM32芯片,具有足够的计算能力和IO口驱动能力。激光器的输入通常需要控制电流或电压,可以利用芯片的PWM输出或DAC输出来实现。此外,还需要考虑输出激光器的功率控制,可以通过调整PWM输出的占空比或DAC输出的电压来实现。 其次,需要添加过流和过热保护电路,以保护激光器的安全运行。过流保护电路可以采用电流检测电阻和运放组成的反馈回路,当激光器输入电流超过设定值时,及时降低输入电流。过热保护电路可以通过温度传感器检测激光器的温度,当温度过高时,及时切断输入电流。 同时,为了实现激光器的稳定调整和控制,可以添加PID控制电路。根据激光器的反馈信号和设定值,将其与STM32芯片连接,通过软件设计PID算法,实现对激光器输出功率的精确控制。 此外,还需要考虑电源和信号隔离电路的设计。激光器对电源和信号的稳定性要求较高,为了避免外部噪声和干扰,可以采用电源滤波电路和光电耦合器等隔离电路,确保激光器的工作稳定性和抗干扰能力。 最后,还需要在设计中考虑到PCB布局和散热设计,确保电路板的信号传输和散热效果良好,提高整个系统的可靠性和稳定性。 综上所述,基于STM32的半导体激光器驱动电路设计需要充分考虑激光器的特性和安全性,合理选择芯片和设计控制和保护电路,同时注重电源和信号隔离以及散热设计,以确保激光器的稳定和高效工作。 ### 回答2: 半导体激光器驱动电路设计是基于STM32芯片的激光器驱动系统的关键部分。激光器驱动电路主要包括调节激光器的电流和控制激光器的开关。 首先,设计师需要根据激光器的规格和要求选择合适的STM32芯片作为控制器。然后,通过STM32的GPIO口控制电源开关,以达到控制激光器开关的目的。同时,在GPIO输出和激光器之间需要添加一个适当的功率放大器来放大控制信号,以确保信号能够正确驱动激光器。 此外,为了稳定和精确调节激光器的输出功率,设计师需要在电路中添加一个电流控制电路。这个电路通过STM32的PWM输出产生一个模拟信号,然后将模拟信号经过放大器放大,再通过一个滤波电路稳定化,最后输入到激光器的电流调节控制。 为了确保激光器的稳定工作,还需要在电路中添加一个反馈保护电路。这个电路通过对激光器输入和输出进行监测,当激光器工作异常时,能够及时切断激光器的电源,以保护激光器和其他部件的安全。 总的来说,基于STM32的半导体激光器驱动电路设计主要集中在控制激光器的开关和调节激光器的输出功率。通过设计电流控制和反馈保护电路,能够确保激光器电流的稳定和工作的安全性。同时,STM32芯片的灵活性和强大的处理能力,能够更好地控制激光器的驱动系统,满足不同应用的需求。 ### 回答3: 基于STM32的半导体激光器驱动电路设计需要考虑到STM32微控制器的特性和半导体激光器的工作原理和特性。 首先,我们可以选择适合于激光器驱动的STM32微控制器型号。选择合适的型号时需要考虑到激光器的驱动功率要求、通信接口、时钟频率等因素。 其次,我们需要设计一个功率驱动电路来提供所需的电流给激光器。这通常包括一个电流源和一个可调节的限流电路。我们可以使用STM32的PWM输出来实现电流调节,通过读取反馈电流进行闭环控制,以确保激光器的稳定工作。 此外,为了防止激光器受到静电放电或过电流的损坏,我们还可以设计保护电路,包括过电流保护、过温保护和静电放电保护等。这些保护机制可以通过检测电流、温度和静电放电等参数来触发相应的保护措施,例如切断电源或降低电流。 另外,我们还可以添加一些辅助功能来提高激光器的性能和稳定性,例如温度补偿、电流调制和光斑控制等。这些功能可以通过读取传感器的数据,并使用STM32的内置模块进行处理和控制来实现。 最后,我们需要综合考虑电路的功耗、尺寸和成本等因素,进行布局和PCB设计,并进行相应的电磁兼容性和可靠性测试,以确保电路的正常工作和长期可靠性。 综上所述,基于STM32的半导体激光器驱动电路设计需要结合STM32的特性和激光器的工作原理,合理设计功率驱动电路、保护电路和辅助功能,同时进行合理布局和PCB设计。这样设计出来的电路可以满足激光器的驱动需求,并具备稳定性、可靠性和高性能。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涔涔OVER

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值