【51单片机系列】C51中的中断系统扩展实验

本文是关于51单片机中断系统的扩展实验。

一、 扩展实验一:使用外部中断0控制蜂鸣器,外部中断1控制直流电机

外部中断扩展实验一实现的功能:使用外部中断0控制蜂鸣器发声/不发声,外部中断1控制直流电机转动/停止。

由蜂鸣器的内容可以知道,蜂鸣器分为有源蜂鸣器和无源蜂鸣器;蜂鸣器有两个管脚,要使蜂鸣器发声,需要有电流通过蜂鸣器,即管脚一端接正极,管脚另一端接负极。有源蜂鸣器只需给一定的电压即可发声,无源蜂鸣器需要一定频率的脉冲才可发声。这里设计了两种蜂鸣器,都由外部中断0控制。

直流电机的驱动方式与蜂鸣器类似。

proteus中硬件设计如下,为显示蜂鸣器的发声,这里使用了一个LED显示发声与否。蜂鸣器的一端连接到电源,另一端经过ULN2003芯片连接P1.5口,当P1.5=0时蜂鸣器发声;直流电机的一端连接到电源,另一端经过ULN2003连接到P1.0口,当P1.0=0时电机转动。为体现中断,使用独立按键模块连接到P3.2和P3.3口,当按键按下,蜂鸣器发声或电机转动。

proteus设计外部中断控制蜂鸣器和直流电机

软件设计如下:

/*
    实现功能:外部中断0控制蜂鸣器发声,外部中断1控制直流电机转动
        - 与外部中断0和外部中断1有关的有两个寄存器IE和TCON,
        - IE是中断允许控制寄存器,TCON是中断请求标志寄存器。
        - IE中包括了
            - 总中断允许位(EA)
            - 外部中断0/1允许位(EX0/EX1)
            - 定时器0/1允许位(ET0/ET1)
            - 串口中断允许位(ES);
        - TCON中的低四位是外部中断允许和触发方式控制位,包括了
            - IT0/IT1是外部中断0/1触发方式控制位,0表示低电平触发,1表示下降沿触发;
            - IE0/IE1是外部中断0/1请求标志位
    [2023-12-19] zoya
*/

#include "reg52.h"
#include "typedef.h"
#include "Delay.h"

sbit BEEP = P1^5;
sbit MOTOR = P1^0;
sbit CTR_INT0 = P3^2;
sbit CTR_INT1 = P3^3;

/*************************************************************************
* 函数名:      IntInit
* 函数功能: 外部中断0/1初始化,设置中断触发方式为边沿触发
* 输入:           void
* 输出:           void
**************************************************************************/
void IntInit()
{
    // 1. 设置中断触发方式
    IT0=1;
    IT1=1;
    // 2. 打开外部中断0/1
    EX0=1;
    EX1=1;
    // 3. 打开总中断
    EA=1;
}


void main()
{
    MOTOR=0;
    BEEP=0;
    IntInit(); 
    while(1);
}

/*************************************************************************
* 函数名:      Int0
* 函数功能: 外部中断0中断服务函数,
*                       控制蜂鸣器发声
* 输入:           void
* 输出:           void
**************************************************************************/
void Int0() interrupt 0
{
    delayms(10);  // 按键延时消抖
    if(0 == CTR_INT0){
        BEEP = ~BEEP;
    }
}

/*************************************************************************
* 函数名:      Int1
* 函数功能: 外部中断1中断服务函数,
*                       控制直流电机转动
* 输入:           void
* 输出:           void
**************************************************************************/
void Int1() interrupt 2
{
    delayms(10);  // 按键延时消抖
    if(0 == CTR_INT1)
    {
        MOTOR=~MOTOR;
    }
}

仿真结果:

外部中断控制蜂鸣器和直流电机

二、扩展实验二:修改定时器初值,设定3秒钟的定时时间让LED模块闪烁

如何计算定时器初值?

以使用12MHz的晶振频率计算。如果使用的是12MHz晶振,单片机内部的时钟频率为12分频即12/12MHz=1MHz;那么对应的机器周期为1/1MHz=1us。即使用12MHz晶振的机器周期为1us。

如果要定时1ms,需要计数1ms/1us=1000个,定时器使用方式1工作,那么初值为2^{16}-1000 = 64536 = 0xFC18。即初值THx=0xfc,TLx=0x18。

