0,0 → 1,209 |
// 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 |
|
|
// 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_DIGITS_PORT C // Digits Port (common anodas) |
#define LED_DIGITS 4 // Number of display digits (1..8) |
#define LED_SEGMENTS_PORT D // Segments Port (catodas) |
#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 Decoder |
unsigned char Convert(unsigned char Number) |
{ |
// 7 Segment Decoder Table |
const unsigned char Decoder[] = |
{ |
// HGFEDCBA |
0b00111111, // 0 |
0b00000011, // 1 |
0b01101101, // 2 |
0b01100111, // 3 |
0b01010011, // 4 |
0b01110110, // 5 |
0b01111110, // 6 |
0b00100011, // 7 |
0b01111111, // 8 |
0b01110111 // 9 |
}; |
|
// Decoding Function |
if(Number<sizeof(Decoder)) return Decoder[Number]; else return 0; |
} |
|
|
// Global Variable - Time Counters |
volatile unsigned int Fractions; // 1/100s |
volatile unsigned char Seconds, Seconds10; // Seconds and tens of seconds |
volatile unsigned char Minutes, Minutes10; // Minutes and tens of minutes |
volatile unsigned char Hours, Hours10; // Hours and tens of hours |
|
|
// Global Variable - Display Multiplex |
volatile 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 second |
ISR(TIMER1_COMPA_vect) |
{ |
// Every 1/500s |
Fractions++; |
if (Fractions>(TICKS_PER_SEC-1)) |
{ |
// Every 1s |
Fractions=0; |
Seconds++; |
if (Seconds>9) |
{ |
// Every 10s |
Seconds = 0; |
Seconds10++; |
if (Seconds10>5) |
{ |
// Every 1m |
Seconds10=0; |
Minutes++; |
if (Minutes>9) |
{ |
// Every 10m |
Minutes=0; |
Minutes10++; |
if (Minutes10>5) |
{ |
// Every 1h |
Minutes10=0; |
Hours++; |
if (Hours10==2 && Hours>3) |
{ |
// Every Day |
Hours=0; |
Hours10=0; |
} |
if (Hours>9) |
{ |
// At 10 and 20 hours |
Hours=0; |
Hours10++; |
} |
} |
} |
} |
} |
} |
|
// LED display time multiplex - next digit |
// 500x per second |
DisplayDigit++; |
DisplayDigitMask<<=1; |
if (DisplayDigit == LED_DIGITS) |
{ |
DisplayDigit = 0; |
DisplayDigitMask = 1; |
} |
|
// Get Segment Combination for Current Digit |
unsigned 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); |
} |
|
// LED display update - All digits off |
PORT(LED_DIGITS_PORT) &= ~LED_DIGITS_MASK; // common anoda 0=off |
// LED display update - New segments combination |
PORT(LED_SEGMENTS_PORT) = (PORT(LED_SEGMENTS_PORT) & ~LED_SEGMENTS_MASK) | (~Segments & LED_SEGMENTS_MASK); |
// LED display update - One digit on |
PORT(LED_DIGITS_PORT) |= (LED_DIGITS_MASK & DisplayDigitMask); // (common anoda 1=on) |
} |
|
|
// Main |
int main() |
{ |
// Enable LED Display Output |
DDR(LED_SEGMENTS_PORT) |= LED_SEGMENTS_MASK; // 8 segments 0b11111111 |
DDR(LED_DIGITS_PORT) |= LED_DIGITS_MASK; // 4 digits 0b00001111 |
|
// Set MAX value for Timer1 |
OCR1A = (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 0 |
TCCR1B |= (1<<CS11) | (1<<CS10) | (1<<WGM12); |
|
// Enable Interrupt for Timer1 OCRA |
TIMSK |= (1<<OCIE1A); |
|
// Enable Global (CPU) Interrupt |
sei(); |
|
// Main Loop |
for(;;) |
{ |
// Do any job here |
} |
|
return 0; |
} |