Sie sind auf Seite 1von 49

INTRODUCERE IN PROGRAMAREA MICROCONTROLLERELOR DIN SERIA ATMEL AVR

1. Resurse necesare Resursele necesare pentru studiul microcontrollerelor din seria Atmel AVR sunt prezentate in fig. 1.

COMPUTER ECHIPAT CU UN COMPILATOR PENTRU LIMBAJUL C

PROGRAMATOR

MODUL DE DEZVOLTARE CU MICROCONTROLLER

Fig. 1 Resurse pentru studiul microcontrollerelor Atmel AVR Acest document prezinta detaliat fiecare din resursele de mai sus. 2. Modulul de dezvoltare cu microcontroller Schema bloc a modulelor cu care vom lucra este prezentata in fig. 2

Sursa de alimentare DRIVERE PE LINIILE DE IESIRE MCU ATMega16 CIRCUITE PENTRU IZOLARE INTRARI

Interfata seriala Rs232 Interfata seriala Rs485 Interfata de programare

Circuite de clock si RESET

Fig. 2 Schema bloc a modulelor de dezvoltare

2.1.

Schema de principiu a modulului cu 2 relee


U1 O0 O1 O2 O3 SS MOSI MISO SCL RESET VCC XT AL2 XT AL1 RXD TXD O4 O5 PWM1 O6 PWM2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 PB0/T0 PB1/T1 PB2/INT2 PB3/OC0 PB4/SS PB5/MOSI PB6/MISO PB7/SCL RESET VCC GND XT AL2 XT AL1 PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4 PD5 PD6 PA0 PA1 PA2 PA3 PA4 PA5 PA6 PA7 AREF GND AVCC PC7 PC6 PC5 PC4 PC3 PC2 PC1 PC0 PD7 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 AN0 AN1

Y1 16MHZ

VREF VREF I7 I6 I5 I4 I3 I2 I1 I0 O7

C1 22p

C2 22p

VCC

R30 10K JP1 RESET C23 2.2uF JUMPER

ATMega16

Fig. 3 Schema de principiu a modulului de dezvoltare (MCU+clock+RESET)


Interfata RS232

U2 1 C3 22uF 3 4 C4 22uF 5 C1+ 13 12 9 8 10 7 11 14 RX232 RXD1 I0 DCD232 TXD TX232 O7 DTR232 DCD232 TX232

C1C2+

R1IN R1OUT R2OUT R2IN

J1 1 3 5 7 9 2 4 6 8 10 RX232 DTR232

C2-

T2IN T2OUT T1IN T1OUT

CON10A
RXD2 RXD

C5 6 22uF C6 V-

22uF MAX232 VCC J3 U3 TXD PA7 RXD2 4 3 1 2 D DE R RE A B 6 7 A B R6 10K R5 10K A 1 3 5 7 9 2 4 6 8 10 B

JUMPER PENTRU SELECTIA RS232-RS485

CON10A

SN75176 Interfata RS485

Conector RS485

Fig. 4 Interfetele seriale RS232/RS485

RXD1

VCC

V+

J2 CON3B

3 2 1

Conector RS232

K1 NC0 C0 NO0 C0 NC0 NO1 C1 NC1 J11 1 2 3 4 5 6

U5 O0 O1 O2 O3 O4 O5 O6 O7 1 2 3 4 5 6 7 8 9 I1 I2 I3 I4 I5 I6 I7 I8 GND O1 O2 O3 O4 O5 O6 O7 O8 KID 18 17 16 15 14 13 12 11 10 REL0 REL1 REL2 REL3 REL4 REL5 REL6 REL7 Vpp REL0 VPP

NO0

K2 NC1 C1

J12 REL2 REL3 REL4 REL5 REL6 REL7 1 2 3 4 5 6

ULN2803S Iesiri digitale NO1 REL1 VPP

Fig. 5. Driverele pentru iesirile digitale si releele

R16 XI0 4K7 1 2 3 R17 XI1 XI2 R18 4K7 4K7 4 5 6 7 R19 XI3 4K7 R20 XI4 4K7 1 2 3 R21 XI5 XI6 R22 4K7 4K7 4 5 6 7 R23 XI7 4K7 8 8

U6 A1 K1 K2 A2 A3 K3 K4 A4 CNY74-4 U7 A1 K1 K2 A2 A3 K3 K4 A4 CNY74-4 E1 C1 C2 E2 E3 C3 C4 E4 16 15 14 13 12 R12G 11 10 9 I6 8 R12H I7 9 1 1 VCC VCC I4 6 I5 7 1 1 VCC VCC J15 XA0 XA1 AO0 AO1 1 2 3 4 E1 C1 C2 E2 E3 C3 C4 E4 16 15 14 13 12 11 10 9 I2 4 I3 5 1 1 VCC VCC XI4 XI5 XI6 XI7 J14 1 2 3 4 I0 2 I1 3 1 1 VCC VCC XI0 XI1 XI2 XI3 J13 1 2 3 4

Fig. 6 Circuitele pentru izolarea intrarilor digitale

VCC VCC 4 XA0 DZ5V1 3 1 2 1K 1 1 VVTL084 C17 2.2uF 9 1 1 TL084 AN0 PH0 U9A R9 10 8 AO0 4 U9C

D3

VCC VCC 4 XA1 DZ5V1 5 7 6 1K 1 1 VVTL084 C18 2.2uF 13 1 1 TL084 AN1 PH1 U9B R10 12 14 AO1 4 U9D

D4

Intrari analogice

Iesiri analogice (PWM)

Fig. 7 Circuitele pentru adaptarea intrarilor analogice si iesirile PWM Schema modulului cu 8 relee este prezentata integral in fig. 8 Diferente intre cele doua module: a. Modulul cu 8 relee nu mai foloseste intrari/iesiri analogice b. In schimb, sunt disponibile mai multe intrari digitale I8-I15, pe conectorul J21 c. Cablajul imprimat permite conectarea a pana la 8 relee direct pe placa. Nota Consultati foile de catalog ale circuitelor de interfata pentru a intelege bine functionarea intregului montaj

K1 U1 XI0 A1 R35A 15 I0 R35B 14 I1 VCC REL0 VPP R35C 11 I2 R35D 10 I3 VCC C1 NO1 J19 9 5 1 K2 VCC 4 1 REL2 J14 13 12 3 1 VCC 2 1 NO0 K1 K2 A2 A3 K3 K4 A4 CNY74-4 E4 C4 C3 E3 E2 C2 C1 C0 NO0 C1 NO1 E1 4K7 2 3 R32 XI1 XI2 4K7 6 7 R34 XI3 4K7 8 R33 4K7 5 4 1 16 C0 R31 U11 J13 1 2 3 4

Y1 8MHZ

O0 O1 O2 O3 SS MOSI MISO SCK RESET I8 I9 I10 I11 I12 I13 I14 I15 VREF C2 NO2 C3 NO3 VCC

C1 22p

C2 22p

1 2 3 4

VCC U5 REL1 VPP REL2

R30 10K JP1 AT90S8535

XTAL2 XTAL1 RXD TXD O4 O5 PWM1 O6 PWM2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 PB0/T0 PB1/T1 PB2/INT2 PB3/OC0 PB4/SS PB5/MOSI PB6/MISO PB7/SCL RESET VCC GND XTAL2 XTAL1 PD0/RXD PD1/TXD PD2/INT0 PD3/INT1 PD4 PD5 PD6 VREF I7 I6 I5 I4 I3 I2 I1 I0 O7 C4 NO4 C5 NO5 PA0 PA1 PA2 PA3 PA4 PA5 PA6 PA7 AREF GND AVCC PC7 PC6 PC5 PC4 PC3 PC2 PC1 PC0 PD7

40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21

1 2 3 4 CON4

RESET JUMPER C2

J20 K3 Vpp C6 NO6 C7 NO7 NO2 1 2 3 4 CON4

C23 2.2uF

O0 O1 O2 O3 O4 O5 O6 O7 I1 I2 I3 I4 I5 I6 I7 I8 GND ULN2803S O1 O2 O3 O4 O5 O6 O7 O8 KID

1 2 3 4 5 6 7 8 9 REL0 REL1 REL2 REL3 REL4 REL5 REL6 REL7

18 17 16 15 14 13 12 11 10

