芯片除了STC52之外就是之前博文中提到的SG90舵机;超声波传感器;震动传感器
蜂鸣器是一个新的模块:
低电平触发
功能实现要求:
检测到靠近时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
发生震动时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
按下按键时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
开盖需要使用到舵机,使用定时器0和定时器0中断;检测靠近需要使用到超声波测距传感器,使用定时器1;震动检测需要用到震动传感器和外部中断0(因为震动的信号通常不连续且较微弱,如果不设置成中断可能会检测不到)。
外部中断0
根据提到的知识,中断号是0,因此要在中断处理函数的申明后加上“interrupt 0", 并且打开EX0 和 EA。
另外不同于定时器0的中断触发行为是溢出标志位,外部中断0的触发行为是IT0的下降沿或低电平触发,正好震动传感器也是低电平触发,美滋滋@
同时接线需要接在P3.2, 因为根据手册,STC89C51/52 的 P3.2可以使用外部中断0,当检测到P3.2的下降沿时触发中断函数(关于下降沿还是其他从电路图可知)。
具体结合垃圾桶的硬件实现需要垃圾桶模型和热熔胶,暂时还没有到货,所以之后把图补上。
同时,我的超声波测距模块似乎有些问题,之后另开一篇博文详解,在这个垃圾桶项目中,暂时将超声波测距的相关代码注释掉了。(2023/6/25: 终于发现了超声波的问题,不知道是因为我第一个烧坏了而第二个再国外买的规格不大一样导致,反正超声波的开始信号应该给Trig一个10ms左右的高电平,而不是10us,再买了4个超声波模块之后,各种排除,终于发现是因为这个无聊的原因....!!!!!!!!!!!!!!!!!!!!!!!!!)
#include "reg52.h"
sbit SG90_CON = P1^1; //舵机
sbit Trig = P1^5; //超声波测距模块Trig口
sbit Echo = P1^6; //超声波测距模块Echo口
//sbit jidian = P2^0; //继电器 0导通
sbit vibe = P3^2; //震动传感器 有震动变0
sbit key1 = P2^1; //按键按下时等于0
sbit beep_go = P1^3;
int cnt = 0;
int angle = 1;
char mark_vibe = 0; //int和char有一部分空间共用,所以对于这种很小的数,为了节省空间可以直接定义为char变量
void Timer0Init(void) //0.5毫秒@11.0592MHz //舵机使用定时器0
{
// AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
// TF0 = 0; //清除TF0标志
// TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1; //打开中断!
}
void Timer1Init(void) //超声波测距使用定时器1
{
// AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TL1 = 0; //设置定时初值
TH1 = 0; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 0; //定时器1 先不计时
}
void Delay2000ms() //@11.0592MHz
{
unsigned char i, j, k;
// _nop_();
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay300ms() //@11.0592MHz
{
unsigned char i, j, k;
// _nop_();
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void beep()
{
beep_go = 1;
beep_go = 0;
Delay300ms();
beep_go = 1;
}
void startHC() //先要给Trig一个至少10us的高电平方波
{
Trig = 1;
Delay10us();
Delay10us();
Trig = 0;
}
double getDis()
{
double time; //double的精度是float的两倍
double dist;
startHC(); //使超声传感器Trig给开始信号
while(Echo == 0); //程序会卡在这里直到Echo变高的一瞬间
TR1 = 1; //定时器0开始计时
while(Echo == 1); //程序会卡在这里直到Echo变低的一瞬间
TR1 = 0;
//十进制2左移1位,变成20,相当于乘以10
//二进制1左移1位,变成10,相当于乘以2
//所以16进制左移1位需要乘以16,通过左移2位(16*16=256)之后将TH0和TL0进行拼接,就可以得到定时器的计数值
time = (TH1 * 256 + TL1)*1.085; //us为单位
dist = 0.034 * time * 0.5; //340m/s换算到cm为34000cm/s;换算到us为34000cm/1000000us = 0.034cm/us
TH1 = 0;
TL1 = 0; //重新给初值!!!!!
return dist;
}
void InitSG90()
{
Delay300ms(); //由于软件实现PWM本来就不太精确且稳定,所以先让系统delay300ms 稳定一下
angle = 1; //对应0度
cnt = 0; //初始化cnt
SG90_CON = 1; //PWM波从高电平开始
}
void EX0_Init()
{
EX0 = 1; //打开外部中断
IT0 = 0; //低电平触发
}
void main()
{
double dist;
Trig = 0;
Echo = 0;
Timer0Init();
Timer1Init();
InitSG90();
EX0_Init();
while(1){ //舵机先在0度保持两秒,然后转到135度,隔两秒后转回0度,隔两秒再转到135度...结果就是舵机每两秒会在0到135度之间切换。
//dist = getDis();
if(key1 == 0 || mark_vibe == 1){ //当key1被按下或有震动产生时
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
angle = 4; //开盖
cnt = 0;
Delay2000ms(); //每隔两秒对舵机转动的角度调整
beep();
angle = 1; //隔两秒后自动关盖
cnt = 0;
mark_vibe = 0;
}
}
}
//timer0的中断处理程序 //中断程序一般写在main函数的后面 //定时器0溢出时将触发这个中断函数
void zhongduan() interrupt 1
{
cnt++;
TL0 = 0x33; //重新给初值!!
TH0 = 0xFE;
//生成PWM波
if(cnt < angle){ //cnt =1 时,爆表了一次,过了0.5ms
SG90_CON = 1;
}else{
SG90_CON = 0;
}
if(cnt == 39){//每经过(40*0.5毫秒 = )20毫秒,PWM波经过一个周期
cnt = 0;
SG90_CON = 1;
}
}
void vibeInter() interrupt 0 //外部中断0,此处是震动传感器的中断函数,即P3.2口变低电平时会自动触发这个中断处理程序
{
mark_vibe = 1;
}