|
La scheda non è la solita demoboard dotata di pulsanti / display / relé ecc, ma è piuttosto pensata per un uso sperimentale spartano "al volo", mette infatti a disposizione in modo rapido le porte di un PIC16F877, sia attraverso un connettore IDC da 34 poli (che porta anche le alimentazioni) sia attraverso dei comuni morsetti a vite, comodi ad esempio per il collegamento ad una breadboard con spezzoni di filo rigido.
Può essere alimentata attraverso un jack coassiale, un morsetto a vite, e attraverso i due pin a lato del morsetto (con dei coccodrilli). Un diodo protegge dall'inversione di polarità. Il regolatore è alettato in modo da poter alimentare a 5V anche circuiti esterni che richiedono qualche centinaio di mA. Per evitare il surriscaldamento è bene non superare i 4W di dissipazione, il che vuol dire assorbire sui 5V una corrente massima di 200mA se si alimenta il tutto a 25V, che può salire fino a 1A alimentando il tutto a 9..10V
Sulla scheda trova posto il classico convertitore RS232/TTL realizzato con l'integrato MAX232, con l'aggiunta di due LED verdi che evidenziano il passaggio dei dati (si accendono quando transitano i bit a zero).
Per quanto riguarda il PIC i pochi componenti necessari sono i condensatori e il quarzo per il clock, il pulsante di reset, e il jumper con la resistenza di pull-up.
Prima di essere inserito sulla scheda, il pic va preprogrammato con
l'apposito bootloader nell'ultima pagina da 256 byte della memoria, e
con le istruzioni di salto nei primi 4 indirizzi, questo permette di
usare la scheda come interfaccia/periferica seriale trasferendovi di
volta in volta l'applicativo necessario tramite un apposito programma
loader. Durante il caricamento da seriale il bootloader si autoprotegge
contro la sovrascrittura.
Una volta caricato il programma applicativo, la scheda lo può eseguire autonomamente ad ogni accensione (o reset), per fare questo basta lasciare aperto il jumper sul pin RE0. Se questo jumper viene invece tenuto chiuso allora all'accensione la scheda si predispone sempre in modalità bootloader attendendo comandi dalla seriale.
IMPORTANTE: Il programma
utente deve iniziare all'indirizzo 4, dove deve esserci una istruzione
NOP oppure una GOTO all'eventuale subroutine di gestione degli
interrupt. Il programma vero e proprio inizia all'indirizzo 5. Alla
fine del programma, all'indirizzo 1FFCH, va sempre messa una
RETLW con funzione di versione firmware. Questo valore viene
letto dal bootloader su richiesta. I 3 indirizzi finali da 1FFDH a
1FFFH possono eventualmente ospitare altre RETLW se un'applicazione lo
richiedesse.
ORG 4 NOP ; NOP oppure GOTO ISR INIZIO .... .... .... .... ORG 1FFCH RETLW 207 ; Versione firmware
Il bootloader effettua una CALL all'indirizzo 5 quando all'accensione/reset trova aperto il jumper, oppure quando gli viene comandato via seriale. La figura seguente mostra i salti che avvengono.
+--------------+ | MOVLW 1FH | 0000H | MOVWF PCLATH | .------ | GOTO 1FF0H | | | NOP | | +--------------+ | | NOP | 0004H | | .... | <---------. | | | | | | | | JUMP 1FF0H | | | | | | | | CALL 0005H | | | | | | | | | | | | | | | | | +--------------+ | '-----> | | 1FF0H | | BOOTLOADER | | | 256 BYTES | ----------' | | +--------------+
Il bootloader configura dapprima tutte le porte come ingressi, poi controlla lo stato del jumper e se lo trova aperto salta all'indirizzo 5 (in realtà non fa un GOTO ma una CALL, in modo che, se servisse, con un semplice RETURN l'applicativo potrebbe ripassare il controllo al bootloader).
Se trova il jumper chiuso si mette in ascolto sulla seriale. La trasmissione/ricezione seriale è realizzata a software in modo da lasciare libera la USART interna. Nonostante l'uso di un quarzo a soli 4MHz, la velocità di comunicazione raggiunta è di 38400 bit/secondo grazie ad un uso ottimizzato dei cicli macchina nelle subroutines di trasmissione e ricezione.
Il bootloader ha tre funzioni che si attivano quando vengono ricevuti i byte 31 32 e 34, la prima è una semplice richiesta di sincronismo, il modulo risponde con un byte 66 (carattere ascii "B") e un secondo byte ricavato da una RETLW all'indirizzo 1FFCH (che si usa come indicatore versione firmware). La seconda riceve i dati da scrivere nella memoria flash, la terza è un "esegui" e forza una CALL all'indirizzo 5 avviando il programma applicativo senza bisogno di resettare il micro o aprire il jumper (la scheda funziona quindi come interfaccia/periferica esterna del PC).
La funzione load è la più complessa, richiede due byte che specificano l'indirizzo di memoria in cui scrivere, seguito da 4 words (8 byte). Il PIC 16F877 potrebbe scrivere in memoria una word alla volta, ma la versione 877A richiede la scrittura di 4 word alla volta inizianti ad indirizzi con i due bit bassi a zero, quindi 0, 4, 8, 12 ecc. Il bootloader di questo progetto utilizza il metodo della versione 877A in modo da poter usare entrambe le versioni senza alcuna modifica.
I valori dell'indirizzo e delle words vengono ricevuti sempre prima il byte basso e poi il byte alto. La scrittura nella flash comprende la verifica automatica, la funzione load risponde con il byte 79 (carattere ascii "O") se tutto è andato a buon fine, con 75 (carattere ascii "K") se si è riscontrato un errore durante la rilettura dei dati, o con 80 (carattere ascii "P") se si è tentato di sovrascrivere il bootloader.
Lo schema seguente riassume le tre funzioni del bootloader:
+--------+ +--------+ 31 --->| | 34 --->| | | SYNC | | EXEC | 'B' + n <---| | | | +--------+ +--------+ +--------+ 32 --->| | | LOAD | L ADDR --->| | H ADDR --->| | | | BYTE 1 --->| | : | | BYTE 8 --->| | | | 'O' or 'K' or 'P' <---| | +--------+
1FF9H Attesa 197 millisecondi silenzio dati 1FFAH Trasmissione seriale del valore di W 1FFBH Ricezione seriale, il valore e' posto in 7FH
Il bootloader sul PIC attende comandi sulla seriale, quindi sul PC occorre una controparte che glieli invii: il loader.
Il loader ha il compito di leggere un file .HEX prodotto dall'assemblatore MPASMWIN, estrarre le informazioni necessarie, ed inviarle al bootloader secondo quanto richiesto dalla funzione load.
Un programma per fare questo può essere scritto in qualsiasi
linguaggio di programmazione che possa accedere ai files su disco e
alla porta seriale. Per le prove con questa scheda il loader è stato
scritto in python 2.5, e puo' usare indifferentemente sia porte seriali
reali che virtuali attraverso convertitore USB/RS232:
#--------------------------------------------------------------------- # Loader per PIC16F877/877A (versione seriale) # By Claudio Fin 26/10/2008 # # REQUISITI: # - Interprete Python 2.5 installato sul PC # - Estensioni per Windows installate # - Modulo pySerial installato # # Scrivere nel programma il nome della porta seriale # che si vuole usare (esempio com1) e il nome del # file .HEX (esempio lamp.hex). Il file HEX si deve # trovare nella stessa directory del programma #--------------------------------------------------------------------- import serial, struct try: ser = serial.Serial("com1", 38400, 8, "N", 2, timeout=1) print "PORTA SERIALE OK" for riga in open("lamp.hex", "r"): riga = riga.rstrip("\n") if len(riga) < 9 or riga[7:9] != "00": continue print riga, ind = int(riga[3:7], 16) / 2 dati = riga[9:-2].ljust(16, "F") if len(dati) > 16: dati = dati.ljust(32, "F") while dati: trasm = [32, ind%256, ind/256] trasm.extend([int(dati[i:i+2], 16) for i in range(0, 16, 2)]) ser.write(struct.pack("B"*len(trasm), *trasm)) risp = ser.read(1) if risp != "O": print "--\n\nVERIFICA SCRITTURA FALLITA!" raise IOError dati = dati[16:] ind += 4 print "OK" print "\nTRASFERIMENTO COMPLETATO\n" except IOError: print "\n\nTRASFERIMENTO FALLITO!\n" ser.close() raw_input("Invio per terminare...")
#------------------------------------------------------------------------------ # load.py - Loader TCP/IP per PIC16F877/877A # By Claudio Fin 10/8/2011 # # REQUISITI: # - Interprete Python da 2.5 a 3.x installato sul PC # # Si puo' lanciare il programma da riga di comando # specificando il nome del file: # # python load.py nomefile.hex # # Oppure, se non specificato, si apre una dialog window # per la scelta del file. #------------------------------------------------------------------------------ ADDR = "192.168.1.3", 4001 # Scrivere qui l'indirizzo del serial server #----------------------------- Import moduli ---------------------------------- import socket, sys from struct import pack PY3 = sys.version_info[0] > 2 if PY3: from Tkinter import Tk from tkFileDialog import askopenfilename else: from tkinter import Tk from tkinter.filedialog import askopenfilename #----------------------- acquisizione nome del file --------------------------- if len(sys.argv) > 1: nomeFile = sys.argv[1] else: gui = Tk() nomeFile = askopenfilename(parent=gui, filetypes=(("Hex File", "*.hex"),)) gui.destroy() #------------------------------ Caricamento ----------------------------------- s = socket.socket() # Socket TCP di default s.settimeout(1) try: s.connect(ADDR) for riga in open(nomeFile, "r"): riga = riga.rstrip("\n") if riga[0:1] != ":" or riga[7:9] != "00": continue print(riga) ind = int(riga[3:7], 16) // 2 dati = riga[9:-2] dati = dati.ljust(32 if len(dati) > 16 else 16, "F") while dati: trasm = [32, ind%256, ind//256] trasm.extend([int(dati[i:i+2], 16) for i in range(0, 16, 2)]) s.sendall(pack("B"*len(trasm), *trasm)) risp = s.recv(1024)[0] if PY3: risp = chr(risp) if risp != "O": raise IOError dati = dati[16:] ind += 4 print("\nTRASFERIMENTO COMPLETATO\n") except: print("\nERRORE TRASFERIMENTO FALLITO\n") finally: s.close() #------------------------ Attesa invio per terminare -------------------------- msg = "Invio per terminare..." input(msg) if PY3 else raw_input(msg)
Qui sotto è riportato un file .HEX, e di seguito la sua scomposizione in colonne per identificare i dati che ci interessano. La prima indica la lunghezza in byte del campo dati, la seconda in blu l'indirizzo a cui iniziano, la terza indica il "tipo record", a noi interessano le righe con "00" che sono quelle che contengono i dati da trasmettere. La quarta colonna sono i singoli byte dati espressi in esadecimale. La quinta è un checksum. Tutti i valori sono espressi in esadecimale.
Il loader deve estrarre dalle righe che ci interessano l'indirizzo e
il campo dati. L'indirizzo va diviso per due in quanto il file hex
conta gli indirizzi in byte, mentre gli indirizzi per la scrittura
nella flash si contano in words. Il campo dati può contenere da 2 a 16
byte (da 1 a 8 words), pertanto per la scrittura nella flash (che
richiede 4 words al colpo) si devono eventualmente aggiungere dei byte
fittizi a 0FFH per completare il blocco di 4 words.
Vista la bassa luminosità del led RX/TX ho dovuto aggiungere un circuito di disaccoppiamento e "allargamento". Adesso l'impulso luminoso causato dalla trasmissione o ricezione anche di un singolo byte è chiaramente visibile.
Per non perdere la praticità di collegamento con una bredboard è utile preparare un connettore/adattatore aggiuntivo da innestare sull' uscita dell'interfaccia, formato da una serie di "connettori strip" saldati ai teminali del DB25. Poiché il passo del DB25 è diverso da quello degli strip di contatti, è necessario tagliare questi ultimi in pezzi da 4 o al massimo 5 contatti. Questa suddivisione aiuta tra l'altro a non confondersi nel contare le posizioni.
L'interfaccia, collegata ad un ethernet serial server, può essere
controllata da remoto attraverso la rete LAN.
ser_par.zip Sorgente e .HEX firmware per usare l'interfaccia come I/O parallelo