当前位置: 首页> 文旅> 酒店 > erp系统教程_中山小程序开发_网站搭建步骤_网络营销平台名词解释

erp系统教程_中山小程序开发_网站搭建步骤_网络营销平台名词解释

时间:2025/8/23 8:12:25来源:https://blog.csdn.net/m0_58749949/article/details/145037837 浏览次数:0次
erp系统教程_中山小程序开发_网站搭建步骤_网络营销平台名词解释

基于STC89C52RC芯片

使用说明

定时器的基本工作原理,是使用一个n位的脉冲计数器,对时钟信号的脉冲进行计数,每个脉冲加1,当脉冲计数器达到最大值(2n)时,也就是溢出时,触发定时器中断。

定时的时间会受到以下几个因素的影响。

  • 脉冲计数器的位数
  • 脉冲计数的初始值
  • 时钟信号的频率

启用定时器中断

STC89C52系列共有3个定时器,每个定时器都有其对应的中断。定时器0的中断允许控制位位于IE寄存器

在这里插入图片描述

开启定时器0的中断需要做出如下配置。

// 中断总开关
EA = 1;
// 定时器0中断开关
ET0 = 1;

定时器工作方式

STC89C52系列的定时器有两种工作方式

  • 定时:用于产生精确的时间延迟
  • 计数:用于统计外部脉冲信号的个数

两种工作方式的本质是相同的,都是使用脉冲计数器对脉冲进行计数

  • 定时方式下,脉冲信号为系统时钟信号
  • 计数方式下,脉冲信号来自单片机外部引脚

每个定时器都有一个控制位,用于设置计数/定时方式。定时器0的控制位是TMOD(Timer Mode,定时器模式)寄存器中的C/T(Counter/Timer)位。

在这里插入图片描述

该位的作用可参考下图(定时器结构图)

在这里插入图片描述

因此,如需使用定时器方式,应将C/T控制位设置为0,注意 TMOD寄存器不可位寻址。

定时器工作模式

  • 模式0——13位定时/计数器
    该模式下的脉冲计数器共有13位,最大计数为8192。如下图所示,TL0和TH0为两个8位寄存器,用于存储脉冲计数器数值,该模式下TL0只用到了低5位。

在这里插入图片描述

  • 模式1——16位定时/计数器
    该模式的脉冲计数器共有16位,最大计数为65536。如下图所示,TL0的8位和TH0的8位都用到了。

在这里插入图片描述

  • 模式2——8位自动重装载
    前两种模式,一次定时完毕后,如需再次定时,需要开发者重新为脉冲计数器设定初始值。而该模式可以在脉冲计数器溢出时,自动重新设置初始值,很适合用于执行周期性任务。
    该模式下,只用TL0寄存器用于存储脉冲计算器数值,TH0则用于存储脉冲计数器的初始值,每次TL0溢出之后, 都会自动将TH0的值重新装入TL0。

在这里插入图片描述

  • 模式3——双8位定时/计数器
    该模式下,TL0和TH0分别用作一个8位脉冲计数器,如果需要使用两个8位定时器可使用该模式。

在这里插入图片描述

这四种工作模式需要两个控制位进行设置,两个控制位位于TMOD(Timer Mode,定时器模式)寄存器

在这里插入图片描述

在这里插入图片描述

设置脉冲计数器初始值

由于51单片机定时器是在脉冲计数器溢出时触发中断,因此定时的长短需要通过脉冲计数器的初始值控制。因此在使用定时器时,需要先根据期望的定时长短计算出脉冲计数器的初始值。

下面以定工作模式1(16位)为例,介绍初始值的计算过程。

1)明确每个计数脉冲的时间
根据定时器的结构图可以看出,传递给脉冲计数器的信号是系统时钟信号经过分频后得到,且分频可选两种,分别是12分频和6分频,默认是12分频。

在这里插入图片描述

当前系统的时钟频率为11.0592MHz,也就是11059200Hz,所以计数脉冲的频率是11059200/12 Hz,因此一个计数脉冲的时间是12/11059200 s,大约是1.08us。

