ATmega128 пример часов с RTC
Вот сегодня немного разобрался с асинхронным режимом таймера 0 в 128 меге и набросал с помощью апноута и даташита часы реального времени с выводом время и даты на LCD16x2. Может кому-нибудь понадобиться сие творение, поскольку в сети ничего подобного найти не мог.
#include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include "LCD.c" #define F_CPU 8000000UL #define HOUR (*(unsigned char *) (0x4F0)) // 01 #define MINUTE (*(unsigned char *) (0x4F1)) // 02 #define SECOND (*(unsigned char *) (0x4F2)) // 03 #define DAY (*(unsigned char *) (0x4F3)) // 04 #define MONTH (*(unsigned char *) (0x4F4)) // 05 #define YEAR_HI (*(unsigned char *) (0x4F5)) // 06 #define YEAR_LO (*(unsigned char *) (0x4F6)) // 07 #define FALSE 0 #define TRUE 1 // Lookup table used to determine the highest date in a specific month unsigned char MaxDate[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /*! \brief Start Timer/Counter2 in asynchronous operation using a * 32.768kHz crystal. */ void RTC_init(void) { cli(); // disabel global interrupt TCCR0 = (1<<CS02) | (1<<CS00); // select precaler: 32.768 kHz / 128 = 1 //sec between each overflow TIMSK = 0; // disable OCIE0 and TOIE0 ASSR = (1<<AS0); // select asynchronous operation of Timer0 TCNT0 = 0; // clear TCNT0 while(ASSR & (0x01 | 0x04)==0); // wait for TCN0UB and TCR0UB to be cleared TIFR = 0xFF; // clear interrupt-flags TIMSK = (1<<TOIE0); // enable Timer0 overflow interrupt sei(); // enable global interrupt // initial time and date setting HOUR = 23; MINUTE = 59; SECOND = 58; DAY = 31; MONTH = 02; YEAR_LO = 12; YEAR_HI = 20; } /*! \brief Updates the time and date variables */ void Time_update(void) { unsigned char LeapYear = FALSE; //the variable SECOND gets updated in the Timer0 //Overflow Interrupt Routine every second if(SECOND > 59) // if one minute { SECOND = 0; // clear SECOND MINUTE++; // increment MINUTE if(MINUTE > 59) // if one hour { MINUTE = 0; // clear MINUTE HOUR++; // increment HOUR if(HOUR > 23) // if one hour { HOUR = 0; // clear HOUR DAY++; // increment DAY if(MONTH == 2) // if it's February { // check for leap year if(!YEAR_LO) // if YEAR_LO = 0, (a century has passed) { if(!(YEAR_HI%4))// check if YEAR_HI is divisible by 4 LeapYear = TRUE; // then it is a leap year } else if(!(YEAR_LO%4) & (YEAR_LO != 0)) // else if YEAR_LO is divisible by 4 and not zero LeapYear = TRUE; // then it's a leap year } if(DAY > (MaxDate[MONTH] + LeapYear)) // if a whole month has passed { DAY = 1; // clear DAY MONTH++; // increment MONTH if(MONTH > 12) // if one year { MONTH = 1; // clear MONTH YEAR_LO++; // increment YEAR_LO if(YEAR_LO > 99) // if one century { YEAR_LO = 0; // clear YEAR_LO YEAR_HI++; // increment YEAR_HI if(YEAR_HI > 99) // if 100 centuries YEAR_HI = 0; // the AVR is still going strong :) } } } } } } } /*! \brief Increment the varible SECOND by one - interrupt function. */ ISR(TIMER0_OVF_vect) { unsigned char key[10]= {'0','1','2','3','4','5','6','7','8','9'}; unsigned char sec_des,sec_ed,min_des,min_ed,hour_des,hour_ed; unsigned char day_des,day_ed,mon_des,mon_ed,year_th,year_hand,year_des,year_ed; hour_des=HOUR/10;hour_ed=HOUR-hour_des*10; min_des=MINUTE/10;min_ed=MINUTE-min_des*10 ; sec_des=SECOND/10;sec_ed=SECOND-sec_des*10 ; day_des=DAY/10;day_ed=DAY-day_des*10; mon_des=MONTH/10;mon_ed=MONTH-mon_des*10; year_th=YEAR_HI/10;year_hand=YEAR_HI-year_th*10; year_des=YEAR_LO/10;year_ed=YEAR_LO-year_des*10; lcd_com(0x80); //Ставим курсор на первую строку lcd_dat(key[hour_des]); lcd_dat(key[hour_ed]); lcd_dat(0x3A); lcd_dat(key[min_des]); lcd_dat(key[min_ed]); lcd_dat(0x3A); lcd_dat(key[sec_des]); lcd_dat(key[sec_ed]); lcd_com(0xC0); lcd_dat(key[day_des]); lcd_dat(key[day_ed]); lcd_dat(0x2E); lcd_dat(key[mon_des]); lcd_dat(key[mon_ed]); lcd_dat(0x2E); lcd_dat(key[year_th]); lcd_dat(key[year_hand]); lcd_dat(key[year_des]); lcd_dat(key[year_ed]); SECOND++; // increment second } void main (void) { RTC_init(); lcd_init(); sei(); while(1) { Time_update(); } }
»
Немного нужно поправить код,
Немного нужно поправить код, при инициалзации ассинхронного режима, в самом начале необходимо установить задержку в 1 секунду, чтобы таймер на медленном квартце успел прийти в себя. Судя по апноутам это особо важно, если данный режим используется для вывода контроллера из спящего режима.