DSP28335基础教程——EQEP实验(直流电机转速检测)

Posted Sk Electronics

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DSP28335基础教程——EQEP实验(直流电机转速检测)相关的知识,希望对你有一定的参考价值。

0 前言

这一节我们来学习DSP的EQEP模块的功能。实验目标:通过光电编码器,将采集直流减速电机的转速并显示在LCD1602上。

由于28335控制LCD1602的例程并不多,在下面解释的过程会详细贴出代码,并给出一些注意事项。

本节将分为硬件部分、软件部分和实验展示三个方面进行介绍,不清楚的欢迎留言。

1 硬件部分

我们需要五个硬件:可调电源、DSP28335核心板、烧写器、自带光电编码器的直流减速电机和LCD1602。其中,电机的额定电压为12V,因此,我们通过可调电源来调整电压,则可以调整电机的转速。

在这里插入图片描述

下面给出整体硬件架构图。在核心板与直流电机通信部分:IO50为EQEP模块的EQEP1A,负责接收电机编码器的A相脉冲;IO51为EQEP1B,负责接收电机编码器的B相脉冲。在核心板与LCD通信部分:IO24负责控制LCD指令/数据选择;IO25负责控制LCD读写功能选择;IO26负责控制LCD的使能;IO0~IO7则是与LCD的8位数据端通信接口。
在这里插入图片描述

2 软件部分

在软件部分,分为编码器功能设计和LCD显示设计。

首先我们看LCD显示设计的代码:(注意:查看代码时双击点进去看,否则会内容不全)。

需要注意的是,在进行写操作函数的时候,需要加点延时,否则会造成LCD通信出错;还有,在LCD初始化过程中,需要多次写LCD1602_WriteCmd(0x38,0)和LCD1602_WriteCmd(0x38,1),否则会造成LCD只显示第一行,第二行不显示,也就是初始化有问题

bsp_lcd1602.c

/**
 *  ********************************************************************************************
 *  @file                 bsp_lcd1602.c
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2021-xx-xx
 *  @brief               lcd1602显示函数接口
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */
#include "bsp_lcd1602.h"

extern void delay_1ms(Uint16 t);

