Version:0.9 StartHTML:0000000105 EndHTML:0000105691 StartFragment:0000001119 EndFragment:0000105675 mikroIDE
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 bytedim type_s as byte,
dim data1_s as bytedim 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 <> 255then '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 = 0then '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 = 0then '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 15or 128
checksum_send_hi =  (checksum_send / 16or 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 bytedim byref address_master as bytedim byref type_ric as bytedim byref data1_ric as bytedim 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] = 83then '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[6and 15
     chk_hi = array_in_data[7and 15
     checksum = (chk_hi * 16or chk_low 'mette insieme i 2 byte per ricostruire il byte di checksum
     'Controllo del dato ricevuto con checksum
     calcolated_checksum = array_in_data[1xor array_in_data[2xor array_in_data[3xor array_in_data[4xor array_in_data[5]
     if (calcolated_checksum = checksum) and (array_in_data[6].7 = 1and (array_in_data[7].7 = 1then 'se il checksum č corretto
      address_ric = array_in_data[1]
      address_master = array_in_data[2]
      type_ric = (array_in_data[3and 252)/4 'preleva il byte che seleziona il tipo di chiamata
      'eliminando gli zero flag inseriti
      if (array_in_data[4] = 48and (array_in_data[3].0 = 1then '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] = 48and (array_in_data[3].1 = 1then '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 = 1then
   '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 = 1then
          if (adr_s = address_packet) and (adr_sender_test = adr_m) and (program_button = 0then
          select case type_packet
           case 1
            if (data1_packet = 63and (data2_packet = 0then
              code_data_to_send(adr_m,1,33,device_type)
            end if
            '----------------------------------------------------------------
            'Procedura richiesta valore INPUT
           case 10
            if (data1_packet = 63and (data2_packet = 0then
              code_data_to_send(adr_m,10,input_value,0)
            end if
            '----------------------------------------------------------------
            'Procedura richiesta valore OUTPUT
           case 11
            if (data1_packet = 63and (data2_packet = 0then
              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 = 0then
              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 = 63and (data2_packet = 0then
             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 = 63and (data2_packet = 0then
              if (read_request.0 = 1then '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 = 63and (data2_packet = 0then
              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 = 63and (data2_packet = 0then
             '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,16not flag_low_byte, flag_hi_byte)
            end if
            '----------------------------------------------------------------
            'Reset valore registri flag input low e flag input hi
           case 17
            if (data1_packet = 82then
              select case data2_packet
               case 76 'Reset solo flag LOW
                flag_low_byte = $FF
                code_data_to_send(adr_m,178276)
               case 72 'Reset solo flag HIGH
                flag_hi_byte = 0
                code_data_to_send(adr_m,178272)
               case 65 'Reset dei due registri
                flag_low_byte = $FF
                flag_hi_byte = 0
                code_data_to_send(adr_m,178265)
              end select
             end if
             '---------------------------------------------------------------
             'Lettura valore registro Pulse Byte e delay pulse byte
           case 18
            if (data1_packet = 63and (data2_packet = 0then
              code_data_to_send(adr_m,18not 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($03not 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 = 2and (data2_packet = 0then '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 = 255and (adr_sender_test = 255then 'indirizzo che richiama tutti i dispositivi
            select case type_packet
            '---------------------------------------------------------------
            'Programmazione indirizzi
             case 60
              if (program_button = 1then'solo se il selettore č im modalitā programmazione parte la procedura
                if (data1_packet <> 0and (data2_packet <> 0and (data1_packet < 255and
                 (data2_packet < 255and (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 5051
              if (data1_packet = 63and (data2_packet = 0and (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.