JP2 I0 VCC JUMPER VCC CON10A 1 3 5 7 9 PD2-MISO PD3-MOSI PD4-SCK PD5-SS SS1 SS2 SS3 MOSI SS0 RESET SCK MISO 1 3 5 7 9 2 4 6 8 10 CON10A 2 4 6 8 10 I9 I11 I13 I15 J10

J21

DCD

REL2 VPP REL2 XI0 XI1 XI2 XI3 K4 C3 NO3

I8 I10 I12 I14

J22 1 2 3 4 CON4

J16

U2 1 C1+ SS 13 12 RX232 RXD1 J1 DCD232 TX232 RX232 DTR232 J17 REL3 VPP REL2 D16 CON10A K5 C4 NO4 VCC Vpp Vpp D19 REL4 VPP REL2 D20 K6 R6 10K C5 NO5 Vpp 3 2 1 J2 CON3B J3 VCC A B VPPX 1 VCC VPP D2 2 1N4001 R3 100 U10 UA7805 C22 C1 Vcc 470 R27 D7 VPP 1 IN OUT G N D 2 C7 NO7 3 VCC C19 2.2uF VREF REL6 VPP REL2 VPP VCC C6 NO6 REL5 VPP REL2 K7 Vpp 4K7 Vpp D23 Vpp D21 D22 4K7 R21 Vpp 4K7 R20 Vpp D17 D18 4K7 R17 R16 SS0 SS1 SS2 SS3 1 3 5 7 9 2 4 6 8 10 VPPX

1 2 3 4

C3 22uF 3 4 C1C2+ R2OUT R2IN C2CON10A 11 14 O7 DTR232 T2IN T2OUT T1IN T1OUT 6 VU3 TXD 2 V+ A B 7 B RXD2 MAX232 RXD SN75176 RXD1 RXD2 R RE 1 2 O7 6 A D DE 4 3 R5 10K 10 7 TXD TX232 9 8 DCD DCD232 1 3 5 7 9 2 4 6 8 10 R1IN R1OUT C4 22uF 5

REL0

REL1 R18 4K7 4K7 R19 REL3 REL2

Fig. 8 Schema completa a modulului cu 8 relee


R22 4K7 4K7 R23 C10 100n CON10A Vpp C11 100n C14 100uF 1 3 5 7 9 2 4 6 8 10 K8 REL7 VPP REL2

C5

22uF C6

VCC

22uF

REL4

REL5 REL6

REL7

VCC

C9 22uF

VCC

C21 C1

3. Programatorul ISP Memoria de program a microcontrollerului ATMega16 este de tip flash si se poate programa in system (ISP) cu ajutorul unor interfete simple. Vom folosi cea mai simpla interfata de programare, denumita in literatura Kanda systems, sau STK200. Schema de principiu a interfetei este prezentata in fig. 9.

Fig. 9 Scheme interfetei de programare Kanda Systems Denumirea Kanda Systems vine de la firma care a comercializat-o initial. Se conecteaza prin J1 la portul paralel al computerului, iar la J2 cu modulul de dezvoltare echipat cu microcontroller. Comunicatia cu microcontrollerul este de fapt seriala. Alimentarea circuitului 74HC244 se face din sursa modulului de dezvoltare prin conectorul J2. Evident ca pe computerul la care se conecteaza interfata trebuie sa existe un soft special care sa controleze dialogul cu microcontrollerul pentru programare. In cazul de fata, compilatorul HP Infotech cu care lucram include un modul de programare.

4. Compilatorul CodeVisionAVR produs de HP INFOTECH Este unul din cele mai bune produse de acest gen disponibil pe piata. Principalul sau avantaj consta in existenta unui asa-numit CodeWizzard, care automatizeaza procedura de creare a unui nou proiect si genereaza automat codul pentru initializarea diverselor subsisteme ale microcontrollerului. Utilizatorului ii ramane doar sarcina de a scrie programul principal. In acest paragraf, este prezentata pas cu pas secventa de creare a unui proiect, prin care se comanda iesirea digitala PB0 alternativ ON/OFF cu durata de 0.5 secunde. Pasul 1. Se lanseaza CVAVR Pasul 2. Din menu-ul principal se selecteaza optiunea File, New Programul prezinta urmatoarea fereastra de dialog:

Fig. 10 Secventa de dialog CVAVR Se selecteaza Project si se apasa butonul OK. Pasul 3. CVAVR prezinta fereastra de dialog din fig. 11

Fig. 11 Dialog CVAVR Se raspunde cu Yes pentru lansarea utilitarului CodeWizard Pasul 4. Se selecteaza tipul de microcontroller cu care se lucreaza in cazul de fata ATMega16 si frecventa oscilatorului (data de cristalul de quartz care echipeaza modulul in cazul de fata 16MHz). (vezi fig. 12)

Fig. 12 Dialog CodeWizardAVR Pasul 5. Configurarea resurselor folosite in proiect. Singura resursa folosita in exemplul considerat este Port B bit 0. Se selecteaza Tab-ul Ports. Se obtine fereastra de dialog din figura 13.

Fig. 13 Dialog CodeWizardAVR

In aplicatia noastra, din toate resursele microcontrollerului folosim doar o linie din portul B, pe care dorim s-o configuram ca linie de iesire. In consecinta, vom selecta Tab-ul marcat Port B, apoi vom face click pe Bit 0. Se obtine fereasta din figura 14.

Fig. 14. Configurarea Port B bit 0 ca linie de iesire cu CodeWizardAVR Pasul 6. Generarea efectiva a proiectului Se alege din menu-ul CodeWizardAVR optiunea File, si apoi Generate Save and Exit. In continuare programul prezinta o fereastra de dialog in care se solicita numele dorit pentru fisierele sursa (.C) project (.prj) si CodeWizardProject (.cwp) (vezi fig. 15). Pentru usurinta gestionarii proiectelor, este recomandabil sa salvati fiecare proiect nou intr-un folder distinct, de exemplu C:\CVAVR\WORK\LED1 Puteti selecta acelasi nume (led1) pentru fisierele sursa (.c) si project (.prj, .cwp). Dupa salvarea proiectului nou generat, se paraseste utilitarul CodeWizardAVR si se intra in editorul CodeVisionAVR, afisandu-se in partea dreapta programul sursa generat de Wizard. Acesta contine chiar o serie de comentarii si indicatii de genul Place you global variables here, Place your code here, etc., care sunt utile la primul contact cu mediul CodeVisionAVR.

Fig. 15 Dialogul pentru salvarea proiectului Pasul 7. Editarea fisierelor generate automat de CodeWizardAVR Iata listing-ul complet al programului generat automat:
/***************************************************** This program was produced by the CodeWizardAVR V1.25.5 Standard Automatic Program Generator Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l. http://www.hpinfotech.com Project : Version : Date : 3/14/2008 Author : Y406 Company : Ugal. Comments: Chip type : ATmega16 Program type : Application Clock frequency : 16.000000 MHz Memory model : Small External SRAM size : 0 Data Stack size : 256 *****************************************************/ #include <mega16.h> // Declare your global variables here void main(void) { // Declare your local variables here // Input/Output Ports initialization // Port A initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T PORTA=0x00; DDRA=0x00; // Port B initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=Out

// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=0 PORTB=0x00; DDRB=0x01; // Port C initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T PORTC=0x00; DDRC=0x00; // Port D initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T PORTD=0x00; DDRD=0x00; // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: Timer 0 Stopped // Mode: Normal top=FFh // OC0 output: Disconnected TCCR0=0x00; TCNT0=0x00; OCR0=0x00; // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: Timer 1 Stopped // Mode: Normal top=FFFFh // OC1A output: Discon. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge // Timer 1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1A=0x00; TCCR1B=0x00; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; // Timer/Counter 2 initialization // Clock source: System Clock // Clock value: Timer 2 Stopped // Mode: Normal top=FFh // OC2 output: Disconnected ASSR=0x00; TCCR2=0x00; TCNT2=0x00; OCR2=0x00; // External Interrupt(s) initialization // INT0: Off // INT1: Off // INT2: Off MCUCR=0x00; MCUCSR=0x00; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x00; // Analog Comparator initialization // Analog Comparator: Off // Analog Comparator Input Capture by Timer/Counter 1: Off

