时钟实现原理:
目录
2.1 74HC138译码器
3.数码管点亮原理
4.计数与定时器
1.硬件环境
普中科技51单片机开发板STC89C52
2.相关原理图解析
2.1 74HC138译码器的作用
74HC138译码器是通过P2^2、P2^3、P2^4三个控制器数码管即等同于三个二进制,通过这三个控制器来实现自己想要点亮哪个数码管位置进而达到理想效果。我们想要点亮第一个的话,通过赋值数值给Locartion,即Display_Nixie(1)就可以实现第一个数码管亮起。(低电平导通点亮)
void Display_Nixie(unsigned int Location) //Location为赋值点亮位置
{
switch(Location) //位码输出
{
case 1:P2_4=0;P2_3=0;P2_2=0;break; //点亮D0的位置
case 2:P2_4=0;P2_3=0;P2_2=1;break;
case 3:P2_4=0;P2_3=1;P2_2=0;break;
case 4:P2_4=0;P2_3=1;P2_2=1;break;
case 5:P2_4=1;P2_3=0;P2_2=0;break;
case 6:P2_4=1;P2_3=0;P2_2=1;break;
case 7:P2_4=1;P2_3=1;P2_2=0;break;
case 8:P2_4=1;P2_3=1;P2_2=1;break; //点亮D7的位置
}
数码管发光二极管是由P0^0~P0^7共八个端口控制,通过真值表来实现位码,通过所给出的三位数据计算出16进制数为一下:
//Nixie_Code为数字0-9的段码
unsigned char Nixie_Code[]={0x3f,0x06,0x5B,0x4f,0x66,0x6D,0x7D,0x07,0x7f,0x6f};
//数码管亮起的位置从左往右为D0~D7的位码
unsigned char Nixie_Bit[]={0xe3,0xe7,0xeb,0xef,0xf3,0xf7,0xfb,0xff};
当想要全部点亮时通过一下代码可得:
unsigned int i;
for(i = 0;i<8;i++)
{
P0 = Nixie_code[i]; //先将段码通过循环依次赋值读入P0
P2 = Nixie_Bit[i]; //由于该板子数码管与P2端口(LED)连在一起的,所以要通过P2来点亮数码管位端
Delay_ms(1); //人体看见物体稳定时间是在15ms左右,所以通过延迟 1Ms X 8次
P0 = 0x00; //每次写入后都要消影,不然人眼所见数字会模糊不清
}
数码管段码的计算原理:
由于该板子为共阴极数码管,所以要给高电平才能亮起.八位二进制x g f e d c b a顺序赋值,当想要亮起数字零时,给它0011 1111八进制转换为16进制即为0X3F。它的最高位为是否亮起小数点,如果想要亮起就将0011 1111的最高位换成1八进制为1011 1111,而十六进制为BF,也可以使用这个方法。
P0 = 0x3F|0x80;
正式代码:
#include <reg52.h>
#include <intrins.h>
unsigned char SEG_Code[]={0x3f,0x06,0x5B,0x4f,0x66,0x6D,0x7D,0x07,0x7f,0x6f,0x00};
unsigned char SEG_Bit[]={0xe3,0xe7,0xeb,0xef,0xf3,0xf7,0xfb,0xff};
void Delay_ms(unsigned int ms)
{
while(ms--)
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
while(1)
{
unsigned int i;
for(i = 0;i<8;i++)
{
P0 = Nixie_code[i]; //先将段码通过循环依次赋值读入P0
P2 = Nixie_Bit[i]; //由于该板子数码管与P2端口(LED)连在一起的,所以需要通过P2来点亮数码管位端
Delay_ms(1); //人体看见物体稳定时间是在15ms左右,所以通过延迟 1Ms X 8次
P0 = 0x00; //每次写入后都要消影,不然人眼所见数字会模糊不清
}
}
}
4.计数与定时器
计数器分为加法计数器和减法计数器,前者每来一一个计数脉冲,计数值加1;后者每来一个计数料计数器分类冲,计数值减1。
计数器的位数确定了计数器的最大计数个数M和计数范围,n位计数器的最大计数个数M=2,计计数器位数范围是0-2-1。例如8位计数器的最大计数个数M=256,计数范围是0-2555。作为计数器使用时,计数时钟源来自外部信号引脚,记录该外部信号的脉冲个数:作为定时器使计数/定时功能用时, 计数时钟源来自内部时钟信号。 对设定好的内部时钟脉冲个数进行计数所需要的时间就是定时时间。
初值计算:
假定计数器为8位加法计数器,计数脉冲来自内部时钟信号,F(技术) = 1MHz,若要定时250us,计算技术初值:
1.计算计数周期:T(计数周期) = 1/f(计数) = 1/(1MHz) = 1us;
2.个数:Count(计数) = T(定时时间)/T(计数周期) = 250/1 = 250;
3.初值计算:8位加法计数器最大计数个数:M = 256;
4.加法计数器初值计算:X(初值) = M-Count(计数) = 256-250 = 6;
寄存器(TMOD):
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
-> | T1 | <- | -> | T0 | <- |
工作方式(TMOD):
M1 M0 | 工作方式 | 功能介绍 |
0 0 | 方式0 | 13位计数器 |
0 1 | 方式1 | 16位计数器 |
1 0 | 方式2 | 初值自动重载8位计数器 |
1 1 | 方式3 | T0:分成两个8位计数器 T1:停止计数器 |
控制寄存器TCON各位的含义
会有一个初始化服务函数来给中断器服务
void Time0_Init() //50ms初值
{
TMOD=0X01;
TH0=(65536-50000)/256;//高八位 计算初值
TL0=(65536-50000)%256;//低八位
EA=1; //打开外部总中断
ET0=1; //打开定时器中断
TR0=1; //打开计数器
}
通过再写一个中断函数
//定义它们的初值均为0
unsigned char Count=0,Second=0,Minute=0,Hour=0;
void Timer_Isp() interrupt 1 //用到中断函数1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
Count++;
if(Count == 20)
{
Count = 0;
Second++;
if(Second == 60)
{
Second =0;
Minute++;
if(Minute == 60)
{
Minute =0;
Hour++;
if(Hour == 24)Hour =0;
}
}
}
}
接下来是通过上述内容来实现数码管点亮相应数字:
1.首先是将所得得数据进行数位分割:
unsigned char Ni_Bit[8] = {0};//定义一个可存八位数得数组
Ni_Bit[0] = Second % 10; //获得秒的个位数
Ni_Bit[1] = (Second/10)%10; //获得秒的十位数
//以下同理
Ni_Bit[3] = Minute%10 ;
Ni_Bit[4] = (Minute/10)%10 ;
Ni_Bit[6] = Hour %10 ;
Ni_Bit[7] = (Hour/10)%10 ;
2.其次将上述数据依次赋值给P0端口,再点亮相应的P2位置:
unsigned int i;
for(i=0;i<8;i++)
{
if(i ==2 || i==5) P0 = 0x40; //该为一条横杠的段码用来区分秒、分、时
else P0 = SEG_Code[Ni_Bit[i]];
P2 = SEG_Bit[i];
Delay_ms(1);
P0 = 0x00;
}
心得:
1.采用定时器T0的方式1的定时中断方式时,需要先设置TOMD寄存器,定时器T0工作在方式1,M1,M0=01,C/T=0为定时器工作模式,GATE=0.所以TMOD初始化值为00x01.
2.计算定时器T0的计数初值
计算过程:
50ms=50000us
50000=(2^16-计数初值)*12/11.0592
TL0 = 0x00;
TH0 = 0x4C;
3.毫秒与秒的转换,进制1000
50ms循环20次为定时1s
4.定时器模式是对单片机系统时钟信号经片内12分频后的内部脉冲信号(脉冲信号周期=机器周期)计数。由于系统时钟频率是定值,所以可根据计数值计算出准确的定时时间。
正式代码如下:
#include <reg52.h>
#include <intrins.h>
//数码管段码
unsigned char Nixie_Code[]={0x3f,0x06,0x5B,0x4f,0x66,0x6D,0x7D,0x07,0x7f,0x6f,0x00};
//数码管段码
unsigned char Nixie_Bit[]={0xe3,0xe7,0xeb,0xef,0xf3,0xf7,0xfb,0xff};
//用于数据进行数位分割
unsigned char Ni_Bit[8] = {0};
//给秒分时初始化为0
unsigned char Count=0,Second=0,Minute=0,Hour=0;
void Display();
void Timer0Init();
void Delay_ms(unsigned int ms);
void main()
{
Timer0Init();
Display();
while(1)
{
Display();
}
}
void Timer0Init() //初始化函数
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x4C; //设置定时初值
EA = 1;
ET0 = 1;
TR0 = 1; //定时器0开始计时
}
void Timer_Isp() interrupt 1
{
TL0 = 0x00; //设置定时初值
TH0 = 0x4C; //设置定时初值
Count++;
if(Count == 20)
{
Count = 0;
Second++;
if(Second == 60)
{
Second =0;
Minute++;
if(Minute == 60)
{
Minute =0;
Hour++;
if(Hour == 24)Hour =0;
}
}
}
}
void Display() //点亮显示时钟
{
unsigned int i;
Ni_Bit[0] = Second % 10;
Ni_Bit[1] = (Second/10)%10;
Ni_Bit[3] = Minute%10 ;
Ni_Bit[4] = (Minute/10)%10 ;
Ni_Bit[6] = Hour %10 ;
Ni_Bit[7] = (Hour/10)%10 ;
for(i=0;i<8;i++)
{
if(i ==2 || i==5) P0 = 0x40;
else P0 = SEG_Code[Ni_Bit[i]];
P2 = SEG_Bit[i];
Delay_ms(1);
P0 = 0x00;
}
}
void Delay_ms(unsigned int ms) //延迟函数
{
while(ms--)
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
效果展示: