joi, 24 noiembrie 2011

Termostat digital cu microcontroler AtMega128

Schema bloc



    Elementul principal al sistemului de achiziţie a temperaturii este senzorul LM335. Circuitul de adaptare este format din amplificatoare operaţionale, necesare pentru a amplifica nivelul semnalului furnizat de traductor, şi un convertor analog-digital, care furnizează intrarea circulitului de prelucrare.
    Circuitul de prelucrare electronică este reprezentat demicrocontrollerul AtMega128, programarea acestuia fiind descrisă mai jos.
    Fiecare bloc component va fi descris în continuare. 
  
Sistemul de achiziţie a temperaturii
Schema sistemului de achiziţie a temperaturii este următoarea:




    Senzorul LM335 este folosit în configuraţie de senzor calibrat. In schemă, senzorul LM335 se poate echivala cu o diodă Zener.
    Rezistenţa R5 stabilizează curentul prin senzor la o valoare de 1 mA. Tensiunea pe această diodă este dependentă de temperatură. Potenţiometrul R7 este folosit pentru calibrarea tensiunii de ieşire la 25oC: 2,98V. Ea se modifică cu 10mV/grad.
    Circuitul are o impedanţă dinamică mai mică de 1Ω şi funcţionează între 0.45mA si 5mA fără degradări ale perfomanţelor. Calibrat,  LM335 are o eroare mai mică de 1oC la o variaţie a temperaturii de 100oC (tipic 0.5oC). Spre deosebire de alţi senzori de temperatură, LM335 are o ieşire liniară în tensiune.

Circuitul de adaptare cu traductorul

    Traductorul folosit, LM335 are o panta de 10mV/oK, iar pentru intervalul cerut  (0oC si 100oC ) tensiunile de iesire vor avea valorile +2.732V la 0oC si 3.732V la 100oC => excursia de tensiune va fi de 1V.
    Circuitul de mai sus are rolul de a mari excursia tensiunii de la iesire de la 1V la 5V pentru a ne putea folosi de intreaga rezolutie a ADC-ului intern al microcontrolerului a carui intrare analogica ia valori intre 0...5V, pentru
Din acestea rezulta ca amplificarea acestui adaptor trebuie sa fie A=ΔUf/ΔUi=5/1=5 pe fiecare grad celsius.
     Amplificarea este: A=(R3+Rv2)/R2
     R6≈R3+Rv2. Pentru R2=10kΩ avem:
     R3+Rv2=50k rezulta ca R3=45K, iar Rv2=5K
    Se alege R6=50k. 


Circuitul de prelucrare electronică

Se foloseşte microcontrolerul AtMega128, produs de Atmel. Acest microcontroler lucrează pe 8 biţi şi are o memorie de 128 KB.
Alte caracteristici ale microcontrollerului:
  1. 4KB memorie EEPROM
  2. 4KB SRAM intern
  3. Două timere pe 8 biţi
  4. Convertor analog-digital integrat pe 10 biţi, cu 8 canale
  5. Selectare a frecvenţei de ceas prin software
  6. 53 de linii de input/output programabile
Microcontrolerul funcţionează la o tensiune cuprinsă între 4,5 şi 5,5 V, la o frecvenţă de până la 16MHz.


    Configuraţia pinilor folosiţi este următoarea:
    Iesirea circuitului de adaptare cu traductorul, se va conecta pe pinul 62, adica pe canalul 0 al convertorului analog – digital (ADC0).
    Butoanele pentru setarea temperaturii de referinta de catre utilizator se vor conecta pe pinii 47 (PA4) pentru ridicarea referintei cu un grad, si 48 (PA5) pentru coborarea acesteia cu un grad.
    Iesirea pentru releul de declansare a agregatului de incalzire si ledul de semnalare a acestui releu sunt pe pinul 49 (PA2), iar racirea se comanda pe pinul 50(PA3).
    Este posibil ca, pentru activarea releului,s emnalul furnizat de mcirocontroller să nu fie suficient de mare. In acest caz, ieşirea poate fi conectată la un AO cu reacţie negativă sau la un tranzistor.
    Ledurile de semnalare a modului de afisare temperature setata / temperature reala sunt conectate pe pinul 0 (PA0) respectiv pinul 1 (PA1).
    Driverele de LCD se conecteaza la microcontroler pe portul E si portul D (PD0-3 (MSB), PE4-7, PE0-3 (LSB)).


Afişajul

Cele 3 afisoare TIL321P sunt controlate de catre 3 drivere HCF4543F:




Tabela de adevar pentru driver:

Afisorul TIL321P:


Funcţionarea     
    Tabelul următor prezintă legătura dintre tensiunea de ieşire a senzorului LM335 (Ui), tensiunea de iesire a circuitului de adaptare a traductorului (Uf), numarul treptei de esantionare a ADC-ului intern al microcontrolerului (Te), numarul aproximat al treptei de esantionare (Nr Te) şi temperatura afişată (T).

T(ºC)
Ui
Uf
Te
Nr Te
Nr binar
0
2,732
0
0
0
00000000
5
2,782
0,25
12,67327
13
00001101
10
2,832
0,5
25,34653
25
00011001
15
2,882
0,75
38,0198
38
00100110
20
2,932
1
50,69307
51
00110011
25
2,982
1,25
63,36634
63
00111111
26
2,992
1,3
65,90099
66
01000010
30
3,032
1,5
76,0396
76
01001100
35
3,082
1,75
88,71287
89
01011001
40
3,132
2
101,3861
101
01100101
45
3,182
2,25
114,0594
114
01110010
50
3,232
2,5
126,7327
127
01111111
55
3,282
2,75
139,4059
139
10001011
60
3,332
3
152,0792
152
10011000
65
3,382
3,25
164,7525
165
10100101
70
3,432
3,5
177,4257
177
10110001
75
3,482
3,75
190,099
190
10111110
80
3,532
4
202,7723
203
11001011
85
3,582
4,25
215,4455
215
11010111
90
3,632
4,5
228,1188
228
11100100
95
3,682
4,75
240,7921
241
11110001
100
3,732
5
253,4653
253
11111101

ADCul intern are urmatoarele caracteristici:
               -eroare totala de ±2 LSB,
               -rezolutia este de 10biti,
               -timpul de conversie este de 13-260µs.
ADC-ul primeste pe intrarea CH1(ADC0) o tensiune VinÎ(0..5V) si este alimentat la Vref=5V.
 Cod la 0V: 0000 0000 (00H)
 Cod la 5V: 1111 1111 (FFH)
Precizia necesare este de 8 biti, deci vom citi doar ADCL ceea ce inseamna 256 de valori reprezentabile. Noi avem 101 de valori adica rezolutia acestui convertor este de aproximativ 0.394oC/bit:
            Rezolutia=(1/256)*Ui=(1/256)*5V=0.01953V≈19.53V/bit
            Rezolutia=(1/256)*T  =(1/256)*101oC≈0.394oC/bit
ADC-ul are o intrerupere proprie care este chemata atunci cand se termina o conversie, si care seteaza registrul “intrare” cu acea valoare. Astfel programul ia intotdeauna ultima valoare esantionata de catre ADC.

Codul pentru programarea microcontrolerului este:



#include "m128def.inc"
.DSEG
.def intrare=R27
.def flg=R29
.def comp=R28
.def z1 = R22
.def z2 = R23
.def temp = R16
.def temp1 = R17
.def temp2 = R18
.def t_conv = R19
.def masura = R20
.def t_set=R21
.def unu=R22
.cseg
.org $000
rjmp  RESET
.org $0020           // vector de intrerupere pt overflow a timer0
rjmp tc0i
.org $002A           // vector de intrerupere pt terminarea conversiei ADC
rjmp ADC_ISR
.CSEG
tc0i:
   in temp, SREG     ; salvez continutul SREG
   mov ZL, z1
   mov ZH, z2
   adiw ZL,0x1       ; incrementez contorul
   mov z1, ZL
   mov z2, ZH
   out SREG,temp           ; setez continutul SREG la val anterioara
   reti
RESET:
   cli                     ; dezactivare intreruperi
   ldi   temp,0x05         ; Initiez Timer/Counter 0 Prescaler
   out   TCCR0,temp        ; to Timer 0 Control Register.
; Fiecare tick are 256 µs.
   ldi t_set,33
   ldi   TEMP,low(RAMEND)  ; setez locatia de memorie pt stiva
   out   SPL,TEMP
   ldi   TEMP, high(RAMEND)
   out   SPH, TEMP
 
   ldi temp,0xCF       ; configurare pini iesire/intrare
   out DDRA, Temp      ; aplic configuratia
   ldi r16, 0                  
   out ADMUX, r16      ; selectez canalul 0 in ADMUX
   ldi r16, 0b11101101 ; selectez: ADC Enable, Start Conversion,
;  Free-Running Mode, write
                       ; zero to ADC Int flag, enable int,
; prescaler: 101 for XTAL/32
   out ADCSR, r16      ; pun setarule in registrul ADC-ului
   rcall stop_timer    ; nu pornesc timerul/opresc dc e deja pornit
   sei                 ; activez toate intreruperile
   sbi PORTA,1         ; setez pe temp reala
   ldi temp, 0x00
   rcall start
   rjmp Loop