如果要定时1s,可以通过初值设置定时1ms,当定时结束重新赋初值,并设定一个全局变量累计定时1ms的次数,当该全局变量累计1000次时表示定时1s。

如果要设定3s时间,可以通过初值设定定时3ms,其它同定时1ms。定时3ms需要计数3ms/1us=3000,定时器使用方式1工作,初值为2^{16} - 3000 = 62536 = 0xF448,即初值THx=0xF4,TLx=0x48。

该实验在前面使用示例的基础上更改计数初值即可实现定时3s实现LED模块的闪烁。proteus中设计LED模块如下,定时器模块在单片机内部。

proteus设计定时器控制LED模块闪烁

软件设计如下:

/*
    实现功能:定时器0定时3s实现LED模块亮灭
        - 与定时/计数器工作有关的寄存器有IE、TCON、TMOD、THx、TLx
        - IE是中断允许控制寄存器,TCON是中断请求标志寄存器,TMOD是定时/计数器工作方式寄存器
        - THx和TLx是计数初值赋值寄存器。
        
        - IE中包括了
            - 总中断允许位(EA)
            - 外部中断0/1允许位(EX0/EX1)
            - 定时器0/1允许位(ET0/ET1)
            - 串口中断允许位(ES);
        
        - TCON中的高四位用于控制定时/计数器的启动和中断申请,包括TR0/1、TF0/1
            - TR0/TR1是T0/T1运行控制位,TR0=1时开始工作,TR0=0时停止工作,TR1与TR0类似;
            - TF0/TF1是T0/T1溢出中断请求标志位,溢出时由硬件自动置位,CPU响应中断后由硬件自动清0
                可随时查询该位状态,也可软件置1或清0.
                
        - TMOD高四位控制T1,低四位控制T0,高四位和低四位分别为有GATE、C/T、M1M0
            - GATE是门控位,
                - GATE=0表示不受外部中断信号影响,仅TR0/TR1控制定时/计数器工作,
                - GATE=1表示受外部中断信号影响,即TR0/TR1+INT0控制定时/计数器工作
            - C/T是定时/计数器模式选择位,C/T=0为定时模式,C/T=1为计数模式;
            - M1M0是工作方式设置位,有四种方式:00 01 10 11
    [2023-12-20] zoya
*/

#include "reg52.h"
#include "typedef.h"
#include "Delay.h"

#define GPIO_LED P2
/*************************************************************************
* 函数名:      Timer0Init
* 函数功能: 定时器0初始化,工作方式1定时3ms,仅TR0启动或停止计数
* 输入:           void
* 输出:           void
**************************************************************************/
void Timer0Init()
{
    // 1. 设置工作方式1,仅TR0控制
    TMOD |= 0x01;
    // 2. 设置定时3ms的初值,0xf448
    TH0 = 0xf4;
    TL0 = 0x48;
    // 3. 打开中断允许位
    EA = 1;
    ET0 = 1;
    // 4. 置位TR0,开始计数
    TR0 = 1;
}


void main()
{
    Timer0Init(); 
    while(1);
}


/*************************************************************************
* 函数名:      Timer0
* 函数功能: 定时器0中断服务函数,定时3s控制LED模块亮灭
* 输入:           void
* 输出:           void
**************************************************************************/
void Timer0() interrupt 1
{
    static u16 i;
    // 重新赋初值
    TH0 = 0xf4;
    TL0 = 0x48;
    i++;
    if(1000 == i)
    {
        i=0;
        GPIO_LED = ~GPIO_LED;
    }
}

仿真结果:

定时器0定时3s控制LED模块亮灭

三、扩展实验三:使用定时器1和数码管设计一个数字时钟

定时器的设置参考扩展实验二。

数字时钟采用24小时制,显示使用“00-00-00”格式。

这里数码管使用一个八位一体的共阴极数码管,使用芯片74HC138控制数码管的位选,使用芯片74HC245控制数码管的段选;P0口控制74HC245的输入,P2.2 ~ P2.4控制74HC138的输入。proteus设计如下:

proteus设计定时器1和数码管实现一个数字时钟

软件设计如下:

