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
3618 miho 17 // 2014 02 07 - Added support for Comon Anoda / Comon Catoda
3460 miho 18  
19  
20 // System Configuration
21 #define F_CPU 4000000 // Do not forget to set FUSEs to external XTAL osc with DIV/8 off and connect xtal
22  
23 // LED Display - Configuration
3618 miho 24 #define LED_COMON_ANODA 0 // 0=Comon Catoda / 1=Comon Anoda
25 #define LED_DIGITS_PORT C // Digits Port (common anodas or comon catodas)
26 #define LED_DIGITS 6 // Number of display digits (1..8)
27 #define LED_SEGMENTS_PORT D // Segments Port (catodas or anodas)
3460 miho 28 #define LED_SEGMENTS 8 // Usualy 7 or 8 (bit 0 is A)
29 #define TICKS_PER_SEC 500 // Number of interrupts per second ( >250 to look steady)
30  
31  
32 // -------------- DO NOT EDIT BELOW THIS LINE --------------
33  
34  
35 // LED Display - Internal Defs
36 #define LED_DIGITS_MASK ((1<<LED_DIGITS)-1) // For 4 digits 0x0F=0b00001111
37 #define LED_SEGMENTS_MASK ((1<<LED_SEGMENTS)-1) // For 7 segments 0x7F=0b01111111
38  
39  
40 // LED Display - Verify Configuration
41 #if (F_CPU / 64 % TICKS_PER_SEC)
42 #error "TICKS_PER_SEC" should be chosen so that "F_CPU / 64 / TICKS_PER_SEC" is a whole number!
43 #endif
44 #if ( (F_CPU / 64 / TICKS_PER_SEC) > 65536)
45 #error "TICKS_PER_SEC" should be chosen bigger (timer is long 16bit only)!
46 #endif
47  
48  
49 // Library Headers
50 #include <avr/interrupt.h>
51  
52  
53 // Compatibility ATmega8 / ATmega88
54 #ifndef TIMSK
55 #define TIMSK TIMSK1
56 #endif
57  
58  
59 // Macro for Port (enables to easily define IO signals)
60 #define GLUE(A,B) A##B
61 #define DDR(PORT_LETTER) GLUE(DDR, PORT_LETTER) // Makes DDRC from DDR(C) etc.
62 #define PORT(PORT_LETTER) GLUE(PORT,PORT_LETTER) // Makes PORTC from PORT(C)
63 #define PIN(PORT_LETTER) GLUE(PIN, PORT_LETTER) // Makes PINC from PIN(C)
64  
65  
66 // 7 Segment Decoder
67 unsigned char Convert(unsigned char Number)
68 {
69 // 7 Segment Decoder Table
3618 miho 70 // A
71 // ---
72 // F| | B
73 // | G |
74 // ---
75 // E| | C
76 // | |
77 // --- * H
78 // D
3460 miho 79 const unsigned char Decoder[] =
80 {
81 // HGFEDCBA
82 0b00111111, // 0
3618 miho 83 0b00000110, // 1
84 0b01011011, // 2
85 0b01001111, // 3
86 0b01100110, // 4
87 0b01101101, // 5
88 0b01111101, // 6
89 0b00000111, // 7
3460 miho 90 0b01111111, // 8
3618 miho 91 0b01101111, // 9
92 0b01110111, // A
93 0b01111100, // b
94 0b00111001, // C
95 0b01011100, // d
96 0b01111001, // E
97 0b01110000 // F
3460 miho 98 };
99  
100 // Decoding Function
101 if(Number<sizeof(Decoder)) return Decoder[Number]; else return 0;
102 }
103  
104  
105 // Global Variable - Time Counters
106 volatile unsigned int Fractions; // 1/100s
107 volatile unsigned char Seconds, Seconds10; // Seconds and tens of seconds
108 volatile unsigned char Minutes, Minutes10; // Minutes and tens of minutes
109 volatile unsigned char Hours, Hours10; // Hours and tens of hours
110  
111  
112 // Global Variable - Display Multiplex
113 volatile unsigned char DisplayDigit; // Multiplex state 0 1 2 3 (binary counter)
114 volatile unsigned char DisplayDigitMask; // Multiplex state 1 2 4 8 (mask 1 from N)
115  
116  
117 // Interrupt Routine for TIMER1 Output Compare A
118 // Activated 500 per second
119 ISR(TIMER1_COMPA_vect)
120 {
121 // Every 1/500s
122 Fractions++;
123 if (Fractions>(TICKS_PER_SEC-1))
124 {
125 // Every 1s
126 Fractions=0;
127 Seconds++;
128 if (Seconds>9)
129 {
130 // Every 10s
131 Seconds = 0;
132 Seconds10++;
133 if (Seconds10>5)
134 {
135 // Every 1m
136 Seconds10=0;
137 Minutes++;
138 if (Minutes>9)
139 {
140 // Every 10m
141 Minutes=0;
142 Minutes10++;
143 if (Minutes10>5)
144 {
145 // Every 1h
146 Minutes10=0;
147 Hours++;
148 if (Hours10==2 && Hours>3)
149 {
150 // Every Day
151 Hours=0;
152 Hours10=0;
153 }
154 if (Hours>9)
155 {
156 // At 10 and 20 hours
157 Hours=0;
158 Hours10++;
159 }
160 }
161 }
162 }
163 }
164 }
165  
166 // LED display time multiplex - next digit
167 // 500x per second
168 DisplayDigit++;
169 DisplayDigitMask<<=1;
170 if (DisplayDigit == LED_DIGITS)
171 {
172 DisplayDigit = 0;
173 DisplayDigitMask = 1;
174 }
175  
176 // Get Segment Combination for Current Digit
177 unsigned char Segments=0;
178 switch(DisplayDigit)
179 {
180 case 0: Segments = Convert(Seconds);
181 break;
182 case 1: Segments = Convert(Seconds10);
183 break;
184 case 2: Segments = Convert(Minutes);
185 break;
186 case 3: Segments = Convert(Minutes10);
3618 miho 187 break;
188 case 4: Segments = Convert(Hours);
189 break;
190 case 5: Segments = Convert(Hours10);
191 break;
3460 miho 192 }
3618 miho 193 #if LED_COMON_ANODA==0
194 // LED display update - All digits off
195 PORT(LED_DIGITS_PORT) |= LED_DIGITS_MASK; // common catoda 1=off
196 // LED display update - New segments combination
197 PORT(LED_SEGMENTS_PORT) = (PORT(LED_SEGMENTS_PORT) & ~LED_SEGMENTS_MASK) | (Segments & LED_SEGMENTS_MASK);
198 // LED display update - One digit on
199 PORT(LED_DIGITS_PORT) &= ~(LED_DIGITS_MASK & DisplayDigitMask); // (common anoda 0=on)
200 #else
201 // LED display update - All digits off
202 PORT(LED_DIGITS_PORT) &= ~LED_DIGITS_MASK; // common anoda 0=off
203 // LED display update - New segments combination
204 PORT(LED_SEGMENTS_PORT) = (PORT(LED_SEGMENTS_PORT) & ~LED_SEGMENTS_MASK) | (~Segments & LED_SEGMENTS_MASK);
205 // LED display update - One digit on
206 PORT(LED_DIGITS_PORT) |= (LED_DIGITS_MASK & DisplayDigitMask); // (common anoda 1=on)
207 #endif
3460 miho 208 }
209  
210  
211 // Main
212 int main()
213 {
214 // Enable LED Display Output
215 DDR(LED_SEGMENTS_PORT) |= LED_SEGMENTS_MASK; // 8 segments 0b11111111
216 DDR(LED_DIGITS_PORT) |= LED_DIGITS_MASK; // 4 digits 0b00001111
217  
218 // Set MAX value for Timer1
219 OCR1A = (F_CPU+64/2)/64/TICKS_PER_SEC-1; // 1/500s
220  
221 // Set Timer1 to CTC with presacaller 1/64
222 // CTC Mmode counts from 0 to value stored in OCR1A
223 // and generates interrupt every time it goes back to 0
224 TCCR1B |= (1<<CS11) | (1<<CS10) | (1<<WGM12);
225  
226 // Enable Interrupt for Timer1 OCRA
227 TIMSK |= (1<<OCIE1A);
228  
229 // Enable Global (CPU) Interrupt
230 sei();
231  
232 // Main Loop
233 for(;;)
234 {
235 // Do any job here
236 }
237  
238 return 0;
239 }