2)计算所需脉冲个数
明确每个计数脉冲的时长之后,在根据期望定时时长便能计算出所需脉冲个数。假如现在需要定时1ms,那么1ms需要的脉冲个数应为0.001/(12/11059200)。

3)计算脉冲计数器初始值
假如现在需要定时1ms,那么1ms需要的脉冲个数应为0.001/(12/11059200),因此定时器的初始值应为65536-0.001/(12/11059200),大约等于64614。
计算完毕后,需要将该值赋予TL0(低8位)和TH0(高8位),如下。

TL0 = 64614;
TH0 = 64614 >> 8;

启动定时器

两种方式
-单片机内部的寄存器控制
单片机的外部引脚控制

在这里插入图片描述

  • 当GATE=0时,外部引脚(INT0,P3.2)无效,此时只能由内部寄存器TR0控制,当TR0=1时,脉冲计数器开始计数,TR0=0时,停止计数。
  • 当GATE=1时,外部引脚(INT0,P3.2)生效,此时只有当内部寄存器TR0和外部引脚INT0都为1时,脉冲计数器才开始计数,否则停止计数。

定时器0的GATE控制位位于TMODE寄存器

在这里插入图片描述

定时器0的TR0控制位,位于TCON寄存器

在这里插入图片描述

实现思路

1)启用定时器0中断

// 中断总开关
EA = 1;
// 定时器0中断开关
ET0 = 1;

2)选择定时器0工作模式
首先需要明确定时/计数的工作方式,其次还需选择脉冲计数器的工作模式。此处选择计时+模式1(16位),具体配置如下。

在这里插入图片描述

另外由于TMOD寄存器不可位寻址,所以可在设置工作模式的同时,将 GATE位也一并设置好,当前案例不需要外部引脚控制定时器,因此将GATE设置为0即可。
TMOD寄存器低四位的值应为0001,而高四位的值应保持原值

// GATE=0;C/T=0;M1=0,M0=1
TMOD &= 0xF0;
TMOD |= 0x01;

3)设置脉冲计数器的初始值
当前需求是令LED1每秒钟闪烁一次,具体来说就是每隔0.5s改变一下LED1的状态,显然这是一个周期性任务。对于该任务,我们可以先考虑为定时器定时0.5s,然后在定时器中断触发后,再次定时0.5s,这样就能实现周期性任务了。
但是需要注意,0.5s所需的脉冲个数为0.5/(12/11059200)= 460800个,显然已经超出了16位置脉冲计数器的最大值(65536),也就是说定时器不支持0.5s的长时间定时。
针对这种情况,就需要令脉冲计数器溢出多次来达到期望的定时时长,具体来讲就是设定一个较短的定时,比如1ms,中断之后,再次定时1ms,直到达到期望的定时时长之后,再去执行具体的任务。
综上所述,对于当前需求,我们就可以将定时时长设置为1ms,每次中断之后,就再定时1ms,除此之外,我们还需要对中断的次数进行统计,每中断500次,就改变一下LED1的状态,这样就能够实现0.5s的周期性任务了。
所以脉冲计数器的初始值应该设置为65536-0.001/(12/11059200)= 64614,具体如下。

TL0 = 64614;
TH0 = 64614 >> 8;

4)启动定时器
由于GATE已经设置为0,因此只需将TR0设置为1,即可启动定时器。

// 启动定时器
TR0 = 1;

5)定义中断服务程序
按照前文的描述,中断服务程序需要完成如下任务。
(1)重新装载脉冲计数器
(2)统计脉冲次数,每500次改变一次LED1的状态
具体代码如下。

void Timer0_Hander() interrupt 1
{//定义静态局部变量static unsigned int count = 0;//重新状态脉冲计数器TL0 = 64614;TH0 = 64614 >> 8;//统计中断次数if (count++ >= 500) {LED1  = ~LED1;count = 0;}
}

实现代码

Com_Util.h

