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 |
} |