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实验(直流电机转速检测)的主要内容,如果未能解决你的问题,请参考以下文章