ACSR=0x80; SFIOR=0x00; while (1) { // Place your code here }; }

Observati urmatoarele elemente: a. #include <mega16.h> - este header-ul care contine definitiile resurselor corespunzator microcontrollerului selectat. b. In functia main() se incepe cu o serie de initializari din care remarcam initializarea registrului de directie asociat cu Port B, DDRB cu valoarea 0x01, ceea ce este echivalent cu configurarea Port B bit 0 ca linie de iesire. c. Dupa initializari, programul continua cu un while (1) o bucla infinita unde trebuie plasat codul aplicatiei propriu-zise. Editarea sursei generate automat are in vedere urmatoarele aspecte: a. O modificare de ordin cosmetic destinata sa faca programul mai usor de citit, consta in eliminarea comentariilor inutile si plasareaq tuturor initializarilor intr-o functie init(), care va fi apelata la inceputul main(). b. Nu uitati sa includeti propriile comentarii care sa va ajute sa rememorati scopul proiectului si eventualele amanunte demne de retinut in legatura cu el. c. Alegerea si includerea fisierelor header necesare aplicatiei (daca este cazul) d. Scrierea functiei main corespunzator cerintelor aplicatiei Iata cum arata programul dupa editare:
/* Un prim test cu microcontrollerul ATMega16 Se comanda comutarea alternativa ON/OFF la intervale de 0.5 sec a unui LED conectat la Portb bit 0. Constantele de timp folosesc functiile predefinite in delay.h */ #include <mega16.h> #include <delay.h> // Declare your global variables here void init(void);

void main(void) { // Declare your local variables here init();

while (1) { // Place your code here PORTB.0=1; delay_ms(500); PORTB.0=0; delay_ms(500); }; } void init(void) { // Input/Output Ports initialization // Port A initialization PORTA=0x00; DDRA=0x00; // Port B initialization PORTB=0x00; DDRB=0x01; // Port C initialization PORTC=0x00; DDRC=0x00; // Port D initialization PORTD=0x00; DDRD=0x00; // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: Timer 0 Stopped // Mode: Normal top=FFh // OC0 output: Disconnected TCCR0=0x00; TCNT0=0x00; OCR0=0x00; // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: Timer 1 Stopped // Mode: Normal top=FFFFh // OC1A output: Discon. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge // Timer 1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1A=0x00; TCCR1B=0x00; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; // Timer/Counter 2 initialization // Clock source: System Clock

// Clock value: Timer 2 Stopped // Mode: Normal top=FFh // OC2 output: Disconnected ASSR=0x00; TCCR2=0x00; TCNT2=0x00; OCR2=0x00; // External Interrupt(s) initialization // INT0: Off // INT1: Off // INT2: Off MCUCR=0x00; MCUCSR=0x00; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x00; // Analog Comparator initialization // Analog Comparator: Off // Analog Comparator Input Capture by Timer/Counter 1: Off ACSR=0x80; SFIOR=0x00; }

Pasul 8 Compilarea programului si generarea codului executabil. Din menu-ul principal se selecteaza Project apoi Make. Daca la compilare se detecteaza erori, acestea sunt semnalate. Pasul 9. Transferul programului executabil in memoria microcontrollerului In urma compilarii, CVAVR genereaza un fisier cu extensia .rom care contine codul executabil al aplicatiei. Inainte de a lansa utilitarul de programare, asigurati-va ca a fost selectat programatorul corect (Kanda systems). In acest scop, selectati din menu-ul principal optiunea Settings si apoi, Programmer si specificati tipul de programator si portul paralel la care acesta este conectat. (LPT1:)

Fig. 16 Selectia tipului de programator

Se conecteaza programatorul la conectorul J10 si se alimenteaza modulul de dezvoltare ca in figura 17. Asigurati-va ca programatorul este conectat si la portul paralel al computerului.
Conector programator

V+ GND

Fig. 17 Conectarea programatorului si a tensiunii de alimentare Nota: Alimentarea modulului se face de la un alimentator stabilizat de 12V dc, cu polaritatea indicata in figura 17. Curentul necesar este de circa 100mA. Programatorul se alimenteaza pe cablul de conectare la modulul cu microcontroller din sursa stabilizata de 5V a acestuia, deci nu mai e necesara o sursa suplimentara pentru programator. Lansarea modulului soft de programare se face alegand din menu-ul principal optiunea Tools si apoi Chip Programmer, sau direct actionand butonul marcat cu un chip ca in figura 18.
LANSARE PROGRAMATOR

Fig. 18 Lansarea modulului soft de programare

Fereastra de dialog a programatorului este prezentata in figura 19.

Fig. 19 Fereastra de dialog a programatorului Programarea efectiva a chipului consta in urmatoarele etape: a. Stergerea memoriei flash. Se face selectand din menu optiunea Program - > Erase chip. b. Incarcarea fisierului .rom in bufferul programatorului. Se face alegand din menu optiunea File ->Load Flash c. Transferul continutului bufferului in memoria flash a microcontrollerului. Se face alegand din menu optiunea Program - > FLASH. Transferul dureaza cateva secunde in functie de lungimea programului, dupa care programul este automat lansat in executie. Daca toate etapele de mai sus au fost parcurse corect, LED-ul conectat la PortB bit 0 incepe sa clipeasca cu frecventa de 1Hz. Precautii importante 1. Inainte de a face orice operatie cu programatorul, asigurati-va ca check-boxul program fuse bits este sters (nemarcat). Programarea la intamplare a fuse-bits conduce aproape intotdeauna la defectarea microcontrollerului in sensul ca puteti dezactiva viitoarele programari, sau puteti configura clock-ul altfel decat cu oscilatorul cu cristal de quartz existent pe placa. In orice situatie, aceste configurari la intamplare fac inutilizabil circuitul. 2. Nu folositi NICIODATA butonul Program All!

Exercitiu: Modificati exemplul de mai sus in asa fel incat LED-ul de pe PORTB bit 0 sa comute la intervale de o secunda, iar LED-ul de pe PORTB bit 1 sa comute la intervale de 500ms. Indicatie: - se modifica functia init, in asa fel incat sa se configureze bit 0 si bit 1 ai PortB ca linii de iesire. - Se modifica main() in asa fel incat sa se comande si PortB bit 1 Introducere in programarea microcontrollerelor din seria Atmel AVR Partea a II-a II.1 Exemplu de folosire a liniilor de intrare iesire digitale. Comanda unui motor pas cu pas. Tema: Sa se scrie un program care sa comande un motor pas cu pas in sens de rotatie direct daca intrarea portului C bitul 0 este in 1 si in sens invers daca intrarea portului C bitul zero este 0. Motorul se conecteaza prin driverul ULN2803 la liniile PORTD bitii 2,3 5, si 7. Solutie: Iata listing-ul programului care corespunde cerintelor de mai sus:
#include <mega16.h> #include <delay.h> // Declare your global variables here unsigned char STEPM[4]={0x04,0x08,0x20,0x80}; int current_position; void step(int i); void init(void);

void main(void) { init(); while (1) { step(current_position); delay_ms(3); }; }

/* Rutina step executa un pas inainte sau inapoi in functie de starea liniei de intrare 0 a portului C

*/ void step(int offset) { if(PINC.1==1) { PORTB.0=1; //semnalizare pe LED PORTD=STEPM[offset]; offset++; if(offset==4) offset=0; current_position=offset; } if(PINC.1==0) { PORTB.0=0; //semnalizare pe LED PORTD=STEPM[offset]; offset--; if(offset<0) offset=3; current_position=offset; } }

Observatii: 1. Listingul de mai sus nu contine si initializarile. Asigurati-va ca in rutina de initializari, ati configurat PORTD in mod output (DDRD=0xFF) si PORTB.1 de asemenea in mod output. 2. Important! Cand se citeste un port de intrare folositi PINx in loc de PORTx . PORTx se foloseste NUMAI in operatiile de scriere intr-un port de iesire. Daca in program faceti citirea PORTx in loc de PINx, obtineti ultima valoarea scrisa in port NU STAREA LINIEI DE INTRARE. II.2 Un exemplu de folosire a timerelor In exemplele precedente, pentru controlul unor intarzieri am folosit functiile predefinite delay_ms(unsigned int) - intarziere cu un numar de milisecunde definit de parametrul functiei si delay_us(unsigned int) intarziere cu un numar de microsecunde definit de parametrul functiei. Dezavantajul acestei solutii este acela ca functiile delay realizeaza intarzierea printr-o bucla de asteptare (decrementarea unui contor). Pe durata asteptarii, microcontrollerul nu mai poate executa alte functii si efectul este ca 99% din timpul total se pierde in asteptare. O solutie mult mai buna este sa folosim timerele interne pentru generarea unor intervale de timp precise. In esenta, subistemul timer al unui microcontroller contine un numarator realizat hardware, care poate numara pe un ceas cu frecventa selectabila prin program (cu ajutorul unui asa-numit prescaller). Susbsitemul timer poate genera cereri de intrerupere in urmatoarele situatii: a. La overflow cand se atinge valoarea maxima, inainte de revenirea la starea 0 a numaratorului (numararea se face de regula in sens direct)

b. La output compare cand valoarea din numarator egaleaza valoarea dintrun registru special (OCR output compare register) care este accesibil pentru scriere/citire prin program c. La o conditie de input capture cand se detecteaza o tranzitie (front crescator sau cazator al unei intrari asociate cu timerul respectiv). Generarea unor intervale precise cu ajutorul intreruperii de overflow a unui timer. La scrierea unei valori CNTVAL in numaratorul timerului, (TCNT) se stie ca acesta va ajunge la overflow peste un numar de (0xFFFF-CNTVAL+1) perioade ale ceasului de numarare. De exemplu, daca dorim ca timerul sa genereze intreruperi la intervale de 2ms, iar frecventa selectata pentru ceasul de numarare a timerului este 2MHz, atunci trebuie sa scriem in TCNT la fiecare intrerupere valoarea CNTVAL=(65535-4000+1). Generarea unor intervale precise de timp cu ajutorul functiei output compare In cazul output compare se genereaza intrerupere in momentul cand numaratorul ajunge sa egaleze valoarea scrisa prin program in registrul OCR asociat. Ca sa generam interruperi la intervale de 2ms cu un ceas de 2MHz, trebuie sa adunam valoarea curenta a OCR cu 4000 si sa scriem rezultatul inapoi in OCR. In acest fel, urmatoarea intrerupere va surveni peste 4000 de perioade ale ceasului de numarare, adica la 2MHz peste 2ms. Exercitiu Folosind timerul 1 in mod output compare, sa se scrie un program care sa comande alternativ LED-urile conectate pe PORTb.0 si PORTB.1 la intervale de timp de 200ms respectiv 330ms. Solutie: 1. 2. 3. 4. 5. Se parcurge procedura de creare a unui nou proiect descrisa anterior Se configureaza PORTB in asa fel incat bitul 0 si 1 sa fie iesiri. Se configureaza Timer1 ca in figura 1: Se salveaza proiectul Se editeaza proiectul generat automat de CodeWizard, conform cu listing-ul urmator:

#include <mega16.h> unsigned char TTAB[8]; unsigned int OCR1; bit qtoc=0;

// Timer 1 output compare A interrupt service routine // se seteaza flagul qtoc // se scrie OCR1 cu valoarea curenta + 20000 - in urmatoarea // intrerupere sa vina peste 10ms interrupt [TIM1_COMPA] void timer1_compa_isr(void) { qtoc=1; OCR1=(OCR1AH*256+OCR1AL)+20000; OCR1AH=(OCR1&0xFF00)>>8; OCR1AL=OCR1&0xFF; }

asa

fel

incat

void init(void); void soft_timers(void); void dectmr10ms(void);

void main(void) { init(); TTAB[0]=20; // 20*10=200ms TTAB[1]=33; // 33*10=330ms

// Global enable interrupts #asm("sei") while (1) { soft_timers(); if(TTAB[0]==0) { TTAB[0]=20; PORTB.0=!(PORTB.0); } if(TTAB[1]==0) { TTAB[1]=33; PORTB.1=!(PORTB.1); } }; }

// Verifica daca a fost intrerupere (qtoc=1) // Daca a fost, sterge flagul qtoc, apoi decrementeaza TTAB[i] daca // valoarea gasita este mai mare decat zero void soft_timers(void) { if(qtoc==0) return; qtoc=0; dectmr10ms();

} void dectmr10ms(void) { unsigned char i; for(i=0;i<8;i++) { if(TTAB[i]!=0) TTAB[i]--; } }

Fig. 1 Configurarea timer1 pentru intreruperi la output compare, cu ceas de 2MHz

Observatii In rutina de tratare a intreruperii, care se executa la intervale de 10ms, se seteaza flagul qtoc (care va fi testat apoi de functia soft_timers() ) si se aduna 20.000 la valoarea curenta din OCR1A, in asa fel incat urmatoarea intrerupere sa vina peste 20000*0.5us=10000us=10ms

Rutina soft_timers() verifica valorile din matricea TTAB[8] daca sunt diferite de zero le decrementeaza. In acest fel, daca scriem prin program o valoare in TTAB, aceasta va fi decrementata la intervale de 10ms pana la zero. Pentru a realiza prin program diverse intervale de timp, tot ce avem de facut este sa initializam TTAB cu valoarea dorita si sa testam momentul cand timerul soft TTAB ajunge la zero.

II.3. Organizarea modulara a proiectelor Pe masura ce complexitatea programelor creste, constatam ca este incomod sa scriem intregul program sursa intr-un singur fisier. CVAVR permite organizarea proiectelor in mai multe fisiere sursa, care sunt compilate simultan. Pe langa avantajul comoditatii, acest mod de organizare are inca un avataj major modulele scrise si testate pot fi refolosite, practic fara modificari in alte proiecte. Exercitiu Sa se reorganizeze proiectul din exercitiul precedent, in asa fel incat toate rutinele refoeritoare la timer sa fie plasate intr-un fisier distinct de modulul main. Solutie 1. Din menu-ul principal, se selecteaza New 2. Apare fereastra de dialog din figura 2

Fig. 2 Fereatra de dialog pentru crearea unui nou fisier sursa Se creaza automat fisierul gol untitled.c. Se salveaza (Save as) cu numele dorit timer1.c.

Fig. 3 Fereastra navigatorului dupa crearea unui nou fisier 3. Cu Cut si Paste se muta din fisierul principal toate variabilele si functiile care se refera la timer in noul fisier creat. Se adauga #include <mega16.h> si in fisierul nou creat!! 4. Se definesc in modulul main ca externe functiile si variabilele mutate in fisierul nou creat. 5. Se alege din menu-ul principal optiunea Project -> Configure si se adauga fisierul nou creat la proiect. (vezi fig. 4)

Fig. 4 Adaugarea fisierului nou creat la proiect 6. Se apasa butonul Add si din fereastra de dialog care urmeaza se selecteaza fisierul nou creat. Fereastra navigatorului devine:

Fig. 5 Fereastra navigatorului dupa adaugarea fisierului timer1.c la proiect 7. Se compileaza proiectul (din menu-ul principal se alege optiunea Project -> Make) Iata listingul celor doua module ale proiectului in forma finala: Modulul principal tmr1.c
#include <mega16.h> extern void init_timer1(void); extern void soft_timers(void); extern unsigned char TTAB[8];

void init(void); void main(void) { init(); init_timer1(); TTAB[0]=20; // 20*10=200ms TTAB[1]=33; // 33*10=330ms

// Global enable interrupts #asm("sei") while (1) { soft_timers(); if(TTAB[0]==0) { TTAB[0]=20; PORTB.0=!(PORTB.0); } if(TTAB[1]==0) {

TTAB[1]=33; PORTB.1=!(PORTB.1); } }; }

Modulul timer1.c
#include <mega16.h> unsigned char TTAB[8]; unsigned int OCR1; bit qtoc=0; void dectmr10ms(void); // // // // Timer 1 output compare A interrupt service routine se seteaza flagul qtoc se scrie OCR1 cu valoarea curenta + 20000 - in asa fel incat urmatoarea intrerupere sa vina peste 10ms

interrupt [TIM1_COMPA] void timer1_compa_isr(void) { qtoc=1; OCR1=(OCR1AH*256+OCR1AL)+20000; OCR1AH=(OCR1&0xFF00)>>8; OCR1AL=OCR1&0xFF; }

void soft_timers(void) { if(qtoc==0) return; qtoc=0; dectmr10ms(); } void dectmr10ms(void) { unsigned char i; for(i=0;i<8;i++) { if(TTAB[i]!=0) TTAB[i]--; } } void init_timer1(void) { // // // // // // // Timer/Counter 1 initialization Clock source: System Clock Clock value: 2000.000 kHz Mode: Normal top=FFFFh OC1A output: Discon. OC1B output: Discon. Noise Canceler: Off

// Input Capture on Falling Edge // Timer 1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: On // Compare B Match Interrupt: Off TCCR1A=0x00; TCCR1B=0x02; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; }

II.4. Refolosirea modulelor soft in alte proiecte Exercitiu Sa se implementeze soft un monostabil care sa activeze LED-ul conectat la PORTB.0 pentru o durata de 2 secunde, de fiecare data cand se activeaza intrarea PORTC.0 (nota: intrarea este actiuva LOW din cauza optocuplorului). 1. Se deschide proiectul tmr1 2. In fereastra navigatorului, se selecteaza fisierul tmr1.c 3. Din menu-ul principal se selecteaza optiunea Save as si se salveaza fisierul intr-un folder nou creat sa zicem C:\cvavr\work\tmr2 4. Se repeta operatia cu toate fisierele din proiectul model pe care dorim sa le folosim in noul proiect. 5. Se inchide proiectul model 6. Se foloseste CodeWizard pentru a genera un nou proiect. Se salveaza noul proiect cu denumirea tmr2 7. Se inlocuieste continutul fisierului tmr2.c cu continutul fisierului tmr1.c folosind Copy/Paste. 8. Se configureaza noul proiect incat sa includa fisierul timer1.c 9. Se recompileaza pentru a verifica corectitudinea operatiilor. daca nu sunt erori, procesul de export a proiectului este incheiat si acum putem trece la editarea noului proiect. Modificarile pentru implementarea functiei de monostabil se executa doar in modulul principal, conform listing-ului urmator:
#include <mega16.h> extern void init_timer1(void); extern void soft_timers(void); extern unsigned char TTAB[8]; void init(void);

void main(void) { init(); init_timer1(); TTAB[0]=0; // Global enable interrupts #asm("sei") while (1) { soft_timers(); if(PINC.0==0) TTAB[0]=200; if(TTAB[0]!=0) PORTB.0=1; if(TTAB[0]==0) PORTB.0=0; } }

Introducere in programarea microcontrollerelor din seria Atmel AVR Partea a III-a

III. Exemple de utilizare a interfetei seriale asincrone III.1. Generalitati despre comunicatia seriala asincrona Majoritatea microcontrollerelor sunt echipate cu o interfata seriala asincrona denumita SCI (Serial Communication Interface) sau UART/USART (Universal Synchronous/Asynchronous Receiver Transmitter). In principiu, o transmisie seriala asincrona poate fi realizata cu ajutorul unor registre de deplasare, ca in figura 1.
Data Data

Tx Shift register TxCLK Transmiter

Serial line

Rx Shift register RxCLK Receiver

Fig. 1 Schema bloc a unei comunicatii asincrone Se observa ca pe linia de comunicatie nu se transmite si un clock de sincronizare si, din acest motiv, sunt necesare urmatoarele masuri pentru sincronizarea emitatorului cu receptorului: a. Emitatorul si receptorul trebuie sa convina asupra unei viteze de transmisie cu alte cuvinte frecventa ceasurilor TXCLK si RXCLK trebuie sa fie aceeasi.

b. Fiecare octet de date trebuie precedat de un bit de start, cau polaritate inversa fata de starea de repaus a liniei de comunicatie (de obicei starea de repaus a liniei este 1 logic, astfel incat bitul de start are valoarea logica zero) c. Dupa fiecare octet e transmite cel putin un bit de stop pe durata caruia linia are polaritatea starii de repaus (1 logic). Cu aceste conventii, octetul 0x31 (codul ASCII al cifre 1) se transmite astfel:

Start

Stop

Tb/2

Tb

Tb

Tb

Tb

Tb

Tb

Tb

Tb

Tb

Fig. 2 Forma de unda a semnalului pentru transmisia asincrona a octetului 0x31 Observati ca octetii se transmit serial incapand cu bitul cel mai putin semnificativ.

Schema bloc a USART este prezentata in figura 3

CLK TxD Tx shift register

BAUD rate generator Rx shift register RxD

Control logic

Control Internal bus

Tx Data

Rx Data

Status

Fig. 3 Schema bloc generala a unui USART Microcontrollerele din seria AVR sunt echipate cu unul sau (uneori) doua USART-uri. ATmega16 are un usart. Registrele de date ale transmitatorului si receptorului sunt vizibile pentru program la aceeasi adresa, denumite simbolic UDR (Usart Data Register), cu precizarea ca la scriere in UDR se scrie in registrul de date al transmitatorului iar la citire se citeste din registrul de date al receptorului. Registrul de control si cel de stare sunt comasate in registrele de control si stare UCSRA, UCSRB, si UCSRC. UCSRA are structura:

Fig. 4 Registrul de control si stare UCSRA Semnificatia bitilor: RXC- RX Complete receptie completa TXC Tx Complete transmisie completa UDRE Usart Data Register Empty setat pe 1 cand registrul de date al transmitatorului este gol (se pot accepta scrieri) FE, DOR, PE sunt biti de eroare (Framing error, Overrun error, si parity error) U2X bit de control Cand e setat dubleaza viteza de comunicatie asincrona. MPCM Multi Processor Communication Mode. Comanda intrarea intr-un regim de functionare multiprocesor, care nu ne intereseaza acum. UCSRB contine biti de control pentru activarea/dezactivarea separata a transmitatorului si receptorului si pentru validarea generarii de intreruperi, iar UCSRC contine biti de control pentru selectia modului de functionare (sincron/asincron) si pentru activarea/dezactivarea controlului de paritate, pentru selectia numarului de biti per caracter etc.

III.2. Programarea interfetei seriale asincrone la AVR Programarea interfetei seriale consta in: a. Initializarea interfetei, prin activarea emitatorului si receptorului, selectia vitezei de comunicatie, a numarului de biti per caracter, si activarea (daca e cazul) intreruperilor asociate cu interfata. b. Programarea emisiei si receptiei la nivel de caracter. O metoda buna de a programa emisia si receptia caracterelor este de a activa intreruperile de receptie si de a testa bitul de stare UDRE (Data register empty) la emisia unui caracter, Iata un exemplu de functie care emite un caracter pe linia seriala:
#define UDRE 5 void send_char(char ch) { UDR = ch; while ( !( UCSRA & (1<<UDRE)) ); }

Se observa ca emisia efectiva a caracterului incepe odata cu scrierea in registrul UDR. Apoi se asteapta momentul cand bitul de stare UDRE (bitul 5) din registrul UCSRA devine 1, moment in care transmisia este incheiata. Se mai poate scrie si asa:

#define TXC 6 void send_char(unsigned char ch) { UDR = ch; while ( ( UCSRA & (1<<TXC))==0 ); UCSRA=(1<<TXC); }

De aceasta data se testeaza bitul de stare TXC (Tx Complete - bitul 6 din UCSRA). Diferenta este ca bitul TXC nu se sterge automat si trebuie sters prin program scriind 1 in pozitia 6 din UCSRA!! Un exemplu de rutina de intrerupere la receptia unui caracter de la USART este urmatorul:

// USART Receiver interrupt service routine interrupt [USART_RXC] void usart_rx_isr(void) { char status, data; status=UCSRA; data= UDR; rxdata=data; rflg=1; }

Se observa ca rutina de intrerupere face urmatoarele operatii: a. Seteaza flagul rflg=1 pentru a indica programului principal ca s-a primit un caracter pe linia seriala b. Salveaza caracterul primit in variabila rxdata c. Salveaza registrul de stare UCSRA (inclusiv bitii de eroare) in variabila status III.3 Exemple de aplicatii simple de folosire a intefetei seriale III.3.1. Exemplul nr. 1 Sa se scrie un program care face primeste pe linia seriala caracterele emise de un terminal ASCII. Face ecou la toate caracterele, dar schimba case de la literele mici la literele mari. Se parcurg urmatorii pasi: a. Se creaza un nou proiect folosind CodeWizardAVR. Sa zicem ca il denumim serial1. b. Se selecteaza tab-ul USART. se obtine urmatoarea fereastra de dialog:

Fig. 5 Dialogul in cazul initializarii USART-ului la ATMEGA16

Se bifeaza check-box-urile Receiver si Transmitter pentru validarea lor. Se obtine urmatoarea fereastra:

Fig. 6 Dialogul pentru initializarea USART-ului Restul perifericelor se lasa la initializarile default.

c. Se editeaza codul generat pentru a obtine urmatoarele fisiere-sursa: Un fisier distinct USART.C cu urmatorul continut:

#include <mega16.h> #define #define #define #define #define #define #define #define #define #define #define #define RXB8 1 TXB8 0 UPE 2 OVR 3 FE 4 UDRE 5 RXC 7 FRAMING_ERROR (1<<FE) PARITY_ERROR (1<<UPE) DATA_OVERRUN (1<<OVR) DATA_REGISTER_EMPTY (1<<UDRE) RX_COMPLETE (1<<RXC)

bit rflg; unsigned char rxdata; void send_char(char ch); void init_usart(void);

// USART Receiver interrupt service routine interrupt [USART_RXC] void usart_rx_isr(void) { char status,data; status=UCSRA; data=UDR; rflg=1; rxdata=data; } void send_char(char ch) { UDR = ch; while ( !( UCSRA & (1<<UDRE)) ); } void init_usart(void) { // USART initialization // Communication Parameters: 8 Data, 1 Stop, No Parity // USART Receiver: On // USART Transmitter: On // USART Mode: Asynchronous // USART Baud Rate: 9600 UCSRA=0x00; UCSRB=0x98;

UCSRC=0x86; UBRRH=0x00; UBRRL=0x67; }

Si programul principal serial1.c cu urmatorul continut:


#include <mega16.h> extern unsigned char rxdata; extern bit rflg; extern void send_char(char ch); extern void init_usart(void); void init(void); unsigned char change_case(unsigned char ch); void main(void) { init(); init_usart(); // Global enable interrupts #asm("sei") while (1) { if(rflg==1) { rflg=0; rxdata=change_case(rxdata); send_char(rxdata); } }; } /* Caracterele ASCII pentru litere mici sunt in plaja 0x61 ("a") pana la 0x7A ("z") Caracterele coresunzatoare literelor mari sunt in plaja 0x41 ("A") pana la 0x5A ("Z"). Conversia consta in stergerea bitului 5 (0x20) */ unsigned char change_case(unsigned char ch) { // verificam daca parametrul corespunde unei litere mici if((ch<0x61)||(ch>0x7A)) return(ch); else { ch=ch&0xDF; return(ch);

} }

Nota: Tabelul urmator contine toate codurile ASCII:


Tabelul 1. Codurile ASCII
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 8 9 NUL SOH STX ETX EOT ENQ ACK BEL BS HT DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SP ! " # $ % & ' ( ) 0 1 2 3 4 5 6 7 8 9 @ A B C D E F G H I P Q R S T U V W X Y ` a b c d e f g h i p q r s t u v w x y A B C LF VT FF SUB ESC FS * + , : ; < J K L Z [ \ j k l z { | D CR GS = M ] m } E F SO SI RS US . / > ? N O ^ _ n o ~ DEL

III.3.2 Exemplul nr. 2 Sa se scrie un program care activeaza releul conectat pe PORTB.0 cand pe linia seriala se primeste ASCII 1 si il dezactiveaza cand se primeste ASCII 0. Se face ecou la toate caracterele primite, pentru control. Se cfolosesc procedurile de copiere a unui proiect cu alt nume descrise in cap[itolul II. Se modifica modulul principal in urmatoarele aspecte: a. Se schimba initializarea portului B in asa fel incat PORTB.0 sa fie configurat ca linie de iesire b. Se modifica bucla infinita din functia main() in felul urmator:

while (1) { if(rflg==1) { rflg=0; send_char(rxdata); if(rxdata==0x31) PORTB.0=1; if(rxdata==0x30) PORTB.0=0; } };

Introducere in programarea microcontrollerelor din seria Atmel AVR Partea a IV-a

IV. Exemple de utilizare a convertorului A/D intern IV.1. Generalitati despre convertorul A/D In esenta, orice microcontroller este o masina digitala, capabila sa prelucreze marimi reprezentate in sistemul binar. Pentru a opera cu semnale analogice, este necesara o interfata speciala, denumita convertor analog-digital, care esantioneaza semnalul analogic la momente discrete de timp, si evalueaza amplitudinea acestuia (vezi figura 1).
f(t) S2 S1 S3 S4 t T1 T2 T3 T4

Fig. 1 Esantionarea unui semnal analogic Convertorul A/D masoara si reprezinta numeric valorile S1, S2, S3 ... ale esantioanelor semnalului analogic, la momente discrete de timp T1, T2, T3... Schema bloc a unui convertor A/D este prezentata in figura 2.
Vref DAC
_

ADCLK SAR & control logic

Analog input

MUX

S/H

ADC CONTROL Internal bus

ADC STATUS

ADC DATA

Fig. 2 Schema bloc a unui convertor A/D tipic Principalele caracteristici ale unui convertor A/D sunt descrise de rezolutia convertorului definita ca numarul de biti ai rezultatului conversiei si timpul de

conversie definit ca intervalul intre momentul cand se genereaza o comanda de conversie si momentul in care rezultatul este disponibil. Din punct de vedere al programarii, convertorul A/D este conectat la unitatea centrala a micorcontrollerului prin intermediul unui registru de control si stari, care contine biti de comanda pentru activarea blocului de conversie, selectia intrarii analogice multiplexate, selectia ceasului de lucru, comanda de start conversie si bitul de stare care indica sfarsit de conversie si a unui registru de date care va contine rezultatul conversiei. IV.2 Exercitii de programare a convertorului A/D 1. Sa se scrie un program care citeste periodic, la intervale de o secunda, linia analogica 0 si transmite rezultatul pe interfata seriala, sub forma a doua caractere ASCII. Solutie: a. Folosind CodeWizard se genereaza un nou proiect si se salveaza intr-un folder distinct sub numele analog1 b. Se copiaza fisierele usart.c si timer1.c din exemplele descrise in capitolele anterioare. c. Se foloseste optiunea Tools -> CodeWizard -> Program preview pentru a initializa convertorul A/D . Se copiaza si se salveaza sectiunea de initializare a convertorului A/D intr-un fisier distinct ADC.C Acesta va avea urmatorul continut:
#include <mega16.h> #include <delay.h> #define ADC_VREF_TYPE 0x60 void init_adc(void) { // ADC initialization // ADC Clock frequency: 1000.000 kHz // ADC Voltage Reference: AVCC pin // Only the 8 most significant bits of // the AD conversion result are used ADMUX=ADC_VREF_TYPE & 0xff; ADCSRA=0x84; } // Read the 8 most significant bits // of the AD conversion result unsigned char read_adc(unsigned char adc_input) { ADMUX=adc_input | (ADC_VREF_TYPE & 0xff); // Delay needed for the stabilization of the ADC input voltage delay_us(10); // Start the AD conversion ADCSRA|=0x40; // Wait for the AD conversion to complete

while ((ADCSRA & 0x10)==0); ADCSRA|=0x10; return ADCH; }

d. Se editeaza modulul principal (analog1.c) ca sa aiba urmatorul continut:


/***************************************************** Un exemplu de folosire a convertorului A/D Programul citeste linia analogica 0 si transmite rezultatul pe linia seriala sub forma a doua caractere ASCII *****************************************************/ #include <mega16.h> extern void init_usart(void); extern void init_timer1(void); extern void init_adc(void); extern extern extern extern void soft_timers(void); void send_char(char ch); unsigned char read_adc(unsigned char adc_input); unsigned char TTAB[8];

unsigned char hinib, lonib; void init(void); void casc(unsigned char ch);

void main(void) { unsigned char adc_value; init(); init_usart(); init_timer1(); init_adc(); TTAB[1]=100;

// Global enable interrupts #asm("sei") while (1) { soft_timers(); if(TTAB[0]==0) { TTAB[0]=100; adc_value=read_adc(0); casc(adc_value); send_char(hinib); send_char(lonib); send_char(0x20); // emite un spatiu

} }; } // casc - converteste un octet binar la doua caractere ascii hiniob, lonib void casc(unsigned char ch) { hinib=(ch&0xf0)>>4; lonib=ch&0x0f; if(hinib>9) hinib=hinib+'7'; else hinib=hinib+'0'; if(lonib>9) lonib=lonib+'7'; else lonib=lonib+'0'; } void init(void) { // Input/Output Ports initialization // Port A initialization // Func7=In Func6=In Func5=In Func4=In Func0=In // State7=T State6=T State5=T State4=T State0=T PORTA=0x00; DDRA=0x00; // Port B initialization // Func7=In Func6=In Func5=In Func0=In // State7=T State6=T State5=T State0=T PORTB=0x00; DDRB=0x0F; // Port C initialization // Func7=In Func6=In Func5=In Func0=In // State7=T State6=T State5=T State0=T PORTC=0x00; DDRC=0x00; // Port D initialization // Func7=In Func6=In Func5=In Func0=In // State7=T State6=T State5=T State0=T PORTD=0x00; DDRD=0x00; // // // // Timer/Counter 0 initialization Clock source: System Clock Clock value: Timer 0 Stopped Mode: Normal top=FFh

