- Messaggi: 4
- Ringraziamenti ricevuti 0
Problema Pic16f887 lettura e stampa temperatura
- Kada
- Autore della discussione
- Nuovo Utente
-
Less
Di più
5 Anni 4 Mesi fa - 5 Anni 4 Mesi fa #1
da Kada
Problema Pic16f887 lettura e stampa temperatura è stato creato da Kada
Salve a tutti!
Sto provando a realizzare un programma per il Pic 16f887 montato in una picboard:
Il pic è inizialmente in stato di sleep per poi risvegliarsi ogni 5 secondi, leggere il valore di temperatura, stamparlo nel formato a due cifre sulla porta seriale eusart e poi tornare in sleep.
Ho messo anche il lampeggio di un led ad ogni lettura.
Per contare 5 secondi ho utilizzato il timer1 e gestisco la lettura della temperatura (conversione adc inclusa) tramite interrupt.
Il problema è che stampa ogni 5 secondi con il relativo lampeggio, ma valori senza alcun senso.
Aiuto please!
Colgo l'occasione per fare gli auguri di buon natale a tutti!
Sto provando a realizzare un programma per il Pic 16f887 montato in una picboard:
Il pic è inizialmente in stato di sleep per poi risvegliarsi ogni 5 secondi, leggere il valore di temperatura, stamparlo nel formato a due cifre sulla porta seriale eusart e poi tornare in sleep.
Ho messo anche il lampeggio di un led ad ogni lettura.
Per contare 5 secondi ho utilizzato il timer1 e gestisco la lettura della temperatura (conversione adc inclusa) tramite interrupt.
Il problema è che stampa ogni 5 secondi con il relativo lampeggio, ma valori senza alcun senso.
Aiuto please!
Code:
list p=16f887 ; direttiva che definisce il tipo di processore
#include <p16f887.inc> ; file che contiene le definizioni dei simboli (nomi registri, nomi bit dei registri, ecc).
#include "macro.inc" ; definizione di macro utili
;**********************************************************************
; *** Configuration bits ***
; I bit di configurazione (impostazioni dell'hardware settate in fase di
; programmazione del dispostitivo) sono definiti
; tramite una direttiva nel codice.
; Impostazioni importanti:
; - Wathcdog timer disattivato (_WDT_OFF).
; - Low voltage programming disattivato (_LVP_OFF), altrimenti il pin RB3 (porta B)
; non puo' essere utilizzato come I/O generico.
__CONFIG _CONFIG1, _INTRC_OSC_NOCLKOUT & _CP_OFF & _WDT_OFF & _BOR_OFF & _PWRTE_OFF & _LVP_OFF & _DEBUG_OFF & _CPD_OFF
tmr_5s EQU (.65536 - .20480) ;(setto prescaler per contare 5s)
; variabili in RAM (shared RAM)
UDATA_SHR
fsr_temp RES 1 ;variabile per salvare fsr quando entro/esco da routine interrupt
temperature RES 1 ;variabile per decine
tmp RES 1 ;variabile temporanea e per unita
w_temp RES 1 ;variabile per salvare W quando entro/esco da routine interrupt
status_temp RES 1 ;variabile per salvare contenuto registro STATUS quando entro/esco da routine interrupt
pclath_temp RES 1 ;variabile per salvare contenuto registro PCLATH quando entro/esco da routine interrupt
cansleep RES 1
; reset vector (quando viene premuto il pulsante di reset questa è la prima operazione che viene eseguita)
rst_vector CODE 0x0000
pagesel start
goto start
;********************************************MAIN***************************************************************************
; programma principale
MAIN CODE
start
pagesel initHw
call initHw ; inizializzazione hardware
; abilita interrupt timer1
setRegK PIE1, B'00000001'
; Abilita interrupt delle periferiche (tra cui timer1)
BANKSEL INTCON
bsf INTCON, PEIE
PAGESEL reload_timer1
call reload_timer1
bsf cansleep,0
main_loop
; Dato che tutto il lavoro e' svolto dalla routine di interrupt,
; il programma principale potrebbe mandare il microcontrollore
; in modalita' sleep per essere risvegliato dal successivo
; interrupt.
; Utilizziamo percio' un bit che indica quando il programma puo'
; andare in sleep, che sara' settato dall'interrupt quando opportuno.
waitSleep
bcf INTCON, GIE ; disabilita interrupt globalmente
btfsc cansleep, 0 ; verifico possibilità di ingresso nello stato di sleep
goto goSleep
bsf INTCON, GIE
goto waitSleep
goSleep
BANKSEL PORTD
bcf PORTD,3 ; spegne LED4 prima di sleep
sleep ; la CPU si ferma!
;(*******************************INTERRUPT********************************************)
; a questo punto la CPU si e' risvegliata per via di un
; interrupt, che nel nostro caso puo' essere solo il timer1.
; Avendo riabilitato gli interrupt (bit GIE), viene subito
; eseguita la routine di interrupt, quindi il programma
; continua.
;(*******************************INTERRUPT********************************************)
BANKSEL PORTD
bsf PORTD,3 ; accende LED4 dopo risveglio
bsf INTCON, GIE
goto main_loop ; ripete il loop principale del programma
;********************************************MAIN***************************************************************************
reload_timer1
; ricarica contatore timer1 per ricominciare conteggio.
; In modalita' asincrona, occorre arrestare il timer prima
; di aggiornare i due registri del contatore
banksel T1CON
bcf T1CON, TMR1ON ; arresta timer
; le funzioni "low" e "high" forniscono il byte meno e piu'
; significativo di una costante maggiore di 8 bit
banksel TMR1L
movlw low tmr_5s
movwf TMR1L
movlw high tmr_5s
movwf TMR1H
banksel PIR1
bcf PIR1, TXIF
banksel T1CON
bsf T1CON, TMR1ON ; riattiva timer
bsf cansleep,0
return
;************************************INIZIO ROUTINE INTERRUPT************************************
IRQ CODE 0x0004
INTERRUPT
; Salvataggio stato registri CPU (context saving).
; A differenza di quasi tutte le altre architetture, il PIC non salva lo stato
; della CPU automaticamente all'ingresso di un interrupt (e non lo ripristina
; all'uscita). Questo perche' non esiste uno stack utilizzabile genericamente,
; ma solo uno stack limitato al salvataggio ed al ripristino di PC.
; In genere quindi, per assicurare che il programma principale funzioni sempre
; correttamente anche in presenza di interrupt, occorre gestire queste due
; fasi manualmente. I registri da salvare per il PIC16 sono W, STATUS e PCLATH.
movwf w_temp ; copia W in w_temp
swapf STATUS,w ; inverte i nibble di STATUS salvando il risultato in W.
; Questo trucco permette di copiare STATUS senza alterarlo
; (swapf e' una delle poche istruzioni che non alterano i bit di stato).
movwf status_temp
movf PCLATH,w ; copia il registro PCLATH in W (registro da salvare perché contiene i
; bit più significativi del program counter, usati da GOTO e CALL,
; e settati dalla direttiva pagesel).
movwf pclath_temp ; copia W (= PCLATH) in pclath_temp.
movf FSR, w
movwf fsr_temp ; altro registro usato dalla routine
; e quindi da salvare
banksel PIR1
btfss PIR1, TMR1IF ; controllo se proprio il timer1 mi ha fatto entrare in interrupt
goto $-1
banksel PIE1
btfss PIE1, TMR1IE
goto $-1
banksel PIR1
bcf PIR1, TMR1IF
;*******************************************ADC************************************************************************************
PAGESEL readAdc
call readAdc ; chiama routine lettura ADC con canale 6
PAGESEL computeTemp
call computeTemp ; calcola valore effettivo di temperatura
PAGESEL formatNumber
call formatNumber; formatta numero in 2 cifre decimali
PAGESEL serial_print
call serial_print
banksel PIR1
bcf PIR1,ADIF
PAGESEL reload_timer1
call reload_timer1
goto irq_end
readAdc
; Legge valore analogico dal canale 6
banksel ADCON0
bsf ADCON0,GO ; inizia conversione
btfsc ADCON0,GO ; controllo go sia 0
goto $-1
banksel ADRESH
movf ADRESH, w
return
computeTemp
; routine di conversione da tensione a gradi centigradi
; input:
; W: tensione campionata (0-255, corrispondente a 0-3.3 V)
; output:
; W: risultato in gradi
;
; Dal datasheet del sensore di temperatura MCP9701A:
; T = (Vadc - V0) / Tc [ dove V0 = 400 mV, Tc = 19.5 mV/C]
; Convertendo da tensioni a valori binari, si ha:
; T = (Nadc - 31) / 1.51
; che puo' essere approssimata in calcoli interi a 8 bit come:
; T = (Nadc - 31) * 2 / 3 [ approssimaz. 1.51 ~= 1.5 = 3/2 ]
; Questa formula permette di calcolare temperature fino a 84 C
; senza incorrere nell'overflow della variabile a 8 bit
movwf tmp ; questo è il valore della temperatura bufferizzato nell'ADC (non è ancora formattato per la stampa!)
movlw .31
subwf tmp, f ; tmp = tmp - 31
bcf STATUS, C
rlf tmp, f ; tmp = tmp * 2 (usando lo shift a sinistra)
; divisione per 3, effettuata con semplice algoritmo di sottrazioni
; successive del valore 3, incrementando ogni volta il risultato,
; fino a che il minuendo non diventa negativo
clrf temperature ; valore iniziale del risultato = 0
loop_div3
movlw .3
subwf tmp, w ; w = tmp - 3
btfss STATUS, C
goto end_div3 ; se risultato negativo (C=0): fine divisione, se c'è il carry continuo
movwf tmp ; tmp = tmp - 3
incf temperature, f ; incrementa risultato di 1
goto loop_div3 ; continua sottrazione
end_div3
movf temperature,w
return
formatNumber
; calcolo delle 2 cifre decimali e riproduzione in codice morse
; input:
; temperature = valore in gradi da riprodurre
; (il valore iniziale di temperature viene perso)
; Il metodo di calcolo delle 2 cifre e' il seguente:
; si divide la temperatura per 10, il quoziente rappresenta le
; decine mentre il resto rappresenta le unita'
; La divisione per 10 e' effettuata come sopra con sottrazioni
; successive
movwf tmp
clrf temperature ; risultato della divisione per 10 (decine)
loop_div10
movlw .10
subwf tmp, w ; w = tmp - 10
btfss STATUS, C
goto end_div10 ; se risultato negativo (C=0): fine divisione
movwf tmp ; tmp = tmp - 10
incf temperature, f ; incrementa risultato di 1
goto loop_div10 ; continua sottrazione
end_div10
return
;************************************ADC*******************************************************************************************************************
;************************************SERIALE************************************
; "temperature" contiene le decine, "tmp" le unita'
; (resto della divisione)
serial_print
; Trasmetto decine
movlw '0'
addwf temperature,w ; w + temperature = w
banksel TXREG
movwf TXREG
banksel PIR1
btfss PIR1,TXIF
goto $-1
; Trasmetto unità
movlw '0'
addwf tmp,w ; w + tmp = w
banksel TXREG
movwf TXREG
banksel PIR1
btfss PIR1,TXIF
goto $-1
; Trasmetto invio
movlw .10
BANKSEL TXREG
movwf TXREG ;scrivo il carattere invio
banksel PIR1
btfss PIR1,TXIF
goto $-1
return
;************************************SERIALE************************************
irq_end
movf fsr_temp,w
movwf FSR
movf pclath_temp,w ; copia pclath_temp in W
movwf PCLATH ; copia W in PCLATH
swapf status_temp,w ; inverte i nibble di status_temp salvando il risultato in W
; anche in questo caso serve a non alterare STATUS stesso
movwf STATUS ; copia W (che contiene lo STATUS originale ripristinato dopo 2 inversioni) in STATUS
; per ripristinare W senza alterare STATUS appena ripristinato, si utilizza sempre swapf
swapf w_temp,f ; prima inversione di w_temp, risultato su se stesso
swapf w_temp,w ; seconda inversione di w_temp, risultato in W (W contiene il valore precedente all'interrupt)
bsf cansleep,0
retfie ; uscita da interrupt e ritorno al punto in cui il programma era stato interrotto
;************************************FIME INTERRUPT************************************
initHw ; inizializzazione hardware per scheda PIC Board - Studio
; registro INTCON:
; - tutti gli interrupt inzialmente disabilitati
; (verranno abilitati nel programma principale, quando tutte
; le periferiche saranno correttamente inizializzate)
clrf INTCON
;Porte I/O:
banksel TRISE
clrf TRISE ;abilito porta per il sensore
;port A:
; RA0-RA5: analog inputs
; RA6-RA7: digital outputs (flash_ce, bus_switch)
setRegK PORTA, B'01000000' ; flash_ce = 1
setRegK ANSEL, B'11111111' ; set RE0-RE2 as analog too
setRegK TRISA, B'00111111'
;porta D:1..3settato come output (LED)
setRegK TRISD, 0xF0
setReg0 PORTD
;ADC
setRegK ADCON0, B'11011000' ; clock = RC, ch. 6, ADC off
setReg0 ADCON1 ; use vdd and vss as reference
banksel ANSEL
bsf ANSEL,6 ;imposto il pin AN6 come analogico
banksel PIR1
bcf PIR1,ADIF ;azzero flag ADC
; Timer1
; Impostazioni:
; - usa quarzo esterno (32768 Hz)
; - modalita' asincrona (funziona con quarzo esterno anche durante sleep)
; - prescaler = 1:8
; - Avvio timer in stop
; Con la frequenza del quarzo ed il prescaler a 8 si ha:
; - singolo tick ~= 24.414 us
; - periodo max = 5 s (contatore a 16 bit)
; - 24.414us ? 1tick = 5 ? x -> x = 5/0,000244141 = 20480 tick -> 65536 - 20480
banksel T1CON
movlw B'00111110';(abilito clock esterno ma non sincronizzo con input clock esterno)
movwf T1CON
;EUSART
; baud rate = 19200 (BRGH = 1, BRG16 = 0) ;(high speed, 8 bit baud rate)
; TXEN = 1 (abilito trasmissione)
; SPEN = 1 (abilito seriale)
; SYNC = 0 (configuro per operazioni asincrone)
setRegK TXSTA, B'00100100'
setRegK RCSTA, B'10000000'
setReg0 BAUDCTL ;(BRG16=0)
setRegK SPBRG, .12
return
;fine codice
END ;direttiva di fine codice
Colgo l'occasione per fare gli auguri di buon natale a tutti!
Ultima Modifica 5 Anni 4 Mesi fa da Kada.
Si prega Accesso o Crea un account a partecipare alla conversazione.
- firstcolle
-
- Platino Utente
-
Less
Di più
- Messaggi: 362
- Ringraziamenti ricevuti 39
5 Anni 4 Mesi fa #2
da firstcolle
Risposta da firstcolle al topic Problema Pic16f887 lettura e stampa temperatura
capperi, un programma in assembly, non lo vedevo da un bel po'.
hai provato in debug a vedere se i valori letti e convertiti della temperatura sono corretti? giusto per capire se il problema è la conversione o l'invio tramite UART.
altra prova da fare è inviare una stringa di test via UART in modo da verificare che quella parte funzioni
hai provato in debug a vedere se i valori letti e convertiti della temperatura sono corretti? giusto per capire se il problema è la conversione o l'invio tramite UART.
altra prova da fare è inviare una stringa di test via UART in modo da verificare che quella parte funzioni
Si prega Accesso o Crea un account a partecipare alla conversazione.
- Kada
- Autore della discussione
- Nuovo Utente
-
Less
Di più
- Messaggi: 4
- Ringraziamenti ricevuti 0
5 Anni 4 Mesi fa #3
da Kada
Risposta da Kada al topic Problema Pic16f887 lettura e stampa temperatura
Allora per il debug sinceramente no ancora non ho provto perchè uso mplabx e non mi è ancora chiaro come funziona.
per quanto riguarda la stampa ho già provato a stampare due valori scelti arbitrariamente e li stampa senza problemi, dunque il problema potrebbe essere nella routine dell'adc o nellla formattazione dei valori.
per quanto riguarda la stampa ho già provato a stampare due valori scelti arbitrariamente e li stampa senza problemi, dunque il problema potrebbe essere nella routine dell'adc o nellla formattazione dei valori.
Si prega Accesso o Crea un account a partecipare alla conversazione.
- Kada
- Autore della discussione
- Nuovo Utente
-
Less
Di più
- Messaggi: 4
- Ringraziamenti ricevuti 0
5 Anni 4 Mesi fa #4
da Kada
Risposta da Kada al topic Problema Pic16f887 lettura e stampa temperatura
Aggiornamento: Ho provato con il debug ma il programma si stoppa con l'ingresso in interrupt (generato dal timer).
Consigli?
Consigli?
Si prega Accesso o Crea un account a partecipare alla conversazione.
5 Anni 4 Mesi fa #5
da Mauro Laurenti
Risposta da Mauro Laurenti al topic Problema Pic16f887 lettura e stampa temperatura
Salve Kada,
dalle risposte hai visto che scrivere in Assembly può rendere le cose complicate agli altri.
Ho scritto molto in assembly anche io, ma a quei tempi di compilatori C gratuiti non ce ne erano.
Quando sono comparsi sono migrato al C.
L'assembly va bene per imparare i dettagli della MCU, ma passata questa fase passa al C.
Quando farai il debug del codice C, conoscere l'assembly ti aiuterà nel debug del codice.
Per cui l'averlo imparato ti tornerà utile.
Ma non prolungare troppo la fase "assembly".
In C andrai molto più veloce nello sviluppo del codice e soprattutto il debug dello stesso.
Tornando al caso specifico:
considera che durante la fase di Debug, il timer rimane in esecuzione in maniera asincrona dal Debugger.
Per cui puoi avere comportamenti strani dell'ISR a seconda della base dei tempi.
dal momento che fai uso di piu' moduli e funzioni, cerca di dividere e conquistare.
Fai il debug di ogni funzione e aggiungine una nuova, una dopo l'altra.
Saluti,
Mauro
dalle risposte hai visto che scrivere in Assembly può rendere le cose complicate agli altri.
Ho scritto molto in assembly anche io, ma a quei tempi di compilatori C gratuiti non ce ne erano.
Quando sono comparsi sono migrato al C.
L'assembly va bene per imparare i dettagli della MCU, ma passata questa fase passa al C.
Quando farai il debug del codice C, conoscere l'assembly ti aiuterà nel debug del codice.
Per cui l'averlo imparato ti tornerà utile.
Ma non prolungare troppo la fase "assembly".
In C andrai molto più veloce nello sviluppo del codice e soprattutto il debug dello stesso.
Tornando al caso specifico:
considera che durante la fase di Debug, il timer rimane in esecuzione in maniera asincrona dal Debugger.
Per cui puoi avere comportamenti strani dell'ISR a seconda della base dei tempi.
dal momento che fai uso di piu' moduli e funzioni, cerca di dividere e conquistare.
Fai il debug di ogni funzione e aggiungine una nuova, una dopo l'altra.
Saluti,
Mauro
Si prega Accesso o Crea un account a partecipare alla conversazione.
Moderatori: Mauro Laurenti, Pinna, StefA, Matteo Garia
Registrati al sito
Accedi a tutte le risorse e articoli non visibili pubblicamente, puoi registrarti con pochi passi.
Forum - Ultimi messaggi
-
- progetto can
- da marcelloraja
-
- MODULO GSM SIM900A
- da Mauro Laurenti
-
- Freedom III e compilazioni fallite
- da Mauro Laurenti
-
- Gestione degli errori su comunicazione seriale
- da Mauro Laurenti
-
- Timer0 e interrupt
- da Mauro Laurenti