Rev Author Line No. Line
3460 miho 1 // timer_demo2.c
2 // -------------
3 //
4 // Example file for ATmega88 (and similar AVR chips) demonstrating how to use
5 // timer interrupt to realize Clocks with LED display.
6 //
7 // The program uses TIMER1 to generate interrupts and the interrupt routine counts
8 // fragments of second, seconds, tens of seconds etc. In the same interrupt routine
9 // the display multiplex is done. We use 4 digit 7 segment display.
10 // Common anodas are connected to LED_DIGITS_PORT and segments are connected
11 // to LED_SEGMENTS_PORT (use serial rezistors here, cca 100 OHM).
12 //
13 // (c) miho 2014 http://www.mlab.cz
14 //
15 // History
16 // 2014 01 31 - First demo
17  
18  
19 // System Configuration
20 #define F_CPU 4000000 // Do not forget to set FUSEs to external XTAL osc with DIV/8 off and connect xtal
21  
22 // LED Display - Configuration
23 #define LED_DIGITS_PORT C // Digits Port (common anodas)
24 #define LED_DIGITS 4 // Number of display digits (1..8)
25 #define LED_SEGMENTS_PORT D // Segments Port (catodas)
26 #define LED_SEGMENTS 8 // Usualy 7 or 8 (bit 0 is A)
27 #define TICKS_PER_SEC 500 // Number of interrupts per second ( >250 to look steady)
28  
29  
30 // -------------- DO NOT EDIT BELOW THIS LINE --------------
31  
32  
33 // LED Display - Internal Defs
34 #define LED_DIGITS_MASK ((1<<LED_DIGITS)-1) // For 4 digits 0x0F=0b00001111
35 #define LED_SEGMENTS_MASK ((1<<LED_SEGMENTS)-1) // For 7 segments 0x7F=0b01111111
36  
37  
38 // LED Display - Verify Configuration
39 #if (F_CPU / 64 % TICKS_PER_SEC)
40 #error "TICKS_PER_SEC" should be chosen so that "F_CPU / 64 / TICKS_PER_SEC" is a whole number!
41 #endif
42 #if ( (F_CPU / 64 / TICKS_PER_SEC) > 65536)
43 #error "TICKS_PER_SEC" should be chosen bigger (timer is long 16bit only)!
44 #endif
45  
46  
47 // Library Headers
48 #include <avr/interrupt.h>
49  
50  
51 // Compatibility ATmega8 / ATmega88
52 #ifndef TIMSK
53 #define TIMSK TIMSK1
54 #endif
55  
56  
57 // Macro for Port (enables to easily define IO signals)
58 #define GLUE(A,B) A##B
59 #define DDR(PORT_LETTER) GLUE(DDR, PORT_LETTER) // Makes DDRC from DDR(C) etc.
60 #define PORT(PORT_LETTER) GLUE(PORT,PORT_LETTER) // Makes PORTC from PORT(C)
61 #define PIN(PORT_LETTER) GLUE(PIN, PORT_LETTER) // Makes PINC from PIN(C)
62  
63  
64 // 7 Segment Decoder
65 unsigned char Convert(unsigned char Number)
66 {
67 // 7 Segment Decoder Table
68 const unsigned char Decoder[] =
69 {
70 // HGFEDCBA
71 0b00111111, // 0
72 0b00000011, // 1
73 0b01101101, // 2
74 0b01100111, // 3
75 0b01010011, // 4
76 0b01110110, // 5
77 0b01111110, // 6
78 0b00100011, // 7
79 0b01111111, // 8
80 0b01110111 // 9
81 };
82  
83 // Decoding Function
84 if(Number<sizeof(Decoder)) return Decoder[Number]; else return 0;
85 }
86  
87  
88 // Global Variable - Time Counters
89 volatile unsigned int Fractions; // 1/100s
90 volatile unsigned char Seconds, Seconds10; // Seconds and tens of seconds
91 volatile unsigned char Minutes, Minutes10; // Minutes and tens of minutes
92 volatile unsigned char Hours, Hours10; // Hours and tens of hours
93  
94  
95 // Global Variable - Display Multiplex
96 volatile unsigned char DisplayDigit; // Multiplex state 0 1 2 3 (binary counter)
97 volatile unsigned char DisplayDigitMask; // Multiplex state 1 2 4 8 (mask 1 from N)
98  
99  
100 // Interrupt Routine for TIMER1 Output Compare A
101 // Activated 500 per second
102 ISR(TIMER1_COMPA_vect)
103 {
104 // Every 1/500s
105 Fractions++;
106 if (Fractions>(TICKS_PER_SEC-1))
107 {
108 // Every 1s
109 Fractions=0;
110 Seconds++;
111 if (Seconds>9)
112 {
113 // Every 10s
114 Seconds = 0;
115 Seconds10++;
116 if (Seconds10>5)
117 {
118 // Every 1m
119 Seconds10=0;
120 Minutes++;
121 if (Minutes>9)
122 {
123 // Every 10m
124 Minutes=0;
125 Minutes10++;
126 if (Minutes10>5)
127 {
128 // Every 1h
129 Minutes10=0;
130 Hours++;
131 if (Hours10==2 && Hours>3)
132 {
133 // Every Day
134 Hours=0;
135 Hours10=0;
136 }
137 if (Hours>9)
138 {
139 // At 10 and 20 hours
140 Hours=0;
141 Hours10++;
142 }
143 }
144 }
145 }
146 }
147 }
148  
149 // LED display time multiplex - next digit
150 // 500x per second
151 DisplayDigit++;
152 DisplayDigitMask<<=1;
153 if (DisplayDigit == LED_DIGITS)
154 {
155 DisplayDigit = 0;
156 DisplayDigitMask = 1;
157 }
158  
159 // Get Segment Combination for Current Digit
160 unsigned char Segments=0;
161 switch(DisplayDigit)
162 {
163 case 0: Segments = Convert(Seconds);
164 break;
165 case 1: Segments = Convert(Seconds10);
166 break;
167 case 2: Segments = Convert(Minutes);
168 break;
169 case 3: Segments = Convert(Minutes10);
170 }
171  
172 // LED display update - All digits off
173 PORT(LED_DIGITS_PORT) &= ~LED_DIGITS_MASK; // common anoda 0=off
174 // LED display update - New segments combination
175 PORT(LED_SEGMENTS_PORT) = (PORT(LED_SEGMENTS_PORT) & ~LED_SEGMENTS_MASK) | (~Segments & LED_SEGMENTS_MASK);
176 // LED display update - One digit on
177 PORT(LED_DIGITS_PORT) |= (LED_DIGITS_MASK & DisplayDigitMask); // (common anoda 1=on)
178 }
179  
180  
181 // Main
182 int main()
183 {
184 // Enable LED Display Output
185 DDR(LED_SEGMENTS_PORT) |= LED_SEGMENTS_MASK; // 8 segments 0b11111111
186 DDR(LED_DIGITS_PORT) |= LED_DIGITS_MASK; // 4 digits 0b00001111
187  
188 // Set MAX value for Timer1
189 OCR1A = (F_CPU+64/2)/64/TICKS_PER_SEC-1; // 1/500s
190  
191 // Set Timer1 to CTC with presacaller 1/64
192 // CTC Mmode counts from 0 to value stored in OCR1A
193 // and generates interrupt every time it goes back to 0
194 TCCR1B |= (1<<CS11) | (1<<CS10) | (1<<WGM12);
195  
196 // Enable Interrupt for Timer1 OCRA
197 TIMSK |= (1<<OCIE1A);
198  
199 // Enable Global (CPU) Interrupt
200 sei();
201  
202 // Main Loop
203 for(;;)
204 {
205 // Do any job here
206 }
207  
208 return 0;
209 }