void GPIO_LCD1602_Init(void)//RS RW E D0~D7进行初始化配置
{
    EALLOW;

    /*配置RS输出管脚IO24*/
    GpioCtrlRegs.GPAMUX2.bit.GPIO24=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO24=0;
    GpioCtrlRegs.GPADIR.bit.GPIO24=1;

    /*配置RW输出管脚IO25*/
    GpioCtrlRegs.GPAMUX2.bit.GPIO25=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO25=0;
    GpioCtrlRegs.GPADIR.bit.GPIO25=1;

    /*配置E输出管脚IO26*/
    GpioCtrlRegs.GPAMUX2.bit.GPIO26=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO26=0;
    GpioCtrlRegs.GPADIR.bit.GPIO26=1;

    /*配置D0输出管脚IO0*/
    GpioCtrlRegs.GPAMUX1.bit.GPIO0=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO0=0;
    GpioCtrlRegs.GPADIR.bit.GPIO0=1;

    /*配置D1输出管脚IO1*/
    GpioCtrlRegs.GPAMUX1.bit.GPIO1=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO1=0;
    GpioCtrlRegs.GPADIR.bit.GPIO1=1;

    /*配置D2输出管脚IO2*/
    GpioCtrlRegs.GPAMUX1.bit.GPIO2=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO2=0;
    GpioCtrlRegs.GPADIR.bit.GPIO2=1;

    /*配置D3输出管脚IO3*/
    GpioCtrlRegs.GPAMUX1.bit.GPIO3=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO3=0;
    GpioCtrlRegs.GPADIR.bit.GPIO3=1;

    /*配置D4输出管脚IO4*/
    GpioCtrlRegs.GPAMUX1.bit.GPIO4=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO4=0;
    GpioCtrlRegs.GPADIR.bit.GPIO4=1;

    /*配置D5输出管脚IO5*/
    GpioCtrlRegs.GPAMUX1.bit.GPIO5=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO5=0;
    GpioCtrlRegs.GPADIR.bit.GPIO5=1;

    /*配置D6输出管脚IO6*/
    GpioCtrlRegs.GPAMUX1.bit.GPIO6=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO6=0;
    GpioCtrlRegs.GPADIR.bit.GPIO6=1;

    /*配置D7输出管脚IO7*/
    GpioCtrlRegs.GPAMUX1.bit.GPIO7=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO7=0;
    GpioCtrlRegs.GPADIR.bit.GPIO7=1;
    EDIS;
}
void GPIO_LCD1602_DB_OUT(void)//配置D0~D7为输出模式
{
    EALLOW;
    GpioCtrlRegs.GPADIR.bit.GPIO0=1;
    GpioCtrlRegs.GPADIR.bit.GPIO1=1;
    GpioCtrlRegs.GPADIR.bit.GPIO2=1;
    GpioCtrlRegs.GPADIR.bit.GPIO3=1;
    GpioCtrlRegs.GPADIR.bit.GPIO4=1;
    GpioCtrlRegs.GPADIR.bit.GPIO5=1;
    GpioCtrlRegs.GPADIR.bit.GPIO6=1;
    GpioCtrlRegs.GPADIR.bit.GPIO7=1;
    EDIS;
}
void GPIO_LCD1602_DB_IN(void)//配置D0~D7为输入模式
{
    EALLOW;
    GpioCtrlRegs.GPADIR.bit.GPIO0=0;
    GpioCtrlRegs.GPADIR.bit.GPIO1=0;
    GpioCtrlRegs.GPADIR.bit.GPIO2=0;
    GpioCtrlRegs.GPADIR.bit.GPIO3=0;
    GpioCtrlRegs.GPADIR.bit.GPIO4=0;
    GpioCtrlRegs.GPADIR.bit.GPIO5=0;
    GpioCtrlRegs.GPADIR.bit.GPIO6=0;
    GpioCtrlRegs.GPADIR.bit.GPIO7=0;
    EDIS;
}
/**
 *  @brief                   LCD检测忙状态
 *  @parameter                  无
 *  @return_value               无
 */
void LCD1602_WaitReady(void)
{
    Uint8 sta;
    GPIO_LCD1602_DB_OUT();
    GpioDataRegs.GPADAT.all|=0x000000FF;
    LCD1602_RS_CLR();
    LCD1602_RW_SET();
    GPIO_LCD1602_DB_IN();
    do
    {
        LCD1602_E_SET();
        sta=GpioDataRegs.GPADAT.bit.GPIO7;
        LCD1602_E_CLR();
    }while(sta);
    GPIO_LCD1602_DB_OUT();
}
/**
 *  @brief                   写指令
 *  @parameter               cmd 要写入的指令 BusyC 忙检测标志
 *  @return_value               无
 */
void LCD1602_WriteCmd(Uint8 cmd, Uint8 BusyC)
{
    if(BusyC) LCD1602_WaitReady();
    LCD1602_E_CLR();
    LCD1602_RS_CLR();
    LCD1602_RW_CLR();
    GpioDataRegs.GPADAT.all &= (cmd|0xFFFFFF00);
    DELAY_US(100);
    LCD1602_E_SET();
    DELAY_US(500);
    LCD1602_E_CLR();
    DELAY_US(10);
}

/**
 *  @brief                   写数据
 *  @parameter               dat 要写入的数据
 *  @return_value               无
 */
void LCD1602_WriteDat(Uint8 dat)
{
    LCD1602_WaitReady();
    LCD1602_E_CLR();
    LCD1602_RS_SET();
    LCD1602_RW_CLR();
    GpioDataRegs.GPADAT.all &= (dat|0xFFFFFF00);
    DELAY_US(100);
    LCD1602_E_SET();
    DELAY_US(500);
    LCD1602_E_CLR();
    DELAY_US(10);
}

