Čítač s Atmega16
Jedná se o"inkrementační" čítač který se zrodil při přípravách stavby digitálního rychloměru pro Fiesta '97. Využívá Atmega16, lze osadit i jiným vhodným AVRkem, při nějaké té modifikaci programu, který slouží spíš jen tak pro inspiraci. Program využívá periferie External Interrupt a Timer Counter. K procesoru je připojen LED display se společnou anodou. Zapojena je i tečka, která v této aplikaci není použita, ale může se hodit pro další vývoj. Jako oscilátor lze použít i krystal nebo nastavit interní pomocí fuse bitů - návod v datasheetu. Ve schématu nevyužitý pin, reagující na náběžnou hranu - INT0(PD2) inkrementuje číslo na displeji. Je nastaven jako vstupní s interním pullupem.
Projekt pro AVR Studio lze stáhnout zde. Hex soubor je součástí.


schema
schéma zapojení

// Device: Atmega16
// Clock frequency: 8Mhz
// (c) mrkvo
// http://mrkvo.wz.cz

#include "avr/io.h"
#include <avr/interrupt.h>
#include <stdio.h>

int digit_conv(int digit) {
	int result;
	switch(digit) 
	{
		case 0x1: 	// 1
			result = 0x60;
			break;
		case 0x2: 	// 2
			result = 0xDA;
			break;
		case 0x3: 	// 3
			result = 0xF2;
			break;
		case 0x4: 	// 4
			result = 0x66;
			break;
		case 0x5: 	// 5
			result = 0xB6;
			break;
		case 0x6: 	// 6
			result = 0xBE;
			break;
		case 0x7: 	// 7
			result = 0xE0;
			break;
		case 0x8: 	// 8
			result = 0xFE;
			break;
		case 0x9: 	// 9
			result = 0xF6;
			break;
		case 0x0: 	// 0
			result = 0xFC;
			break; 
		default: 	// other
			result = 0;
			break;
	
	}
	return result;
}





volatile int counter = 0; // pocitadlo (volatile protoze se prepisuje v hl. prog. i v preruseni)
int display_i = 0; // ukazatel na aktualni aktivni segmentovku
int display[4]; // obsah displaye
char display_sprinf_result[4];

int main() {
	

	// nastaveni
    // ------------
	// spolecne anody

	#define display_anodes_offset 3 // bitovy posun od bitu 0
	#define display_anodes_port PORTD
	#define display_anodes_ddr DDRD

	display_anodes_ddr = (1<<DDD3|1<<DDD4|1<<DDD5|1<<DDD6); // nastaveni jako vystup
	display_anodes_port = (1<<PD3|1<<PD4|1<<PD5|1<<PD6);

	// data pro segmenty
	// ------------
	#define display_bus_port PORTA 
	#define display_bus_ddr DDRA

	display_bus_ddr |= 0xFF; // nastaveni jako vystup
	display_bus_port |= 0x00;

	// nastaveni external interrupt
	// ------------
	// nastavi se jako vstup + pullup (mozno konfigurovat podle tabulky 
	// na strane 52 http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf)
	// tady se to muze napsat rovnou bez pouziti #define, nebude to jiz potreba

	DDRD &=~(1<<DDD2); 
	PORTD |=(1<<PD2); 

	//  externi preruseni je aktivni pri akci na pinu INT0 
	MCUCR |= 0x3; // nastaveni akce pri nabezne hrane signalu na INT0
	GICR |= 1<<INT0; // pokud je v tomto registru nastaven 6. bit 
					 // + I je aktivni v SREG, externi preruseni je zapnuto.
	
	// TC2 obnovovani displaye
	TCNT2 = 0x00; // pocatecni hodnota timer counteru (pro jistotu)
	TCCR2 |= 0x03; // delicka "inkrementatoru" TCNT2 nastavena na 32
	TIMSK |= 1<<TOIE2; // TC2 preruseni zapnuto


	// SMYCKA
	// ------------	
	while(1) {
		sei(); // globalni povoleni interruptu

		// zde ve smycce se do pole display
		// nahraji jednotlive rady integeru counter
		// int se prevede pomoci funkce digit_conv na
		// prislusny znak na segmentu displaye
		// vysledne hodnoty se neguji protoze na port
		// jsou pripojeny katody

		sprintf(display_sprinf_result ,"%04d",counter); // vice o sprintf zde http://www.cppreference.com/wiki/c/io/sprintf

		// vysvetleni
		// vysledek funkce sprintf je v poli display_sprinf_result jako char 
		// rozlozeni na jednotlive rady [0] - tisice, [1] - stovky atd.
		// vysledny char se prevede na int a odecte se hodnota 48.
		// takto se prevadi ASCII znak cisla na ten "pravy" int.

		display[0] = ~digit_conv(((int) display_sprinf_result[3])-48); // jednotky
		display[1] = ~digit_conv(((int) display_sprinf_result[2])-48); // desitky
		display[2] = ~digit_conv(((int) display_sprinf_result[1])-48); // stovky
		display[3] = ~digit_conv(((int) display_sprinf_result[0])-48); // tisice
	}
}

SIGNAL(SIG_INTERRUPT0) { // sem skoci program counter pri externim preruseni
	counter++;
	// pokud je counter 10000, vynuluje se
	if(counter==10000) {
		counter=0;
	}
}

SIGNAL(SIG_OVERFLOW2)	// sem skoci program counter pri TC2 preruseni
{
	// obnoveni displaye
	// pri kazdem preteceni TC2 se zobrazi udaj na jedne z cislic

	// nejdrive se vynuluji data na datove lince displaye
	display_bus_port = 0xFF; // (obracene pokud je spolecna katoda)
	asm("nop"); // po kazdem zapisu hodnoty na port se doporucuje podle
				// datasheetu vlozit NOP pro synchronizaci

	// vynulujeme spolecne anody (katody)
	// situace: 
	// zname port, zname offset, zname pocet spol. anod
	// posuneme hodnotu 0xf (0b1111) o x offset doleva 
	// nasledne negujeme a masku aplikujeme pomoci AND
	// port = 		0b10111010
	// maska= 		0b00001111
	// vysledek =	0b00001010 
	display_anodes_port &= ~(0xf << display_anodes_offset);
	asm("nop");


	// rozsviceni segmentovky podle ukazatele display_i
	// situace:
	// k dispozici offset, port, pocet spod. anod
	// hodnota 0x1(0b1) se posune doleva o offset
	// + offset segmentovky(display_i) ktera se ma rozsvitit
	// zde se uplatnuje OR
	// port = 0b00001010
	// maska= 0b00010000
	// vysl.= 0b00011010
	display_anodes_port  |= (0x1 << (display_anodes_offset+display_i));	// common anode select 

	// nastaveni dat pro segment z pole display
	display_bus_port = display[display_i];
	asm("nop");
	// nasledna inkrementace ukazatele
	display_i++;
	// pokud ma ukazatel hodnotu 4, vynulovat
	if(display_i==4) {
		display_i=0;
	}
	// uz se jen vynuluje TC2
	TCNT2=0x00;
}

zdroják
výsledek
takhle to nějak ve výsledku vypadá na kontaktním poli