1. Intreruperi in Arduino - users.utcluj.rousers.utcluj.ro/~rdanescu/pmp-lab7.pdf · Laborator 7 1....
Transcript of 1. Intreruperi in Arduino - users.utcluj.rousers.utcluj.ro/~rdanescu/pmp-lab7.pdf · Laborator 7 1....
Laborator 7
1. Intreruperi in Arduino
2. Generare de semnale PWM in Arduino
3. Senzori Analogici
1. Intreruperi in Arduino
Ne amintim din laboratorul 3 ca intreruperile sunt evenimente care necesita atentia imediata a
microcontrollerului. In momentul in care se intampla un eveniment care are ca efect
declansarea unei intreruperi, microcontrollerul opreste taskul pe care il executa si incepe sa se
ocupe de intrerupere declansand o ISR (intrerupt service routine), o rutina care este atasata
intreruperii respective. Pentru ca microcontrollerul sa raspunda la intreruperi, trebuie activat
un bit (Global Intrerupt Enable) si bitul corespunzator intreruperii. Urmatoarele lucruri sunt
esentiale in momentul in care lucrati cu intreruperi:
- Intreruperea trebuie sa fie activata activand bitul corespunzator din registrul corespunzator
- Bitul Global corespunzator intreruperilor, I, din SREG trebuie si el activat
- Stiva trebuie initializata. In momentul in care se realizeaza o intrerupere, inainte de a intra
in procedura dedicata trebuie sa stocam informatiile esentiale pe stiva pentru a nu le
pierde la revenirea din rutina; asadar stiva trebuie initializata
- Fiecare rutina se finalizeaza cu RETI. In acel moment microcontrollerul stie sa se intoarca
la taskul precedent
Intreruperile sunt de doua feluri : interne si externe. Intreruperile interne sunt asociate cu
perifericele microcontrollerului (Timer/Counter intrerupts, Analog comparator etc.)
Intreruperile externe sunt declansate de pini externi (amintiti-va de intreruperile declansate de
apasarea butoanelor PMOD sau butoanelor tastaturii).
Fiecare avr are o lista de intreruperi, care include tipul de eveniment care va declansa
intreruperea. In momentul in care intreruperile sunt activate si unul din aceste evenimente se
intampla procesorul va realiza un salt in memorie la o anumita locatie (vectorul intreruperii,
locatie pe care o va gasi in lista/tabela de intreruperi). Scriind o ISR si apoi facand un link
rutinei la adresa locatiei intreruperii corespunzatoare, putem sa ii spunem sistemului sa
realizeze ceva specific in momentul in care evenimentul declanseaza intreruperea.
In primul exemplu, vom folosi o intrerupere declansata de butoane si vom afisa pe LCD un
mesaj corespunzator butoanelor. In momentul in care butonul este apasat se declanseaza
intreruperea si programul afiseaza un mesaj pe LCD. Pentru acest exemplu aveti nevoie de o
placa Arduino, un afisor LCD, un modul pModBtn si un bread board. Atmega 2560 are 6 pini
de intrerupere. Pentru a vedea lista de pini si intreruperile corespunzatoare accesati linkul 1.
Vom conecta butoanele la pinii corespunzatori intreruperilor INT0 si INT1, adica pinii digitali
20 si 21. In figura 1 se observa o imagine a conexiunilor necesare, iar mai jos codul
corespunzator programului.
Fig 1. Conexiunile necesare primului exemplu
//includem headerul responsabil pentru operatii
//cu intreruperi pentru avr
#include "avr/interrupt.h"
//include libraria de manipulat LCD
#include <LiquidCrystal.h>
//initializeaza lcd-ul la valorile stabilite ale pinilor
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
volatile int buttonVariable;
void setup(void)
{
buttonVariable = 0;//initializam variabila shared intre ISR si programul principal
Serial.begin(9600); //deschidem conexiunea seriala
Serial.println("A inceput");//afisam un mesaj la consola pentru a vedea ca a inceput
comunicarea
//seteaza numarul de randuri si coloane ale LCD-ului
lcd.begin(16, 2);
lcd.print("The show has begun");
delay(1000);//facem o scurta pauza pentru a vizualiza mesajul de pe ecran
pinMode(21 ,INPUT); //setam pinul 21 ca si pin de input; acesta este pinul pe care se
afla intreruperea 0 INT0
pinMode(13, OUTPUT); //setam pinul 13 ca si pin de output
pinMode(20, INPUT);
digitalWrite(20, HIGH);
digitalWrite(21,HIGH);//facem pentru pinul 21 sa fie tras in 1 printr-un rezistor de tip
pull up
digitalWrite(13,HIGH);
delay(1000);
sei(); //activam intreruperile si flaglul de intreruperi din SREG va fi setat in 1
EIMSK |= (1 << INT0); //activam intreruperea INT0
EIMSK |= (1 << INT1); //activam intreruperea INT1
EICRA |= (1 << ISC01); //activam intreruperea cand semnalul este pe falling edge.
EICRA |= (1 << ISC11);// ca si mai sus
//amintiti-va ca pe eicra setam cand si cum vrem sa activam inpulsul ex : front
crescator, descrescator, pe nivel pozitiv sau negativ
digitalWrite(13,LOW);
Serial.println(EICRA,BIN);//afisam valoarea din EICRA
Serial.println(EIMSK,BIN);//afisam valoarea din EIMSK
lcd.clear();//curatam ecranul
}
void loop()
{
//DO NOTHING
if(buttonVariable == 1)//daca am fost intr-o rutina trebuie sa stergem ecranul si sa
afisam iar mesajul din principal
{
lcd.clear();//curatam ecranul
buttonVariable = 0;
}
delay(1000);
Serial.println("Acum nu se intampla nimic");//afisam un mesaj
lcd.setCursor(0,0);//setam cursorul
lcd.print("Voi stiti asta..");//afisam mesaj
}
//Rutina pentru handlingul intreruperii atasata la INT0
ISR(INT0_vect)
{
digitalWrite(13, !digitalRead(13));//facem toggle la pinul 13
lcd.setCursor(0,0);//setam cursorul
lcd.print("Intrerupem");//afisam mesaj
lcd.setCursor(0,1);
lcd.print("ptr stirea zilei");
buttonVariable = 1;
}
ISR(INT1_vect)
{
digitalWrite(13, !digitalRead(13));
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Stirea Doi");
buttonVariable = 1;
}
Este bine sa ne tinem ISR-urile cat mai scurte intrucat programul principal este oprit in acele
momente asteptand terminarea rutinei.
Tineti minte ca delay() si millis() nu functioneaza „pe parcursul” unei rutine de intrerupere.
Acest lucru se intampla pentru ca intreruperile globale sunt dezactivate pe parcursul unei
rutine. Acest lucru deterimina ca in cazul in care un alt eveniment corespunzator unei alte
intreruperi se declanseaza programul nu va putea prinde acea intrerupere (de aceea e
important ca tot ce este scris in codul rutinei sa fie cat de scurt posibil). De asemenea, daca
vreti sa modificati o variabila in interiorul unei rutine si doriti ca valoarea acestei variabile sa
fie vizibila in tot programul faceti acea variabila de tipul volatile. Introducand volatile
inaintea tipului variabilei informam compilatorul ca variabila este o variabila shared.
Arduino ne permite sa utilizam sistemul de intreruperi si fara avea cunostinte specifice despre
mecanismul specific microcontrollerului, punandu-ne la dispozitie anumite functii. Prima
functie pe care o vom prezenta este attachIntrerrupt(). Functia aceasta are rolul de a atasa
un anumit ISR la o intrerupere, de a inlocuii orice functie precedenta care a fost atasata la
intreruperea respectiva. Sintaxa este attachInterrupt(interrupt, ISR, mode). Primul pin la
functia attach interrupt reprezinta numarul intreruperii.
!!Atentie Pinii digitali de pe arduino corespnzatori intreruperii nu sunt aceasi cu pinii chipului
atmega2560 si nu sunt la fel cu numarul intreruperii pe care trebuie sa o introducem in
functia attachinterrupt(). Distinctia dintre pini este specificata in tabelul de mai jos:
Fig 2. Numarul care trebuie introdus in attach intrerrupt
pentru o anumita intrerupere din atmega2560.
Al doilea prametru din aceasta functie reprezinta procedura care e asociata intreruperii(ISR).
Al treilea parametru reprezinta modul in care ar trebuii sa fie declansata intreruperea (pe front
crescator (RISING), descrescator (FALLING), pe nivel o (LOW) sau nivel 1 (HIGH) sau
pur si simplu cand se intampla o schimbare ex – pinul atasat intreruperii isi schimba
valoarea(CHANGE)).
Functia noInterrupts() dezactiveaza intreruperile. Acestea pot fi reactivate cu functia
interupts().
Functia interrupts() reactiveaza intreruperile dupa ce au fost dezactivate cu functia
noInterrupts in prealabil. Intreruperile permit anumitor taskuri importante sa se execute in
fundal si sunt activate implicit. Intreruperile pot afecta negativ anumite sectiuni de cod si de
aceea pe durata acelor sectiuni de cod ele se dezactiveaza, urmand sa fie reactivate ulterior.
Functia detachInterrupt() dezactiveaza o intrerupere al carei numar este pasat ca si
parametru. Sintaxa este detachInterrupt(interrupt);
Exemplul anterior a fost implementat folosind registri de configurare ai AVR, echivalentul in
C++ al abordarii din laboratorul 3. In continuare, vom prezenta un exemplu cu functionalitate
similara, realizat cu ajutorul mediului de programare Arduino. Codul care se repeta din
exemplul precedent nu il vom mai comenta.
#include <LiquidCrystal.h>
LiquidCrystal lcd(7,6,5,4,3,2);
volatile int buttonVariable;
void setup()
{
buttonVariable = 0;
Serial.begin(9600);
Serial.println("A inceput");
lcd.begin(16,2);
lcd.print("The show has begun");
lcd.setCursor(0,1);
lcd.print("again");
delay(1000);
//atasam intreruperii zero functia unu care sa se declanseze cand //pinul isi schimba
valoarea
digitalWrite(20, HIGH);
digitalWrite(21,HIGH); attachInterrupt(2, functieUnu, RISING);
attachInterrupt(3, functieDoi, CHANGE);
}
void loop()
{
//aici sunt taskuri care se executa in mod normal cand se ruleaza //programul
Serial.println("Tasks that are executed");
delay(1000);
}
//prima ISR
void functieDoi()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Functia Doi");
}
//a doua ISR
void functieUnu()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Functia Unu");
}
In afara intreruperilor externe, exista si intreruperi interne, care nu sunt cauzate de evenimente
pe pinii exteriori, ci de componente hardware incluse in microcontroller. In aceasta categorie
sunt incluse intreruperile de timer.
Prin utilizarea acestor intreruperi, puteti genera actiuni la intervale precise de timp, fara a
utiliza functii de tip delay sau millis. Intreruperile de temporizatoare functioneaza asincron,
lucru care permite ca programul sa execute bucla principala si doar cand s-a ajuns la un
anumit prag de timp sa se declanseze o rutina specifica. Timerul incrementeza un registru
numit counter register si in momentul in care apare un overflow un flag de overflow se
seteaza. Acest flag poate fi verificat manual sau putem atasa o intrerupere in caz de overflow.
Rutina (ISR) va reseta si flagul. Fiecare timer are nevoie de sursa de clock. In general se alege
ca si sursa oscilatorul placii si in functie de acesta se determina si rezolutia timerului ( tineti
minte formula T = 1/ F unde F e frecventa in Hz).
Arduino Mega are 5 timere care pot fi folosite. Pentru a folosi aceste timere va trebuii sa
setam valori specifice pentru configurarea temporizatoarelor. Doua din aceste registre contin
valori de configurare si sunt TCCRxA si TCCRxB, unde x este numarul temporizatorului.
TCCR vine de la Timer Counter Control Register. Cand incepem sa folosim timerul cei mai
importanti sunt cei trei biti de control care seteaza cum sa fie scalat timerul. In figura 3
observati registrii A si B si posibile configurari pentru ultimii biti ai registrului de timer.
Fig 3 Configurarea temporizatorului.
Implicit cei trei biti sunt setati la 0. Pentru a sumariza, mai jos sunt cei mai importanti registrii
care se folosesc cu temporizatoare.
- TCCRx - Timer/Counter Control Register. Valoarea de pre scalling poate fi stabilita aici
- TCNTx - Timer/Counter Register. Valoarea actuala este stocata aici. Aici puteti vedea
valoarea adevarata a timerului.
- OCRx - Output Compare Register
- ICRx - Input Capture Register (only for 16bit timer)
- TIMSKx - Timer/Counter Interrupt Mask Register. Pentru activarea sau dezactivarea
intreruperilor de timp
- TIFRx - Timer/Counter Interrupt Flag Register. Indicates a pending timer interrupt.
Registrii A si B sunt descrisi in figura 4 de mai jos.
Fig 4 Registrii Importanti de timere in AVR
In exemplu care va urma vom incrementa o variabila in momentul in care timerul
nostru(TIMER1) va face overflow. Valoarea incrementarii la overflow va fi afisata pe LCD.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
volatile int myVar;
void setup()
{
myVar = 0;
//initializarea primului numarator
cli(); //facem disable la intreruperile globale pentru a face //modificarile
corespunzatoare timerelor
TCCR1A = 0;// SETAM TCCR1A si B la 0
TCCR1B = 0;
lcd.begin(16, 2);
lcd.print("Timere");
//facem enable la intrerupere de overflow pentru timerul 1
TIMSK1 = (1 << TOIE1); //timer overflow intrerupt eneble for timer 1
//setam timerul sa ruleze o frecventa divizata cu 1024
//DE MENTIONAT CA FRECVENTA PROCESORULUI e de 16 MHZ si timer 1
//este un timer de 16 biti
//cu un prescaller de 1024 avem de a face cu v = 1 / (16 * 10 ^6)
//timerul va da overflow la fiecare 4.194 s ( v * 2 ^ 16)
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);
//activam intreruperile globale
sei();
}
void loop()
{
// Serial.println("Ruleaza programul");
lcd.setCursor(0,1);
lcd.print(myVar);
lcd.setCursor(5, 1);
lcd.print(TCNT1);
}
ISR(TIMER1_OVF_vect)
{
myVar = myVar + 1;
}
Pentru a face ca intreruperea de timer de la avr sa se declanseze la un anumit moment (nu
atunci cand counterul face overflow) vom folosi un alt mod de declansare al intreruperii numit
CTC. Pentru a stabilii de cate cicluri de ceas sunt necesare pentru a ajunge la noul puls al
timerului nostru folosim formula (1)
(# timer counts + 1) = (target time) / (timer resolution) (1)
In aceasta formula target time reprezinta timpul la care vrem sa ajungem, timer resolution
reprezinta rezolutia clockului actual, setat prin intermediul bitilor CS (in exemplul precedent
1024) iar timer counts reprezinta valoarea la care va trebuii sa ne divizam clockul pentru a
obtine target time-ul dorit. Se ia valoarea + 1 intrucat resetarea valorilor timerului in
momentul in care a ajuns la valoarea dorita dureaza un ciclu de ceas.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
volatile int myVar;
void setup()
{
// initialize Timer1
cli(); // facem disable la intreruperile globale
TCCR1A = 0; // setam TCCR1A si B la 0
TCCR1B = 0;
lcd.begin(16, 2);
lcd.print("Timere with CTC");
// setam registrul cu valoarea caruia vom compara TCNT
OCR1A = 15624;
// activam modul CTC:
TCCR1B |= (1 << WGM12);
// divizam clockul placii cu 1024:
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);
// facem enable la modul de comparare prin setarea bitului
//corespunzator din masca
TIMSK1 |= (1 << OCIE1A);
// enable global interrupts:
sei();
}
void loop()
{
lcd.setCursor(0,1);
lcd.print(myVar);
lcd.setCursor(5, 1);
lcd.print(TCNT1);
}
ISR(TIMER1_COMPA_vect)
{
myVar = myVar + 1;
}
Spre deosebire de exemplul anterior in care incrementam variabila doar la overflow (la
aproximativ 4 secunde) in acest exemplu avem mai mult cotrol asupra perioadei de
incrementare a variabilei noastre; variabila se incrementeaza la fiecare secunda.
In continuare vom vedea o alta metoda pentru utilizarea temporizatoarelor, folosind biblioteca
TimerOne. Exemplul de mai jos va activa o rutina la fiecare secunda. Biblioteca Timer One
poate fi descarcata de pe site-ul Arduino, de la adresa [2].
#include <TimerOne.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
volatile int myVar;
void setup(void)
{
Timer1.initialize(1000000);//se initializeaza valoarea CTC a temporizatorului
//de fiecare data cand acea valoare va fi atinsa se va executa functia de mai jos
//
Timer1.attachInterrupt(ShowMessage); // increment variable to run every 1 second
}
void ShowMessage(void)
{
lcd.setCursor(0,0);
lcd.print(myVar);
myVar++;
}
void loop(void)
{
}
Observatie In exemplele precedente ati vazut cum se pot folosii fie functii avr pentru manipularea
intreruperilor, fie librarii din mediul arduino. Desi librariile arduino simplifica mult lucrurile,
recomandam sa se studieze si sa se inteleaga si functiile avr, intrucat daca veti dori vreodata
sa manipulati alte microcontrollere care nu sunt compatibile cu librariile din arduino nu veti
avea fundamentarea tehnica pentru a le folosi.
2. Generare de semnale PWM in Arduino
PWM este o metoda de a obtine un semnal analogic prin intermediul unui semnal digital
generand tensiuni de 1 si 0 la anumite perioade de timp. Fractiunea de perioada cat semnalul
este activ (1 logic) se numeste factor de umplere, sau duty cycle. In Arduino se poate folosi
PWM in trei moduri, fie prin temporizatoare, fie folosind functia analogWrite, sau variind
manual durata cat un pin este activ (1 logic).
In aceasta parte vom folosi functia analogWrite(factor de umplere). Valorile posibile pentru
analogWrite sunt de la 255 care reprezinta 5V la 0. Factorul de umplere de 50% se realizeaza
pentru valoarea 127. In figura 5 veti obsera cateva exemple de PWM.
Fig. 5 Exemple PWM cu factor de
umplere diferit
In exemplul care va urma vom genera un semnal PWM pentru generatorul de sunete
piezoelectric. PWM-ul care va fi atasat buzerului va fi afisat si pe ledul pinului 13 al Arduino
Mega. Montajul pentru acest exemplu este unul simplu, nemaifiind nevoie de o imagine. Pinul
de semnal al difuzorului (firul rosu) se conecteaza la pinul 9 pe placa iar firul negru la GND.
Ledul pinului 13 este deja pe placa.
int buzerPin = 9; //pinul la care atasam buzerul
int puls = 0; // pwm-ul care il dam perifericelor
int pas = 10; // pasul de incrementare al pwm-ului
int ledPin = 13;//ledul este cel de pe placa
void setup() {
// declararea pinilor ca pini de output
pinMode(buzerPin, OUTPUT);
pinMode(ledPin, OUTPUT);
}
void loop() {
// setam pwm-ul buzerului si ledului
analogWrite(buzerPin, puls);
analogWrite(ledPin, puls);
// modificam pwm-ul pentru urmatoarea iteratie
puls = puls + pas;
// schimbam directia de fading
if (puls == 0 || puls == 255) {
puls = -puls ;
}
// un mic delay pentru a vedea efectul
delay(30);
}
3. Senzori Analogici
Microcontrollerele sunt capabile sa detecteze semnale binare, 0 sau 1, cum este de
exemplu starea unui buton (apasat sau ridicat). Aceste semnale se numesc semnale digitale.
Cand un microcontroller este alimentat de la 5 V el intelege valoarea tensiunii de 5V ca si 1
logic si 0V ca si 0 logic. Cu toate acestea noi avem nevoie sa masuram si altfel de semnale in
lumea reala, semnale intermediare valorilor extreme (de exemplu, 2.57 V). Aceste semnale
contin informatie relevanta in nivelul tensiunii lor, si poarta numele de semnale analogice.
Un ADC (convertor analog la digital) converteste un semnal analogic la un numar. Prin
intermediul acestui dispozitiv avem posibilitatea de a interfata tot felul de periferice la
microcontrollerul nostru si de a masura informatiile analogice din jurul nostru. Nu toti pinii de
pe arduino pot face astfel de conversii. Pinii care pot fi folositi impreuna cu senzori analogici
sunt pinii care au un ‚A’ im fata numelui lor pe placa. Pinii incercuiti cu rosu in figura 6.
Fig 6 Pinii Analogici din Arduino Mega
ADC-urile pot varia mult intre diferite tipuri de microcontrollere. Spre exemplu pe Arduino
Mega avem ADC-uri care au o precizie de 10 biti. Acest lucru inseamna ca aceste convertoare
pot detecta pana la 1024 valori. Exista si adc-uri care au rezolutie de 8 sau 16 biti. ADC-ul
intoarce o valoare ratiometrica. Asta inseamna ca adc-ul considera 5 V ca 1023 si orice
valoare mai mica ca si 5V va fi construita ca si o fractie intre 5V si 1023 (2).
�
�����
��� �� � ���
� �� �� �� ������ � ��� (2)
Pinii analogici de pe arduino pot fi folositi si ca pini de tip I/O general (GPIO), ei avand
aceleasi functionalitati ca si pinii digitali, in cazul in care cei oferiti de placa nu sunt
suficienti. Pinii analogici de pe placa au la randul lor pull up rezitors. Sintaxa arduino pentru
activarea acestor rezistori este similara cu cea de la pinii digitali: digitalWrite(A0,
HIGH);//pinul A0 fiind setat ca input.
Pentru citirea unei valori de la un senzor se foloseste comanda analogRead().
!!Atentie Comanda analogRead() nu va functiona corect daca pinul de pe care incercati sa cititi a fost
setat ca si pin de output.
Datasheetul de la ATMEGA mai avertizeaza despre folosirea senzorilor analogici in pozitii
apropiate. In momentul in care realizam o citire daca executam rapid switching-ul intre
pozitiile pe care dorim sa le citim, se vor introduce zgomote in citirea semnalului. Se
recomanda folosirea unui mic delay() inaintea citirii unei valori analogice consecutive.
O alta functie importanta legata de utilizarea senzorilor analogici o reprezinta
analogReference().
Pentru a masura o tensiune analogica trebuie sa existe o tensiune de referinta fata de care sa o
raportam. Functia analogReference() seteaza tensiunea maxima cu care sa efectuam
masuratoarea.
Configuratii posibile pentru aceasta referinta sunt :
- DEFAULT – foloseste tensiunea de referinta a placii (5V pentru placile Arduino care
folosesc tensiune de 5V sau 3.3 V pentru placi cu tensiune de referinta de 3.3 V).
- INTERNAL – seteaza o tensiune de referinta de 1.1 V. Poate fi folosita pe placile care
contin ATMEGA 328 spre exemplu dar nu poate fi folosita pe ATMEGA2560.
- INTERNAL1V1 – Tensiune de referinta de 1.1 V folosita pe placile MEGA
- INTERNAL2V56 – tensiune de referinta de 2.56 V aceasta e valabila doar pe placile
MEGA
- EXTERNAL – tensiune aplicata pinului AREF. Aceasta tensiune este intre 0 si 5V.
Folositi tensiunea de referinta cea mai buna pentru senzorul analog utilizat. Ideal, referinta
trebuie sa fie valoarea maxima pe care o poate genera senzorul analogic, pentru a obtine o
rezolutie cat mai buna la conversia in digital. Daca referinta este mai mica decat valoarea
maxima pe care o poate avea semnalul, tensiunea ce depaseste valoarea de referinta nu va
putea fi cuantizata, ea generand la digitizare valoarea de saturatie 1023.
In exemplul urmator vom citi valoarea de la un potentiometru liniar. Valoarea citita va fi
afisata pe LCD. Circuitul pentru acest exemplu este ilustrat in figura 7. (legati pinii VCC si
GND ai potentiometrului la +5V si GND de pe placa, si semnalul de iesire la un pin
analogic)
Fig 7 Conexiunile pentru exemplul cu senzorii analogici
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
void setup()
{
analogReference(DEFAULT); //setarea tensiunii de referinta la tensiunea default
lcd.begin(16, 2); //initializarea LCD ului
lcd.setCursor(0,0);
lcd.print("Cititi senzor");
pinMode(A1, INPUT); // setarea pinului analogic A1 ca si pin de input
digitalWrite(A1, HIGH); //activarea rezistorului pull up pentru pinul A1
}
void loop()
{
int val = analogRead(A1); //citirea valorii analogice
lcd.setCursor(0,1);
lcd.print(val);
}
In al doilea exemplu pe care il vom realiza vom folosi o tastatura analogica (Fig 8)
Fig 8. Tastatura analogica
Nu vom intra in foarte mare detaliu in functionarea acestei componente. Important este ca la
apasarea unei taste, in functie de tensiunea de alimentare, pe pinul Vout se transmite o
anumita tensiune. In imaginea de mai jos( fig 9) aveti tensiunile pentru fiecare tasta.
Fig 9. Valori in functie de tasta apasata
In momentul in care aveti mai mult de o tasta apasata se va afisa valoarea tensiunea celei mai
mari.
Pentru exemplul acesta veti avea nevoie de un LCD shield si o tastatura analogica. Realizati
montajul din figura 10 (legati semnalele VCC si GND ale tastaturii la +5V si GND de pe
placa, si semnalul de iesire al tastaturii la un pin analogic) iar apoi introduceti codul de
mai jos.
Fig 10 Montaj pentru tastatura cu LCD
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
//se creaza o valoare intermediara in asa fel incat
//sa nu stergem ecranul LCD ului doar cand valoarea se modifica
int valIntermediar;
void setup()
{
//setam tensiunea de referinta la 5V
analogReference(DEFAULT); //setarea tensiunii de referinta la tensiunea default
lcd.begin(16, 2); //initializarea LCD ului
lcd.setCursor(0,0);
lcd.print("Cititi senzor");
pinMode(A1, INPUT); // setarea pinului analogic A1 ca si pin de input
digitalWrite(A1, HIGH); //activarea rezistorului pull up pentru pinul A1
//initializam valoarea intermediara la o valoare foarte mare
valIntermediar = 1000;
}
void loop()
{
int val = analogRead(A1); //citirea valorii analogice
//luam tasta aferenta tensiunii analogice
val = getTasta(val);
if(valIntermediar != val)
{
//setam ca noua valoare intermediara sa fie egala cu val
valIntermediar = val;
lcd.clear();//curatam ecranul
lcd.setCursor(0,0);
lcd.print("Cititi senzor");
lcd.setCursor(0,1);//si afisam noua valoare
lcd.print(valIntermediar);
}
}
/*
in aceasta functie se paseaza ca si parametru o valoare analogica
si se returneaza o tasta corespunzatoare acelei valori
Din cauza faptului ca semnalul analogic nu este filtrat si stabilizat acesta
fluctueaza in continuu.
Pentru a prinde doar acele valori care ne intereseaza se face o histereza
Intervalele au fost alese experimental, bineinteles bazandu-ne si pe tensiunile din data
sheet.
*/
int getTasta(int val)
{
if(val > 10 && val < 40) return 0;//nu e apasata nici o tasta
else if(val > 50 && val < 80)return 12;//e apasata tasta 12
else if(val > 90 && val < 120) return 11; //e apasata tasta 11
else if(val > 120 && val < 160) return 10;
else if(val > 160 && val < 200) return 9;
else if(val > 200 && val < 240)return 8;
else if(val > 240 && val < 290) return 7;
else if(val > 290 && val < 320) return 6;
else if(val > 320 && val < 370) return 5;
else if(val > 370 && val < 410) return 4;
else if(val > 410 && val < 450) return 3;
else if(val > 450 && val < 490) return 2;
else if(val > 490 && val < 530) return 1;
}
!!!Atentie Una dintre probleme atunci cand lucram cu senzori analgici este ca valorie citite fluctueaza
destul de mult. Acest lucru poate fi reparat hardware punand un condensator sau un filtru
trece jos pentru a ameliora efectul spike-urilor sau mai poate fi reparat software fie printr-o
variabila intermediara fie citirea a n valori si afisarea mediei lor aritmetice ca fiind valoarea
de la senzor.
Lucru Individual
1. Implementati toate exemplele din laborator. Intrebati cadrul didactic pentru orice
nedumerire legata de conceptele din laborator sau conectivitatea cu placa.
2. Night Light. Realizati un sistem care sa faca urmatoarele – sa citeasca datele de la un
senzor de lumina si cu cat valoarea citita de la senzorul de la lumina scade cu atat nivelul
de iluminare la un led va creste. Pentru acest punct aveti nevoie de un senzor de lumina
brick, un led sau un grup de leduri (folositi pmodurile), o placa arduino si fire de
conectare.
3. Ceas electronic. Realizati un ceas electronic pentru secunde si minute. Valoarea initiala
pentru secunde si minute va fi setata de la tastatura analogica. Cand apasati un buton(nu
de pe tastatura) acea valoare va fi setata. In momentul in care ceasul ajunge la o anumita
valoare (scrisa in cod) se va declansa o alarma pe difuzor si se va afisa mesajul
„Desteptarea” pe lcd. Pana la atingerea punctului alarmei se afiseaza valoarea curenta a
ceasului.
Pentru acest proiect aveti nevoie de o placa arduino, un lcd shield, un difuzor, o tastatura
analogica, un modul Pmod BTN, fire de conectare.
Referinte
[1] http://arduino.cc/en/Hacking/PinMapping2560
[2] https://github.com/PaulStoffregen/TimerOne