/**
 *  @brief                   设置坐标
 *  @parameter               x 横坐标 y 纵坐标
 *  @return_value               无
 */
void LCD1602_SetCursor(Uint8 x, Uint8 y)
{
    Uint8 addr;
    if (y == 0)  //由输入的屏幕坐标计算显示RAM的地址
        addr = 0x00 + x;  //第一行字符地址从0x00开始
    else
        addr = 0x40 + x;  //第二行字符地址从0x40开始
    LCD1602_WriteCmd(addr|0x80,1);  //设置RAM地址
}

/**
 *  @brief                   显示字符串
 *  @parameter               x,y设置列的起始和结束地址;str字符   len长度
 *  @return_value               无
 */
void LCD1602_ShowStr(Uint8 x, Uint8 y, Uint8 *str, Uint8 len)
{
    LCD1602_SetCursor(x, y);    //设置起始地址
    while (len--)         //连续写入len个字符数据
    {
        LCD1602_WriteDat(*str++);
    }
}
/**
 *  @brief                   显示数字
 *  @parameter                 x,y设置列的起始和结束地址;num 数字
 *  @return_value               无
 */
void LCD_ShowNum(Uint8 x, Uint8 y,Uint8 num)
{
    LCD1602_SetCursor(x, y);    //设置起始地址
    LCD_ShowChar(x,y,num+'0');
}

/**
 *  @brief                   显示字符
 *  @parameter               x,y设置列的起始和结束地址;dat 数据
 *  @return_value               无
 */
void LCD_ShowChar(Uint8 x, Uint8 y,Uint8 dat)
{
    LCD1602_SetCursor(x, y);    //设置起始地址
    LCD1602_WriteDat(dat);
}


/**
 *  @brief                   1602初始化
 *  @parameter               无
 *  @return_value               无
 */
void LCD1602_Init(void)
{
    GPIO_LCD1602_Init();   //开启GPIO口
    DELAY_US(15000);
    LCD1602_WriteCmd(0x38,0);  //三次显示模式设置,不检测忙信号
    delay_1ms(50);
    LCD1602_WriteCmd(0x38,0);  //三次显示模式设置,不检测忙信号
    delay_1ms(50);
    LCD1602_WriteCmd(0x38,0);  //三次显示模式设置,不检测忙信号
    delay_1ms(50);
    LCD1602_WriteCmd(0x38,0);  //
    delay_1ms(50);
    LCD1602_WriteCmd(0x38,1);  //
    delay_1ms(50);
    LCD1602_WriteCmd(0x08,1);  //关闭显示
    delay_1ms(50);
    LCD1602_WriteCmd(0x01,1);  //清屏
    delay_1ms(50);
    LCD1602_WriteCmd(0x06,1);  //显示光标和移动设置
    delay_1ms(50);
    LCD1602_WriteCmd(0x0C,1);  //显示器开及光标设置
    delay_1ms(50);
}

bsp_lcd1602.h

/**
 *  ********************************************************************************************
 *  @file                 bsp_lcd1602.h
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2021-xx-xx
 *  @brief               LCD1602函数接口头文件
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */

#ifndef _BSP_LCD1602_H_
#define _BSP_LCD1602_H_
#include "DSP28x_Project.h"


#define Uint8                unsigned char
#define LCD1602_RS_SET()        GpioDataRegs. GPASET.bit.GPIO24=1
#define LCD1602_RS_CLR()        GpioDataRegs. GPACLEAR.bit.GPIO24=1

#define LCD1602_RW_SET()        GpioDataRegs. GPASET.bit.GPIO25=1
#define LCD1602_RW_CLR()        GpioDataRegs. GPACLEAR.bit.GPIO25=1

