Sari la conținut
ELFORUM - Forumul electronistilor

Proiecte Atmel-based pentru incepatori


ole

Postări Recomandate

Aici se vor posta doar proiecte pentru incepatori gen led-blinker sau mai stiu eu ce licurici, chiar si productii proprii. Va rog sa scrieti doar o scurta descriere si un link catre proiect. Ideal ar fi ca proiectul sa aiba o descriere cat mai detaliata si codul comentat "ca la gradinita".

Link spre comentariu
  • Răspunsuri 14
  • Creat
  • Ultimul Răspuns

Top autori în acest subiect

  • Stefan

    2

  • ole

    2

  • caddyct

    1

  • ionu x

    1

Top autori în acest subiect

  • 2 săptămâni mai târziu...
  • 3 luni mai târziu...
  • 4 săptămâni mai târziu...
  • 2 luni mai târziu...
  • 2 luni mai târziu...
  • 9 luni mai târziu...
Vizitator radur

Hai ca sparg eu gheata dupa un an, motivat fiind de faptul ca n-am gasit pe acest forum bucati de cod explicate "ca la gradinita".

 

Va salut, incepator fiind propun altor incepatori un ceas binar cu afisaj pe 12 leduri, folosind pe post de creier orice attiny cu 8 pini (in cazul de fata attiny25, insa codul se poate modifica foarte usor pentru attiny13a de exemplu).

Sa incepem :speriat

 

Pentru inceput vom incepe cu schema de legare a ledurilor la uC, pentru ca avem doar 6 pini disponibili (de fapt, la inceput, vor fi doar 5 pini disponibili pentru ca pinul de reset ne trebuie la programare) le vom lega in mod charlieplex si le vom numerota in ordinea adaugarii lor (de la 0 la 11).

De retinut ca in modul charlieplex nu se poate aprinde mai mult de 1 led simultan, asadar pentru a aprinde mai multe leduri ne vom folosi de faptul ca ochiul uman nu mai percepe diferente peste 60hz sa zicem. Cu alte cuvinte ca sa aprindem 2 leduri "simultan" pentru noi, vom aprinde si vom stinge fiecare led in parte, succesiv, foarte rapid.

Avand in vedere ca vor sta aprinse o perioada relativ scurta ledurile ar trebui alese de tipul "ultrabright".

Rezistentele trebuie alese astfel inca 2 rezistente din schema atasate la 1 led sa dicteze curentul acestuia. In cazul de fata am folosit 4 rezistente de 220Ohm, astfel curentul ce va curge prin fiecare led este I = U / 2x220Ohm, unde U este tensiunea de alimentare - tensiunea necesara aprinderii ledului (in cazul nostru 5 - 3.1), rezulta ca I = (5-3.1) / 440 = 0.004 Ah, sau mai pe romaneste 4 miliamperi!

PB0,PB1,PB3,PB4 sunt pinii uC-ului, conform datasheet-ului, acestia sunt pinii 5,6,2,3. Veti observa ca am lasat liberi pinii PB2 si PB5 respectiv INT0 si RESET. Pinul INT0 il vom folosi pentru a seta ceasul (cand oi avea timp sa implementez aceasta functie), iar pinul RESET il folosim pentru programare. Retineti asta daca adaptati codul pentru alt attiny.

In primul atasament gasiti cablajul fara rezistente pentru aceasta schema (vedere dinspre leduri, chiar daca este "inversat" fata de schema).[attachment=0]cablaj leduri.png[/attachment]

 

Pentru alimentarea uC-ului se presupune ca folositi o sursa stabilizata de 5V.

Vom prezenta 2 programele, primul se foloseste de oscilatorul intern al uC-ului, iar pentru al 2-lea program vom incerca sa obtinem un ceas mai precis folosind un generator de semnal de 1Hz sau 2Hz. Daca sunteti norocosi le puteti gasi in ceasurile cu quartz. exemplu1, exemplu2. Daca nu sunteti norocosi veti folosi PCF8583, un circuit integrat RTC care are marele avantaj ca la pornire (fara a fi setat implicit) sa genereze un semnal de 1Hz pe pinul INT (cel putin asa reiese din datasheet sectiunea 7.11).

Acestea fiind spuse sa prezentam si niste cod. Eu personal folosesc AVR Studio 5 !

 

Cazul 1, folosind oscilatorul intern, pseudocod

 

