Version:0.9 StartHTML:0000000105 EndHTML:0000105691 StartFragment:0000001119 EndFragment:0000105675
program pic_siors
'-----------------------------------------
'Autore: Marco Massari
'Data: 03/2010
'Descrizione: programma gestione Multi I/O
'board del sistema MassaBus
'-----------------------------------------
'Dichiarazione variabili registri I/O
dim led_activity as sbit at portc.3
dim tx_uart_enable as sbit at txsta.5
dim rx_uart_enable as sbit at rcsta.4
dim RS485_bus_direction as sbit at portc.5
dim program_button as sbit at portc.4
'Dichiarazione variabili flag timer
dim tmr1_enable as sbit at t1con.0
dim tmr1_interrupt as sbit at pir1.0
'PIN OUTPUT
dim OUT1 as sbit at porta.1
dim OUT2 as sbit at porta.2
dim OUT3 as sbit at porta.3
dim OUT4 as sbit at porta.4
dim OUT5 as sbit at porta.5
dim OUT6 as sbit at portc.0
dim OUT7 as sbit at portc.1
dim OUT8 as sbit at portc.2
'REGISTRO INPUT
dim port_input as byte at PORTB
'-----------------------------------------
'Dichiarazione variabili memoria
dim adr_m as byte
dim adr_s as byte
dim adr_sender_test as byte
dim get_data_received as string[11]
dim string_compose as string[11]
dim analog_value as word 'parola contenente il valore letto dall'ADC
dim read_request as byte
dim out_value_r as byte
dim pulse_byte as byte
dim delay_set as word
dim flag_hi_byte as byte
dim flag_low_byte as byte
dim last_flag_hi_byte as byte
dim last_flag_low_byte as byte
dim input_value as byte
dim last_send_data as string[11]
dim correct_packet as byte
dim address_packet as byte
dim type_packet as byte
dim data1_packet as byte
dim data2_packet as byte
dim count_tmr1 as word
dim reset_pulse as byte
'-----------------------------------------
'Dichiarazione costante device type (tipologia dispositivo)
const device_type as byte = 77 'dispositivo tipo M
'-----------------------------------------
'Procedura Interrupt
sub procedure interrupt
'Se interrupt provocato da Timer1 (OverFlow)
if tmr1_interrupt then
'Resetto il flag di interrupt timer
tmr1_interrupt = 0
'Essendo (a 4 Mhz) la frequenza degli interrupt di 2Hz (500ms)
'il delay_byte viene moltiplicato * 2 per ottenere un delay_byte
'pari ai secondi di intervallo
if (count_tmr1 = delay_set) then
'Resetto i registri del timer dopo averlo disabilitato
tmr1_enable = 0
tmr1h = 0
tmr1l = 0
'Resetto variabile di conteggio interrupt timer
count_tmr1 = 0
'----------------------------------
'Attivo il flag che segnala la fine del timeout
reset_pulse.0 = 1
else 'altrimenti incremento count_tmr1
count_tmr1 = count_tmr1 + 1
end if
end if
end sub
'-----------------------------------------
'Procedura invio dati su bus
sub procedure send_data(dim byref datasend as string[11])
last_send_data = datasend
led_activity = 1 'accendo led
delay_ms(5)'Attende il rilascio del bus
tx_uart_enable = 1 'abilita invio seriale
RS485_bus_direction = 1 'seleziona la direzione di comonicazione (invio)
delay_us(400) 'attende attivazione max485
UART1_Write_Text(datasend) 'invia la stringa generata
delay_ms(2) 'attende invio segnali
led_activity = 0 'disattivo led
RS485_bus_direction = 0 'ritorno in modalitā ascolto
tx_uart_enable = 0 'disabilita invio seriale
end sub
'-------------------------------------------
'Procedura creazione trama da inviare su bus
sub procedure code_data_to_send(dim address_master_send as byte, dim type_s as byte,
dim data1_s as byte, dim data2_s as byte)
dim checksum_send as byte
dim checksum_send_low as byte
dim checksum_send_hi as byte
dim type_to_send as byte
dim data_send_1 as byte
dim data_send_2 as byte
dim address_slave_send as byte
type_to_send = type_s * 4 'sposto di 2 bit la parte del tipo di paccketto
if (address_master_send <> 255) then 'se l'indirizzo č in modalitā normale
address_slave_send = adr_s
else 'altriemtni se in broadcast
address_slave_send = 255
end if
if (data1_s = 0) then 'se data1 č pari a 0
data_send_1 = 48 'invio 48 e segno con il bit in type to send che č uno 0
type_to_send.0 = 1
else
data_send_1 = data1_s 'altrimenti mantengo il dato
end if
if (data2_s = 0) then 'se data2 č pari a 0
data_send_2 = 48 'invio 48 e segno con il bit in type to send che č uno 0
type_to_send.1 = 1
else
data_send_2 = data2_s 'altrimenti mantengo il dato
end if
'Calcolo e asegnazione bit checksum
checksum_send = address_master_send xor address_slave_send xor type_to_send xor data_send_1 xor data_send_2
checksum_send_low = (checksum_send and 15) or 128
checksum_send_hi = (checksum_send / 16) or 128
'Composizione pacchetto da inviare
string_compose[0] = "S"
string_compose[1] = address_master_send
string_compose[2] = address_slave_send
string_compose[3] = type_to_send
string_compose[4] = data_send_1
string_compose[5] = data_send_2
string_compose[6] = checksum_send_low
string_compose[7] = checksum_send_hi
string_compose[8] = 3 'byte di fine trama
string_compose[9] = 3
string_compose[10] = 3
string_compose[11] = 0 'carattere NULL
'Invio pacchetto
send_data(string_compose)
end sub
'-------------------------------------------------------
'Procedura decodifica dati ricevuti
sub procedure decode_ric_data(dim byref array_in_data as string[11], dim byref correct_data as byte,
dim byref address_ric as byte, dim byref address_master as byte, dim byref type_ric as byte, dim byref data1_ric as byte, dim byref data2_ric as byte)
dim chk_low as byte
dim chk_hi as byte
dim checksum as byte
dim calcolated_checksum as byte
correct_data = 0
if (array_in_data[0] = 83) then 'se start byte č corretto
'il controllo della terminazione del pacchetto viene effettuata dalla procedura di lettura seriale
'Ricostruzione byte checksum
chk_low = array_in_data[6] and 15
chk_hi = array_in_data[7] and 15
checksum = (chk_hi * 16) or chk_low 'mette insieme i 2 byte per ricostruire il byte di checksum
'Controllo del dato ricevuto con checksum
calcolated_checksum = array_in_data[1] xor array_in_data[2] xor array_in_data[3] xor array_in_data[4] xor array_in_data[5]
if (calcolated_checksum = checksum) and (array_in_data[6].7 = 1) and (array_in_data[7].7 = 1) then 'se il checksum č corretto
address_ric = array_in_data[1]
address_master = array_in_data[2]
type_ric = (array_in_data[3] and 252)/4 'preleva il byte che seleziona il tipo di chiamata
'eliminando gli zero flag inseriti
if (array_in_data[4] = 48) and (array_in_data[3].0 = 1) then 'se data1 č uguale a zero (controllo zero flag 1)
data1_ric = 0
else 'altrimenti viene copiato il byte
data1_ric = array_in_data[4]
end if
if (array_in_data[5] = 48) and (array_in_data[3].1 = 1) then 'se data2 č uguale a zero (controllo zero flag 2)
data2_ric = 0
else 'altrimenti viene copiato il byte
data2_ric = array_in_data[5]
end if
correct_data = 1
end if
end if
end sub
'------------------------------------------------------------
'Procedura lettura valore in OUTPUT
sub procedure read_output()
out_value_r.0 = OUT1
out_value_r.1 = OUT2
out_value_r.2 = OUT3
out_value_r.3 = OUT4
out_value_r.4 = OUT5
out_value_r.5 = OUT6
out_value_r.6 = OUT7
out_value_r.7 = OUT8
end sub
'-----------------------------------------------------------
'Procedura scrittura valore in OUTPUT
sub procedure write_output(dim out_value_w as byte)
OUT1 = out_value_w.0
OUT2 = out_value_w.1
OUT3 = out_value_w.2
'*************************************************************************************************'
'ATTENZIONE! L'uscita RB4 oggetto del seguente operatore va connessa ad una resistenza di pull-up,'
'č consigliabile non collegare carichi che richiedono uscite impulsive dato che ad ogni reset o '
'accensione e spegnimento il rele 4 si attiverā per alcuni milliscondi. '
'*************************************************************************************************'
OUT4 = out_value_w.3
OUT5 = out_value_w.4
OUT6 = out_value_w.5
OUT7 = out_value_w.6
OUT8 = out_value_w.7
end sub
'------------------------------------------------------------
'Procedura RESET valori in EEPROM
sub procedure reset_EEPROM_value()
EEPROM_Write($02, $00)
delay_ms(1) 'attendo 1 millisecondo
EEPROM_Write($03, $FF)
delay_ms(1)
EEPROM_Write($04, $FF)
delay_ms(1)
EEPROM_Write($05, $00)
delay_ms(1)
EEPROM_Write($06, $00)
end sub
'------------------------------------------------------------
'Procedura controllo attivazione degli INPUT
sub procedure test_in_data()
'Inverto gli ingressi (essendo gli ingressi a 1 quando l'ingresso PIC č a 0)
input_value = not port_input
'Verifico quali bit sono passati da livello 0 a livello 1 dalla precedente lettura
'in questo caso tali bit passeranno da livello logico 0 a livello logico 1
flag_hi_byte = flag_hi_byte or input_value
'Verifico quali bit sono passati da livello 1 a livello 0 dalla precedente lettura
'in questo caso tali bit passeranno da livello logico 1 a livello logico 0
flag_low_byte = flag_low_byte and input_value
'Verifico se i valori sono cambiati rispetto a lettura precedente
if (last_flag_hi_byte <> flag_hi_byte) or (last_flag_low_byte <> flag_low_byte) then
'se si memorizzo i nuovi valori
last_flag_low_byte = flag_low_byte
last_flag_hi_byte = flag_hi_byte
'e li salvo nella EEPROM
EEPROM_Write($04, flag_low_byte)
EEPROM_Write($05, flag_hi_byte)
end if
end sub
'--------------------------------------------------------------
'Sub reset timer
sub procedure tmr1_reset()
tmr1h = 0
tmr1l = 0
'Resetto variabile di conteggio interrupt timer
count_tmr1 = 0
end sub
'--------------------------------------------------------------
'Inizio Programma
main:
'--------------------------------------------------------------
'PROCEDURA DI INIZIALIZZAZIONE PROGRAMMA
'Configuro registri I/O
trisa = $01 'configura gli i/o di porta (bin 00000001) 5 uscite, 1 ingresso
trisb = $FF 'configura gli i/o di portb (bin 11111111) 8 ingressi
trisc = $10 'configura gli i/o di portc (bin 00010000) 7 uscite, 1 ingresso
'Reset delle porte
porta = 0
portb = 0
portc = 0
'Configurazione ADC e INTERRUPT
ADCON0 = $C7
ADCON1 = $8E
INTCON = $C0
PIE1 = $01
PIR1 = $00
T1CON = $34
'Reset variabili e timer
tmr1_enable = 0
tmr1_reset()
reset_pulse = 0
'Configurazione Preescaler Watchdog Timer e resistenze Pull-Up su portb
option_reg = $7F
'----------------------------------------------------------------
'Ripristino valori registri flag high/low salvati in EEPROM
flag_hi_byte = EEPROM_Read($05)
last_flag_hi_byte = flag_hi_byte
flag_low_byte = EEPROM_Read($04)
last_flag_low_byte = flag_low_byte
'----------------------------------------------------------------
uart1_init(9600) 'inizializzazione uart hardware pic a 9600 bps
tx_uart_enable = 0 'diasbilita invio seriale
rx_uart_enable = 0 'disabilito ricezione seriale
'Caricamento indirizzi bus da memoria
adr_m = EEPROM_Read($00) 'address master
adr_s = EEPROM_Read($01) 'address slave
'----------------------------------------------------------------
'Ripristino valore output precedente
write_output(EEPROM_Read($02)) 'ripristina il valore di output precedente
read_output()
'Eseguo una lettura su ADC
analog_value = adc_read(0)
'Ripristino tempo di attesa pulse byte
delay_set = EEPROM_Read($06) * 2
'----------------------------------------------------------------
'Avviso della avvenuta inizializzazione
led_activity = 1 'attivo led
delay_ms(750) 'accende led per alcuni ms per avvisare della corretta accensione
led_activity = 0 'spegne led
'----------------------------------------------------------------
get_data_received = "" 'azzero dato ricezione
read_request = 0 'azzero richiesta di lettura
rx_uart_enable = 1 'riabilita ricezione
'----------------------------------------------------------------
'Loop programma principale
while true
clrwdt 'resetto watchdog timer
'Vado alla sub che rileva gli ingressi che hanno cambiato stato
test_in_data()
'--------------------------------------------------------------------------
'Controllo se avvenuto timeout uscite impulsive
if (reset_pulse.0 = 1) then
'Reset byte timeout uscite impulsive
reset_pulse.0 = 0
'scrive in output il valore con i bit impulsivi azzerati
write_output(pulse_byte)
end if
'---------------------------------------------------------------------------
if (UART1_Data_Ready() = 1) then 'se č stato ricevuto qualche byte da UART
UART1_Read_Text(get_data_received,"",11) 'effettuo la procedura di lettura cercando i 3 byte
'dal valore 3 che indicano la fine del pacchetto
rx_uart_enable = 0 'Disabilita modulo ricezione UART (interrompe ricezione durante procedura)
delay_ms(2) 'attende 2ms prima della procedura
decode_ric_data(get_data_received, correct_packet, address_packet, adr_sender_test, type_packet, data1_packet, data2_packet)
if (correct_packet = 1) then
if (adr_s = address_packet) and (adr_sender_test = adr_m) and (program_button = 0) then
select case type_packet
case 1
if (data1_packet = 63) and (data2_packet = 0) then
code_data_to_send(adr_m,1,33,device_type)
end if
'----------------------------------------------------------------
'Procedura richiesta valore INPUT
case 10
if (data1_packet = 63) and (data2_packet = 0) then
code_data_to_send(adr_m,10,input_value,0)
end if
'----------------------------------------------------------------
'Procedura richiesta valore OUTPUT
case 11
if (data1_packet = 63) and (data2_packet = 0) then
read_output() 'Legge il dato attualmente in output
code_data_to_send(adr_m,11,out_value_r,0)
end if
'----------------------------------------------------------------
'Procedura scrittura nuovo valore in OUTPUT
case 12
if (data2_packet = 0) then
write_output(data1_packet) 'scrive il nuovo valore in output
read_output() 'legge il dato in output
code_data_to_send(adr_m,12,out_value_r,0) 'invia il dato appena letto
'-------------------------------------
'Reset TMR1 (e registri correlati) per eseguire interrupt e indicare il timeout delle uscite impulsive
tmr1_reset()
tmr1_enable = 1 'Attiva timer
'--------------------------------------
'Procedura controllo uscite impulsive
'Azzera i bit impulsivi che andranno scritti successivamente
pulse_byte = out_value_r and EEPROM_Read($03) 'confronta i due valori dei registri
if (EEPROM_Read($02) <> pulse_byte) then 'se il nuovo valore č diverso da quello
'giā presente in eeprom viene riscritta la eeprom
EEPROM_Write($02, pulse_byte)
end if
end if
'----------------------------------------------------------------
'Lettura valore ADC
case 13
if (data1_packet = 63) and (data2_packet = 0) then
analog_value = adc_read(0) 'Legge il valore dell'ADC
code_data_to_send(adr_m, 13, analog_value, Hi(analog_value))
read_request.0 = 0
end if
'----------------------------------------------------------------
'Lettura valore ADC con controllo di richiesta
case 14
if (data1_packet = 63) and (data2_packet = 0) then
if (read_request.0 = 1) then 'Se č la pima richiesta di dato dalla lettura
code_data_to_send(adr_m, 14, analog_value, Hi(analog_value))
read_request.0 = 0
else
code_data_to_send(adr_m,63,3,0) 'invia codice errore lettura senza richiesta (errore 3)
end if
end if
'----------------------------------------------------------------
'Lettura valore ADC senza controllo di richiesta
case 15
if (data1_packet = 63) and (data2_packet = 0) then
code_data_to_send(adr_m,15, analog_value, Hi(analog_value))
end if
'----------------------------------------------------------------
'Lettura registri flag input low e flag input hi
case 16
if (data1_packet = 63) and (data2_packet = 0) then
'Il flag low byte viene invertito portando a 1 gli ingressi che hanno
'avuto una tranzazione da 1 a 0
code_data_to_send(adr_m,16, not flag_low_byte, flag_hi_byte)
end if
'----------------------------------------------------------------
'Reset valore registri flag input low e flag input hi
case 17
if (data1_packet = 82) then
select case data2_packet
case 76 'Reset solo flag LOW
flag_low_byte = $FF
code_data_to_send(adr_m,17, 82, 76)
case 72 'Reset solo flag HIGH
flag_hi_byte = 0
code_data_to_send(adr_m,17, 82, 72)
case 65 'Reset dei due registri
flag_low_byte = $FF
flag_hi_byte = 0
code_data_to_send(adr_m,17, 82, 65)
end select
end if
'---------------------------------------------------------------
'Lettura valore registro Pulse Byte e delay pulse byte
case 18
if (data1_packet = 63) and (data2_packet = 0) then
code_data_to_send(adr_m,18, not EEPROM_Read($03), EEPROM_Read($06))
end if
'---------------------------------------------------------------
'Scrittura nuovo valore Pulse Byte e delay pulse byte
case 19
'------------------------------
'Scrittura nuovi valori eeprom e reimposto timer
EEPROM_Write($03, not data1_packet) 'Il valore vine invertito prima di essere memorizzato
EEPROM_Write($06, data2_packet)
code_data_to_send(adr_m,19, data1_packet, data2_packet) 'Invia il contenuto del registro pulse byte nell'EEPROM
'Disattivazione e reset timer
tmr1_enable = 0
tmr1_reset()
'-------------------------------
'imposto tempo di attesa pulse byte
delay_set = data2_packet * 2
'-------------------------------
'Procedura disattivazione uscite impulsive
read_output()'Legge gli output dopo la modifica
pulse_byte = out_value_r and EEPROM_Read($03)'Azzera i bit impostati come Pulse
write_output(pulse_byte)'scrive in output il valore con i bit pulse azzerati
if (EEPROM_Read($02) <> pulse_byte) then 'Se il valore č diverso da quello presente in eeprom
'viene scritto il nuovo valore in eeprom
EEPROM_Write($02, pulse_byte)
end if
'---------------------------------------------------------------
case 63 'Ricevuto codice errore
if (data1_packet = 2) and (data2_packet = 0) then 'se l'invio precedente era errato (errore 2)
send_data(last_send_data) 'reinvia l'ultimo dato
end if
'---------------------------------------------------------------
end select
else
if (address_packet = 255) and (adr_sender_test = 255) then 'indirizzo che richiama tutti i dispositivi
select case type_packet
'---------------------------------------------------------------
'Programmazione indirizzi
case 60
if (program_button = 1) then'solo se il selettore č im modalitā programmazione parte la procedura
if (data1_packet <> 0) and (data2_packet <> 0) and (data1_packet < 255) and
(data2_packet < 255) and (data1_packet <> data2_packet) then
'salvo i nuovi indirizzi
EEPROM_Write($00, data1_packet)
EEPROM_Write($01, data2_packet)
code_data_to_send(255,60,65,79) 'invia pacchetto programmazione corretta
led_activity = 1
'reimposto i valori eeprom a default
reset_EEPROM_value()
delay_ms(950)
led_activity = 0
else
code_data_to_send(255,63,1,0) 'invia pacchetto programmazione errata (errore 1)
led_activity = 1 'avvisa con lampeggio led
delay_ms(50)
led_activity = 0
end if
end if
'--------------------------------------------------------------
'Richiesta lettura valore ADC (senza invio)
case 50, 51
if (data1_packet = 63) and (data2_packet = 0) and (program_button = 0) then
led_activity = 1 'segnala attraverso l'attivazione del led
analog_value = adc_read(0) 'legge il nuovo valore
read_request.0 = 1 'segna la lettura
delay_ms(20)
led_activity = 0
end if
'--------------------------------------------------------------
end select
end if
end if
end if
get_data_received = "" 'svuoto array valori ricevuti
rx_uart_enable = 1 'Abilita modulo ricezione UART
end if
wend
end.