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