#define LCD1602_E_SET()        GpioDataRegs. GPASET.bit.GPIO26=1
#define LCD1602_E_CLR()        GpioDataRegs. GPACLEAR.bit.GPIO26=1

void LCD1602_Init(void);
void LCD_ShowChar(Uint8 x, Uint8 y,Uint8 dat);
void LCD_ShowNum(Uint8 x, Uint8 y,Uint8 num);
void LCD1602_ShowStr(Uint8 x, Uint8 y, Uint8 *str, Uint8 len);
void LCD1602_SetCursor(Uint8 x, Uint8 y);
void LCD1602_WriteDat(Uint8 dat);
void LCD1602_WriteCmd(Uint8 cmd, Uint8 BusyC);
void LCD1602_WaitReady(void);
void GPIO_LCD1602_Init(void);
void GPIO_LCD1602_DB_OUT(void);
void GPIO_LCD1602_DB_IN(void);


#endif /*_BSP_LCD1602_H_ */

在编码器设计部分,采用的是传统M法测速方式,即:
在这里插入图片描述
其中v(k)代表k时刻的转速,x(k)代表k时刻的位置,x(k-1)代表上一时刻的位置,T代表采样时间间隔。当T时间越短时,所测得的转速精确度越高。

由于本电机采用的是500线的编码器,且程序的eQEP模块中设置的是4倍频的,即编码器AB两相脉冲的上升沿和下降沿都会被算入计数里面。因此,电机旋转一周的时候,将会有500*4=2000个计数脉冲。

此外,eQEP模块还设置了每0.001s采样一次,在这里采样溢出时间如果设置太大的话,容易造成测速不准确。因为当前计数值可能是上一次计数值经过了几个周期了,而程序还误以为是在当前的周期。比如说,上一次计数值是500,而当前计数值是600,此时程序误以为计数值之差只有100,但事实上由于采样时间太大,计数值已经从500到2000,再从0到600,实际之差是2100,因此造成误差!

这里我们先给出测转速r/min的公式:
在这里插入图片描述
其中,c(k)表示当前计数寄存器的值,c(k-1)表示上一次计数寄存器的值。

bsp_encoder.c

/**
 *  ********************************************************************************************
 *  @file                 bsp_encoder.c
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2020-xx-xx
 *  @brief               编码器直流电机测速应用函数接口
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */
#include "bsp_encoder.h"
#include "bsp_lcd1602.h"
extern unsigned int motor_speed;
extern int DirectionQep;
extern int LineEncoder;
extern int Encoder_N;
extern float Speed_Mr_RPM;
extern float Position_k_1;
extern float Position_k;
unsigned int delay_show=0;
void QEP_pos_speed_get_init(void)
{
    #if(CPU_FRQ_150MHZ)
        EQep1Regs.QUPRD=150000;//当SYSCLKOUT=150MHZ时,设定Unit Timer
                                //溢出频率为1000HZ
    #endif
    #if(CPU_FRQ_100MHZ)
        EQep1Regs.QUPRD=100000;//当SYSCLKOUT=100MHZ时,设定Unit Timer
                                //溢出频率为1000HZ
    #endif
    EQep1Regs.QDECCTL.bit.QSRC=00;//设置eQEP计数模式
    EQep1Regs.QEPCTL.bit.FREE_SOFT=2;
    EQep1Regs.QEPCTL.bit.PCRM=00;//设定PCRM=00,即QPOSCNT在每次Index
                                 //脉冲都复位
    EQep1Regs.QEPCTL.bit.UTE=1;//使能UTE单元溢出功能
    EQep1Regs.QEPCTL.bit.QCLM=1;//当UTE单元溢出时允许锁存
    EQep1Regs.QEPCTL.bit.QPEN=1;//使能eQEP
    EQep1Regs.QCAPCTL.bit.UPPS=5;//1/32 for unit position
    EQep1Regs.QCAPCTL.bit.CCPS=7;//1/128 for CAP clock
    EQep1Regs.QCAPCTL.bit.CEN=1;//使能eQEP的捕获功能
    EQep1Regs.QPOSMAX=Encoder_N;//设定计数器的最大值
    EQep1Regs.QEPCTL.bit.SWI=1;//软件强制产生一次index脉冲
    InitEQep1Gpio();
}