Func3=In State3=T

Func2=In State2=T

Func1=In State1=T

Func4=In State4=T

Func3=In State3=T

Func2=In State2=T

Func1=In State1=T

Func4=In State4=T

Func3=In State3=T

Func2=In State2=T

Func1=In State1=T

Func4=In State4=T

Func3=In State3=T

Func2=In State2=T

Func1=In State1=T

// OC0 output: Disconnected TCCR0=0x00; TCNT0=0x00; OCR0=0x00;

// Timer/Counter 2 initialization // Clock source: System Clock // Clock value: Timer 2 Stopped // Mode: Normal top=FFh // OC2 output: Disconnected ASSR=0x00; TCCR2=0x00; TCNT2=0x00; OCR2=0x00; // External Interrupt(s) initialization // INT0: Off // INT1: Off // INT2: Off MCUCR=0x00; MCUCSR=0x00; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x10; // Analog Comparator initialization // Analog Comparator: Off // Analog Comparator Input Capture by Timer/Counter 1: Off ACSR=0x80; SFIOR=0x00; }

Observatii: Se observa ca rezultatul binar al conversiei obtinut in variabila adc_value, actualizata prin apelarea functiei read_adc(numar_canal) este un octet binar. Daca emitem pe linia seriala acest octet, terminalul il va afisa intr-un mod neinteligibil. Din acest motiv este necesar ca inainte de a emite octetul citit de la ADC, sa-l convertim la doua caractere ASCII, corespunzator reprezentarii hexazecimale a numarului. Exemplu Sa presupunem ca octetul citit de la ADC are valoarea binara 0011 1010 (0x3A). Daca emitem direct pe linia seriala acest octet, terminalul va interpreta valoarea primita ca un digit ASCII si va afisa :. Rutina de conversie casc(unsigned char ch) foloseste doua variabile globale hinib si lonib pentru a stoca rezultatul conversiei.
void casc(unsigned char ch) { hinib=(ch&0xf0)>>4; lonib=ch&0x0f; if(hinib>9) hinib=hinib+'7';

else hinib=hinib+'0'; if(lonib>9) lonib=lonib+'7'; else lonib=lonib+'0'; }

