前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >(十一)DSP28335基础教程——EQEP实验(直流电机转速检测)

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

作者头像
全栈程序员站长
发布2022-11-10 15:49:45
1.5K0
发布2022-11-10 15:49:45
举报
文章被收录于专栏:全栈程序员必看

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

代码语言:javascript
复制
/** * ******************************************************************************************** * @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

代码语言:javascript
复制
/** * ******************************************************************************************** * @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

代码语言:javascript
复制
/** * ******************************************************************************************** * @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

代码语言:javascript
复制
/** * ******************************************************************************************** * @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

代码语言:javascript
复制
/** * ******************************************************************************************** * @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 Position_k_1=0;
float Position_k=0;
void delay_1ms(Uint16 t);
/** * @brief 主函数 * @parameter 无 * @return_value 无 */
void main(void)
{ 

/*第一步:初始化系统控制:*/
InitSysCtrl();
/*第二步:初始化GPIO口*/
InitGpio();
/* 第三步:清除所有中断 和初始化 PIE 向量表:*/
DINT;// 关闭全局中断
InitPieCtrl();// 初始化 PIE 控制寄存器到默认状态,默认状态是全部 PIE 中断被禁用和标志位被清除
IER = 0x0000;// 禁用 CPU 中断和清除所有 CPU 中断标志位:
IFR = 0x0000;
InitPieVectTable();// 初始化 PIE 中断向量表
// 中断重映射,注册中断程序入口(用户按需求添加)
EALLOW;
EDIS;
//
/*程序烧录入28335(可选的)*/
#if RUN_TYPE==FLASH_RUN
MemCopy(&RamfuncsLoadStart,&RamfuncsLoadEnd,&RamfuncsRunStart);
InitFlash();
#endif
/* 第四步: 初始化片上外设*/
// InitPeripherals(); //初始化所有外设(本例程不需要)
delay_1ms(5000);//等待LCD进入工作状态
LCD1602_Init();//初始化1602显示屏
LCD1602_ShowStr(0,0,"speed:",6);
LCD1602_ShowStr(7,0," r/min",10);
LCD1602_ShowStr(0,1,"SK ELECTRONICS",14);
QEP_pos_speed_get_init();
/* 第五步:添加用户功能具体代码*/
EINT;
ERTM;
for(;;)
{ 

QEP_pos_speed_get_Calc();
// GpioDataRegs. GPATOGGLE.bit.GPIO0=1;
// delay_1ms(1000);
}
}
void delay_1ms(Uint16 t)
{ 

while(t--)
{ 

DELAY_US(1000);
}
}

3 实验展示

程序烧录进去后,大家可以看到这个效果图,通过调整可调电源的电压来控制电机转速,明显可以测速并显示在LCD上。实验展示视频可在评论区链接查看!

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210504140342813.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjU1NjY5Ng==,size_16,color_FFFFFF,t_70#
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210504140342813.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjU1NjY5Ng==,size_16,color_FFFFFF,t_70#

大家可以参考代码尝试一下, 有疑问的欢迎留言!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/184832.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月7日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0 前言
  • 1 硬件部分
  • 2 软件部分
  • 3 实验展示
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档