/*
工程机械工作小时计示例程序
文件名:main.c
MCU :ATMEGA8
时 钟 :内部4MHz,外接32768Hz晶振
芯艺 2005-1-13
*/
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <avr/delay.h>
#define uchar unsigned char
#define uint unsigned int
// CS:PD7 WR:PD6 DT:PD5
#define LCD_CS PD7
#define LCD_WR PD6
#define LCD_DT PD5
#define LCD_PORT PORTD
uchar eep_bMinute __attribute__((section(".eeprom")))=30; //分钟数据EEPROM存储区
uint eep_wHour __attribute__((section(".eeprom")))=33; //小时数据EEPROM存储区
uchar g_bMinute; //分钟数据RAM缓冲区
uint g_wHour; //小时数据RAM缓冲区
uchar g_bDisplayFlag=0; //全局显示更新标记
//0~9 显示码,每字节格式:A3:D1,D0 A2:D1,D0 A1:D1,D0 A0:D1,D0 ,A3~0 为位对应的存储器相对地址
uchar g_aDisplayBuf[10]={0x6f,0x22,0x79,0x7a,0x36,0x5e,0x5f,0x2a,0x7f,0x7e};
//误差不会太大的延时1ms函数
void DelayMs(uint ms)
{
uint i;
for(i=0;i<ms;i++)
_delay_loop_2(4 *250);
}
//lcd 用延时函数
void lcd_delaybus(void)
{
_delay_loop_1(3);
}
//写入时钟发生
void lcd_clk(void)
{
lcd_delaybus();
LCD_PORT&=~_BV(LCD_WR);
lcd_delaybus();
LCD_PORT|=_BV(LCD_WR);
lcd_delaybus();
}
//写命令
void lcd_writecmd(uchar dat)
{
uchar i;
LCD_PORT&=~_BV(LCD_CS);
//发送 100
LCD_PORT|=_BV(LCD_DT);
lcd_clk();
LCD_PORT&=~_BV(LCD_DT);
lcd_clk();
lcd_clk();
//发送C0~C8
for(i=0;i<9;i++)
{
if(dat&0x80)
LCD_PORT|=_BV(LCD_DT);
else
LCD_PORT&=~_BV(LCD_DT);
dat<<=1;
lcd_clk();
}
LCD_PORT|=_BV(LCD_CS);
}
//按显示码格式连续写16位数据,即4个地址对应数据
void lcd_writedat(uchar addr,uchar dat)
{
uchar i,j,temp;
LCD_PORT&=~_BV(LCD_CS);
//发送 101
LCD_PORT|=_BV(LCD_DT);
lcd_clk();
LCD_PORT&=~_BV(LCD_DT);
lcd_clk();
LCD_PORT|=_BV(LCD_DT);
lcd_clk();
// 发送6位地址
for(i=0;i<6;i++)
{
if(addr&0x20)
LCD_PORT|=_BV(LCD_DT);
else
LCD_PORT&=~_BV(LCD_DT);
addr<<=1;
lcd_clk();
}
//将dat视为上述格式的显示码
//根据显示码发送16位数据
for(i=0;i<4;i++)
{
temp=dat&0x03;
for(j=0;j<4;j++)
{
if(temp&0x01)
LCD_PORT|=_BV(LCD_DT);
else
LCD_PORT&=~_BV(LCD_DT);
temp>>=1;
lcd_clk();
}
dat>>=2;
}
LCD_PORT|=_BV(LCD_CS);
}
//LCD显示器初始化程序
void lcd_init(void)
{
//初始化控制I/O口
DDRD=_BV(LCD_CS)|_BV(LCD_WR)|_BV(LCD_DT);
LCD_PORT=_BV(LCD_CS)|_BV(LCD_WR)|_BV(LCD_DT);
lcd_writecmd(0x20); //模块初始化选择
lcd_writecmd(0x18); //选择震荡器(内部RC)
lcd_writecmd(0x01); //震荡器开
lcd_writecmd(0x03); //LCD ON
}
//清除
void lcd_clear(void)
{
uchar i;
for(i=0;i<24;i+=4)
lcd_writedat(i,0x00);
}
//显示十进制一位, bit:显示位 从右0~5位 num:显示数0~9 dot:是否显示小数点
void lcd_displaybit(uchar bit , uchar num ,uchar dot )
{
uchar temp;
num%=10;
bit=(5-bit)*4;
temp=g_aDisplayBuf[num];
if(dot)
temp|=0x80;
lcd_writedat(bit++,temp);
}
//显示小时,显示在从左4位 范围:0~9999
void lcd_displayhour(uint h)
{
uint temp;
uchar flag=0;
h%=10000;
//显示千位
temp=h/1000;
if(temp)
{
flag=1;
lcd_displaybit(5,temp,0);
}
h%=1000;
//显示百位
temp=h/100;
if(temp || flag)
{
flag=1;
lcd_displaybit(4,temp,0);
}
h%=100;
//显示十位
temp=h/10;
if(temp || flag)
lcd_displaybit(3,temp,0);
h%=10;
//显示个位
lcd_displaybit(2,h,1);
}
//显示分钟
void lcd_displayminute(uchar m)
{
m%=60;
lcd_displaybit(1,m/10,0);
lcd_displaybit(0,m%10,0);
}
//掉电检测(外部)中断例程
SIGNAL(SIG_INTERRUPT0)
{
//保护计时数据
eeprom_write_word(&eep_wHour,g_wHour);
eeprom_busy_wait();
eeprom_write_byte(&eep_bMinute,g_bMinute);
//只有电压上升到正常情况才返回
while((PIND & _BV(PD2))==0)
DelayMs(1);
}
//T/C2 时钟中断例程 8秒中断一次
SIGNAL(SIG_OVERFLOW2)
{
static uchar sec=0;
//根据秒信号计时值增加
sec+=8;
if(sec>=60)
{
if(++g_bMinute>=60)
{
g_wHour++;
g_bMinute=0;
}
sec-=60;
g_bDisplayFlag=1;
}
}
int main(void)
{
DelayMs(1000);
//读取工作时间
g_bMinute=eeprom_read_byte(&eep_bMinute);
g_wHour=eeprom_read_word(&eep_wHour);
//显示器初始化并显示当前时间
lcd_init();
lcd_clear();
lcd_displayhour(g_wHour);
lcd_displayminute(g_bMinute);
//等待电压上升到18v
while((PIND & _BV(PD2))==0)
DelayMs(1);
//掉电检测外部中断使能
MCUCR=0; //INT0 低电平触发中断
GICR=_BV(INT0); //INT0 中断允许
//计时用T/C2初始化
TCCR2=0;
TCNT2=0;
ASSR=_BV(AS2); //选择异步时钟(32768Hz)
TCCR2=_BV(CS21)|_BV(CS20); //64分频 , 中断周期为0.5秒
TIMSK=_BV(TOIE2); //T/C2 溢出中断允许
sei(); //总中断允许
while(1)
{
if(g_bDisplayFlag) //时间是否得到更新
{
lcd_displayhour(g_wHour);
lcd_displayminute(g_bMinute);
g_bDisplayFlag=0;
}
else
{
DelayMs(1);
}
}//while
}//main