//include headere utile//defineste variabile (secunde,minute,ore,etc)//functie care incrementeaza secundele pana la 59, minutele pana la 59, orele pana la 23//functie care stinge toate ledurile//functie care defineste ledurile folosite pentru afisarea minutelor//functie care defineste ledurile folosite pentru afisarea orelor//functie care aprinde ledurile pentru minute in functie de variabila "minute"//functie care aprinde ledurile pentru ore in functie de variabila "ore"//functie ce intrerupe bucla while(1) din main() odata la o secunda pentru a rula cod in afara buclei//in cazul nostru va rula functia care incrementeaza secundele/minutele/orele si va aprinde un led//prgramul propriuzisint main(void){	//seteaza chestii initiale(gen starea porturilor, interrupturi, etc)	//bucla infinita care sa cheme functiile ce aprind ledurile pentru minute si ore}

Si acum pentru fiecare portiune de pseudocod vom adauga cod real.

 

Headerele utile, in cazul de fata, ne permit accesarea unor variabile definite deja, de exemplu vom putea folosi PORTB sau PIN1 daca includem headerul io.h

 

//include headere utile#define F_CPU 1000000UL //frecventa in Hz la care ruleaza uC-ul, Attiny25 vine setat din fabrica sa functioneze pe oscilatorul intern de 1Mhz#include <util/delay.h> //necesar pentru a putea folosi _delay_ms()#include <avr/io.h> //definitii pentru porturi#include <avr/interrupt.h> //definitii pentru ISR-uri

Variabilele sunt ... variabile si trebuiesc definite :) Asadar ca sa putem folosi niste valori trebuie sa stim ce fel de valori sunt acestea si ce valoare au.

Declaram secundele minutele si orele de tipul "unsigned 8 bit integer", ceea ce inseamna ca pot avea valori intre 0 si 255, volatile ca sa fie "vazute" si in bucla infinita din main().

//defineste variabile (secunde,minute,ore,etc)volatile uint8_t seconds, minutes, hours; //valorile se schimba in ISR (Interrupt Service Request), ca sa stie si bucla while(1) ca s-au schimbat trebuie sa fie VOLATILEuint8_t overflow_counter; //o simpla variabila pe care o vom folosi sa stim de cate ori o valoare "da pe dinafara"

O functie ce incrementeaza secundele pana la 59, cand secundele ajung la 60 setam secundele = 0 si incrementam minutele cu 1, la fel pentru ore.

Aceasta functie o vom executa odata pe secunda.

 

//functie care incrementeaza secundele pana la 59, minutele pana la 59, orele pana la 23void time_tick() //numele functiei{	seconds++; //incrementeaza secundele cu 1	if (seconds > 59) //daca secundele sunt peste 59	{		seconds = 0; //reseteaza secundele		minutes++; //si incrementeaza minutele cu 1		if (minutes > 59)		{			minutes = 0;			hours++;			if (hours > 23)			{				hours = 0;			}		}	}}

Metoda prin care am legat ledurile (charlieplexing) nu ne permite sa aprindem mai mult de un led simultan, asadar inainte de a aprinde vreun led ne trebuie o functie care sa stinga toate ledurile.

Daca urmariti schema de legare a ledurilor la uC veti observa ca putem face asta in 3 feluri:

1. Setam toti pinii folositi pe OUTPUT LOW (in schema ca si cum am avea toate ledurile conectate la cate un semnal logic LOW)

2. Setam toti pinii folositi pe INPUT LOW (in schema ca si cum nici un led nu ar fi conectat la nici un fel de semnal)

