// timer_demo2.c// -------------//// Example file for ATmega88 (and similar AVR chips) demonstrating how to use// timer interrupt to realize Clocks with LED display.//// The program uses TIMER1 to generate interrupts and the interrupt routine counts// fragments of second, seconds, tens of seconds etc. In the same interrupt routine// the display multiplex is done. We use 4 digit 7 segment display.// Common anodas are connected to LED_DIGITS_PORT and segments are connected// to LED_SEGMENTS_PORT (use serial rezistors here, cca 100 OHM).//// (c) miho 2014 http://www.mlab.cz//// History// 2014 01 31 - First demo// 2014 02 07 - Added support for Comon Anoda / Comon Catoda// System Configuration#define F_CPU 4000000 // Do not forget to set FUSEs to external XTAL osc with DIV/8 off and connect xtal// LED Display - Configuration#define LED_COMON_ANODA 0 // 0=Comon Catoda / 1=Comon Anoda#define LED_DIGITS_PORT C // Digits Port (common anodas or comon catodas)#define LED_DIGITS 6 // Number of display digits (1..8)#define LED_SEGMENTS_PORT D // Segments Port (catodas or anodas)#define LED_SEGMENTS 8 // Usualy 7 or 8 (bit 0 is A)#define TICKS_PER_SEC 500 // Number of interrupts per second ( >250 to look steady)// -------------- DO NOT EDIT BELOW THIS LINE --------------// LED Display - Internal Defs#define LED_DIGITS_MASK ((1<<LED_DIGITS)-1) // For 4 digits 0x0F=0b00001111#define LED_SEGMENTS_MASK ((1<<LED_SEGMENTS)-1) // For 7 segments 0x7F=0b01111111// LED Display - Verify Configuration#if (F_CPU / 64 % TICKS_PER_SEC)#error "TICKS_PER_SEC" should be chosen so that "F_CPU / 64 / TICKS_PER_SEC" is a whole number!#endif#if ( (F_CPU / 64 / TICKS_PER_SEC) > 65536)#error "TICKS_PER_SEC" should be chosen bigger (timer is long 16bit only)!#endif// Library Headers#include <avr/interrupt.h>// Compatibility ATmega8 / ATmega88#ifndef TIMSK#define TIMSK TIMSK1#endif// Macro for Port (enables to easily define IO signals)#define GLUE(A,B) A##B#define DDR(PORT_LETTER) GLUE(DDR, PORT_LETTER) // Makes DDRC from DDR(C) etc.#define PORT(PORT_LETTER) GLUE(PORT,PORT_LETTER) // Makes PORTC from PORT(C)#define PIN(PORT_LETTER) GLUE(PIN, PORT_LETTER) // Makes PINC from PIN(C)// 7 Segment Decoderunsigned char Convert(unsigned char Number){// 7 Segment Decoder Table// A// ---// F| | B// | G |// ---// E| | C// | |// --- * H// Dconst unsigned char Decoder[] ={// HGFEDCBA0b00111111, // 00b00000110, // 10b01011011, // 20b01001111, // 30b01100110, // 40b01101101, // 50b01111101, // 60b00000111, // 70b01111111, // 80b01101111, // 90b01110111, // A0b01111100, // b0b00111001, // C0b01011100, // d0b01111001, // E0b01110000 // F};// Decoding Functionif(Number<sizeof(Decoder)) return Decoder[Number]; else return 0;}// Global Variable - Time Countersvolatile unsigned int Fractions; // 1/100svolatile unsigned char Seconds, Seconds10; // Seconds and tens of secondsvolatile unsigned char Minutes, Minutes10; // Minutes and tens of minutesvolatile unsigned char Hours, Hours10; // Hours and tens of hours// Global Variable - Display Multiplexvolatile unsigned char DisplayDigit; // Multiplex state 0 1 2 3 (binary counter)volatile unsigned char DisplayDigitMask; // Multiplex state 1 2 4 8 (mask 1 from N)// Interrupt Routine for TIMER1 Output Compare A// Activated 500 per secondISR(TIMER1_COMPA_vect){// Every 1/500sFractions++;if (Fractions>(TICKS_PER_SEC-1)){// Every 1sFractions=0;Seconds++;if (Seconds>9){// Every 10sSeconds = 0;Seconds10++;if (Seconds10>5){// Every 1mSeconds10=0;Minutes++;if (Minutes>9){// Every 10mMinutes=0;Minutes10++;if (Minutes10>5){// Every 1hMinutes10=0;Hours++;if (Hours10==2 && Hours>3){// Every DayHours=0;Hours10=0;}if (Hours>9){// At 10 and 20 hoursHours=0;Hours10++;}}}}}}// LED display time multiplex - next digit// 500x per secondDisplayDigit++;DisplayDigitMask<<=1;if (DisplayDigit == LED_DIGITS){DisplayDigit = 0;DisplayDigitMask = 1;}// Get Segment Combination for Current Digitunsigned char Segments=0;switch(DisplayDigit){case 0: Segments = Convert(Seconds);break;case 1: Segments = Convert(Seconds10);break;case 2: Segments = Convert(Minutes);break;case 3: Segments = Convert(Minutes10);break;case 4: Segments = Convert(Hours);break;case 5: Segments = Convert(Hours10);break;}#if LED_COMON_ANODA==0// LED display update - All digits offPORT(LED_DIGITS_PORT) |= LED_DIGITS_MASK; // common catoda 1=off// LED display update - New segments combinationPORT(LED_SEGMENTS_PORT) = (PORT(LED_SEGMENTS_PORT) & ~LED_SEGMENTS_MASK) | (Segments & LED_SEGMENTS_MASK);// LED display update - One digit onPORT(LED_DIGITS_PORT) &= ~(LED_DIGITS_MASK & DisplayDigitMask); // (common anoda 0=on)#else// LED display update - All digits offPORT(LED_DIGITS_PORT) &= ~LED_DIGITS_MASK; // common anoda 0=off// LED display update - New segments combinationPORT(LED_SEGMENTS_PORT) = (PORT(LED_SEGMENTS_PORT) & ~LED_SEGMENTS_MASK) | (~Segments & LED_SEGMENTS_MASK);// LED display update - One digit onPORT(LED_DIGITS_PORT) |= (LED_DIGITS_MASK & DisplayDigitMask); // (common anoda 1=on)#endif}// Mainint main(){// Enable LED Display OutputDDR(LED_SEGMENTS_PORT) |= LED_SEGMENTS_MASK; // 8 segments 0b11111111DDR(LED_DIGITS_PORT) |= LED_DIGITS_MASK; // 4 digits 0b00001111// Set MAX value for Timer1OCR1A = (F_CPU+64/2)/64/TICKS_PER_SEC-1; // 1/500s// Set Timer1 to CTC with presacaller 1/64// CTC Mmode counts from 0 to value stored in OCR1A// and generates interrupt every time it goes back to 0TCCR1B |= (1<<CS11) | (1<<CS10) | (1<<WGM12);// Enable Interrupt for Timer1 OCRATIMSK |= (1<<OCIE1A);// Enable Global (CPU) Interruptsei();// Main Loopfor(;;){// Do any job here}return 0;}