ATmega128 пример часов с RTC

Вот сегодня немного разобрался с асинхронным режимом таймера 0 в 128 меге и набросал с помощью апноута и даташита часы реального времени с выводом время и даты на LCD16x2. Может кому-нибудь понадобиться сие творение, поскольку в сети ничего подобного найти не мог.

  1. #include <avr/io.h>
  2. #include <avr/interrupt.h>
  3. #include <util/delay.h>
  4. #include "LCD.c"
  5.  
  6. #define F_CPU 8000000UL
  7. #define HOUR (*(unsigned char *) (0x4F0)) // 01
  8. #define MINUTE (*(unsigned char *) (0x4F1)) // 02
  9. #define SECOND (*(unsigned char *) (0x4F2)) // 03
  10. #define DAY (*(unsigned char *) (0x4F3)) // 04
  11. #define MONTH (*(unsigned char *) (0x4F4)) // 05
  12. #define YEAR_HI (*(unsigned char *) (0x4F5)) // 06
  13. #define YEAR_LO (*(unsigned char *) (0x4F6)) // 07
  14. #define FALSE 0
  15. #define TRUE 1
  16.  
  17.  
  18. // Lookup table used to determine the highest date in a specific month
  19. unsigned char MaxDate[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  20.  
  21.  
  22. /*! \brief Start Timer/Counter2 in asynchronous operation using a
  23.  * 32.768kHz crystal.
  24.  */
  25. void RTC_init(void)
  26. {
  27.  
  28.  
  29. cli(); // disabel global interrupt
  30.  
  31. TCCR0 = (1<<CS02) | (1<<CS00); // select precaler: 32.768 kHz / 128 = 1
  32. //sec between each overflow
  33.  
  34. TIMSK = 0; // disable OCIE0 and TOIE0
  35.  
  36. ASSR = (1<<AS0); // select asynchronous operation of Timer0
  37.  
  38. TCNT0 = 0; // clear TCNT0
  39.  
  40. while(ASSR & (0x01 | 0x04)==0); // wait for TCN0UB and TCR0UB to be cleared
  41.  
  42. TIFR = 0xFF; // clear interrupt-flags
  43. TIMSK = (1<<TOIE0); // enable Timer0 overflow interrupt
  44.  
  45. sei(); // enable global interrupt
  46.  
  47. // initial time and date setting
  48. HOUR = 23;
  49. MINUTE = 59;
  50. SECOND = 58;
  51. DAY = 31;
  52. MONTH = 02;
  53. YEAR_LO = 12;
  54. YEAR_HI = 20;
  55. }
  56.  
  57.  
  58. /*! \brief Updates the time and date variables
  59.  */
  60. void Time_update(void)
  61. {
  62. unsigned char LeapYear = FALSE;
  63.  
  64. //the variable SECOND gets updated in the Timer0
  65. //Overflow Interrupt Routine every second
  66.  
  67. if(SECOND > 59) // if one minute
  68. {
  69. SECOND = 0; // clear SECOND
  70. MINUTE++; // increment MINUTE
  71.  
  72. if(MINUTE > 59) // if one hour
  73. {
  74. MINUTE = 0; // clear MINUTE
  75. HOUR++; // increment HOUR
  76.  
  77. if(HOUR > 23) // if one hour
  78. {
  79. HOUR = 0; // clear HOUR
  80. DAY++; // increment DAY
  81.  
  82. if(MONTH == 2) // if it's February
  83. {
  84. // check for leap year
  85. if(!YEAR_LO) // if YEAR_LO = 0, (a century has passed)
  86. {
  87. if(!(YEAR_HI%4))// check if YEAR_HI is divisible by 4
  88. LeapYear = TRUE; // then it is a leap year
  89. }
  90. else if(!(YEAR_LO%4) & (YEAR_LO != 0))
  91. // else if YEAR_LO is divisible by 4 and not zero
  92. LeapYear = TRUE; // then it's a leap year
  93. }
  94.  
  95. if(DAY > (MaxDate[MONTH] + LeapYear)) // if a whole month has passed
  96. {
  97. DAY = 1; // clear DAY
  98. MONTH++; // increment MONTH
  99.  
  100. if(MONTH > 12) // if one year
  101. {
  102. MONTH = 1; // clear MONTH
  103. YEAR_LO++; // increment YEAR_LO
  104.  
  105. if(YEAR_LO > 99) // if one century
  106. {
  107. YEAR_LO = 0; // clear YEAR_LO
  108. YEAR_HI++; // increment YEAR_HI
  109.  
  110. if(YEAR_HI > 99) // if 100 centuries
  111. YEAR_HI = 0; // the AVR is still going strong :)
  112. }
  113. }
  114. }
  115. }
  116. }
  117. }
  118. }
  119.  
  120.  
  121. /*! \brief Increment the varible SECOND by one - interrupt function.
  122.  */
  123. ISR(TIMER0_OVF_vect)
  124. {
  125. unsigned char key[10]= {'0','1','2','3','4','5','6','7','8','9'};
  126.  
  127. unsigned char sec_des,sec_ed,min_des,min_ed,hour_des,hour_ed;
  128. unsigned char day_des,day_ed,mon_des,mon_ed,year_th,year_hand,year_des,year_ed;
  129.  
  130. hour_des=HOUR/10;hour_ed=HOUR-hour_des*10;
  131. min_des=MINUTE/10;min_ed=MINUTE-min_des*10 ;
  132. sec_des=SECOND/10;sec_ed=SECOND-sec_des*10 ;
  133. day_des=DAY/10;day_ed=DAY-day_des*10;
  134. mon_des=MONTH/10;mon_ed=MONTH-mon_des*10;
  135. year_th=YEAR_HI/10;year_hand=YEAR_HI-year_th*10;
  136. year_des=YEAR_LO/10;year_ed=YEAR_LO-year_des*10;
  137.  
  138.  
  139. lcd_com(0x80); //Ставим курсор на первую строку
  140. lcd_dat(key[hour_des]);
  141. lcd_dat(key[hour_ed]);
  142. lcd_dat(0x3A);
  143. lcd_dat(key[min_des]);
  144. lcd_dat(key[min_ed]);
  145. lcd_dat(0x3A);
  146. lcd_dat(key[sec_des]);
  147. lcd_dat(key[sec_ed]);
  148.  
  149. lcd_com(0xC0);
  150. lcd_dat(key[day_des]);
  151. lcd_dat(key[day_ed]);
  152. lcd_dat(0x2E);
  153. lcd_dat(key[mon_des]);
  154. lcd_dat(key[mon_ed]);
  155. lcd_dat(0x2E);
  156. lcd_dat(key[year_th]);
  157. lcd_dat(key[year_hand]);
  158. lcd_dat(key[year_des]);
  159. lcd_dat(key[year_ed]);
  160.  
  161. SECOND++; // increment second
  162. }
  163.  
  164. void main (void)
  165. {
  166. RTC_init();
  167. lcd_init();
  168. sei();
  169. while(1)
  170. {
  171. Time_update();
  172. }
  173. }

Немного нужно поправить код,

Немного нужно поправить код, при инициалзации ассинхронного режима, в самом начале необходимо установить задержку в 1 секунду, чтобы таймер на медленном квартце успел прийти в себя. Судя по апноутам это особо важно, если данный режим используется для вывода контроллера из спящего режима.