一、实验目的
掌握步进电机的驱动原理和控制方式
掌握使用CH451进行数码管显示的原理和方法
二、实验设备
*单片机实验箱
*步进电机模块
*CH451显示模块
*Keil开发系统
三、实验原理
步进电机





CH451
CH451 的操作命令均为 12 位,下表列出了 CH451 的各个操作命令所对应的 12 位串行数据。其中, 标注为 x 的位表示该位可以是任意值;标有名称的位表示该位在 CH451 芯片内部具有相应的寄存器, 其数据根据操作命令的不同而变化。

*设定系统参数:0100000[CKHF][DPLR][WDOG][KEYB][DISP]B
设定系统参数命令用于设定 CH451 的系统级参数:输出高频时钟 CKHF(选择快速闪烁),字驱动输出极性 DPLR(选择 DIG 字驱动输出极性:0=低电平有效;1=高电平有效),看门狗使能 WDOG,键盘扫描使能 KEYB,显示驱动使能 DISP。各个参数均通过 1 位数据控制,将相应的数据位置为 1 则启用该功能,否则关闭该功能(默认值)。例如,命令数据 010000000011B 表示选择低频时钟、关闭看门狗的功能、启用键盘扫描的功能、启用显示扫描驱动的功能、字驱动为低电平有效。
*设定显示参数:0101[MODE][LIMIT][INTENSITY]B
设定显示参数命令用于设定 CH451 的显示参数:译码方式 MODE,扫描极限 LIMIT,显示亮度 INTENSITY。译码方式 MODE 通过 1 位数据控制,置 1 时选择 BCD 译码方式,置 0 时选择不译码方式(默认值)。扫描极限 LIMIT 通过 3 位数据控制,数据 001B~111B 和 000B 分别设定扫描极限为 1~7 和 8 (默认值)。显示亮度 INTENSITY 通过 4 位数据控制,数据 0001B~1111B 和 0000B 分别设定显示驱动占空比为 1/16~15/16 和 16/16(默认值)。例如,命令数据 010101110000B 表示选择不译码方式、 扫描极限为 7、显示驱动占空比为 16/16;命令数据 010110001010B 表示选择 BCD 译码方式、扫描极限为 8、显示驱动占空比为 10/16。
*加载字数据:1[DIG_ADDR][DIG_DATA]B
加载字数据命令用于将字数据 DIG_DATA 写入 DIG_ADDR 指定地址的数据寄存器中。DIG_ADDR 通过 3 位数据指定数据寄存器的地址,数据 000B~111B 分别指定地址 0~7,对应于 DIG0~DIG7 引脚驱动的 8 个数码管。DIG_DATA 是 8 位的字数据。例如,命令数据 100001111001B 表示将字数据 79H写入第 1 个数据寄存器,如果是不译码方式,则 DIG0 引脚驱动的数码管将显示 E;命令数据110010001000B 表示将字数据 88H 写入第 5 个数据寄存器,如果是 BCD 译码方式,则 DIG4 引脚驱动的数码管将显示 8.。
四、实验内容
使用C51编写程序,控制步进电机转动,并将状态显示到CH451控制的数码管上,具体要求如下:
*在SWITCH区使用两个开关,当开关1为0时,电机不运行,否则电机按一定速度旋转;当开关2为0时,电机顺时针旋转;当开关2为1时,电机逆时针旋转。
*同时在数码管的最左边显示转动方向的标识,右边使用十进制显示已转动的步数(顺时针每转一步加1,逆时针减1)。
五、实验步骤
1. 设计电路连接方案,进行设备连线
建议的接线如下: P0接口中的IO1--IO4分别连接到步进电机模块的ORANGE、YELLOW、PINK、BLUE,并连到LAMP区的发光管上。 连接CH451建议使用P1的引脚,不要和P0共用。
参考连接图中,CH451模块的DIN连接SDA(对应C51芯片管脚P1.1),DCLK连接SCL(对应C51芯片管脚P1.2),LOAD连接PWM0(对应C51芯片管脚P1.3)。SWITCH区中,一个开关连接SPICLK(对应C51芯片管脚P1.6),另一个开关连接到SPISTE(对应C51芯片管脚P1.7)。