start_timer:
   cli
   ldi z1,0
   ldi   temp,$02      ; setez Bitul 1
   out   TIMSK,temp    ; in Timer Interupt Mask Register pt a
;  dezactiva intreruperile timer0
   sei
   ret
stop_timer:
   cli
   ldi   temp,$00      ; resetez Bitul 1
   out   TIMSK,temp    ; in Timer Interupt Mask Register pt a
;  dezactiva intreruperile timer0
   sei
   ret
Loop:                  ; Loop Label
   rcall Ia_temp;
   rcall set_temp
   rcall mas_temp      ; verifica dc
         rcall Setare_lcd    
         rjmp Loop          
mas_temp:
   in temp, TIMSK
   cpi temp, 0x0       // merge timerul?
   breq fin2
   mov t_conv, t_set   // se va afisa t_set
   mov ZL, z1
   mov ZH, z2
   subi ZH, 0x4C
   brne fin3
   subi ZL, 0x4B       // au trecut 5 sec? 256us * 19531 = 5 s   brne fin3
   rcall stop_timer
   cbi PORTA,0         // aprind ledul de T_real
   sbi PORTA,1
fin2: mov t_conv, masura   // se va afisa masura
fin3: ret
set_temp:
   IN temp,PORTA           ; ce buton s-a apasat? bitul 4 up, 5 down
   bst temp,4
   brts creste
   bst temp,5
   brts scade
fin1:
   ret
creste:
   cbi PORTA,1
   sbi PORTA,0         // aprind ledul de setare temp
   inc t_set
   rcall start_timer;
   rjmp fin1
scade:
   cbi PORTA,1
   sbi PORTA,0         // aprind ledul de setare temp
   dec t_set
   rcall start_timer
   rjmp fin1
compara:
   mov temp, t_set
   ldi flg,1
   rcall start         ; converteste t_set intr-un nr pt comparatie
   cp intrare, temp    ; decide incalzirea/racirea
   brlo incalzire
   brlt incalzire
   cp temp, intrare
   brlo racire
   brlt racire
   cbi PORTA,3         ; sting ambele leduri (incalzire/racire)
   cbi PORTA,2
endcompara:
   ret
incalzire:
   cbi PORTA, 3        ; aprind ledul de incalzire
   sbi PORTA, 2
   rjmp endcompara
racire:
   cbi PORTA, 2        ; aprind ledul de racire
   sbi PORTA, 3
   rjmp endcompara
Ia_temp:
   ldi temp, 83        // doar pt simulare
   mov intrare, temp
   ldi flg,0
   rcall start         // convertesc intrarea de la adc in o temp
   mov masura, temp
   rcall compara
   ret
Setare_lcd:
   call BIN2BCD        // intoarce in temp2 valoarea BCD a unui nr
//  binar [0,255] si in unu cifra sutelor
   out PORTE, temp2
   out PORTD, unu
   ret
#include "math.asm"
#include "rutine.asm"
ADC_ISR:
   push r16
   in r16, SREG
   push r16
   push r17
   in r16, ADCL
   in r17, ADCH
   lsr r17
   ror r16
   lsr r17
   ror r16
   mov intrare, r15
   com r16
   mov r15, r16
   pop r17
   pop r16
   out SREG, r16
   pop r16   reti



Fisierul “rutine.asm”:


.cseg
BIN2BCD:
   ldi temp,0x8
   ldi unu, 0
   mov temp1, t_conv
   ldi temp2,0x0
BK:      lsl temp2
   brcs UN
   brcc UN1
BK1:lsl temp1
   brcc ET
   inc temp2               // daca am carry la lsl atunci il transfer
//  in registru
  
ET: cpi temp,0x1
   breq FIN
   call TEST
   call TEST2
   dec temp
   rjmp BK
FIN:
   clr temp                // sterg temp
   ret
TEST:
   push temp
   ldi temp, 0xF
   and temp,temp2          // pun in temp primii 4 biti ai masurii
   cpi temp,0x5            // compar acesti biti cu 5
   brsh et2                // daca >=5 atunci sar la et2
et3:pop temp
   ret
et2:inc temp               // adaug 3 la nr
   inc temp
   inc temp
   andi temp2, 0xF0        // sterg primii 4 biti ai masurii
   add temp2,temp          // ii inlocuiesc cu temp
   rjmp et3         
TEST2:
   push temp
   ldi temp, 0xF0
   and temp,temp2          // pun in temp primii 4 biti ai masurii
   lsr temp                // mut la dreapta cu 4
   lsr temp
   lsr temp
   lsr temp
   cpi temp,0x5            // compar acesti biti cu 5
   brsh et21               // daca >=5 atunci sar la et2