Observam ca, pentru inceput, casc izoleaza cei doi semiocteti ai parametrului in variabilele hinib si lonib:
hinib=(ch&0xf0)>>4; lonib=ch&0x0f;

In acest moment, hinib=0x03 si lonib=0x0A. In continuare, observam ca reprezentarea ASCII a numerelor de la 0 la 9 este 0x30-0x39, ceea ce inseamna ca pentru a converti un numar situat in plaja [0-9] la ASCII este suficient sa adunam constanta 0x30 care este codul SCII pentru 0. In mod similar pentru a obtine codurile ASCII corespunzatoare numerelor hexazecimale [A-F] este suficient sa adunam constanta 0x37 (echivalent cu ASCII 7).
if(hinib>9) hinib=hinib+'7'; else hinib=hinib+'0'; if(lonib>9) lonib=lonib+'7'; else lonib=lonib+'0';

In final, hinib=0x33 si lonib=0x41 (0x0A+0x37=0x41). 2. Adaugati la exercititiul precedent urmatoarea functie: - Sa se activeze releul conectat la PORTB.0 cand se depaseste un prag PRAG_SUS si releul conectat la PORTB.1 cand valoarea semnalului scade sub PRAG_JOS. Solutie: Se modifica doar modulul main in felul urmator:
while (1) { soft_timers(); adc_value=read_adc(0); if(TTAB[0]==0) { TTAB[0]=100; casc(adc_value); send_char(hinib); send_char(lonib); send_char(0x20); // emite un spatiu } if(adc_value>=PRAG_SUS) PORTB.0=1; else PORTB.0=0; if(adc_value<=PRAG_JOS) PORTB.1=1; else PORTB.1=0; }; }

3. Sa se scrie un program care activeaza si dezactiveaza alternativ releul conectat la PORTB.0 pentru o durata controlata printr-un potentiometru conectat la intrarea analogica 0. Solutie: Se modifica functia main() dupa cum urmeaza:
while (1) { soft_timers(); if(TTAB[0]==0) { adc_value=read_adc(0); TTAB[0]=~adc_value; PORTB.0=!PORTB.0; casc(adc_value); send_char(hinib); send_char(lonib); send_char(0x20); // emite un spatiu } }; }

Introducere in programarea microcontrollerelor din seria Atmel AVR Partea a V-a

V. Notiuni de baza despre PWM V.1 Definitie. Principiul de functionare al generatoarelor PWM PWM este un acronim de la Pulse Width Modulator Modulator de impulsuri in durata, mai precis e vorba de un generator de semnale dreptunghiulare cu frecventa fixa, dar cu factor de umplere modificat dinamic.
PWMOUT

Fig. 1 Forma de unda a unui semnal PWM Un astfel de semnal poate fi generat cu o schema ca in figura 2.

PWM R Carry Clock Prescaller Counter S Comparator Compare register Flip flop Q

Fig. 2 Schema logica a unui circuit generator PWM Circuitul consta intr-un numarator, care numara permanent pe un ceas obtinut prin divizarea programabila cu ajutorul unui prescaller a ceasului principal. Continutul numaratorului este comparat de un comparator digital cu valoarea continuta de un registru programabil (compare register) si la coincidenta se seteaza un bistabil R-S. Stergerea bistabilului se face in momentul cand numaratorul ajunge la overflow. Functionarea schemei din figura 2 este ilustrata in figura 3.

TOP COMPARE
T ime

PWMOUT

T ime

Fig. 3 Principiul de functionare a unui generator PWM Se observa ca frecventa semnalului PWM este constanta, data de frecventa ceasului principal si de constantele de divizare ale prescallerului si ale numaratorului PWM. Factorul de umplere al semnalului este insa dependent de valoarea regsitrului de comparatie (compare register). Aceasta particularitate face ca sistemul de timere al microcontrollerelor sa poata fi usor fi folosit si ca generator PWM.

In cazul microcontrollerelor din seria AVR, toate timerele principale pot fi folosite si ca generatoare PWM. ATMega16, are 3 timere , iar timer1 are asociate doua registre output compare, deci in total se pot genera 4 semnale PWM simultan. Principala utilitate a semnalelor PWM deriva din faptul ca prin filtrarea trece-jos a unui semnal PWM se obtine un semnal proportional cu factorul de umplere al semnalului PWM, ceea ce este echivalent cu o demodulare. In acest mod, subsistemul PWM al unui microcontroller este echivalent cu un convertor DigitalAnalog. V.2 Exemple de programare a timerelor in mod PWM la AVR 1. Folosind timer1 al ATMega16 sa se genereze la iesirile OC1A si OC1B (pinii 18 si 19 ai microcontrollerului) doua ceasuri, unul cu factor de umplere iar celalat cu factor de umplere . Solutie Folosind CodeWizardAVR, se selecteaza initializarile pentru timer1 ca in figura 4. Apoi se editeaza programul generat pentru a configura iesirile PORTD corespunzatoare pinilor OC1A si OC1B ca iesiri si se aleg valorile de initializare pentru OCR1AL si OCR1BL la jumatate, respectiv un sfert din 255. (0x80 si 0x40). Programul principal se reduce la o bucla infinita de asteptare totul se reduce la aceste initializari. Odata initializat subsistemul timer PWM functioneaza independent de programul principal.

Fig. 4 Initializarile cerute de exercitiul 1

2. Folosind iesirea PWM OC1B asociata cu timer1 sa se genereze un semnal care, dupa filtrarea trece-jos, sa aproximeze un semnal liniar variabil. Solutie: Se initializeaza timer1 in mod similar cu initializarea din exercitiul porecedent. In bucla infinita din functia main se scrie urmatorul cod:
while (1) { for (i=0;i<10;i++) { OCR1BL=PWMTAB[i]; delay_ms(1); } };

Unde variabila PWMTAB a fost definita ca:


unsigned char PWMTAB[10]={10,30,50,70,90,110,130,150,170,190};

3. Sa se scrie un program care modifica factorul de umplere al unui ceas generat la iesirea OC1B in functie de valoarea masurata pe intrarea analogica 0.

Introducere in programarea microcontrollerelor din seria Atmel AVR Partea a VI-a

VI. Programarea interfetei SPI VI.1 Definitie. Principiul de functionare SPI este un acronim pentru Synchronous Peripheral Interface interfata de comunicatie sincrona cu perifericele. Principiul de functionare al SPI se bazeaza pe modul de functionare al registrelor de deplasare. In figura 1, sunt prezentate doua registre de deplasare concatenate si controlate de acelasi ceas de sincronizare. In aceasta structura, continutul registrului A este transferat bit cu bit (serial) in registrul B, sincron cu ceasul exterior, aplicat ambelor registre.

A Data In

SERIN CLK

SEROUT

SERIN CLK

SEROUT

Clock

Fig. 1 Un exemplu elementar de comunicatie sincrona cu registre de deplasare Intr-o astfel de structura, dispozitivul care genereaza clock-ul de sincronizare, controleaza integral momentul cand are loc transmisia, precum si viteza de comunicatie si, din acest motiv este denumit MASTER, iar dispozitivul pasiv este numit SLAVE. In practica, pentru a asigura comunicatia bidirectionala intre MASTER si SLAVE, se folosesc circuite care contin patru registre de deplasare, conectate ca in figura 2.
Rx Data Register SCK Tx Data Register MISO SCK MOSI

Tx Data Register

Rx Data Register Slave

Master

SS

Fig. 2 Schema bloc a unei conexiuni SPI bidirectionale Se observa ca toate cele patru registre sunt controlate de acelasi clock SCK, generat de dispozitivul MASTER. Liniile de date se numesc MOSI (Master Out Slave In iesirea de date a dispozitivului MASTER) si MISO (Master In Slave Out intrarea de date pentru MASTER) Suplimentar fata de aceste semnale, dispozitivul MASTER genereaza inca un semnal de control, denumit SS (SLAVE SELECT), activ de obicei pe zero. In acest mod se pot realiza retele cu un MASTER si mai multe dispozitive SLAVE., fiecare SLAVE avand un semnal distinct de selectie. La un moment dat, numai unitatea SLAVE care primeste semnal de selectie participa la transferul de date. O consecinta evidenta si importanta a faptului ca toate registrele implicate intr-o conexiune SPI sunt controlate de acelasi ceas, este faptul ca transmisia si receptia datelor au loc simultan, cu alte cuvinte, comunicatia SPI este in mod inerent full-duplex.

VI.2 Structura interfetei SPI care echipeaza microcontrollerele Majoritatea microcontrollerelor moderne sunt echipate cu un subsistem SPI, cu structura descrisa in figura 3.
SCK MOSI Tx shift register Rx shift register MISO

CLK

Control logic

Interrupt request Data register

Control register Internal bus

Status register

Fig. 3 Schema bloc a interfetei SPI a unui microcontroller Microcontrollerele din seria Atmel AVR, au urmatoarele registre in structura subsistemului SPI: a. Registrul de date al interfetei, denumit SPDR (SPI Data Register) La nivelul MASTER, o scriere in acest registru initiaza imediat procesul de transfer serial al datelor, prin generarea a 8 impulsuri de ceas. La sfarsitul transferului, SPDR al MASTER contine datele receptionate de la SLAVE. b. Registrul de stare SPSR (SPI Status Register), contine doi biti de stare, si anume: SPIF (SPI transfer complete Flag) care indica incheierea transferului si WCOL Write Collision, un bit de eroare, setat atunci cand se incearca o scriere soft in registrul SPDR pe durata cat exista un transfer in curs. c. Registrul de control SPCR (SPI Control Register) contine biti de control cu urmatoarele functii: - selectia vitezei de transfer (frecventa ceasului) - polaritatea clock-ului generat - ordinea de transfer a bitilor (incepand cu MSB sau cu LSB) - selectia regimului de functionare (ca MASTER sau SLAVE) - activarea generala a interfetei - activarea optionala a emisiei unei cereri de intrerupere la sfarsitul unui transfer SPI. VI.3 Aplicatii ale interfetei SPI a. Conectarea a doua sau mai multe microcontrollere in sisteme multiprocesor. In acest mod se pot realiza viteze de transfer relativ mari (4Mb/s). b. Conectarea unor memorii sau dispozitive periferice (display-uri, tastaturi etc.) la un microcontroller. Acest sistem are avantajul ca implica putini pini ai microcontrollerului.

VI.4 Programarea interfetei SPI Programarea interfetei SPI vizeaza urmatoarele aspecte: a. Initializarea interfetei proces care are ca obiectiv selectia regimului de functionare (MASTER sau SLAVE), activarea generala a interfetei, selectia frecventei si a polaritatii ceasului, si eventual activarea intreruperii de sfarsit de transfer. Este de asemenea necesara configurarea liniilor I/O asignate pentru MOSI si SCK, ca linii de iesire iar a liniei MISO ca linie de intrare. b. Scrierea functiei de emisie/receptie (reamintim ca procesul de emisie si cel de receptie au loc simultan) O rutina tipica de emisie/receptie SPI este urmatoarea (ATMega16, MASTER):
unsigned char spi_send(unsigned char ch) { PORTB.4=0; // genereaza semnalul SS=0 delay_us(10); // intarziere necesara pentru periferice lente SPDR=ch; // inceputul transmisiei while(!(SPSR&0x80)); // asteptarea incheierii transmisiei PORTB.4=1; // Deselecteaza SLAVE return(SPDR); // intoarce caracterul receptionat de la SLAVE }

Exemplu Sa se scrie un program care sa configureze microcontrollerul ATMega16 ca SPI MASTER si sa comande aprinderea unor LED-uri conectate la un circuit MC14489. Solutie Circuitul MC14489 poate comanda direct 24 de LED-uri, sau 5 digiti de afisare cu 7 segmente. Datele care se afiseaza se primesc pe o interfata SPI. Evident, circuitul se comporta ca un SPI SLAVE. Formatul datelor transmise catre MC14489 este urmatorul: a. Dispozitivul MASTER trimite 4 octeti, dintre care primul octet este un cuvand de configurare. Pentru a accesa individual cel 24 de LED-uri acest cuvant are valoarea 0x3F. b. In continuare, se trannsmit 3 octeti de date, corespunzator celor 24 de LEDuri pe care le poate controla circuitul. Toti cei patru octeti se transmit cu bitul cel mai semnificativ primul. Programul de initializare a SPI pentru comanda MC14489 se obtine cu CodeWizardAvr, selectand optiunile din figura 4.

Fig. 4 Dialogul CodeWizardAVR pentru initializarea SPI Se vor configura de asemenea liniile PORTB.4, PORTB.5 si PORTB.7 ca linii de iesire (pentru SS, MOSi si SCK respectiv). Se scrie apoi programul principal ca mai jos:
void main(void) { init(); PORTB.4=1; while (1) { send_spi(0x3F); send_spi3(0x81,0x0F,0x55); delay_ms(1000); }; }

Iar functiile send_spi() si send_spi3() au urmatorul continut:


unsigned char send_spi(unsigned char ch) { PORTB.4=0; delay_us(100); SPDR=ch; while(!(SPSR&0x80)); PORTB.4=1; return(SPDR); } void send_spi3(unsigned char ch1, unsigned char ch2, unsigned char ch3) { PORTB.4=0; delay_us(10);

SPDR=ch1; while(!(SPSR&0x80)); SPDR=ch2; while(!(SPSR&0x80)); SPDR=ch3; while(!(SPSR&0x80)); PORTB.4=1; }

Nota: Functia send_spi3() a fost necesara pentru ca MC14489 cere ca toate cele 3 caractere care urmeaza dupa caracterul de configurare trebuie transmise fara ca linia SS sa revina in 1. Exercitii 1. Sa se scrie un program care sa afiseze pe LED-urile conectate la un circuit MC14489 caracterele ASCII primite pe linia seriala asincrona.

Das könnte Ihnen auch gefallen