目录
一、具体实现功能
1、用独立按键控制传送带的启动或停止。
2、用基于光敏电阻的光电传感器检测通过其的产品数量。
3、用LED显示传送带通过产品的数量。
4、用矩阵键盘预置终值,当计数达到终值时,传送带停止。
5、可以通过矩阵键盘完成预设终值的输入、退位和清零,以及计数数值清零。
二、设计方案
在开始设计前,首先需要进行的是对于单片机的选择,我在这里Proteus仿真运行时采用的是正常的89C51单片机,然后在实物方面(由于有现成的开发板)用的是52RC单片机。
2.1、设计思路
下面是本次设计的设计思路:
(1)分析传送带产品计数设计方案中所需要满足的各种功能,选择适合的电子器件作为课程设计的基础。
(2)学习整理单片机相关学习内容(包括直流电机运行、外部中断计数、动态数码管显示、矩阵键盘运用等知识点),同时了解三极管和光敏电阻的基本知识,对于课程设计方案有一个最基本的知识框架。
(3)选用51系列单片机,编写代码以依次实现直流电机的正常运行、动态数码管各位数字的显示、矩阵键盘的按键检测、外部中断I/O口P3.2检测下降沿变化并计数等功能。
(4)根据选用光敏电阻的暗电阻和亮电阻阻值,结合三极管的特点,设计光电开关,用来接在外部中断I/O口P3.2。可以通过检测外部光照强度的大小,改变光敏电阻的阻值,以此来实现高低电平的转换。
(5)将上述功能通过逻辑规划整合到一起,实现4*4矩阵键盘中左侧3*4按键对动态数码管左侧四位预设终止值的数字输入、退位以及清零的功能,右侧四个按键实现控制直流电机正转、倒转、急停,以及对动态数码管右侧四位外部中断计数的清零。
(6)在电脑上通过仿真软件实现该设计的正常运行。在进行软件系统的设计和仿真中,程序在Keil中用单片机c语言编写,电路的搭建和仿真实现在Proteus软件中进行实现。
(7)在电脑的仿真软件中顺利运行后,进行实物的接线以及程序的烧录,调节中断函数中延迟时间来控制外部中断计数的间隔,使其达到通过一个产品,计数加一的效果。
2.2、设计版块
本次设计主要内容分为四个版块,如下:
这里我为大家分板块进行讲解。
1、数码管显示模块
数字显示 | LED显示部分 | 二进制显示 | 十六进制显示 |
0 | abcdef | 0011 1111 | 0x3f |
1 | bc | 0000 0110 | 0x06 |
2 | abdeg | 0101 1011 | 0x5b |
3 | abcdg | 0100 1111 | 0x4f |
4 | bcfg | 0110 0110 | 0x66 |
5 | acdfg | 0110 1101 | 0x6d |
6 | acdefg | 0111 1101 | 0x7d |
7 | abc | 0000 0111 | 0x07 |
8 | abcdefg | 0111 1111 | 0x7f |
9 | abcdfg | 0110 1111 | 0x6f |
采用的是共阴极八位数码管,通过74LS138解码器进行动态显示。静态数码管显示的原理如图表示,a—g七个部分分别控制每一位上七段LED的亮灭,dp是控制小数点部位LED的亮灭。可以对a-g每个部分进行高低电平的赋予,来实现数码管数字的显示。(共阴极数码管是高电平驱动,共阳极数码管是低电平驱动)
而动态数码管是静态数码管利用上了74LS138解码器,其中三个接口(LSA、LSB、LSC)连接到单片机三个I/O口上,通过高低电平,三组0和1的排列组合,共八(即二的三次方)种,来表示八位数码管显示第几位。
2、矩阵键盘控制模块
矩阵键盘本质是使用8个I/O口来进行16个按键的控制读取,可以减小I/O口的使用,用4条I/O线作为行线,4条I/O线作为列线组成的键盘。在行线和列线的每个交叉点上,设置一个按键。而这样的按键中按键的个数是4 X 4个。
这样的行列式键盘结构能够有效地提高单片机系统中I/O口的利用率。节约单片机的资源,其本质和独立按键类似,就是进行逐行扫描和逐列扫描,然后判断是第几行的第几列个按键,进而进行整体按键值的确定。
3、直流电机驱动模块
直流电机没有正负之分,在两端加上直流电就能工作。交换电极可以改变电机旋转的方向。
EN1 | IN1 | IN2 | 实现功能 |
0 | —— | —— | 停止 |
1 | 1 | 0 | 顺时针 |
1 | 0 | 1 | 逆时针 |
1 | 0 | 0 | 停止 |
1 | 1 | 1 | 刹停 |
基于电机驱动器L293D,EN1、IN1、IN2的三个端口分别接在管脚P3^5、P3^6和P3^7上面,我们可以根据三个串口的高低电平来控制直流电机(传送带)去实现停止、顺时针转动、逆时针转动、刹停等功能,实现内容如上述表格所示。
4、基于光敏电阻的光电开关模块
光敏电阻随光照强度增大,阻值会变小。
根据光敏电阻的这一个原理,我们再结合一个三极管或者晶体管(这里选用的是npn型三极管),就可以做成一个简单的光电开关。设计内容如下:
这就是一个简单的光电开关,实物采用的是型号为8050的三极管、型号为5516的光敏电阻、一个470欧姆的电阻、一个200千欧的滑动变阻器。(实物验证过,可以在添加一个小LED灯下,通过遮挡LED灯进入到外部中断,取消遮挡会回到主程序,滑动变阻器可以调节光敏电阻的灵敏度,正常情况下150千欧是较为合适的。其实在连接I/O口P3.2之前,R3右侧再添加一个电容接地更为严谨,目的是缓冲作用)
中断发生的过程就是当光敏电阻阻值改变时,基极电流发生改变,会引起集电极电流发生很大改变,I/O口便可以检测到下降沿变化,从而实现外部中断。
(注释:三极管原理:三极管主要用来控制电流的大小,以共发射极接法为例(信号从基极输入,从集电极输出,发射极接地),当基极电压UB有一个微小的变化时,基极电流IB也会随之有一小的变化,受基极电流IB的控制,集电极电流IC会有一个很大的变化,基极电流IB越大,集电极电流IC也越大,反之,基极电流越小,集电极电流也越小,即基极电流控制集电极电流的变化。但是集电极电流的变化比基极电流的变化大得多,这就是三极管的电流放大作用。)
三、proteus仿真图
注释:
按键AC表示预置终值的清零
按键BACK表示预置终值的退位
按键JSAC表示计数数值的清零
按键FORD表示直流电机正转
按键DC表示直流电机停止
按键REV表示直流电机倒转
keil编写的代码需要将它的hex格式在这个地方配置给仿真的51单片机。
四、proteus仿真代码部分
#include<reg51.h>
#define PKEY P1
#define PDIG P2
sbit LSA = P3 ^ 0;
sbit LSB = P3 ^ 1;
sbit LSC = P3 ^ 3;
sbit IN0 = P3 ^ 4;
sbit IN1 = P3 ^ 5;
sbit E = P3 ^ 6;
unsigned char code smgnum[]={ 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//?????0~9??
unsigned char code smgmum[]={ 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//?????0~9??
unsigned int gd;
unsigned int num=99;
unsigned int keyswitch, tuiwei, wei = 0, x, y;
unsigned int box[8]={0};
unsigned int i;
void delay(unsigned int x) //延迟函数
{
unsigned int i, j;
for (i = 0; i < x; i++)
for (j = 0; j < 120; j++) ;
}
void kaishi() //电机正转
{
IN0 = 1;
IN1 = 0;
delay(5);
}
void daofang() //电机倒转
{
IN1 = 1;
IN0 = 0;
delay(5);
}
void int0_init(void) //开启外部中断
{
EX0=1;
IT0=1;
EA=1;
}
void key_scan() //矩阵键盘检测函数
{
unsigned char temp0 = 0, temp1 = 0, temp = 0;
unsigned int a = 0;
PKEY = 0xf0;
if (PKEY != 0xf0)
{
delay(20);
if (PKEY != 0xf0)
{
temp0 = PKEY;
PKEY = 0x0f;
if (PKEY != 0x0f)
{
temp1 = PKEY;
}
temp = temp0 + temp1;
if (temp == 0xee) { num = 1; }
else if (temp == 0xed) { num = 2; }
else if (temp == 0xeb) { num = 3; }
else if (temp == 0xe7) { num = 12; }
else if (temp == 0xde) { num = 4; }
else if (temp == 0xdd) { num = 5; }
else if (temp == 0xdb) { num = 6; }
else if (temp == 0xd7) { num = 13; }
else if (temp == 0xbe) { num = 7; }
else if (temp == 0xbd) { num = 8; }
else if (temp == 0xbb) { num = 9; }
else if (temp == 0xb7) { num = 14; }
else if (temp == 0x7e) { num = 10; }
else if (temp == 0x7d) { num = 0; }
else if (temp == 0x7b) { num = 11; }
else if (temp == 0x77) { num = 15; }
if (num == 0 || num == 1 || num == 2 || num == 3 || num == 4 || num == 5 || num == 6 || num == 7 || num == 8 || num == 9)
keyswitch = 1;
if (num == 10)
tuiwei = 1;
while ((a < 50) && (PKEY != 0x0f))
{
delay(10);
a++;
}
}
}
}
void smg() //数码管显示函数
{
LSA = 1; LSB = 1; LSC = 1;
PDIG = smgnum[box[0]];
delay(1); PDIG = 0x00;
LSA = 0; LSB = 1; LSC = 1;
PDIG = smgnum[box[1]];
delay(1); PDIG = 0x00;
LSA = 1; LSB = 0; LSC = 1;
PDIG = smgnum[box[2]];
delay(1); PDIG = 0x00;
LSA = 0; LSB = 0; LSC = 1;
PDIG = smgnum[box[3]];
delay(1); PDIG = 0x00;
LSA = 1; LSB = 1; LSC = 0;
PDIG = smgnum[box[4]];
delay(1); PDIG = 0x00;
LSA = 0; LSB = 1; LSC = 0;
PDIG = smgnum[box[5]];
delay(1); PDIG = 0x00;
LSA = 1; LSB = 0; LSC = 0;
PDIG = smgnum[box[6]];
delay(1); PDIG = 0x00;
LSA = 0; LSB = 0; LSC = 0;
PDIG = smgnum[box[7]];
delay(1); PDIG = 0x00;
}
void main() //主函数
{
int0_init();
while (1)
{
smg();
key_scan();
if (num == 12) //按键控制直流电机正转
{
x = 1;
y = 0;
}
if (x == 1)
{
kaishi();
if ((box[7] == 0) && (box[6] == 0) && (box[5] == 0) && (box[4] == 0))
{
kaishi();
}
else
{
if ((box[3] == box[7]) && (box[2] == box[6]) && (box[1] == box[5]) && (box[0] == box[4]))
{
num = 13;
}
}
}
if (num == 14) //按键控制直流电机倒转
{
y = 1;
x = 0;
}
if (y == 1)
daofang();
if ((box[7] == 0) && (box[6] == 0) && (box[5] == 0) && (box[4] == 0))
{
daofang();
}
else
{
if ((box[3] == box[7]) && (box[2] == box[6]) && (box[1] == box[5]) && (box[0] == box[4]))
{
num = 13;
}
}
}
if (num == 13) //按键控制直流电机停止
{
E = 1;
IN0 = 1;
IN1 = 1;
x = 0;
y = 0;
num = 99;
}
if ((keyswitch == 1) && (wei < 4)) //预设数值输入
{
for (i = 7; i > 4; i--)
{ box[i] = box[i - 1]; }
box[4] = num;
keyswitch = 0;
wei++;
}
if (num == 11) //预设数值清零
{
for (i = 4; i < 8; i++)
{
box[i] = 0;
}
wei = 0;
}
if (num == 15) //计数数值清零
{
gd = 0;
num = 99;
}
if (tuiwei == 1) //预设数值退位
{
box[4] = box[5];
box[5] = box[6];
box[6] = box[7];
box[7] = 0;
if (wei > 0)
wei--;
tuiwei = 0;
}
box[0] = gd % 10;
box[1] = gd / 10 % 10;
box[2] = gd / 100 % 10;
box[3] = gd / 1000 % 10;
}
}
void int0() interrupt 0 //中断函数
{
gd++;
delay(200);
}
五、实物运行视频
链接:https://pan.baidu.com/s/1YT7GfhHj1hKg8rdouowqVg?pwd=4c9d
提取码:4c9d
--来自百度网盘超级会员V1的分享
六、实物代码部分
#include <REGX52.H>
#define PKEY P1 //宏定义单片机I/O口的P1为PKEY
#define uchar unsigned char //宏定义unsigned char为uchar
#define uint unsigned int //宏定义unsigned int为uint
sbit IN0 = P3 ^ 7;
sbit IN1 = P3 ^ 6;
sbit E = P3 ^ 5;
uchar NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
uint x,num,wei,keyswitch,i,shu,a,tuiwei,jishu;
uint box[4]={0};
uchar Counter,Compare,Speed;
void Delay(unsigned int xms)//延迟函数
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 23;
do
{
while (--j);
} while (--i);
}
}
void int0_init(void) //开启外部中断
{
EX0=1;
IT0=1;
EA=1;
}
void key_scan() //矩阵键盘函数
{
uchar temp0 = 0, temp1 = 0, temp = 0;
PKEY = 0xf0;
if (PKEY != 0xf0)
{
Delay(20);
if (PKEY != 0xf0)
{
temp0 = PKEY;
PKEY = 0x0f;
if (PKEY != 0x0f)
{
temp1 = PKEY;
}
temp = temp0 + temp1;
if (temp == 0xee) { num = 1; }
else if (temp == 0xed) { num = 2; }
else if (temp == 0xeb) { num = 3; }
else if (temp == 0xe7) { num = 12; }
else if (temp == 0xde) { num = 4; }
else if (temp == 0xdd) { num = 5; }
else if (temp == 0xdb) { num = 6; }
else if (temp == 0xd7) { num = 13; }
else if (temp == 0xbe) { num = 7; }
else if (temp == 0xbd) { num = 8; }
else if (temp == 0xbb) { num = 9; }
else if (temp == 0xb7) { num = 14; }
else if (temp == 0x7e) { num = 10; }
else if (temp == 0x7d) { num = 0; }
else if (temp == 0x7b) { num = 11; }
else if (temp == 0x77) { num = 15; }
if (num == 0 || num == 1 || num == 2 || num == 3 || num == 4 || num == 5 || num == 6 || num == 7 || num == 8 || num == 9)
{
keyswitch = 1;
}
if (num == 10)
{
tuiwei = 1;
num=99;
}
while ((a < 50) && (PKEY != 0x0f))
{
Delay(600);
a++;
}
}
}
}
void Nixie(unsigned char Location,Number)//数码管显示函数
{
switch(Location)
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
P0=NixieTable[Number];
Delay(1);
P0=0x00;
}
void Timer0_Init(void) //开启定时器中断
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x9C; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
void main()//主函数
{
E=0;
Timer0_Init();
while(1)
{
Nixie(1,box[0]);
Delay(1);
Nixie(2,box[1]);
Delay(1);
Nixie(3,box[2]);
Delay(1);
Nixie(4,box[3]);
Delay(1);
Nixie(5,x/1000%10);
Delay(1);
Nixie(6,x/100%10);
Delay(1);
Nixie(7,x/10%10);
Delay(1);
Nixie(8,x%10);
Delay(1);
key_scan();
if ((keyswitch == 1) && (wei < 4))
{
for (i = 0; i < 3; i++)
{ box[i] = box[i + 1]; }
box[3] = num;
keyswitch = 0;
wei++;
num=99;
}
if (tuiwei == 1)
{
box[3] = box[2];
box[2] = box[1];
box[1] = box[0];
box[0] = 0;
if (wei > 0)
wei--;
tuiwei = 0;
}
if (num == 11)
{
for (i = 0; i < 4; i++)
{
box[i] = 0;
}
wei = 0;
num=99;
}
jishu=1000*box[0]+100*box[1]+10*box[2]+box[3];
if(num==12)
{
E=1;
Speed=1;
num=99;
}
if(num==14)
{
E=1;
Speed=2;
num=99;
}
if(Speed==1){Compare=0;}
if(Speed==2){Compare=51;}
if(Speed==0){Compare=100;}
if (num==13)
{
E = 1;
IN0 = 1;
IN1 = 1;
Speed=0;
num = 99;
}
if(num==15)
{
x=0;
num=99;
}
if(x!=0 && jishu==x)
{
E = 1;
IN0 = 1;
IN1 = 1;
Speed=0;
num = 99;
}
int0_init();
}
}
void int0() interrupt 0 //外部中断函数
{
x++;
Delay(300);
}
void Timer0_Routine() interrupt 1 //定时器中断函数
{
TL1 = 0x9C;
TH1 = 0xFF;
Counter++;
Counter%=50;
if(Compare!=100)
{
if(Counter<Compare)
{
IN0=1;
IN1=0;
}
if(Counter>Compare)
{
IN0=0;
IN1=1;
}
}
}
需要提出来一些需要注意的地方,因为可能是电压的原因,传送带在直流电机控制的情况下,给转动信号的时候,不能马上转动起来,需要用手轻微拨动一下,才可以实现传送带的转动。
然后,实物中,我又比Proteus仿真多用了一个定时器中断,原本想通过定时器中断来精准控制电机正转倒转的时间分配,结果好像用处不大,因为传送带好像大部分都是有按钮控制转动,一个按钮控制停止,转动的时候朝向一个方向不停转动,几乎不会出现我想的正转多少时间再倒转多少时间一直持续这样工作。于是,我保留了定时器中断的格式,稍微修改,还是按照上面Proteus仿真的模式来的。这个代码部分可能乍一看,有点迷,但是到这里应该就会有所理解了。
还有一个需要注意的是,因为我购买的52RC单片机开发板默认八位数码管连接的是I/O口P0,所以代码里面没有添加进去,所以实物的整体连线是和Proteus仿真有一些区别的,要特别注意!!!!!!
如果有要制作实物的想法,需要将keil代码烧录进单片机里,不懂的话可以搜索相关教程,一步步来。
若有需求可以联系作者本人,看到后会回复。