写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!
本文写于:2025.03.14
51单片机学习——第17节: [7-1]定时器
前言
本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始51单片机的学习之路。
欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习51单片机了,就跟着B站上的江协科技开始学习了.
在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!
另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!
开发板说明
本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。不再另外购买视频中的普中开发板了。
原理图如下
视频中的都用这个开发板来实现,如果有资源就利用起来。
仔细看了看:开发板的晶振为:11.0592Mhz;12Mhz晶振是用来给CH340G芯片外置晶振;
下图是实物图
引用
51单片机入门教程-2020版 程序全程纯手打 从零开始入门
还参考了下图中的书籍:
手把手教你学51单片机(C语言版)
解答和科普
1、逻辑电路和逻辑运算
&&逻辑与;||逻辑或;!逻辑非
&按位与,|按位或,~按位取反,^按位异或
一、定时器的简介
定时器/计数器0有4种工作模式:模式0(13位定时器/计数器) ,模式1(16位定时器/计数器模式),模式2(8位自动重装模式),模式3(两个8位定时器/计数器)。定时器/计数器1除模式3外,其他工作模式与定时器/计数器0相同,T1在模式3时无效,停止计数。
①定时器/计数器控制寄存器TCON
TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源。
TF1:定时器/计数器溢出标志,T1被允许计数以后,从初值开始加1计数。当最高位产生溢出时,由硬件置"1"TF1,向CPU请求中断,一直保持到CPU响应中断时,才由硬件清零。(TF1也可由程序查询清0)。
==TR1:定时器T1的运行控制位。该位由软件置位和清零。==当GATE(TMOD.7)=0,TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。当GATE(TMOD.7)=1,TR1=1,且INT1输入高电平时,才允许T1计数。
IE1:外部中断1请求源(INT1/P3.3)标志IE1=1,外部中断向CPU请求中断,当CPU响应该中断时,由硬件清0.
IT1:外部中断触发方式控制位。 IT1=0时,外部中断1为低电平触发方式,当(-INT1/P3.3)输入低电平时,置为IE1,采用低电平触发方式,必须保持低电平有效,直到该中断被CPU响应,同时在该中断服务程序执行完之前,外部中断源必须被清除(P3.3要变高),否则将产生另一次中断。IT1=1时,则外部中断1为下降沿触发。
T0也是同理。
②定时器/计时器工作模式寄存器TMOD
定时和计数功能由特殊功能寄存器TMOD的控制位C/-T进行选择,2个定时/计时器有4种操作模式,通过TMOD的M1和M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同。
下面接着介绍工作模式:
时钟周期: 时钟周期T 是时序中最小的时间单位,具体计算的方法就是 1/ 时钟源频率,单片机开发板上用的晶振是 11.0592M ,那么对于我们这个单片机系统来说,时钟周期 =1/11059200 秒。
机器周期: 我们的单片机完成一个操作的最短时间。机器周期主要针对汇编语言而言,单片机系列,在其标准架构下一个机器周期是 12 个时钟周期,也就是 12/11 059200 秒。
SYSclk表示时钟频率,因为 1 个机器周期等于 12 个时钟周期,所以那个 d 就等于 12 。或者1个周期等于6个时钟周期,d就等于6,这个可以在烧录的时候选择。
==要想让定时器工作,就是自动加 1 ,从图 上看有两种方式,第一种方式是那个开关打就是 C/T = 0 的时候,一个机器周期 TL 就会加 1 一次,此时为定时器。==当开关打到下边的箭头,即 C/T =1 的时候, T0 引脚即 P3.4 引脚来一个脉冲, TL 就加 1 一次,这也就是计数器。
下边 GATE 右边的那个门是一个非门电路,再右侧是一个或门,再往右是一个与门电路,图上可以看出来,下边部分电路是控制了上边部分,那我们先来看下边是如何控制的,
我们以定时器 0 为例。
1、 TR0 和下边或门电路的结果要进行与运算, TR0 如果是 0 的话, 与运算完了肯定是 0所以如果要让定时器工作,那么 TR0 就必须置 1 。
2、这里的与门结果要想得到 1 ,那么前面的或门出来的结果必须也得是 1 才行。在 GATE位为 1 的情况下,经过一个非门变成 0 ,或门电路结果要想是 1 的话,那 INT0 即 P3.2 引脚必须是 1 的情况下,这个时候定时器才会工作, 而 INT0 引脚是 0 的情况下,定时器不工作,这就是 GATE 位的作用。
当 GATE 位为 0 的时候,经过一个非门会变成 1 ,那么不管 INT0 引脚是什么电平,经过或门电路后都肯定是 1 ,定时器就会工作。
模式1 ,是 THn 和 TLn 组成了一个 16 位的定时器,计数范围是 0 65535 ,溢出后,只要不对 THn 和 TLn 重新赋值,则从 0 开始计数。
模式 2 ,是 8 位自动重装载模式,只有 TLn做加 1 计数,计数范围 0 255 THn 的值并不发生变化,而是保持原值, TLn 溢出后, TFn就直接置 1 了,并且 THn 原先的值直接赋给 TLn ,然后 TLn 从新赋值的这个数字开始计数。这个功能可以用来产生串口的通信波特率,我们讲串口的时候要用到。
写程序之前,我们要先来学会计算如何用定时器定时时间。我们的晶振是11.0592M ,时钟周期就是 1/11059200 ,机器周期是 12/11059200 ,假如要定时 20ms ,就是 0.02 秒,要经过x 个机器周期得到 0.02 秒,我们来算一下 x*12/11059200=0.02 ,得到 x= 18432 。 16 位定时器的溢出值是 65536 (因 65535 再加 1 才是溢出),于是我们就可以这样操作,先给 TH0 和 T L0一个初始值,让它们经过18432 个机器周期后刚好达到 65536 ,也就是溢出,溢出后可以通过检测 TF0 的值得知,就刚好是 0.02 秒。那么初值 y = 65536 18432 = 47104 ,转成 16 进制就是 0xB800 ,也就是 TH0 = 0xB8 TL0 = 0x00。
这样
0.02 秒的定时我们就做出来了,细心的同学会发现,如果初值直接给一个 0x0000一直到 65536 溢出,定时器定时值最大也就是 71ms 左右,那么我们想定时更长时间怎么办呢?用你小学学过的逻辑,倍数关系就可以解决此问题。
单片机通过配置寄存器来控制内部线路的连接,主要是配置寄存器,来完成整个定时器的过程。
实验现象:
二、用数码管显示15秒
1.代码
1.1main.c
#include <REGX52.H>
#include <INTRINS.H>
sbit wei= P2^1;
sbit duan=P2^0;
sbit dianzheng=P2^2;
unsigned char NixieDuanTable[]= {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
unsigned char NixieWeiTable[]={0xFF,0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};
void Timer0_Init()
{
TMOD &= 0xF0; //0000 0001 T0设置为模式1
TL0 = 0x00; //设置定时初值
TH0 = 0xB8; //设置定时初值
TF0=0;
TR0=1;
}
void Nixie(unsigned char WeiNum, DuanNum)
{
wei=1;
P0=NixieWeiTable[WeiNum];
wei=0;
duan=1;
P0=NixieDuanTable[DuanNum];
duan=0;
}
void Delayxms(unsigned int xms) //@11.0592MHz
{
while(xms--)
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
unsigned char count=0;
unsigned char sec=0;
Timer0_Init();
Nixie(1,sec);
while(1)
{
if(TF0==1)
{
TF0=0;
TL0 = 0x00; //设置定时初值
TH0 = 0xB8; //设置定时初值
count++;
}
if(count>=50)
{
count=0;
Nixie(1,sec);
sec++;
}
if(sec>=16)
{
sec=0;
}
}
}
实验现象:
数码管1s定时器完成
2.代码
#include <REGX52.H>
#include <INTRINS.H>
sbit wei= P2^1;
sbit duan=P2^0;
sbit dianzheng=P2^2;
unsigned char NixieDuanTable[]= {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
unsigned char NixieWeiTable[]={0xFF,0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};
void Timer0_Init()
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void Nixie(unsigned char WeiNum, DuanNum)
{
wei=1;
P0=NixieWeiTable[WeiNum];
wei=0;
duan=1;
P0=NixieDuanTable[DuanNum];
duan=0;
}
void Delayxms(unsigned int xms) //@11.0592MHz
{
while(xms--)
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
unsigned char count=0;
unsigned char sec=0;
Timer0_Init();
Nixie(1,sec);
while(1)
{
Nixie(1,sec);
if(TF0==1)
{
TF0=0;
TL0 = 0x00; //设置定时初值
TH0 = 0xB8; //设置定时初值
count++;
}
if(count>=50)
{
count=0;
sec++;
}
if(sec>=16)
{
sec=0;
Nixie(1,sec);
}
}
}
实验现象:
数码管定时1s加一正确
二、用数码管显示60秒(当然可以60秒倒计时)
#include <REGX52.H>
#include "Delay.h"
#include "Nixie.h"
#include "Init.h"
void Timer0_Init()
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void main()
{
unsigned char count=0;
unsigned char sec=0,SWEI=0;
DianZhengGuan();
Timer0_Init();
Nixie(1,sec);
while(1)
{
Nixie(1,SWEI);
Nixie(2,sec);
if(TF0==1)
{
TF0=0;
TL0 = 0x00; //设置定时初值
TH0 = 0xB8; //设置定时初值
count++;
}
if(count>=50)
{
count=0;
// Nixie(2,SWEI);
// Nixie(1,sec);
sec++;
}
if(sec>=10)
{
sec=0;
SWEI++;
// Nixie(1,sec);
// Nixie(2,SWEI);
}
if(SWEI>=6)
{
SWEI=0;
sec=0;
// Nixie(1,sec);
// Nixie(2,SWEI);
}
}
}
我在写代码是犯了一个很大的错误,那就是可以看到我想让他们在这里直接显示,好像和LCD不一样的地方,也就是为什么说数码管是需要不断的扫描,只能放在while下面(应该吧),反正就是得不断的扫描,我刚开始设置的是只有一个数码管亮,我把数码管放在while里,就完成了动态扫描。
实验现象:
数码管显示60s
问题
注意是不断的扫描呀!;
1、不知道为什么我的时间好像不是1s很奇怪呀。
有关系,我改成TMOD=0x01,就是不行的。
2、我的数码管貌似没有做到真真的消影呀,我看着还是有其他数码管显示(虽然不是很亮)。
总结
本节课主要学了了定时器的有关知识,主要是如何配置定时器寄存器,还浅显的学习了中断系统。还有可寻址的寄存器和不可寻址的寄存器。code(Flash)。