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