void QEP_pos_speed_get_Calc(void)
{
    float tmp1;
    delay_show++;
    //检测转动方向
    DirectionQep=EQep1Regs.QEPSTS.bit.QDF;

    //检测索引信号
    if(EQep1Regs.QFLG.bit.IEL==1)
    {
        EQep1Regs.QCLR.bit.IEL=1;//清除中断信号
    }

    if(EQep1Regs.QFLG.bit.UTO==1)//如果定时器基准单元出现溢出事件 0.001中断一次
    {
        Position_k_1=1.0*EQep1Regs.QPOSLAT;//获取当前位置
        if(DirectionQep==0)//POSCNT is counting down
        {
            if(Position_k >= Position_k_1)
                tmp1=Position_k - Position_k_1;//当前位置还没有过零
            else
                tmp1=Encoder_N+(Position_k - Position_k_1);//当前位置已过零
        }
        else if(DirectionQep==1)//POSCNT is counting up
        {
            if(Position_k <= Position_k_1)
                tmp1=Position_k_1 - Position_k;//当前位置还没有过零
            else
                tmp1=Encoder_N+(Position_k_1 - Position_k);//当前位置已过零
        }
        Speed_Mr_RPM=(tmp1/80)*60;//转/分钟
        Position_k=Position_k_1;//更新上一次位置
        EQep1Regs.QCLR.bit.UTO=1;

        if(delay_show%1000==0)//1秒显示一次
        {
            motor_speed=Speed_Mr_RPM;
            LCD_ShowNum(7,0,motor_speed/100%10);//显示百位
            LCD_ShowNum(8,0,motor_speed/10%10);//显示十位
            LCD_ShowNum(9,0,motor_speed/1%10);//显示个位
            delay_show=0;
        }
    }
}

bsp_encoder.h

/**
 *  ********************************************************************************************
 *  @file                 bsp_encoder.h
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2020-xx-xx
 *  @brief               编码器直流电机测速应用函数接口头文件
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */

#ifndef _BSP_ENCODER_H_
#define _BSP_ENCODER_H_
#include "DSP28x_Project.h"


void QEP_pos_speed_get_init(void);
void QEP_pos_speed_get_Calc(void);


#endif /*_BSP_ENCODER_H_ */

main.c

/**
 *  ********************************************************************************************
 *  @file                 main.c
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2021-xx-xx
 *  @brief               编码器直流电机转速测试
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */
#include "DSP28x_Project.h"
#include "bsp_encoder.h"
#include "bsp_lcd1602.h"

#define FLASH_RUN 1
#define SRAM_RUN 2
#define RUN_TYPE FLASH_RUN
#if RUN_TYPE==FLASH_RUN
extern Uint16 RamfuncsLoadStart;
extern Uint16 RamfuncsLoadEnd;
extern Uint16 RamfuncsRunStart;
#endif
int DirectionQep=0;
int LineEncoder=500;
int Encoder_N=2000;
unsigned int motor_speed=0;
float Speed_Mr_RPM=0;
float

以上是关于DSP28335基础教程——EQEP实验(直流电机转速检测)的主要内容,如果未能解决你的问题,请参考以下文章

DSP28335基础教程——GPIO输入(矩阵按键扫描)

DSP28335基础教程——GPIO输入(矩阵按键扫描)

DSP28335基础教程——ECAP实验(超声波测距)

DSP28335基础教程——ECAP实验(超声波测距)

DSP28335基础教程——ECAP实验(超声波测距)

DSP28335基础教程——ECAP实验(超声波测距)