参考连接图
2. 编程和调试
参考代码:
//ch451.h
#ifndef __CH451_H__
#define __CH451_H__
#include<reg51.h>
#define CH451_RESET 0x0201 //复位
#define CH451_LEFTMOV 0x0300 //设置移动方式-作移
#define CH451_LEFTCYC 0x0301 //设置移动方式-左循
#define CH451_RIGHTMOV 0x0302 //设置移动方式-右移
#define CH451_RIGHTCYC 0x0303 //设置移动方式-右循
#define CH451_SYSOFF 0x0400 //关显示、键盘、看门狗
#define CH451_SYSON1 0x0401 //开显示
#define CH451_SYSON2 0x0403 //开显示、键盘
#define CH451_SYSON3 0x0407 //开显示、键盘、看门狗功能
#define CH451_DSP 0x0500 //设置默认显示方式
#define CH451_BCD 0x0580 //设置BCD译码方式
#define CH451_TWINKLE 0x0600 //设置闪烁控制
#define CH451_DIG0 0x0800 //数码管位0显示
#define CH451_DIG1 0x0900 //数码管位1显示
#define CH451_DIG2 0x0a00 //数码管位2显示
#define CH451_DIG3 0x0b00 //数码管位3显示
#define CH451_DIG4 0x0c00 //数码管位4显示
#define CH451_DIG5 0x0d00 //数码管位5显示
#define CH451_DIG6 0x0e00 //数码管位6显示
#define CH451_DIG7 0x0f00 //数码管位7显示
#define USE_KEY 1//如果使用键盘中断请定义
//以下四句:加extern后,错误为:syntax error near sbit,所以不把它作为外部变量且必须赋值
sbit ch451_load = P1^3; //串行命令加载,上升延激活
sbit ch451_din = P1^1; // 串行数据输出,接CH451的数据输入
sbit ch451_dclk = P1^2; //串行数据时钟上升延激活
sbit ch451_dout = P3^2; //INT0对应P3.2,键盘中断和键值数据输入,接CH451的数据输出
extern unsigned char ch451_key;// 全局变量,存放键盘中断中读取的键值
void ch451_init();
void ch451_write(unsigned int command);
unsigned char ch451_read();
void ch451_inter();
#endif
//ch451.c
#include "ch451.h"
//初始化子程序
void ch451_init()
{
ch451_din=0; //先低后高,选择4线输入
ch451_din=1;
#ifdef USE_KEY
IT1=0; //设置下降沿触发
IE1=0; //清中断标志
PX1=0; //设置低优先级
EX1=1; //开中断
#endif
}
//*****************************************************
//输出命令子程序
//定义一无符号整型变量存储12字节的命令字。
void ch451_write(unsigned int command)
{
unsigned char i;
#ifdef USE_KEY
EX1=0; //禁止键盘中断
#endif
ch451_load=0; //命令开始
for(i=0;i<12;i++){ //送入12位数据,低位在前
ch451_din=command&1;
ch451_dclk=0;
command>>=1;
ch451_dclk=1; //上升沿有效
}
ch451_load=1; //加载数据
#ifdef USE_KEY
EX1=1;
#endif
}
#ifdef USE_KEY
//*************************************************
//输入命令子程序,MCU从451读一字节
unsigned char ch451_read()
{
unsigned char i;
unsigned char command,keycode; //定义命令字,和数据存储器
EX1=0; //关中段
command=0x07; //输入读451命令字
ch451_load=0;
for(i=0;i<4;i++){
ch451_din=command&1; //送入最低位
ch451_dclk=0;
command>>=1; //往右移一位
ch451_dclk=1; //产生时钟上升沿锁通知CH451输入位数据
}
ch451_load=1; //产生加载上升沿通知CH451处理命令数据
keycode=0; //清除keycode
for(i=0;i<7;i++){
keycode<<=1; //数据移入keycode,高位在前,低位在后
keycode|=ch451_dout; //从高到低读入451的数据
ch451_dclk=0; //产生时钟下升沿通知CH451输出下一位
ch451_dclk=1;
}
IE1=0; //清中断标志
EX1=1;
return(keycode); //反回键值
}
//*************************************************
//中断子程序 使用中断0,寄存器组1
void ch451_inter() interrupt 0//外部中断0(INT0)对应interrupt 0 ,外部中断1(INT1)对应interrupt 2
{
unsigned char i; //定义循环变量
unsigned char command,keycode; //定义控制字寄存器,和中间变量定时器
command=0x07; //读取键值命令的高4位0111B
ch451_load=0; //命令开始
for(i=0;i<4;i++){
ch451_din=command&1; //低位在前,高位在后
ch451_dclk=0;
command>>=1; //右移一位
ch451_dclk=1; //产生时钟上升沿锁通知CH451输入位数据
}
ch451_load=1; //产生加载上升沿通知CH451处理命令数据
keycode=0; //清除keycode
for(i=0;i<7;i++){
keycode<<=1; //数据作移一位,高位在前,低位在后
keycode|=ch451_dout; //从高到低读入451的数据
ch451_dclk=0; //产生时钟下升沿通知CH451输出下一位
ch451_dclk=1;
}
ch451_key=keycode; //保存上次的键值,读取的按键代码与按键键值的关系需要测试
IE0=0; //清中断标志
}
//***********************************************
#endif
//main.c
#include <reg51.h>
#include "ch451.h"
unsigned int ch451_dig[8]={CH451_DIG0,CH451_DIG1,CH451_DIG2,CH451_DIG3,CH451_DIG4,CH451_DIG5,CH451_DIG6,CH451_DIG7};
sbit switch1 = P1^6; // 开关1连接到P1.6,SPICLK
sbit switch2 = P1^7; // 开关2连接到P1.7,SPISTE
sbit orange = P0^1; // 步进电机ORANGE连接到P0.1
sbit yellow = P0^2; // 步进电机YELLOW连接到P0.2
sbit pink = P0^3; // 步进电机PINK连接到P0.3
sbit blue = P0^4; // 步进电机BLUE连接到P0.4
unsigned char stepsClockwise[] = {0x06, 0x07, 0x04, 0x0b, 0x09, 0x0d, 0x0c, 0x0e};//顺时针
unsigned char stepsCounterclockwise[] = {0x0e, 0x0c, 0x0d, 0x09, 0x0b, 0x04, 0x07, 0x06};//逆时针
int stepCount = 0; // 步数计数
//ch451展示步数
void display(int cnt){
unsigned char a[5];
unsigned char i;
if(cnt<0){
ch451_write(CH451_DIG5|0X12); //DIG5显示负号
cnt=-cnt;
}else{
ch451_write(CH451_DIG5|0X10); //DIG5显示空格
}
//DIG0-DIG5展示数字
for(i=0;i<5;i++){
a[i]=cnt%10;
ch451_write(ch451_dig[i]|a[i]);
cnt=cnt/10;
}
}
void delay(int ms) {
int i, j;
for (i = 0; i < ms; i++) {
for (j = 0; j < 110; j++);
}
}
void rotateMotor(int clockwise) {
int i;
unsigned char* steps;
if (clockwise) {// 方向为顺时针
steps = stepsClockwise;
stepCount++;
display(stepCount);
} else {// 方向为逆时针
steps = stepsCounterclockwise;
stepCount--;
display(stepCount);
}
for (i = 0; i < 8; i++) {
P0 = steps[i];
delay(10);
}
}
void main() {
ch451_init();
ch451_write(0x403);
ch451_write(0x580);
ch451_write(CH451_DIG5|0X10); //DIG5显示空格
ch451_write(CH451_DIG6|0X10); //DIG6显示空格
while (1) {
if (switch1 == 0) {// 开关1为0,电机不运行
P0 = 0x00; // 停止步进电机
} else {// 开关1为1,电机按一定速度旋转
if (switch2 == 0) {
// 开关2为0,电机顺时针旋转
ch451_write(CH451_DIG7|0X14); //显示顺时针符号
rotateMotor(1);
} else {
// 开关2为1,电机逆时针旋转
ch451_write(CH451_DIG7|0X15); //显示逆时针符号
rotateMotor(0);
}
}
}
}
*代码说明:
ch451_write(0x403);表示向CH451写命令字010000000011B,表示选择低频时钟、关闭看门狗的功能、启用键盘扫描的功能、启用显示扫描驱动的功能、字驱动为低电平有效。
ch451_write(0x580);表示向CH451写命令字010110000000B,表示选择 BCD 译码方式、扫描极限为 8、显示驱动占空比为 16/16。
ch451_write(CH451_DIG5|0X10); 表示DIG5显示空格
ch451_write(CH451_DIG5|0X12); 表示DIG5显示负号
ch451_write(CH451_DIG7|0X14); 表示DIG7显示顺时针符号
ch451_write(CH451_DIG7|0X15); 表示DIG7显示逆时针符号
以上四条语句是采用BCD译码方式使相应数码管显示特定字符,字符对应的5位数据(数据寄存器中字数据的位 4~位 0)参照下表:
