实验题目:驱动与API设计初步
实验时间:2022.3.21(周一)~2022.4.3(周日)
实验地点:在规定时间中自主寻找合适实验地点并在规定时间内完成
实验条件:
1.STC-B学习板(2021暑假小学期每个学生自己做的) 2.个人电脑:自备 3.Keil、ISP、CH340驱动软件:自备 4.STC-BSP及Demo 程序:教师提供 5.“STC_B学习板”软件支持包使用说明:教师提供 6.C51语言语法:课程提供参考电子书籍,或自找自学 7.STC_BSP源码示范_sys&显示模块(教师提供源码和工程文件) 8.“STC-B学习板”相关参考程序_夏季小学期相关案例 9. 步进电机(C栋214提供调式用步进电机)
实验目的:
1.学习并练习“并口输出”应用和编程; 2.参照所提供的STC-BSP核心sys源码和显示模块源码,学习软件设计的层次化抽象方法并实践; 3.实验具体目的是:针对“无人驾驶汽车”应用场景,设计具有同时控制三个步进功能的程序模块,该模块能提供适当的API函数(一个或多个),方便应用层编写控制电机程序。
实验内容:
1.查找资料,熟悉四线步进电机控制方法; 2.阅读所提供的displayer模块参考源程序和其API函数设计方法,规划构造控制三个步进电机程序模块的API函数(一个或多个函数)。三个步进电机分别假设为:SM接口(可接1个真实步进电机)、8个LED指示灯(假设为另外2个步进电机控制接口); 3.从模块的初始化程序、后台服务程序(驱动程序)、应用层接口API函数三个层次设计该模块对应程序,最终实现你所规划的API函数,并验证API函数的正确性。(暂不要求按模板方式封装);
实验过程、步骤、现象、结果:
查找资料,熟悉四线步进电机控制方法:
28BYJ-48型步进电机的旋转是以固定的角度一步一步运行的。可以通过控制脉冲个数来控制角位移量,从而达到准确定位的目的;同时可以通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。控制步进电机定子绕组的通电顺序可以控制步进电机的转动方向。28BYJ-48型接线图如图1所示。
图1 28BYJ-48步进电机控制接线 步进电机的励磁方式有三种: (1)一相励磁——在每一瞬间,步进电机只有一个线圈导通。每送一个励磁信号,步进电机旋转5.625°,这是三种励磁方式中最简单的一种。 如图7-3-2所示,开始时,开关SB接通电源,SA、SC、SD断开,B相磁极和转子0、3号齿对齐,同时,转子的1、4号齿就和C、D相绕组磁极产生小角度错齿,2、5号齿就和D、A相绕组磁极产生大角度(前小角度的2倍)错齿。 当开关SC接通电源,SB、SA、SD断开时,由于C相绕组的磁力线和1、4号齿之间磁力线的作用,使转子转动,1、4号齿和C相绕组的磁极对齐。而0、3号齿和A、B相绕组产生错齿,2、5号齿就和A、D相绕组磁极产生错齿。依次类推,A、B、C、D四相绕组轮流供电,则转子会沿着A、B、C、D方向转动。
(2)二相励磁——在每一瞬间,步进电机有两个线圈同时导通。每送一个励磁信号,步进电机旋转5.625°。1/64
(3)一-二相励磁——为一相励磁与二相励磁交替导通的方式。每送一个励磁信号,步进电机旋转2.8125°。八拍模式是这类四相步进电机的最佳工作模式,能最大限度的发挥电机的各项性能。当相邻两相同时导通的节拍时,由于该两相绕组的定子齿对它们附近的转子齿同时产生相同的吸引力,这将导致这两个转子齿的中心线对到两个绕组的中心线上,也就使转动精度增加了一倍,还会增加电机的整体扭力输出。
学习板上步进电机电路图如图2所示,一路高电平信号由IO端输出经ULN2003取反为低电平输入至步进电机驱动一相绕组。
图2 步进电机电路示意
阅读所提供的displayer模块参考源程序和其API函数设计方法,规划构造控制三个步进电机程序模块的API函数(一个或多个函数)。三个步进电机分别假设为:SM接口(可接1个真实步进电机)、8个LED指示灯(假设为另外2个步进电机控制接口);
对于显示模块的实现:首先是初始化函数也就是显示模块的驱动,这里即是设置P0寄存器(用于控制LED和数码管段选的寄存器)8位为推挽输出,这样可以是的输出的亮度更大。
驱动之后是显示模块: 该显示模块首先对P2寄存器地4位进行赋值(下面的switch)若要输出LED则这低4位的最高位为1,显示数码管时为0,第3位在选择数码管时有效,表示显示8位数码管的第几号数码管,后续则是对数码管显示的译码和计算下一次要显示的位置(第几号数码管或者LED),最后只要我们对该函数进行每1ms一次的循环运行就能使我们的数码管和LED正常显示(虽然每次只能显示一个地方,但间隔时间短,所以肉眼看不出来)
最后则是一些修改参数的API函数:
对于sys模块的实现:sys模块主要用于实现系统基本的中断处理函数的实现 比如这里sys用于设置定时器和开启定时器中断:
然后MySTC_OS将会不断的运行(即放入while(1)死循环中),每次对标志位进行判断(每个标志位表示系统运行了多少时间了),标志位的更新通过1ms的定时器来完成
其他的标志位通过对1ms事件的计数即可实现。 然后在1ms事件中加入上面的显示轮询函数或者其他需要的函数,即可实现显示操作(而不会全占系统资源,导致其他功能无法运行)。
从模块的初始化程序、后台服务程序(驱动程序)、应用层接口API函数三个层次设计该模块对应程序,最终实现你所规划的API函数,并验证API函数的正确性。(暂不要求按模板方式封装);
驱动部分:将A,B,C,D电圈对应的引脚p4的低4位设置为推挽输出,然后初始化p4为0
电机运行函数:将该函数放到和显示模块的地方,每1ms进行轮询运行(这里使用的老师的BSP环境,不能直接修改sys中的函数,所以用1ms的回调事件代替)
分为3个部分分别对应于电机1,2,3其中电机1位真实电机,通过判断每个电机的速度,剩余转动步长以及转动频率(即每多少ms转动一次,通过1000/speed进行计算)来判断每个电机是否旋转。
旋转时对于虚拟电机2,3通过LED灯进行模拟速度和方向,先进行step的正负判断,这决定电机的旋转方向,随后将LED向相应方向移动一位,并重置延时和将剩余转动步长减1。虚拟电机3同理,只是LED的流动部分在LED的高4位。
对于真实电机1而言,根据电机的资料,A,B,C,D,4个线圈分别运行一次后电机旋转1/64 * 4=1/16圈,所以重复该步骤16次即完成电机一圈的旋转。其余操作和虚拟电机一致,实现代码如下,反转代码将顺序改为D,C,B,A即可
最后实现改变电机参数的函数:
该函数作用为对电机1,2,3进行速度和剩余旋转步数的设置,经过设置后电机就可以正常旋转,函数的使用与老师BSP中电机的相应函数使用方法一致 急停函数:紧急停止某一电机 根据电机,将相应电机速度置0,然后返回剩余的旋转步数。
状态查询函数:
根据电机是否有剩余步数,返回电机是否空闲
最后运行程序1:测试速度调整
首先的初始化部分,需要先运行电机的初始化函数,随后需要将电机的运行函数放到1ms的回调事件中(原因上文已说明) 另外两个回调函数,分别用于调整电机速度和显示当前待设置的电机序号和该电机的速度。
按键3用于调整,要设置的电机序号,按键1,2分别用于增加和减少电机的运行速度,代码详情如图所示(按键2同理这里就没贴出来)
最后没10ms更新一次显示内容即可
运行效果: 可以看到目前扩展接口SM的4个引脚灯,正在按照电机运行顺序发光,说明我们的电机驱动程序是成功的,而8个LED灯部分则是在模拟电机的运行。
运行程序2:测试方向调整 总体代码与程序2类似,按键1改为对电机进行急停操作,按键2改为对电机进行反向操作 显示部分改为显示电机的剩余步长 运行效果: 初始化时设置的电机旋转步数为10000。
实验遇到的问题和难点
在测试速度时将速度调整为0 后LED灯正常停止,但再次将速度调整为大于0时,LED会停止一段时间后才开始移动 产生原因: 一开始我在电机运行函数中判断电机是否旋转的条件写的是如下方式:
问题就出现在如果speed1调整到0后,当delay1减为0后,电机不会运行旋转的程序以及该程序中的重置delay1的程序,导致delay1减到负数,但因为我定义的类型为unsigned int所以会使得delay1变为2^16,从而出现上面,将速度调回大于0后任要过一段时间后才会运行LED灯。 解决方式:将speed条件单独拿出来即可
完整代码
电机头文件代码(代码供以参考,要copy的话记得改下变量名和顺序): 头文件命名为motor.h
#include "STC15F2K60S2.H"
#include "displayer.H"
#include "beep.h"
sbit s1 =P4^1;
sbit s2 =P4^2;
sbit s3 =P4^3;
sbit s4 =P4^4;
unsigned int delay1=0,delay2=0,delay3=0,i1=0,i2=0,i,j;
unsigned char led=0;
unsigned char speed1;
unsigned char speed2;
unsigned char speed3;
int steps;
int steps1=0;
int steps2=0;
int steps3=0;
enum StepMotorName {enumStepMotor1=0,enumStepMotor2,enumStepMotor3};
enum StepMotorActName {enumStepMotorFree,enumStepMotorBusy,enumSetStepMotorOK,enumSetStepMotorFail};
void delay_ms( unsigned int n)
{
while(n)
{
unsigned char i, j;
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
n--;
}
}
void MotorInit()
{
P4M0=0Xff;
P4M1=0X00;
P4 =0X00;
}
void PlayMotor()
{
if (speed1 != 0)
if (delay1-- == 0 && steps1 != 0)
{
if (steps1 > 0)
{
for (j = 0; j < 16; j++)
{
s1 = 1;
s2 = 0;
s3 = 0;
s4 = 0;
s1 = 0;
s2 = 1;
s3 = 0;
s4 = 0;
s1 = 0;
s2 = 0;
s3 = 1;
s4 = 0;
s1 = 0;
s2 = 0;
s3 = 0;
s4 = 1;
}
steps1--;
delay1 = 1000 / speed1;
}
else
{
for (j = 0; j < 16; j++)
{
s1 = 0;
s2 = 0;
s3 = 0;
s4 = 1;
s1 = 0;
s2 = 0;
s3 = 1;
s4 = 0;
s1 = 0;
s2 = 1;
s3 = 0;
s4 = 0;
s1 = 1;
s2 = 0;
s3 = 0;
s4 = 0;
}
steps1++;
delay1 = 1000 / speed1;
}
}
if (speed2 != 0)
if (delay2-- == 0 && steps2 != 0)
{
if (steps2 > 0)
{
led = (led & 0xf0) + (0x01 << (i1 % 4));
LedPrint(led);
i1++;
steps2--;
delay2 = 1000 / speed2;
}
else
{
led = (led & 0xf0) + (0x08 >> (i1 % 4));
LedPrint(led);
i1++;
steps2++;
delay2 = 1000 / speed2;
}
}
if (speed3 != 0)
if (delay3-- == 0 && steps3 != 0)
{
if (steps3 > 0)
{
led = (led & 0x0f) + (0x10 << (i2 % 4));
LedPrint(led);
i2++;
steps3--;
delay3 = 1000 / speed3;
}
else
{
led = (led & 0x0f) + (0x80 >> (i2 % 4));
LedPrint(led);
i2++;
steps3++;
delay3 = 1000 / speed3;
}
}
}
char SetMotor(char StepMotor, unsigned char speed, int steps)
{
switch (StepMotor)
{
case enumStepMotor1:
steps1 = steps;
speed1 = speed;
return enumSetStepMotorOK;
case enumStepMotor2:
steps2 = steps;
speed2 = speed;
return enumSetStepMotorOK;
case enumStepMotor3:
steps3 = steps;
speed3 = speed;
return enumSetStepMotorOK;
default:
return enumSetStepMotorFail;
}
}
int EmStop(char StepMotor)
{
switch (StepMotor)
{
case enumStepMotor1:
steps = steps1;
steps1 = 0;
return steps;
case enumStepMotor2:
steps = steps2;
steps2 = 0;
return steps;
case enumStepMotor3:
steps = steps3;
steps3 = 0;
return steps;
default:
return 0;
}
}
unsigned char GetMotorStatus(char StepMotor)
{
switch (StepMotor)
{
case enumStepMotor1:
if (steps1 == 0)
return enumStepMotorFree;
else
return enumStepMotorBusy;
case enumStepMotor2:
if (steps2 == 0)
return enumStepMotorFree;
else
return enumStepMotorBusy;
case enumStepMotor3:
if (steps3 == 0)
return enumStepMotorFree;
else
return enumStepMotorBusy;
default:
return enumSetStepMotorFail;
}
}
测试代码:速度测试(为什么不把两个功能写在一起?当然是我嫌麻烦不想用导航按键)
#include "STC15F2K60S2.H"
#include "beep.h"
#include "Key.h"
#include "sys.h"
#include "motor.h"
#include "STC15F2K60S2.H"
#include "displayer.h"
enum StepMotorName Motor=enumStepMotor1;
unsigned char MotorSpeed1=0x01;
unsigned char MotorSpeed2=0x01;
unsigned char MotorSpeed3=0x01;
void Motor_CallBack()
{
PlayMotor();
}
void key_callback()
{
if (GetKeyAct(enumKey3) == enumKeyPress)
{
SetBeep(1000, 10);
if (Motor < 2)
Motor++;
else
Motor = enumStepMotor1;
}
if (GetKeyAct(enumKey1) == enumKeyPress)
{
SetBeep(1000,10);
switch (Motor)
{
case enumStepMotor1:
if (MotorSpeed1 != 0xff)
MotorSpeed1++;
SetMotor(enumStepMotor1, MotorSpeed1, steps1);
break;
case enumStepMotor2:
if (MotorSpeed2 != 0xff)
MotorSpeed2++;
SetMotor(enumStepMotor2, MotorSpeed2, steps2);
break;
case enumStepMotor3:
if (MotorSpeed3 != 0xff)
MotorSpeed3++;
SetMotor(enumStepMotor3, MotorSpeed3, steps3);
break;
}
}
if (GetKeyAct(enumKey2) == enumKeyPress)
{
SetBeep(1000,10);
switch (Motor)
{
case enumStepMotor1:
if (MotorSpeed1 != 0x00)
MotorSpeed1--;
SetMotor(enumStepMotor1, MotorSpeed1, steps1);
break;
case enumStepMotor2:
if (MotorSpeed2 != 0x00)
MotorSpeed2--;
SetMotor(enumStepMotor2, MotorSpeed2, steps2);
break;
case enumStepMotor3:
if (MotorSpeed3 != 0x00)
MotorSpeed3--;
SetMotor(enumStepMotor3, MotorSpeed3, steps3);
break;
}
}
}
void Display_CallBack()
{
switch (Motor)
{
case enumStepMotor1:
Seg7Print(Motor + 1, 10, 10, 10, 10, MotorSpeed1 / 100 % 10, MotorSpeed1 / 10 % 10, MotorSpeed1 % 10);
break;
case enumStepMotor2:
Seg7Print(Motor + 1, 10, 10, 10, 10, MotorSpeed2 / 100 % 10, MotorSpeed2 / 10 % 10, MotorSpeed2 % 10);
break;
case enumStepMotor3:
Seg7Print(Motor + 1, 10, 10, 10, 10, MotorSpeed3 / 100 % 10, MotorSpeed3 / 10 % 10, MotorSpeed3 % 10);
break;
}
}
code unsigned long SysClock=11059200;
#ifdef _displayer_H_
code char decode_table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00,0x08,0x40,0x01, 0x41, 0x48,
0x3f|0x80,0x06|0x80,0x5b|0x80,0x4f|0x80,0x66|0x80,0x6d|0x80,0x7d|0x80,0x07|0x80,0x7f|0x80,0x6f|0x80 };
#endif
void main()
{
DisplayerInit();
BeepInit();
Key_Init();
MotorInit();
SetDisplayerArea(0,7);
Seg7Print(10,10,10,10,10,10,10,10);
SetEventCallBack(enumEventKey,key_callback);
SetEventCallBack(enumEventSys1mS,Motor_CallBack);
SetEventCallBack(enumEventSys10mS,Display_CallBack);
SetMotor(enumStepMotor1,MotorSpeed1,10000);
SetMotor(enumStepMotor2,MotorSpeed2,10000);
SetMotor(enumStepMotor3,MotorSpeed3,10000);
MySTC_Init();
while (1)
{
MySTC_OS();
}
}
方向,急停测试:
#include "STC15F2K60S2.H"
#include "beep.h"
#include "Key.h"
#include "sys.h"
#include "motor.h"
#include "STC15F2K60S2.H"
#include "displayer.h"
enum StepMotorName Motor=enumStepMotor1;
unsigned char MotorSpeed1=0x01;
unsigned char MotorSpeed2=0x01;
unsigned char MotorSpeed3=0x01;
void Motor_CallBack()
{
PlayMotor();
}
void key_callback()
{
if (GetKeyAct(enumKey3) == enumKeyPress)
{
SetBeep(1000, 10);
if (Motor < 2)
Motor++;
else
Motor = enumStepMotor1;
}
if (GetKeyAct(enumKey1) == enumKeyPress)
{
SetBeep(1000,10);
switch (Motor)
{
case enumStepMotor1:
EmStop(enumStepMotor1);
break;
case enumStepMotor2:
EmStop(enumStepMotor2);
break;
case enumStepMotor3:
EmStop(enumStepMotor3);
break;
}
}
if (GetKeyAct(enumKey2) == enumKeyPress)
{
SetBeep(1000,10);
switch (Motor)
{
case enumStepMotor1:
SetMotor(enumStepMotor1, MotorSpeed1, -steps1);
break;
case enumStepMotor2:
SetMotor(enumStepMotor2, MotorSpeed2, -steps2);
break;
case enumStepMotor3:
SetMotor(enumStepMotor3, MotorSpeed3, -steps3);
break;
}
}
}
int abs(int n)
{
if(n>0)
return n;
else
return -n;
}
void Display_CallBack()
{
switch (Motor)
{
case enumStepMotor1:
Seg7Print(Motor + 1, 10, 10, abs(steps1)/10000%10, abs(steps1)/1000%10, abs(steps1)/100%10, abs(steps1)/10%10, abs(steps1)%10);
break;
case enumStepMotor2:
Seg7Print(Motor + 1, 10, 10, abs(steps2)/10000%10, abs(steps2)/1000%10, abs(steps2)/100%10, abs(steps2)/10%10, abs(steps2)%10);
break;
case enumStepMotor3:
Seg7Print(Motor + 1, 10, 10, abs(steps3)/10000%10, abs(steps3)/1000%10, abs(steps3)/100%10, abs(steps3)/10%10, abs(steps3)%10);
break;
}
}
code unsigned long SysClock=11059200;
#ifdef _displayer_H_
code char decode_table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00,0x08,0x40,0x01, 0x41, 0x48,
0x3f|0x80,0x06|0x80,0x5b|0x80,0x4f|0x80,0x66|0x80,0x6d|0x80,0x7d|0x80,0x07|0x80,0x7f|0x80,0x6f|0x80 };
#endif
void main()
{
DisplayerInit();
BeepInit();
Key_Init();
MotorInit();
SetDisplayerArea(0,7);
Seg7Print(10,10,10,10,10,10,10,10);
SetEventCallBack(enumEventKey,key_callback);
SetEventCallBack(enumEventSys1mS,Motor_CallBack);
SetEventCallBack(enumEventSys10mS,Display_CallBack);
SetMotor(enumStepMotor1,MotorSpeed1,10000);
SetMotor(enumStepMotor2,MotorSpeed2,10000);
SetMotor(enumStepMotor3,MotorSpeed3,10000);
MySTC_Init();
while (1)
{
MySTC_OS();
}
}
|