3. Setam toti pinii folositi pe INPUT HIGH (in schema ca si cum toate ledurile ar fi conectate la cate un semnal logic HIGH, INSA cu rezistentele modificate la valori de gen 20KOhm-40KOhm

 

In loc de PB0, PB1, etc puteam folosi binemersi DDB0, DDB1, sau chiar 0 si 1 pentru ca in headerul io.h sunt definite astfel: PB0 = 0 si DDB0 = 0.

Setam intai daca portul este HIGH sau LOW si dupa aceea daca este INPUT sau OUTPUT pentru a evita o stare accidentala OUTPUT_HIGH

 

//functie care stinge toate ledurilevoid leds_off() //functie ce seteaza (doar) pinii folositi de noi OUTPUT_LOW{	PORTB &= ~(1<<PB0) & ~(1<<PB1) & ~(1<<PB3) & ~(1<<PB4); //clear bit, seteaza bitii PB0, PB1, PB3, PB4 pe 0, adica LOW	DDRB |= (1<<PB0) | (1<<PB1) | (1<<PB3) | (1<<PB4); //set bit, seteaza bitii PB0, PB1, PB3, PB4 pe 1, adica OUTPUT}

Dupa urmatorul tabel stim exact in ce stare sa fie pinii pentru a aprinde cutarica led.

H = OUTPUT_HIGH

L = OUTPUT_LOW

X = INPUT_LOW

 

PB0	PB1	PB3	PB4	LEDH		L		X		X		0L		H		X		X		1X		H		L		X		2X		L		H		X		3H		X		L		X		4L		X		H		X		5X		X		H		L		6X		X		L		H		7X		H		X		L		8X		L		X		H		9H		X		X		L		10L		X		X		H		11

Astfel ca sa aprindem ledul 0 trebuie sa setam o stare implicita, si anume ca nici un bec nu e aprins, si anume ca PB0,PB1,PB3,PB0 sunt OUTPUT_LOW, si anume fix ceea ce face functia leds_off();

Dupa aceea trebuie sa setam PB3 si PB4 pe INPUT.

Dupa aceea setam PB0 pe HIGH.

 

Daca facem invers nu prea e bine, si anume:

Setam intai PB0 pe HIGH. (rezultatul final este ca toti pinii sunt OUTPUT, 1 pin e HIGH, 3 sunt LOW, vezi pe schema de legare a ledurilor la uC ce se intampla in cazul asta.

 

 

Sa recapitulam pentru LED0

stare initiala: ledul este stins

PB0	PB1	PB3	PB4	LEDL		L		L		L		0
Setam PB3 si PB4 pe INPUT: ledul este stins

PB0	PB1	PB3	PB4	LEDL		L		X		X		0
Setam PB0 pe HIGH.: ledul se aprinde

PB0	PB1	PB3	PB4	LEDH		L		X		X		0

Pentru minute nu avem nevoie decat de definitiile ledurilor de la 0 la 5. Vezi cablajul cu leduri.

 

//functie care defineste ledurile folosite pentru afisarea minutelorvoid minute_leds(uint8_t led) //functie ce are ca argument variabila led, se cheama ca minute_leds(led); unde led trebuie sa fie o valoare de la 0 la 5{	//inainte sa aprindem vreun led le stingem pe toate	leds_off();		switch(led) //in functie de valoarea variabilei "led" va executa codul respectiv	{		case 0: //setari pentru led 0		DDRB &= ~(1<<PB3) & ~(1<<PB4); //clear bit, seteaza PB3, PB4 pe 0, adica INPUT		PORTB |= (1<<PB0); //set bit, seteaza PB0 pe 1, adica HIGH		break;		case 1:		DDRB &= ~(1<<PB3) & ~(1<<PB4); //etc 		PORTB |= (1<<PB1);		break;		case 2:		DDRB &= ~(1<<PB0) & ~(1<<PB4);		PORTB |= (1<<PB1);		break;		case 3:		DDRB &= ~(1<<PB0) & ~(1<<PB4);		PORTB |= (1<<PB3);		break;		case 4:		DDRB &= ~(1<<PB1) & ~(1<<PB4);		PORTB |= (1<<PB0);		break;		case 5:		DDRB &= ~(1<<PB1) & ~(1<<PB4);		PORTB |= (1<<PB3);		break;		default: //in cazul in care "led" nu este intre 0 si 5 aceasta functie nu va face nimic		break;	}}

Functia pentru ore este identica insa trebuie luat aminte ca aceasta tine valorile pentru ledurile de la 6 la 11 !

Imediat voi explica de ce avem 2 functii ce tin setarile de la 0 la 5 fiecare in loc de o functie cu setari de la 0 la 11.

 

//functie care defineste ledurile folosite pentru afisarea orelorvoid hour_leds(uint8_t led){	leds_off();		switch(led)	{		case 0: //ATENTIE, setari pentru led 6		DDRB &= ~(1<<PB0) & ~(1<<PB1);		PORTB |= (1<<PB3);		break;		case 1: //led 8		DDRB &= ~(1<<PB0) & ~(1<<PB1);		PORTB |= (1<<PB4);		break;		case 2: //etc		DDRB &= ~(1<<PB0) & ~(1<<PB3);		PORTB |= (1<<PB1);		break;		case 3:		DDRB &= ~(1<<PB0) & ~(1<<PB3);		PORTB |= (1<<PB4);		break;		case 4:		DDRB &= ~(1<<PB1) & ~(1<<PB3);		PORTB |= (1<<PB0);		break;		case 5:		DDRB &= ~(1<<PB1) & ~(1<<PB3);		PORTB |= (1<<PB4);		break;		default: //in cazul in care "led" nu este intre 0 si 5 aceasta functie nu va face nimic		break;	}}

Motivul pentru care avem 2 functii ca cele de mai sus este simplu si voi incerca sa-l explic si eu cat se poate de simplu.

 

Avem 12 leduri, 6 sus, 6 jos.

Pe cele 6 de sus vrem sa afisam orele. (Ignoram momentan ledul pentru secunde)

Pe cele 6 de jos vrem sa afisam minutele.

 

Vom trata cele 6 leduri ca pe 6 biti, cu 6 biti poti numara pana la 63, in binar 111111.

Minutele pot fi maxim 59, ceea ce in binar inseamna 111011.

 

Asadar pentru a aprinde ledurile ce reprezinta 59 vrem sa putem zice:

 

minute_leds(0); //stinge toate ledurile si dupa aprinde led 0.

minute_leds(1);

minute_leds(3);

minute_leds(4);

minute_leds(5);

 

La fel si pentru ore.

Sa incercam sa aprindem ledurile pentru orele 23, in binar 10111. (dupa cum vedeti maximul pentru ore se poate reprezenta si in 5 biti, chiar daca noi avem definita functia pentru 6 biti)

 

hour_leds(0);

hour_leds(1);

hour_leds(2);

hour_leds(4);

 

Avand 2 functii separate de cate 6 leduri fiecare ne este foarte usor sa identificam cei 6 biti cu cele 6 leduri.

 

Mai departe avem doua functii ce verifica, pentru fiecare din cei 6 biti, daca bitul respectiv este 1 sau nu (sa stim daca-l aprindem sau nu).

Pentru minutes = 59, in binar 111011, functia minutes_led_on(59); ne va spune ca bitul 0 este 1, bitul 1 este 1, bitul 3 este 1, bitul 4 este 1 si bitul 5 este 1.

In functie de acesti biti care au valoarea 1 noi vom putea sa chemam functia minute_leds(led); si ca argument led o sa fie acesti biti.

 

//functie care aprinde ledurile pentru minute in functie de variabila "minute"void minutes_led_on(uint8_t minute) //exemplu: pentru minutes = 59 (0b111011), minutes_led_on(59); va aprinde ledurile 0,1,3,4,5 (numaratoarea incepe de la 0 de la dreapta la stanga){	for (int i=0; i<6; i++) //pentru fiecare bit in parte din cei 6 (de la 0 la 5)	{		if (minute & (1<<i)) //check bit si vezi daca e 1		{			minute_leds(i); //aprinde led-ul ce tine de bitul respectiv		}	}}//functie care aprinde ledurile pentru ore in functie de variabila "ore"void hours_led_on(uint8_t hour) //exemplu: pentru hours = 23 (0b010111), hours_led_on(23); va aprinde ledurile 0,1,2,4 (numaratoarea incepe de la 0 de la dreapta la stanga){	for (int i=0; i<6; i++) //pentru fiecare bit in parte din cei 6 (de la 0 la 5)	{		if (hour & (1<<i)) //check bit si vezi daca e 1		{			hour_leds(i); //aprinde led-ul ce tine de bitul respectiv		}	}}

Acest interrupt bazat pe timer, intervine in program, ii pune pauza la executie, executa un cod anume, revine la executia programului.

In cazul nostru programul este bucla infinita while(1) ce ne clipoceste la nesfarsit ledurile in functie de variabilele "minutes" si "hours".

Attiny25 are 2 timere de 8 biti, vom folosi 1 singur timer, iar cand acesta va "da pe dinafara" (overflow) se va cere un interrupt.

Viteza cu care acest timer "da pe dinafara" este setata si explicata in main().

 

//functie ce intrerupe bucla while(1) din main() odata la o secunda pentru a rula cod in afara buclei//in cazul nostru va rula functia care incrementeaza secundele/minutele/orele si va aprinde un ledISR(TIM0_OVF_vect) //atunci cand timerul ajunge la 256 (la overflow){	if(++overflow_counter > 61) //daca a ajuns la overflow de 62 de ori	{		hours_led_on(32); //aprinde ledul pentru ora 32, in binar 100000, adica va aprinde ledul de secunde! 		overflow_counter = 0; //reseteaza counterul		time_tick(); //incrementeaza secundele, etc	}}

Programul propriuzis ce este executat la initializarea uC-ului.

timer0 interrupt ruleaza la frecventa uC-ului, adica la 1Mhz pentru attiny25, insa il putem micsora (pentru a-l imparti mai usor la 256) cu ajutorul prescalerului.

Avem astfel 1000000/64 = frecventa la care ruleaza timer0 pe care daca o impartim la 256 aflam de cate ori trebuie sa ajunga la overflow timer0 pentru a contoriza o secunda.

1000000/64/256=61.03 (valoarea de trecut in ISR-ul de mai sus), daca nu am fi folosit prescaler overflow_counter ar trebui sa fie mai mare de 3906.25 pentru a contoriza o secunda.

 

int main(void){	//seteaza chestii initiale(gen starea porturilor, interrupturi, etc)	//seteaza toti pinii ca OUTPUT_LOW	PORTB = 0b000000;	DDRB = 0b111111;		//timer0 interrupt	TCCR0B |= (1<<CS00) | (1<<CS01); // seteaza prescaler la clock/64	TIMSK |= 1<<TOIE0; //seteaza interrupt atunci cand timer0 ajunge la overflow	sei(); //activeaza sistemul de interrupts	    while(1) //coloana lui Brancusi    {		minutes_led_on(minutes);		leds_off();		hours_led_on(hours);		leds_off();    }}

Pai ... cam asta e tot momentan, voi pune cand am timp si imbunatatirile, anume cum sa functioneze cu un semnal de 1Hz si cum sa-l setam. :sparge:

De asemenea orice tras de urechi/imbunatarile/sugestie este binevenita.

Link spre comentariu
  • 2 luni mai târziu...
:multumesc Sa-mi pice fata, nu alta. Mai repede inteleg codul tau intins pe 3 pagini decat alea 3 linii de cod folosite sa aprinzi 1 led din orice "tutorial" de pe net. :aplauze
Link spre comentariu
  • 3 luni mai târziu...

Hai ca sparg eu gheata dupa un an, motivat fiind de faptul ca n-am gasit pe acest forum bucati de cod explicate "ca la gradinita".Va salut, incepator fiind propun altor incepatori un ceas binar cu afisaj pe 12 leduri...

Sa zicem ca fiind un proiect conceput de tine si neavand o pagina web pe care sa-l postezi se accepta astfel de posturi, desi tot textul ala putea fi scris intr-un txt si pus intr-o arhiva impreuna cu sursele si schema si urcata pe un site, iar apoi postat doar link-ul si descrierea. In felul acesta, cine doreste sa-si aleaga un proiect poate usor sa citeasca descriere dupa descriere, sa parcurga proiectele listate in scurt timp si sa se opreasca doar asupra celor care prezinta interes, accesand link-ul postat.
Link spre comentariu
  • 1 an mai târziu...
Vizitator mincior

Un tutorial pas cu pas pentru pre-incepatori in ale microcontrolerelor Atmel, adica cei care n-au programat niciodata un ATTiny13 folosind Algorithm Builder. De fapt sunt mai multe articole la sfarsitul carora un incepator isi construieste singur un programator, o placa de test si face un program pentru o lumina dinamica cu 5 canale folosind cel mai simplu microcontroler de la Atmel: ATTiny13.

http://electroni-city.com

Link spre comentariu
  • 1 an mai târziu...
  • 2 săptămâni mai târziu...

Creează un cont sau autentifică-te pentru a adăuga comentariu

Trebuie să fi un membru pentru a putea lăsa un comentariu.

Creează un cont

Înregistrează-te pentru un nou cont în comunitatea nostră. Este simplu!

Înregistrează un nou cont

Autentificare

Ai deja un cont? Autentifică-te aici.

Autentifică-te acum



×
×
  • Creează nouă...

Informații Importante

Am plasat cookie-uri pe dispozitivul tău pentru a îmbunătății navigarea pe acest site. Poți modifica setările cookie, altfel considerăm că ești de acord să continui.Termeni de Utilizare si Ghidări