C52单片机独立按键模块,中断系统,定时器计数器以及蜂鸣器
独立按键模块:
需要进行初始化操作将P14到P17默认状态设置为为高电平,按下对应按键转为低电平,即高四位置1低四位不变:
void init_key(void)
{P1 |= (0x0F << 4);
}
判断特定位的电平高低:
=需要注意运算符优先级问题:“==”运算符优先级高于“&”
按下不同的按键通过动态数码管显示键位数:
#include<reg52.h>
#include"digiter.h"
#include"delay.h"
#include"key.h"
void delay(unsigned int n)
{while(n--) ;
}
void init_key(void)
{P1 |= (0x0F << 4);
}
int num=0;
int key_pressed(void)
{if((P1&(1<<4))==0){num=1;}else if((P1&(1<<5))==0){num=2;}else if((P1&(1<<6))==0){num=3;}else if((P1&(1<<7))==0){num=4;}return num;
}void bit_select(int n) //选择点亮的动态数码管
{P1&=~(0x0F<<0);P1|=(1<<n);
}
void segment_select(int n) //选择数码管显示的数字
{unsigned char t[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};P0=t[n];delay(300);P0=0;delay(100);
}
void shownumber(int n) //0~9999
{int t=0;if(n==0){bit_select(0);segment_select(0);}else if(n>9999){return;}while(n){bit_select(t++);segment_select(n%10);n/=10;}
}
int main(void)
{int ret=0;init_key();while(1){ret=key_pressed();shownumber(ret);/* if(ret!=0){P1 = 0 ;} */
// delay(0x9000);}return 0;
}
中断:
概念总结:
中断系统是为让CPU具备对外界紧急事件实时处理能力而设置的。
- 中断:CPU处理某件事时,外界有紧急事件请求,CPU暂停当前工作去处理该紧急事件,处理完后回到原被中断处继续工作的过程。
- 中断系统:实现中断功能的部件。
- 中断源:请示CPU中断的请求源,微型机中断系统一般允许多个中断源。
- 中断优先级:当多个中断源同时请求中断时,按中断源轻重缓急排队,优先处理最紧急事件的中断请求源,每个中断源有相应优先级别,CPU先响应优先级最高的中断请求。
- 中断嵌套:CPU正在处理一个中断源请求(执行相应中断服务程序)时,又有优先级更高的中断源请求,CPU暂停对原来中断源的服务程序,转去处理优先级更高的中断请求源,处理完后再回到原低级中断服务程序的过程。
- 多级中断系统:具有中断嵌套功能的中断系统;单级中断系统:没有中断嵌套功能的中断系统。
51单片机的八个中断源:分为外部中断,计时器中断以及串口中断三大类:
C语言编程中断源格式:中断源可以理解为发生中断请求的函数,只是对于返回值参数以及格式有特定的要求:
中断发生及处理过程:
1、中断源发出中断请求;
2、内核检查是否相应响应中断以及该中断是否被屏蔽;
3、内核会检查中断的中断优先级;
4、保护现场;
5、执行中断服务函数;
6、恢复现场。
在编写代码时我们需要考虑2,3,5步
==中断向量表:==储存函数指针(中断源指针)的数组
中断向量表是存储中断服务程序入口地址的特殊表格,用于CPU快速定位中断服务程序的位置。
在计算机系统中,每个中断源(如外部设备请求、定时器溢出等)都对应一个唯一的中断类型号,而中断向量表就是将这些中断类型号与对应的中断服务程序入口地址建立映射关系的表格。
当CPU接收到中断请求时,会根据中断类型号在中断向量表中找到对应的入口地址,然后跳转到该地址执行中断服务程序,从而实现对中断的快速响应和处理。
以8051系列单片机为例,其中断向量表固定在程序存储器的起始地址区域(如0003H、000BH等),每个中断源对应一个特定的存储单元,该单元中存放的就是对应中断服务程序的入口地址。这种结构确保了CPU在响应中断时能迅速找到并执行相应的处理程序。
重点学会看懂操作手册:
通过此图查询触发特定中断需要的条件
以中断源中的:外部中断0为例:
==下降沿:==电平切换发生的极短的过程(中断发生在切换高低电平的极短时间内)
低电平:电平处于0状态下的过程(中断发生在处于低电平的极短时间内)
以代码为例:
#include<reg52.h>
#include"delay.h"
#include"digiter.h"
void init_enit0(void)
{IE |= (1 << 7) | (1 << 0) | (1 << 2);
// TCON |= (1 << 0) | (1 << 2);//下降沿触发中断TCON |= (1 << 2);//低电平持续触发中断TCON &=~(1<<0);P3 |= (1 << 2) | (1 << 3);
}
int num=0;
void enit0_handler(void) interrupt 0
{++num;if(num>9999){num=0;}
}
void enit1_handler(void) interrupt 2
{--num;if(num<0){num =9999;}
}int main(void)
{init_enit0();P2 = 0xFF;while(1){shownumber(num);}
}
需要控制TCON的第0位来实现触发行为的切换,控制INT0对应的P3.3置1和置0来实现中断的开关,以及IE的特定位置1来实现中断的允许
对于控制中断的优先级,c51单片机最高支持两层中断嵌套,默认情况下各中断优先级均为最高,仔细阅读工作手册也可做到设置中断的优先级:
定时器:
在8051系列单片机中,TCON和TMOD是两个不同功能的特殊功能寄存器,它们的区别如下:
功能用途
- TCON(定时器控制寄存器):主要用于控制定时器/计数器的启动和停止, 以及管理外部中断请求。它还可以锁定外部中断请求信号,记录定时器/计数器的溢出标志和外部中断触发方式等 。比如,当定时器/计数器发生溢出时,TCON中的相应溢出标志位会被置位,用于向CPU发出中断请求信号;还能设置外部中断是上升沿触发还是下降沿触发。
- TMOD(定时器方式寄存器):主要用于设置定时器/计数器的工作模式。它确定了定时器/计数器是工作在定时模式还是计数模式,以及工作在何种具体的工作方式(如方式0、方式1、方式2、方式3 )。不同的工作模式决定了定时器/计数器的计数位数、计数初值的设置方法等。
位定义及功能
- TCON :TCON寄存器共8位,字节地址为88H,可进行位寻址。以51单片机为例,其8位从高位到低位依次是TF1、TR1、TF0、TR0、IE1、IT1、IE0、IT0 。
- TF1(定时器1溢出标志位):当定时器1计满溢出时,由硬件自动置1,申请中断。进入中断服务程序后,由硬件自动清0。
- TR1(定时器1运行控制位):由软件置1或清0来启动或停止定时器1。
- IE1(外部中断1请求标志位):外部中断1产生中断请求时,该位由硬件自动置1,CPU响应中断后,由硬件自动清0。
- IT1(外部中断1触发方式控制位):当IT1 = 0时,外部中断1为低电平触发方式;当IT1 = 1时,外部中断1为下降沿触发方式 。
- TMOD:TMOD寄存器也是8位,字节地址为89H,不能进行位寻址 。8位分为两组,高4位用于控制定时器1,低4位用于控制定时器0 。从高位到低位依次是GATE1、C/T1、M11、M01、GATE0、C/T0、M10、M00 。
- GATE(门控位):当GATE = 0时,只要用软件使TCON中的TR0或TR1为1,就可以启动定时器/计数器工作;当GATE = 1时,只有在INT0或INT1引脚为高电平,且TR0或TR1置1时,才能启动定时器/计数器 。
- C/T(定时/计数模式选择位):当C/T = 0时,工作在定时模式;当C/T = 1时,工作在计数模式 。
- M1、M0(工作方式选择位):这两位的不同组合可以确定定时器/计数器的4种工作方式 。例如,M1M0 = 00时,定时器工作在方式0,是13位定时器/计数器。
对系统运行的影响
- TCON:对中断和定时器/计数器的实时控制起着关键作用。它能及时响应外部中断请求,调整定时器/计数器的运行状态,从而使单片机能够灵活应对各种外部事件和定时任务。比如在外部设备数据准备好时,通过设置TCON使单片机快速响应中断,读取设备数据。
- TMOD:主要是在系统初始化阶段,根据具体的应用需求,为定时器/计数器配置合适的工作模式,确定其基本的工作特性,它的设置会影响定时器/计数器的计数范围、精度等,一旦设置好,在系统运行过程中通常不会频繁更改。
先让我们看看代码:设置TMOD以及中断实现LED的定时亮灭:
#include <reg52.h>void init_timer0(void)
{TMOD &= ~(3 << 2);TMOD &= ~(3 << 0);TMOD |= (1 << 0);TH0 = (65535-922) >> 8;TL0 = (65535-922);TCON |= (1 << 4);IE |= (1 << 7) | (1 << 1);
}void timer0_handler(void) interrupt 1
{static int t = 0;++t;if(t >= 500){P2 ^= 0xFF;t = 0;}TH0 = (65535-922) >> 8;TL0 = (65535-922);
}int main(void)
{init_timer0();while(1){}return 0;
}
c51开发板计数器最大计数为2^16-1,即为65535,计数范围即为(0~65535)
由于51单片机特殊的架构设置:定时器/计数器实际工作频率位晶振频率的1/12
我们需要根据单片机上定时器/计数器的频率计算计数一次所需的时间:以晶振频率12MHZ为例:
计数一次1HZ
12/12=1MHZ
1S/1MHZ*(106)=1US
即处理计数一次花费1us的时间,
在1ms内处理103的计数
若要使定时器从开始到溢出执行时间恰好是1ms,则需要设置定时器的初值,即65535-1000=64535,对于其他不同的晶振频率我们也以类似的方法对定时器的初值进行计算
定时器初值赋值:
由高四位TH0和低四位TL0组合表示:
由于高位(等号右边为16位)向地位(等号左侧TH0和TL0为8位)赋值会发生高位截断,因此我们可以这样对高八位和低八位进行赋值:
————————————————————————————
由图:不需要对TF0进行操作,对TRO置1允许T0进行计数
再设置TMOD:
故有代码:
#include <reg52.h>void init_timer0(void)
{TMOD &= ~(3 << 2);TMOD &= ~(3 << 0);TMOD |= (1 << 0);TH0 = (65535-922) >> 8;TL0 = (65535-922);TCON |= (1 << 4);IE |= (1 << 7) | (1 << 1);
}void timer0_handler(void) interrupt 1
{static int t = 0;++t;if(t >= 500){P2 ^= 0xFF;t = 0;}TH0 = (65535-922) >> 8;TL0 = (65535-922);
}int main(void)
{init_timer0();while(1){}return 0;
}
蜂鸣器:
由于c51单片机蜂鸣器属于无源蜂鸣器若需要控制蜂鸣器持续蜂鸣,需要让P21电平置1并不断以高频率切换蜂鸣器的高低电平,需要使用定时器。
蜂鸣器特点总结
- 分类:分为有源蜂鸣器和无源蜂鸣器。有源蜂鸣器自带振荡源,通电即可发声;无源蜂鸣器需外部提供振荡信号才能工作,比如通过PWM(脉冲宽度调制)信号来驱动,像图中提到用特定频率(200Hz、400Hz、800Hz等)的信号去控制。
- 驱动与控制:常借助PWM技术。PWM能产生方波,通过调整占空比(完整周期内高电平所占时间比率),可对蜂鸣器的发声相关特性(如在一些场景下配合调节音量等,不过蜂鸣器主要是发声有无和频率调整,占空比也可辅助控制发声的有效时长等)进行控制。例如图中计算不同频率下高/低电平的时间,就是在为生成合适的PWM信号以驱动蜂鸣器做准备。
- 频率相关:不同频率会产生不同的音调,像200Hz、400Hz、800Hz等不同频率,蜂鸣器发出的声音音调有明显差异,可用于实现不同的提示音等功能。
占空比是指在一个脉冲周期内,高电平持续的时间与整个脉冲周期(高电平持续时间加上低电平持续时间)的比值,通常用百分数来表示。它是脉冲宽度调制(PWM)技术中的一个关键参数,通过改变占空比,可以调节诸如蜂鸣器的发声效果、电机的转速、屏幕的亮度等。例如,当占空比为50%时,意味着在一个周期内,高电平时间和低电平时间各占一半;若占空比增大,高电平持续时间相对更长,在驱动蜂鸣器时,可能会影响其发声的有效能量等相关表现,进而实现不同的控制目的。
代码部分:通过不同按键实现蜂鸣器的不同频率蜂鸣:
#include <reg52.h>
#include"key.h"
#define hz200 63156
#define hz400 64383
#define hz800 64959
unsigned short n=hz200;
void init_key(void)
{P1 |= (0x0F << 4);
}
int num=0;
int key_pressed(void)
{if((P1&(1<<4))==0){num=1;}else if((P1&(1<<5))==0){num=2;}else if((P1&(1<<6))==0){num=3;}else if((P1&(1<<7))==0){num=4;}return num;
}void init_timer0(void)
{TMOD &= ~(3 << 2);TMOD &= ~(3 << 0);TMOD |= (1 << 0);TH0 = n >> 8;TL0 = n;IE |= (1 << 7) | (1 << 1);
}void timer0_handler(void) interrupt 1
{P2 ^=(1<<1);TH0 = n >> 8;TL0 = n;
}int main(void)
{init_timer0();init_key();while(1){int key;key=key_pressed();if(key==1){n=hz200;TCON |= (1 << 4);}else if(key==2){n=hz400;TCON |= (1 << 4);}else if(key==3){n=hz800;TCON |= (1 << 4);}else if(key==0){TCON &= ~(1 << 4);}}return 0;
}