实验题目
编制MCS-51程序使步进电机按照下面规定的转速和方向进行旋转:
-
以40转/分速度正转5周,等待S1开关按下
-
以25转/分速度反转10周,等待S1开关按下
-
以10转/分速度持续正转,当按下S2开关时暂停旋转
将已转动的步数(不管正反)以十进制显示在数码管上。
本程序要求使用定时器中断来实现,不准使用程序延时的方式。
学习路径
1.步进电机工作原理
步进电机与驱动芯片FAN8200连接,FAN8200有4个引脚,分别是
FAN8200 | CPU | 备注 |
---|---|---|
P1.1 | CE1 | CE1是控制引脚,相当于IN1的使能引脚 |
P1.4 | CE2 | CE2是控制引脚,相当于IN2的使能引脚 |
P3.2 | IN1 | 控制步进电机转动的引脚 |
P1.0 | IN2 | 控制步进电机转动的引脚 |
在实验中,IN1和IN2按照 01->11->10->00->01 输出,电机就会朝一个方向转动,电机转动一周需要24步
2.定时器中断
简单来说,定时器就是一个累加器,每经过一个定时周期,累加器加一,直到累加器溢出,就会产生一个中断信号。
定时器的4种工作方式
- 方式0,13位定时器
- 方式1,16位定时器
- 方式2,8位定时器,自动重装计数初值
- 方式3,T0分成两个8位定时器
定时器的初值设定
定时周期 = 12/CPU晶振频率(本实验中晶振频率为12M)
累加器最大计数值 = 2^定时器最大位数(定时器最大位数取决于工作方式)
计数初值 = 累加器的最大计数值 - 计时时间/定时周期
使用定时器需要用到的寄存器
寄存器 | 功能 | 计数结束后是否需要重新设置 |
---|---|---|
IE | 中断允许寄存器 | 否 |
IP | 中断优先级寄存器 | 否 |
TMOD | 定时器控制字(设置定时器工作方式) | 否 |
TCON | 定时器控制字 | 否 |
THx,TLx | 定时器初值 | 除方式3外,需重新设置 |
也可以参考网上关于定时器中断的更多资料
3.数码管显示
试验箱上的数码管与实验二仿真实验中的数码管连接方式相同,但是增加到了三个,使用P4.4和P4.5控制,分别是数据线和检测上跳沿移位。74hc595的复位引脚并未连接至CPU。
P4口是扩展接口,是把内存中的一个地址映射为P4口,所以使用前需要进行如下设置
P4 EQU 0C0H ;宏定义,把内存中的一个单元映射为P4口
P4SW EQU 0BBH ;宏定义,给P4SW的地址起一个别名
MOV P4SW,#30H ;设置启用P4口
4.学习定义变量
本次实验需要存储很多数据,所以要学会定义变量。单片机中的变量定义很简单,就是给某一个内存地址起一个别名,后面就可以直接用这个别名访问这个地址。但是要注意以下几个问题:
1.这个地址不能和已经有用的地址重合,比如堆栈区,特殊功能寄存器。
2.我们定义的变量相当于内存数,使用的时候要注意指令能否把内存数作为操作数。
3.并非每一个地址都能进行位编址,也就是不是每一个地址都能定义成bool型变量。
如果能理解透彻单片机的存储结构,那么变量定义就不会是一件难事,建议参考网上的关于单片机存储结构(单片机片内数据存储区)的相关文章。
5.学习keil软件和stm烧录软件
主要步骤为
1.选择cpu型号,建立工程
2.建立新文件,编写代码
3.设置生成.HEX文件
4.在stm软件中烧录程序(使用stm软件打开时的默认参数就可以可以完成烧录)
编程思路
首先,我们要明确控制步进电机转动的方式。根据题目中给出的转速,我们可以计算出转一圈的时间,因为一圈需要24步,进而计算出前进一步的时间。使用定时器,每经过预设的时间,向IN1和IN2输出下一步的电平,就可以让步进电机以预设的转速转动。
这一步存在一个问题,前进一步的时间会超过定时器能设置的最大时间,这就需要我们记录进入中断的次数,直到多次进入中断,达到预设时间时,才进行下一步操作。
然后,我们需要确定程序主体的框架。这个程序要求很长,但仅仅是长,并没有很多控制逻辑,所以我们可以像写顺序结构的c语言代码一样写这个程序。实现第一行要求的伪代码如下
//以40转/分速度正转5周
for(int step=0;step<120;step++) //5周*24步/周=120步
TL0=...;TH0=...;//设置定时器初值,设置进入定时器中断多少次达到预设时间
out_display(); //调用八段数码管显示的模块,显示之后自动将步数+1
out_motor(); //调用步进电机转动的模块,向IN1和IN2输出下一步的信号
while(sym==0)
; //等待时钟中断
//等待S1开关按下
while(S1未按下)
; //等待S1按下
参考代码
代码很长,实现的功能与题目并不完全一致,仅供参考。
;R0用于标志时间是否改变
;R1用于记录一次计时进入中断的次数
;R2R4 DISPLAYONE
;R3用于记录是否暂停
P4 EQU 0C0H
P4SW EQU 0BBH
LEDCLK EQU P4.4
LED EQU P4.5
S1 EQU P3.6
S2 EQU P3.7
ONE EQU 30H
TEN EQU 31H
HUNDRED EQU 32H
extra_intr EQU 33H
step_target EQU 34H
step_completed EQU 35H
motor_voltage EQU 36H
ORG 0000H
LJMP start
ORG 000BH
LJMP timer_intr
TABLE:DB 0C0H,0F9H,0A4H,0B0H,99H,92H,82H,0F8H,80H,90H
ORG 0040H
start: ;51单片机初始化
MOV DPTR,#TABLE
MOV SP,#40H
MOV P4SW,#30H ;!!设置P4SW 4,5两位为1
SETB P1.1 ;CE1
SETB P1.4 ;CE2
MOV TMOD,#01H ;工作方式寄存器TMOD 方式1
MOV IE,#82H ;中断允许寄存器IE
MOV IP,#02H ;中断优先级寄存器IP
MOV R1,#0
MOV ONE,#00H ;计数值清零
MOV TEN,#00H
MOV HUNDRED,#00H
MOV motor_voltage,#01111000B
init_for_status1:
CLR P0.0 ;test
MOV step_completed,#0
MOV step_target,#121
MOV extra_intr,#0
load_status1: ;计数器初值3036
MOV TL0,#0DCH
MOV TH0,#0BH
MOV TCON,#10H ;工作方式寄存器TCON,计时器0开始工作
CALL OUT_DISPLAY
CALL OUT_MOTOR_FRONT
MOV R0,#0
wait_status1:
MOV A,R0
JZ wait_status1
;检查步数是否达到要求
INC step_completed
MOV A,step_target
SUBB A,step_completed
JNZ load_status1
wait_s1_status1: ;等待S1按下
SETB P3.6
JB P3.6,wait_s1_status1
init_for_status2:
CLR P0.1 ;test
MOV step_completed,#0
MOV step_target,#240
MOV extra_intr,#1
MOV R1,#0
load_status2:
MOV TL0,#0DCH
MOV TH0,#0BH
MOV TCON,#10H ;工作方式寄存器TCON,计时器0开始工作
CALL OUT_DISPLAY
CALL OUT_MOTOR_BACK
MOV R0,#0
wait_status2:
MOV A,R0
JZ wait_status2
;检查步数是否达到要求
INC step_completed
MOV A,step_target
SUBB A,step_completed
JNZ load_status2
wait_s1_status2: ;等待S1按下
SETB P3.6
JB P3.6,wait_s1_status2
init_for_status3:
CLR P0.2 ;test
MOV step_completed,#0
MOV step_target,#240
MOV extra_intr,#3
MOV R1,#0
load_status3:
MOV TL0,#0DCH
MOV TH0,#0BH
MOV TCON,#10H ;工作方式寄存器TCON,计时器0开始工作
CALL OUT_DISPLAY
CALL OUT_MOTOR_FRONT
MOV R0,#0
MOV R3,#0
wait_status3: ;检查S2是否按下
MOV P0,R3
SETB P3.7
JNB P3.7,timer_chagne
check_timer:
MOV A,R0
JZ wait_status3
JMP load_status3
timer_chagne: ;计时器停止/计时状态切换
MOV A,R3
JZ timer_stop
MOV TCON,#10H
MOV R3,#0
JMP check_timer
timer_stop:
MOV TCON,#00H
MOV R3,#1
JMP check_timer
timer_intr: ;时钟中断处理程序
INC R1
MOV A,R1
SUBB A,extra_intr
JZ start_new_timer
MOV TL0,#00H
MOV TH0,#00H
MOV TCON,#10H
SETB TR0
RETI
start_new_timer:
MOV R0,#1
MOV R1,#0
RETI
OUT_DISPLAY: ;八段数码管显示,计数值+1
MOV A,ONE ;在数码管显示ONE
MOVC A,@A+DPTR
MOV R2,A
LCALL displayone
MOV A,TEN ;在数码管显示TEN
MOVC A,@A+DPTR
MOV R2,A
LCALL displayone
MOV A,HUNDRED ;在数码管显示THUNDRED
MOVC A,@A+DPTR
MOV R2,A
LCALL displayone
;计数值+1
INC ONE ;个位+1
MOV A,ONE ;ONE增加到10时清零
CJNE A,#0AH,END_DISPLAY;比较不相等转移指令
INC TEN ;十位+1
MOV ONE,#0
MOV A,TEN
CJNE A,#0AH,END_DISPLAY
INC HUNDRED ;百位+1
MOV TEN,#0
END_DISPLAY:
RET
displayone:
MOV A,R2 ;R2左移
RL A
MOV R2,A
MOV R4,#8 ;循环次数
lp:
;取r2最低位
MOV A,#1
ANL A,R2
;将r2最低位赋值给c
CLR C
JZ lpend
SETB C
lpend:
;输出
CLR LEDCLK
MOV LED, C
SETB LEDCLK
;R2左移
MOV A,R2
RL A
MOV R2,A
DJNZ R4,lp ;循环
RET
OUT_MOTOR_FRONT:
MOV A,motor_voltage ;输出至两个端口
RRC A
MOV P1.0,C
RRC A
MOV P3.2,C
MOV A,motor_voltage ;修改motor_voltage
RR A
RR A
MOV motor_voltage,A
RET
OUT_MOTOR_BACK:
MOV A,motor_voltage
RLC A
MOV P3.2,C
RLC A
MOV P1.0,C
MOV A,motor_voltage
RL A
RL A
MOV motor_voltage,A
RET
避坑指南
1.试验箱数码管采用共阳极连接方式,与实验二仿真实验中数码管连接方式相反,记得修改段码表。
2.一定要设置堆栈指针SP。CPU在响应中断或者子程序调用的时候,会将16位PC指针入栈,如果不设置堆栈指针,默认为07H,可能与定义的变量冲突。
3.如果步进电机出现前后反复跳动,不朝一个方向转动的情况,可以试试每次改变IN1和IN2的时候,将CE1和CE2置0再置1,或者更换实验箱。
4.实验箱的P0口连接了LED指示灯,可以利用LED指示灯debug。