#ifndef _UTIL_H_
#define _UTIL_H_
#include <INTRINS.H>#define FOSC 11059200 // 晶振频率
#define NT   12       // 单片机的工作周期为12T// 8bit无符号数
typedef unsigned char u8;
// 16bit无符号数
typedef unsigned int u16;
// 32bit无符号数
typedef unsigned long u32;/*** @brief 延时一定时长** @param count 延时时长,单位1ms*/
void Com_Util_Delay1ms(u16 count);#endif

Com_Util.c

#include "Com_Util.h"void Com_Util_Delay1ms(u16 count) //@11.0592MHz
{u8 i, j;while (count > 0) {count--;_nop_();i = 2;j = 199;do {while (--j);} while (--i);}
}

Dri_Timer0.h

#ifndef __DRI_TIMER0_H__
#define __DRI_TIMER0_H__
#include <STC89C5xRC.H>
#include "Com_Util.h"typedef void (*Timer0_Callback)(void);#define MAX_CALLBACK_COUNT 4/*** @brief 定时器初始化**/
void Dri_Timer0_Init();/*** @brief 提供注册入口,用这个函数注册完成的函数,会以1000Hz的频率被调用** @return 成功返回1,失败返回0**/
bit Dri_Timer0_RegisterCallback(Timer0_Callback);/*** @brief 反注册回调函数,反注册的函数不会再被周期调用** @return bit 反注册的结果,成功位1,失败为0*/
bit Dri_Timer0_DeregisterCallback(Timer0_Callback);
#endif

Dri_Timer0.c

#include "Dri_Timer0.h"
#include <STDIO.H>#define T1MS (65536 - FOSC / NT / 1000)static Timer0_Callback s_timer0_callbacks[MAX_CALLBACK_COUNT];void Dri_Timer0_Init()
{u8 i;// 总中断开关EA = 1;// 定时器中断开关ET0 = 1;// 设置定时器0的工作模式:16位定时器TMOD &= 0xF0;TMOD |= 0x01;// 设置定时器的初始值TL0 = T1MS;TH0 = T1MS >> 8;// 定时器0的开关TR0 = 1;for (i = 0; i < MAX_CALLBACK_COUNT; i++){s_timer0_callbacks[i] = NULL;}
}bit Dri_Timer0_RegisterCallback(Timer0_Callback callback)
{// 判断这个函数有没有被注册过u8 i;for (i = 0; i < MAX_CALLBACK_COUNT; i++){if (s_timer0_callbacks[i] == callback){// 如果该函数被注册过,直接返回return 1;}}// 注册该函数for (i = 0; i < MAX_CALLBACK_COUNT; i++){if (s_timer0_callbacks[i] == NULL){s_timer0_callbacks[i] = callback;return 1;}}return 0;
}bit Dri_Timer0_DeregisterCallback(Timer0_Callback callback)
{u8 i;for (i = 0; i < MAX_CALLBACK_COUNT; i++){if (s_timer0_callbacks[i] == callback){s_timer0_callbacks[i] = NULL;return 1;}}return 0;
}/*** @brief 1ms调用一次这个函数**/
void Dri_Timer0_Func() interrupt 1
{u8 i;// 定义下次进入时钟中断的时间TL0 = T1MS;TH0 = T1MS >> 8;// 调用所有的回调函数for (i = 0; i < MAX_CALLBACK_COUNT; i++){if (s_timer0_callbacks[i]){s_timer0_callbacks[i]();}}
}

main.c

#include <STC89C5xRC.H>
#include "Dri_Timer0.h"
#define LED    P20
static u16 s_counter = 0;
void ToggleLED() {s_counter++;// 每500ms切换LED亮灭if (s_counter >= 500) {s_counter = 0;LED = ~LED;}
}
void main() {Dri_Timer0_Init();// 在定时器回中注册回调函数ToogleLED// 这样ToogleLED这个函数会每ms被调用一次Dri_Timer0_RegisterCallback(ToggleLED);while (1);
}
关键字:erp系统教程_中山小程序开发_网站搭建步骤_网络营销平台名词解释

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: