速度测量光电编码器设计及验证

文章目录

概要

1.电路功能:使用光电传感器对直流电机转速进行测量,并用数码管显示当前转速。

2.工作原理:利用遮断式红外控制原理对通过的物件计数。红外发光管与光电接收管相对摆放,每当物件通过一次,红外光被遮挡一次,光电接收管的输出电压发生一次变化,这个变化的电压信号通过放大和处理后,形成计数脉冲,送入单片机进行计数,同时利用单片机定时器定时一段时间,即可测出电机的速度。

3.基本要求:使用模块电路,搭建测速系统,满足:A、速度可显示范围 1~9999。B、可设置3档速度模式。

4.扩展要求:使用分立原件(非模块),设计电路基本要求,并且满足:利用PID原理,实现电机速度的稳定闭环控制。

整体架构流程

`

电机转速测量技术在工业自动化、精密制造、航空航天等领域有广泛应用,大部分设备是通过光电码盘或霍尔元件测量电机转速,反馈至伺服驱动器实现闭环控制。

本设计转速信号采集部分选用的是光电码盘和红外光电传感器,其电路可分为前端信号采集、信号处理、显示和电机驱动三个部分。

(一)方案设计框图

(二)电路工作原理

具体电路图如下图所示,包括电源、单片机、光电信号采集与处理、电机驱动、显示五部分。

1.电源部分:整个系统采用5V供电,可利用烧录器提供的5V或者可调电源提供的5V信号。

2.光电信号采集与处理:可以采用槽型光电耦合器作为传感器,其输出信号经过LM393比较器进行处理,将信号整形放大输出,当有无物体遮挡,模块输出高低电平,同时有LED同步显示输出。模块输出的计数脉冲信号送入后续电路进行处理。

3.显示部分:采用四位共阳数码管,由四个PNP型三极管来驱动,由单片机P0、P3端口控制。

4.电机驱动部分:采用TB6612芯片驱动电机,芯片内部集成双通道H桥电路,可通过端口给定逻辑实现电机正反转、制动等功能,改变输入PMW信号占空比可控制电机转速变化。

(三)主要元件介绍

1.TB6612

TB6612FNG是一款新型驱动器件,能独立双向控制2个直流电机,它具有很高的集成度,同时能提供足够的输出能力,运行性能和能耗方面也具有优势,因此在集成化、小型化的电机控制系统中,它可以作为理想的电机驱动器件。AINI/AIN2、BIN1/BIN2、PWMA/PWMB为控制信号输人端;A01/A02、B01/B02为2路电机控制输出端;STBY为正常工作/待机状态控制引脚;VM(<10 V)和VCC(2.7~5.5 V)分别为电机驱动电压输入和逻辑电平输入端。具体引脚定义与控制逻辑见下图所示。

2.LM393

LM393是一种广泛使用的电压比较器IC,提供8引脚DIP、SO-8和其他封装。LM393包含两个独立的高精度比较器运算放大器,可由单电源或双电源供电。电源电压范围宽,可以用于多种应用。该芯片需要低工作电流,非常适合便携式和电池供电设备,输出驱动逻辑系统可以用在数字电路中。LM393的最大输出电流为20mA,足以驱动晶体管和逻辑系统。

技术细节

PCB顶层

PCB底层

c 复制代码
#include <reg52.h>
#include <intrins.h>
#include <stdlib.h> 

// =================== 1. ???? ===================
#define MOTOR_DISK_HOLES 36      // ???????36?
#define PID_PERIOD_MS    50      // PID ???? 50ms

// =================== 2. ???? ===================
sbit DIG_1 = P3^4; 
sbit DIG_2 = P3^7; 
sbit DIG_3 = P3^6; 
sbit DIG_4 = P3^5; 

// TB6612
sbit PWM_PIN = P2^0; 
sbit AIN2    = P2^1; 
sbit AIN1    = P2^2; 

// ??
sbit KEY_SW1 = P1^0;

// =================== 3. ???? ===================
volatile unsigned int pulse_count = 0;   
int real_rpm = 0;               
int target_rpm = 0;             

// --- PID ?? (?? 1000 RPM ??????) ---
int Kp = 120;   // ?? (1.2) - ?????
int Ki = 45;    // ?? (0.45) - ?????????
int Kd = 0;     // ??

long err = 0;         
long last_err = 0;    
long sum_err = 0;     

int pwm_duty = 0;     
unsigned char pwm_tick = 0;
unsigned int pid_timer = 0;
unsigned char speed_mode = 0;

// ?????
unsigned char code SMG_Code[] = {0x28, 0xEE, 0x32, 0xA2, 0xE4, 0xA1, 0x21, 0xEA, 0x20, 0xA0};
unsigned char Display_Buf[4] = {0x28, 0x28, 0x28, 0x28}; 

// =================== ???? ===================
void Delay_Short(unsigned int t);
void Display_Scan();
void Set_Display_Num(int num);
void PID_Compute_Fast();       
void Motor_Set_Mode(unsigned char mode);

// =================== ??? ===================
void main()
{
    AIN1 = 0; AIN2 = 0; PWM_PIN = 0; 

    // ???0 ???
    TMOD &= 0xF0; TMOD |= 0x01; 
    TH0 = (65536 - 100) / 256; 
    TL0 = (65536 - 100) % 256;
    ET0 = 1; TR0 = 1; 

    // ????0 ???
    IT0 = 1; EX0 = 1; EA = 1;  

    while(1)
    {
        Display_Scan(); // ??????

        if(KEY_SW1 == 0)
        {
            Delay_Short(200); // ??
            if(KEY_SW1 == 0)
            {
                speed_mode++;
                if(speed_mode > 3) speed_mode = 0;
                
                // ????????
                Motor_Set_Mode(speed_mode);
                
                while(KEY_SW1 == 0) Display_Scan(); 
            }
        }
    }
}

// =================== ???? ===================
void Int0_Routine() interrupt 0 
{ 
    pulse_count++; 
}

void Timer0_Routine() interrupt 1
{
    TH0 = (65536 - 100) / 256; 
    TL0 = (65536 - 100) % 256;

    // --- PWM ?? ---
    pwm_tick++;
    if(pwm_tick >= 100) pwm_tick = 0;
    
    if(target_rpm > 0)
    {
        if(pwm_tick < pwm_duty) PWM_PIN = 1; 
        else PWM_PIN = 0;
    }
    else
    {
        PWM_PIN = 0;
    }

    // --- PID ?? (50ms) ---
    pid_timer++;
    if(pid_timer >= 500) 
    {
        pid_timer = 0;

        // ?? RPM: (?? * 1200) / 36
        real_rpm = (int)(((long)pulse_count * 1200) / MOTOR_DISK_HOLES);
        pulse_count = 0;

        if(target_rpm > 0)
        {
            PID_Compute_Fast();
            AIN1 = 1; AIN2 = 0; // ??
        }
        else
        {
            pwm_duty = 0; 
            sum_err = 0; 
            AIN1 = 0; AIN2 = 0;
        }

        Set_Display_Num(real_rpm);
    }
}

// =================== ?? PID ?? ===================
void PID_Compute_Fast()
{
    long output_val;

    // 1. ????
    err = (long)target_rpm - (long)real_rpm;

    // 2. ????
    sum_err += err;
    
    // ????
    if(sum_err > 4000) sum_err = 4000;
    if(sum_err < -4000) sum_err = -4000;

    // 3. ?? P + I
    output_val = (Kp * err) + (Ki * sum_err);
    
    // 4. ??? (??100)
    output_val = output_val / 100;

    // 5. ???? (????)
    // ???1000?,?????????,?? 25
    if(target_rpm > 0)
    {
        output_val += 25; 
    }

    // 6. ????
    if(output_val > 100) output_val = 100;
    if(output_val < 0)   output_val = 0;

    pwm_duty = (int)output_val;
}

// =================== ?????? (????) ===================
void Motor_Set_Mode(unsigned char mode)
{
    switch(mode) {
        case 0: 
            target_rpm = 0;    
            break; 
        case 1: 
            target_rpm = 1000; // ???????? 1000
            break; 
        case 2: 
            target_rpm = 2000; // ???????? 2000
            break; 
        case 3: 
            target_rpm = 3000; // ???????? 3000
            break; 
    }
    // ??:?????????????2000?,????3000??,
    // PWM????100%,?????????????????
}

// =================== ????? ===================
void Set_Display_Num(int num)
{
    if(num < 0) num = 0;
    if(num > 9999) num = 9999;
    
    Display_Buf[0] = SMG_Code[num / 1000];
    Display_Buf[1] = SMG_Code[num % 1000 / 100];
    Display_Buf[2] = SMG_Code[num % 100 / 10];
    Display_Buf[3] = SMG_Code[num % 10];
}

void Display_Scan()
{
    P0 = 0xFF; DIG_1 = 0; DIG_2 = 1; DIG_3 = 1; DIG_4 = 1; P0 = Display_Buf[0]; Delay_Short(30);
    P0 = 0xFF; DIG_1 = 1; DIG_2 = 0; DIG_3 = 1; DIG_4 = 1; P0 = Display_Buf[1]; Delay_Short(30);
    P0 = 0xFF; DIG_1 = 1; DIG_2 = 1; DIG_3 = 0; DIG_4 = 1; P0 = Display_Buf[2]; Delay_Short(30);
    P0 = 0xFF; DIG_1 = 1; DIG_2 = 1; DIG_3 = 1; DIG_4 = 0; P0 = Display_Buf[3]; Delay_Short(30);
    P0 = 0xFF;
}

void Delay_Short(unsigned int t) 
{ 
    while(t--); 
}

小结

本文设计并实现了一个基于51单片机的直流电机转速测量与控制系统。系统采用遮断式红外光电传感器(槽型光电耦合器)进行转速信号采集,通过LM393比较器对信号进行整形放大,生成计数脉冲送入单片机。单片机(STC89C52)通过定时器中断和外部中断对脉冲进行计数与定时,计算出实时转速,并驱动四位共阳数码管进行显示。电机驱动部分采用TB6612芯片,通过PWM信号控制电机转速。系统实现了三档速度模式(0、1000、2000、3000 RPM)的切换,并引入了简化的PID控制算法,对电机转速进行闭环调节,以提高系统的稳定性和响应速度。整个设计涵盖了从传感器信号采集、处理、显示到电机驱动的完整流程,并提供了详细的电路原理图、PCB布局以及核心单片机代码,是一个完整的嵌入式测控系统实践案例。