/*
    实现功能:定时器1和数码管设计一个数字时钟
        - 与定时/计数器工作有关的寄存器有IE、TCON、TMOD、THx、TLx
        - IE是中断允许控制寄存器,TCON是中断请求标志寄存器,TMOD是定时/计数器工作方式寄存器
        - THx和TLx是计数初值赋值寄存器。
        
        - IE中包括了
            - 总中断允许位(EA)
            - 外部中断0/1允许位(EX0/EX1)
            - 定时器0/1允许位(ET0/ET1)
            - 串口中断允许位(ES);
        
        - TCON中的高四位用于控制定时/计数器的启动和中断申请,包括TR0/1、TF0/1
            - TR0/TR1是T0/T1运行控制位,TR0=1时开始工作,TR0=0时停止工作,TR1与TR0类似;
            - TF0/TF1是T0/T1溢出中断请求标志位,溢出时由硬件自动置位,CPU响应中断后由硬件自动清0
                可随时查询该位状态,也可软件置1或清0.
                
        - TMOD高四位控制T1,低四位控制T0,高四位和低四位分别为有GATE、C/T、M1M0
            - GATE是门控位,
                - GATE=0表示不受外部中断信号影响,仅TR0/TR1控制定时/计数器工作,
                - GATE=1表示受外部中断信号影响,即TR0/TR1+INT0控制定时/计数器工作
            - C/T是定时/计数器模式选择位,C/T=0为定时模式,C/T=1为计数模式;
            - M1M0是工作方式设置位,有四种方式:00 01 10 11
        
        使用一个八位一体的共阴极数码管显示时间,74HC138芯片控制数码管的位选,74HC245控制数码管的段选。
    [2023-12-20] zoya
*/

#include "reg52.h"
#include "typedef.h"
#include "Delay.h"

#define GPIO_DISPLAY P0
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

// 共阴极数码管的码表,0-9以及:
u8 code smg[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x67, 0x40};

static u16 h, m, s;

/*************************************************************************
* 函数名:      Timer0Init
* 函数功能: 定时器0初始化,工作方式1定时3ms,仅TR0启动或停止计数
* 输入:           void
* 输出:           void
**************************************************************************/
void Timer1Init()
{
    // 1. 设置工作方式1,仅TR0控制
    TMOD |= 0x10;
    // 2. 设置定时1ms的初值,0xFC18
    TH1 = 0xFC;
    TL1 = 0x18;
    // 3. 打开中断允许位
    EA = 1;
    ET1 = 1;
    // 4. 置位TR1,开始计数
    TR1 = 1;
}

void DigDisplay()
{

    LSA=0; LSB=0; LSC=0; GPIO_DISPLAY = smg[h/10];
    delayms(1);
    LSA=1; LSB=0; LSC=0; GPIO_DISPLAY = smg[h%10];
    delayms(1);
    LSA=0; LSB=1; LSC=0; GPIO_DISPLAY = smg[10];
    delayms(1);
    LSA=1; LSB=1; LSC=0; GPIO_DISPLAY = smg[m/10];
    delayms(1);
    LSA=0; LSB=0; LSC=1; GPIO_DISPLAY = smg[m%10];
    delayms(1);
    LSA=1; LSB=0; LSC=1; GPIO_DISPLAY = smg[10];
    delayms(1);
    LSA=0; LSB=1; LSC=1; GPIO_DISPLAY = smg[s/10];
    delayms(1);
    LSA=1; LSB=1; LSC=1; GPIO_DISPLAY = smg[s%10];
    delayms(1);
}

void main()
{
    GPIO_DISPLAY = 0x00;
    Timer1Init(); 
    while(1)
    {
        DigDisplay();
    }
}

/*************************************************************************
* 函数名:      Timer1
* 函数功能: 定时器1中断服务函数,控制数码管显示
* 输入:           void
* 输出:           void
**************************************************************************/
void Timer1() interrupt 3
{
    static u16 j;
    // 重新赋初值
    TH1 = 0xFC;
    TL1 = 0x18;
    j++;
    if(1000 == j)
    {
        j=0;
        s++;
        if(60 == s)
        {
            s=0; m++;
            if(60 == m)
            {
                m=0; h++;
                if(24 == h)
                {
                    h=0;
                }
            }
        }
    }
}

仿真结果:

定时器1和数码管实现一个数字时钟
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容