基于AVR单片机的上下课自动打铃系统的实现
2015-06-23 来源:51hei
昨晚花一晚上把这些资料整理了一下,全部发出来了。
进入单片机查看更多内容>>
由于补课没有铃。3月份做了这个东西,已实际使用,稳定运行一个学期。
上运行图:
硬件如下:AVR ATmega16单片机,开发板(用到上面的:继电器、LED走马灯、两个按钮)、门铃、LED若干、16Mhz无源晶振。
硬件照片:
开发板:
AVR单片机:
晶振:
——————————好了 照片晒完了,下面开始正文——————————
原理图如下:
好吧我的能耐真大,这电路图不是用什么CAD专业软件画的,而是用Windows画 图板用鼠标一笔一划画的。。。所以效果不是很好,凑合着看吧,知道个大概就行了。(开发板上肯定还有其他资源,电路图中的资源也不止开发板上的。这个电路 图是本系统的原理图而不是开发板的全部电路图,画的只是和本系统有关的东西,开发板上其他无关的就不画了,也不需要画)
简单说明下:
L1~L8对应开发板上的8个LED;D2~D6对应上面运行图上的那5个红色“上课指示灯”;D1为一盏绿色的LED(运行图上注意点看,用透明胶包住的那个绿色LED),用于监视程序是否在运行(晶振是否起振,是否死机);K1 K2用于选择上午下午(上午有早读,下午没有)。继电器不用说了,控制门铃开关用。
下面放出源程序。(刚刚我已在程序中加入了很多注释了,后面还是要做下解析):
————源程序(本程序版权归李彦锋所有)————
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long int
volatile uint nowtime;//计时变量。然后下面的几个变量是一些逻辑控制的
volatile uint ledmode;
volatile uint shangkeledmode;
volatile uint shangkeledtime;
volatile uint class;//上下课逻辑,上课为1,下课为0,见主函数
#include
#include
void port_init(void)
{
DDRB=0xFF;
PORTB=0x00;
DDRC=0xFF;
DDRA=0XFF;
DDRD|=0XF0;
//初始化IO口
}
void timer0_init(void)
{
TCCR0 = 0x00;
TCNT0 = 0x06;
OCR0 = 0xFA;
TCCR0 = 0x03;
//初始化timer0
}
//timer0本系统中用于控制上下课指示灯闪烁
#pragma interrupt_handler timer0_ovf_isr:iv_TIM0_OVF
void timer0_ovf_isr(void)
{
tm0rsf();//别看漏
TCNT0 = 0x06;
}
//TIMER1 initialize - prescale:256
// WGM: 0) Normal, TOP=0xFFFF
// desired value: 1Sec
// actual value: 1.000Sec (0.0%)
void timer1_init(void)
{
TCCR1B = 0x00; //stop
TCNT1H = 0x0B; //setup
TCNT1L = 0xDC;
OCR1AH = 0xF4;
OCR1AL = 0x24;
OCR1BH = 0xF4;
OCR1BL = 0x24;
ICR1H = 0xF4;
ICR1L = 0x24;
TCCR1A = 0x00;
TCCR1B = 0x04; //start Timer
//初始化timer1
}
//timer1本系统中用于上课时间和下课时间计时
#pragma interrupt_handler timer1_ovf_isr:iv_TIM1_OVF
void timer1_ovf_isr(void)
{
tm1rsf();//哈哈可能有人漏看了这里,回调函数执行另一个函数,这样程序看起来更整洁,这是我的编程习惯^v^
TCNT1H = 0x0B;
TCNT1L = 0xDC; //重载高低值
//timer1回调函数
}
void init_devices(void)
{
CLI();
port_init();
timer0_init();
timer1_init();
MCUCR = 0x00;
GICR = 0x00;
TIMSK = 0x05;
SEI();
}
void delay(uint ms) //这个就不用说了 死循环延迟
{
uint i,j;
for(i=0;i
{
for (j=0;j<2300;j++);
}
}
void tm0rsf()//控制上课指示灯闪烁的函数,不喜欢写在回调函数里,这样程序看起来更工整。。。
{
if(class==1)//是否上下课,上课就闪,下课就不闪。
{
shangkeledtime++;
if (shangkeledtime==150)//150ms闪一下。其他自己看吧。。。。
{
shangkeledtime=0;
if(shangkeledmode==1) //开灯,相应IO输出高电平
{
PORTA|=0X54;
PORTC|=0x02;
PORTD|=0x20;
shangkeledmode=0;
}
else //关灯,相应IO输出低电平(接地)
{
PORTA&=0X03;
PORTC&=0xFC;
PORTD&=0XDF;
shangkeledmode=1;//逻辑自己看吧。。。。
}
}
}
else
{
PORTA&=0X03;
PORTC&=0xFC;
PORTD&=0XDF;
}
}
void tm1rsf()//timer1回调函数执行的函数
{
nowtime++;//计时变量自增
if(ledmode==1)//嗯这个就是控制D0 LED闪烁的了,主要就是看timer1是不是在走。
{
ledmode=0;
PORTA|=0x01;
}
else
{
ledmode=1;
PORTA&=0XFE;
}
}
void ring()//响铃函数,控制继电器的。。(PC7输出高电平)
{
PORTC=0xFF;//因为PC口只有用到一个PC7所以不管这么多懒得算了直接全部输出高电位
delay(300);
PORTC=0x00;
delay(8300);
PORTC=0xFF;
delay(300);
PORTC=0x00;
delay(8300);
}
uchar key_press()//检测K1K2是否按下。。
{
uchar j;
DDRD|=0X0F;
PORTD|=0X0F;
DDRD&=0XF0;
j=PIND;
j=j&0X0F;
if(j==0X0F)
{
return 0;
}
else
{
return 1;
}
}
uchar key_scan()//检测是K1还是K2按下。。
{
uchar key;
delay(10);
if(key_press())
{
key=PIND;
key&=0X0F;
switch(key)
{
case 0X0E:
key=1;
break;
case 0X0D:
key=2;
break;
default:
key=0;
}
while(key_press());
}
else
{
key=16;
}
return key;
}
void main()//好了主函数开始了。。。
{
uchar i,j;
uint k,mode;
uint ledmode;
init_devices();//初始化IO。。上面有。
k=0;
ledmode=1;//逻辑自己看,不用解释。。。
while(k==0) //注意了 这一段是上电后等待按下K1K2的。
{
if(ledmode==1)//上电后LED走马灯在那狂闪(按下之前)
{
PORTB=0XF0;delay(80);
ledmode=0;
}
else{PORTB=0X0F;delay(80);ledmode=1;}
i=key_press();
if(i)
{//判断按的哪个
j=key_scan();
if (j==1)
{mode=1;k=1;}
if (j==2)
{mode=2;k=1;}
}
}
if (mode==1)//如果按的K1,也就是早上用,有早读的
{
uint overzaodu;
uint canoverzaodu;
overzaodu=0;
canoverzaodu=0;
PORTB=0XF3;//早读上课时跑马LED亮上面2盏(亮上面。。代表上午^v^)
nowtime=0;
class=1;
while(canoverzaodu==0)
{
if (class==1)
{
if (nowtime==1200)//先上20分钟的早读
{
class=0;
nowtime=0;
overzaodu++;
ring();//早读下课打铃
}
}
else
{
if (nowtime==300)//早读休息5分钟,接下来自己看吧
{
class=1;
nowtime=0;
overzaodu++;
ring();
ring();
PORTC=0xFF;
delay(300);
PORTC=0x00;
}
}
if (overzaodu==1)
{
PORTB=0XF1;//早读下课,LED多亮一盏
}
if (overzaodu==2)
{
canoverzaodu=1;
PORTB=0XF0;//早读结束,LED亮完上面4盏。。
}//然后跳出早读while,进入到下面正常上课while--上课40分钟,下课10分钟,上课,下课…………无限循环。。。
}
}
if (mode==2)//按下K2,也就是下午用的
{
PORTB=0X0F;
class=1;//逻辑,上课为1下课为0
ring();
ring();
PORTC=0xFF;
delay(300);
PORTC=0x00;//这都是打铃用的。。上课比较吵,要打5遍。(一个ring打两遍,两个ring后直接在这里控制IO再打一遍)
}
nowtime=0;
while(1)//正常上课while,这里就不解释太多了,逻辑和上面早读的差不多的
{
if (class==1)
{
if (nowtime==2400)//是否够40分钟
{
class=0;
nowtime=0;//计时变量清零
ring();//打铃,下课打2遍就好了(一个ring两遍,具体看上面ring函数)
}
}
else
{
if (nowtime==600)
{
class=1;
nowtime=0;
ring();
ring();
PORTC=0xFF;
delay(300);
PORTC=0x00;//上课打5遍铃,其他就不解释这么多了,逻辑一样的自己看就行。
}
}
}
}
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long int
volatile uint nowtime;//计时变量。然后下面的几个变量是一些逻辑控制的
volatile uint ledmode;
volatile uint shangkeledmode;
volatile uint shangkeledtime;
volatile uint class;//上下课逻辑,上课为1,下课为0,见主函数
#include
#include
void port_init(void)
{
DDRB=0xFF;
PORTB=0x00;
DDRC=0xFF;
DDRA=0XFF;
DDRD|=0XF0;
//初始化IO口
}
void timer0_init(void)
{
TCCR0 = 0x00;
TCNT0 = 0x06;
OCR0 = 0xFA;
TCCR0 = 0x03;
//初始化timer0
}
//timer0本系统中用于控制上下课指示灯闪烁
#pragma interrupt_handler timer0_ovf_isr:iv_TIM0_OVF
void timer0_ovf_isr(void)
{
tm0rsf();//别看漏
TCNT0 = 0x06;
}
//TIMER1 initialize - prescale:256
// WGM: 0) Normal, TOP=0xFFFF
// desired value: 1Sec
// actual value: 1.000Sec (0.0%)
void timer1_init(void)
{
TCCR1B = 0x00; //stop
TCNT1H = 0x0B; //setup
TCNT1L = 0xDC;
OCR1AH = 0xF4;
OCR1AL = 0x24;
OCR1BH = 0xF4;
OCR1BL = 0x24;
ICR1H = 0xF4;
ICR1L = 0x24;
TCCR1A = 0x00;
TCCR1B = 0x04; //start Timer
//初始化timer1
}
//timer1本系统中用于上课时间和下课时间计时
#pragma interrupt_handler timer1_ovf_isr:iv_TIM1_OVF
void timer1_ovf_isr(void)
{
tm1rsf();//哈哈可能有人漏看了这里,回调函数执行另一个函数,这样程序看起来更整洁,这是我的编程习惯^v^
TCNT1H = 0x0B;
TCNT1L = 0xDC; //重载高低值
//timer1回调函数
}
void init_devices(void)
{
CLI();
port_init();
timer0_init();
timer1_init();
MCUCR = 0x00;
GICR = 0x00;
TIMSK = 0x05;
SEI();
}
void delay(uint ms) //这个就不用说了 死循环延迟
{
uint i,j;
for(i=0;i
for (j=0;j<2300;j++);
}
}
void tm0rsf()//控制上课指示灯闪烁的函数,不喜欢写在回调函数里,这样程序看起来更工整。。。
{
if(class==1)//是否上下课,上课就闪,下课就不闪。
{
shangkeledtime++;
if (shangkeledtime==150)//150ms闪一下。其他自己看吧。。。。
{
shangkeledtime=0;
if(shangkeledmode==1) //开灯,相应IO输出高电平
{
PORTA|=0X54;
PORTC|=0x02;
PORTD|=0x20;
shangkeledmode=0;
}
else //关灯,相应IO输出低电平(接地)
{
PORTA&=0X03;
PORTC&=0xFC;
PORTD&=0XDF;
shangkeledmode=1;//逻辑自己看吧。。。。
}
}
}
else
{
PORTA&=0X03;
PORTC&=0xFC;
PORTD&=0XDF;
}
}
void tm1rsf()//timer1回调函数执行的函数
{
nowtime++;//计时变量自增
if(ledmode==1)//嗯这个就是控制D0 LED闪烁的了,主要就是看timer1是不是在走。
{
ledmode=0;
PORTA|=0x01;
}
else
{
ledmode=1;
PORTA&=0XFE;
}
}
void ring()//响铃函数,控制继电器的。。(PC7输出高电平)
{
PORTC=0xFF;//因为PC口只有用到一个PC7所以不管这么多懒得算了直接全部输出高电位
delay(300);
PORTC=0x00;
delay(8300);
PORTC=0xFF;
delay(300);
PORTC=0x00;
delay(8300);
}
uchar key_press()//检测K1K2是否按下。。
{
uchar j;
DDRD|=0X0F;
PORTD|=0X0F;
DDRD&=0XF0;
j=PIND;
j=j&0X0F;
if(j==0X0F)
{
return 0;
}
else
{
return 1;
}
}
uchar key_scan()//检测是K1还是K2按下。。
{
uchar key;
delay(10);
if(key_press())
{
key=PIND;
key&=0X0F;
switch(key)
{
case 0X0E:
key=1;
break;
case 0X0D:
key=2;
break;
default:
key=0;
}
while(key_press());
}
else
{
key=16;
}
return key;
}
void main()//好了主函数开始了。。。
{
uchar i,j;
uint k,mode;
uint ledmode;
init_devices();//初始化IO。。上面有。
k=0;
ledmode=1;//逻辑自己看,不用解释。。。
while(k==0) //注意了 这一段是上电后等待按下K1K2的。
{
if(ledmode==1)//上电后LED走马灯在那狂闪(按下之前)
{
PORTB=0XF0;delay(80);
ledmode=0;
}
else{PORTB=0X0F;delay(80);ledmode=1;}
i=key_press();
if(i)
{//判断按的哪个
j=key_scan();
if (j==1)
{mode=1;k=1;}
if (j==2)
{mode=2;k=1;}
}
}
if (mode==1)//如果按的K1,也就是早上用,有早读的
{
uint overzaodu;
uint canoverzaodu;
overzaodu=0;
canoverzaodu=0;
PORTB=0XF3;//早读上课时跑马LED亮上面2盏(亮上面。。代表上午^v^)
nowtime=0;
class=1;
while(canoverzaodu==0)
{
if (class==1)
{
if (nowtime==1200)//先上20分钟的早读
{
class=0;
nowtime=0;
overzaodu++;
ring();//早读下课打铃
}
}
else
{
if (nowtime==300)//早读休息5分钟,接下来自己看吧
{
class=1;
nowtime=0;
overzaodu++;
ring();
ring();
PORTC=0xFF;
delay(300);
PORTC=0x00;
}
}
if (overzaodu==1)
{
PORTB=0XF1;//早读下课,LED多亮一盏
}
if (overzaodu==2)
{
canoverzaodu=1;
PORTB=0XF0;//早读结束,LED亮完上面4盏。。
}//然后跳出早读while,进入到下面正常上课while--上课40分钟,下课10分钟,上课,下课…………无限循环。。。
}
}
if (mode==2)//按下K2,也就是下午用的
{
PORTB=0X0F;
class=1;//逻辑,上课为1下课为0
ring();
ring();
PORTC=0xFF;
delay(300);
PORTC=0x00;//这都是打铃用的。。上课比较吵,要打5遍。(一个ring打两遍,两个ring后直接在这里控制IO再打一遍)
}
nowtime=0;
while(1)//正常上课while,这里就不解释太多了,逻辑和上面早读的差不多的
{
if (class==1)
{
if (nowtime==2400)//是否够40分钟
{
class=0;
nowtime=0;//计时变量清零
ring();//打铃,下课打2遍就好了(一个ring两遍,具体看上面ring函数)
}
}
else
{
if (nowtime==600)
{
class=1;
nowtime=0;
ring();
ring();
PORTC=0xFF;
delay(300);
PORTC=0x00;//上课打5遍铃,其他就不解释这么多了,逻辑一样的自己看就行。
}
}
}
}
————源程序(本程序版权归李彦锋所有)————
再简单说下吧,程序中的逻辑就不说了。
1.点亮L1~L8为PB0~PB7输出低电平(接地),因为那头接的是VCC +5V的正电压。
2.PC7输出高电位,继电器B和C导通,接通门铃,就响了。
3.D1~D6我是直接把LED的正负接在IO口上的,所以输出电位的时候一高一低。其实可以IO口上全接正然后统一接地,但是这样麻烦,反正有足够的IO口,就这么干了。
附:最早是用内部时钟,然后后来跑不准,折腾了我一个星期,后来才发现是内部时钟的问题,上了个晶振,解决问题。。。
好了就说到这里,结束。
上一篇:AVR中断运用报警2路指示
相关文章
- 基于 Microchip AVR DA MCU 的带加热 HoD 离手检测+触摸多功能方向盘方案
- Microchip推出AVR® DU系列USB单片机,支持增强型代码保护和高达15W 的 功率输出
- 贸泽电子开售面向工业和汽车应用的Microchip Technology AVR64EA 8位AVR MCU
- AVR32 MCU上的ABDAC外设音频播放设计
- 使用AVR微控制器控制GSM模块实现发送和接收短信
- 如何使用USBASP烧写器和Atmel Studio 7.0对AVR微控制器进行编程
- 使用AVR微控制器Atmega16连接伺服电机的方法
- 使用AVR微控制器ATmega16的循迹机器人
- 了解Atmega16 / 32 AVR微控制器中的脉冲宽度调制(PWM)
- 如何使用AVR微控制器ATmega16连接霍尔传感器