et31:pop temp
   ret
et21:inc temp              // adaug 3 la nr
   inc temp
   inc temp
   andi temp2, 0x0F        // sterg cei 4 biti ai masurii
   lsl temp                // pun la loc cei 4 biti mutati anterior
   lsl temp
   lsl temp
   lsl temp
   or temp2,temp           // ii inlocuiesc cu temp
   rjmp et31
UN:      inc unu
   rjmp bk1
UN1: lsl unu
   rjmp bk1


Fisierul “math.asm”:


;***** Subroutine Register Variables
.def drem16uL=r14
.def drem16uH=r15
.def dres16uL=r16
.def dres16uH=r17
.def dd16uL =r16
.def dd16uH =r17
.def dv16uL =r18
.def dv16uH =r19
.def dcnt16u =r20
.def count = r21
//se face conversia round(x*0,394)=temperatura sau round(temp/0,394)=x
//unde x este intrarea adc-ului
start:
push r0
push r1
push r14
push r15
push r17
push r18
push r19
push r20
push r21
push r22
push ZH
push ZL
push r27
mov mc16uL, temp
ldi mc16uH,0x0
cpi flg, 0x1
breq conv0
ldi mp16uL, low(394)
ldi mp16uH, high(394)
rjmp conv1
conv0:
ldi mp16uL, low(1000)
ldi mp16uH, high(1000)
conv1:
rcall mpy16u
back:
lsr m16u3
ror m16u2
ror m16u1
ror m16u0
mov dd16uL,m16u0
mov dd16uH,m16u1
cpi flg, 0x1
breq conv2
ldi dv16uL,low(1000)
ldi dv16uH,high(1000)
rjmp conv3
conv2:
ldi dv16uL,low(394)
ldi dv16uH,high(394)
conv3: rcall div16u
again:
lsl drem16uL   //inm cat cu 2
lsl drem16uH
lsl dres16uL
lsl dres16uH
ldi count, 0xA
mov ZH,drem16uH
mov ZL,drem16uL
no:
sbiw ZH:ZL,0x32
dec count
brne no
sbiw ZH:ZL, 0
brlt end1
cpi flg, 0x1
breq end
ldi count, 0x1e
mov ZH,drem16uH
mov ZL,drem16uL
no1:
sbiw ZH:ZL,0x32
dec count
brne no1
sbiw ZH:ZL, 0
brlt end
inc dres16uL
end:inc dres16uL
end1:
mov temp,dres16uL
pop r27
pop ZL
pop ZH
pop r22
pop r21
pop r20
pop r19
pop r18
pop r17
pop r15
pop r14
pop r1
pop r0
ret
;***** Subroutine Register Variables
.def mc16uL =r16 ;multiplicand low byte
.def mc16uH =r17 ;multiplicand high byte
.def mp16uL =r18 ;multiplier low byte
.def mp16uH =r19 ;multiplier high byte
.def m16u0 =r18 ;result byte 0 (LSB)
.def m16u1 =r19 ;result byte 1
.def m16u2 =r20 ;result byte 2
.def m16u3 =r21 ;result byte 3 (MSB)
.def mcnt16u =r22 ;loop counter
;***** Code
mpy16u:
clr m16u3 ;clear 2 highest bytes of result
clr m16u2
ldi mcnt16u,16 ;init loop counter
lsr mp16uH
ror mp16uL
m16u_1:
brcc noad8 ;if bit 0 of multiplier set
add m16u2,mc16uL ;add multiplicand Low to byte 2 of res
adc m16u3,mc16uH ;add multiplicand high to byte 3 of res
noad8:ror m16u3 ;shift right result byte 3
ror m16u2 ;rotate right result byte 2
ror m16u1 ;rotate result byte 1 and multiplier High
ror m16u0 ;rotate result byte 0 and multiplier Low
dec mcnt16u ;decrement loop counter
brne m16u_1 ;if not done, loop more
ret
div16u:
clr drem16uL ;clear remainder Low byte
sub drem16uH,drem16uH;clear remainder High byte and carry
ldi dcnt16u,17 ;init loop counter
d16u_1:
rol dd16uL ;shift left dividend
rol dd16uH
dec dcnt16u ;decrement counter
brne d16u_2 ;if done
ret ; return
d16u_2:
rol drem16uL ;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL ;remainder = remainder - divisor
sbc drem16uH,dv16uH ;
brcc d16u_3 ;if result negative
add drem16uL,dv16uL ; restore remainder
adc drem16uH,dv16uH
clc ; clear carry to be shifted into result
rjmp d16u_1 ;else
d16u_3:
sec ; set carry to be shifted into result
rjmp d16u_1

0 comentarii:

Trimiteți un comentariu