; pin 21 (per-pic-led, RD2/PSP2/C1IN) states:
; high H = blue (=green), low L = orange (=red), float Z = black
-
+;***************************************************************************
+;SETUP AND DEFINITIONS
+
+; CONVENTIONS:
+;
+; In subroutines, unless otherwise stated, W and S may have any
+; value on entry and will be undefined on exit.
+;
+; labels ending _if_... and _endif_... are used for if ... then [... else ...]
+; labels ending _loop are for loops
+; labels ending _isr are at the start of interrupt service routines
+; (which must end with retfie)
+; other labels in lowercase are normal subroutines (ending in `return')
+; labels in UPPERCASE are defined addresses (in RAM or flash)
+
+;---------------------------------------------------------------------------
+; boilerplate:
include /usr/share/gputils/header/p18f458.inc
-
radix dec
-; reserved for NMRA:
-NMRACTRL equ 0x4 ; byte 4: state relevant to NMRA control
-TRANSMITBIT equ 0x7 ; bit 7: 0/1 bit currently being transmitted
-NEXTACTION equ 0x6 ; bit 6: change polarity on next interrupt y/n
-
-FROMSERIAL equ 0x1 ; byte 1: from-serial buffer location (in BSR5)
-TOTRACK equ 0x2 ; byte 2: to-track buffer location (in BSR5)
-TOTRACKBIT equ 0x3 ; byte 3: bit location of pointer within byte
-
-NMRA_BUFF_PAGE equ 5
-
-; flash locations for PIC-dependent info:
-
-F_PIC_NO_U equ 20h ; flash location of PIC number
-F_PIC_NO_H equ 00h
-F_PIC_NO_L equ 00h
-
-F_I2C_CTRL_U equ 20h ; flash location of i2c control byte
-F_I2C_CTRL_H equ 00h
-F_I2C_CTRL_L equ 01h
-
-; i2c specific stuff
-
-I2C_BUFF_PAGE equ 4 ; put i2c relevant stuff into buffer page 4
-PIC_NO equ 00h ; pic no goes to 400h
-I2C_CTRL equ 01h ; i2c ctrl bit goes to 401h
-I2C_MASTER equ 0 ; bit 0 of I2C_CTRL is 1=master 0=slave
-
-
-
-
ifdef SLOW_VERSION
messg "hello this is the slow version"
endif
messg "and an else"
endif
- org 0
- goto initialise
+;---------------------------------------------------------------------------
+; ID locations layout, in flash - see README.protocol
-;****************************************************************************
+F_PIC_NO equ 0x200000
+F_I2C_CTRL equ 0x200001
-; high priority interrupt
+;---------------------------------------------------------------------------
+; RAM - ie, variables etc.
- org 000008h
- goto interrupt_high
+; i2c specific stuff
+ udata 0x400
-; low priority interrupt
+PIC_NO res 1
- org 000018h
- goto interrupt_low
+I2C_CTRL res 1
+I2C_CTRL_MASTER equ 0 ; bit 0 of I2C_CTRL is 1=master 0=slave
;****************************************************************************
+; VECTORS: special locations, where the PIC starts executing
+; after reset and interrupts
- code
+ org 0
+ goto vector_reset
-;****************************************************************************
+ org 000008h
+ goto vector_interrupt_high
+
+ org 000018h
+ goto vector_interrupt_low
-macros
+;****************************************************************************
+; MACROS
+
+;----------------------------------------
+; debugvalue(BYTE)
+; writes BYTE through the serial port
+; serial port hardware must be suitably initialised
+; serial port transmit interrupts must be disabled
+; will spin until the byte is transmitted
+; Before After
+; W any undefined
+; S any undefined
; macro to call subroutine to transmit over serial port for debugging
; takes 8-bit value, puts in W, invokes debug_serial_transmit
-
ifndef SLOW_VERSION
debug macro debugvalue
endm
endm
endif
-debug_serial_transmit
+;--------------------
+debugvalue_serial_transmit
+; writes W through the serial port
+; for use by debugvalue macro only
+
movwf TXREG,0 ; move contents of W (i.e. debugvalue)
; to TXREG for transmission
-waitfortsr
+debugvalue_waitfortsr_loop
btfss TXSTA,1,0
- bra waitfortsr
+ bra debugvalue_waitfortsr_loop
+
return
;****************************************************************************
-initialise
+ code
+
+;****************************************************************************
+; PORTMANTEAU CODE
+; which contains lists of checks and calls to function-specific
+; routines etc.
+
+;----------------------------------------
+vector_reset
+ call serial_setup
+ call copy_per_pic_data
+ call i2c_setup
+ call enable_interrupts
+ goto main
+
+;----------------------------------------
+main
+ banksel I2C_CTRL ; ser BSR=i2c BSR (4)
+ btfsc I2C_CTRL,I2C_CTRL_MASTER ; check =master?, if so
+ goto master_main ; goto master main routine
+ goto slave_main ; elso goto slave main routine
+
+;----------------------------------------
+vector_interrupt_high
+ debug 'H'
+ goto panic
+
+;----------------------------------------
+vector_interrupt_low
+; checks which interrupt and as soon as it finds one jumps straight
+; to the relevant ISR. That routine will return with retfie and if
+; there was another interrupt we will re-enter, which is OK.
+
+ btfsc PIR1,SSPIF,0 ; check if MSSP interrupt generated, if so
+; goto i2c_isr ; I2C ISR will check whether master or slave
+ goto panic ; nothing should generate these ATM....
+
+ btfsc PIR1,5,0 ; check for serial receive interrupt
+ goto serial_rx_isr ; receive serial
+
+ debug 'L' ; else panic - interrupt but don't know why
+ goto panic
+
+;----------------------------------------
+enable_interrupts
+; globally enable interrupts - p77
+; etc.
-; serial set-up
+ bsf RCON,7,0 ; enable priority levels
+ bsf INTCON,7,0 ; enable high-priority interrupts
+ bsf INTCON,6,0 ; enable low-priority interrupts
+ bcf PIE1,3,0 ; disable master synchronous serial port
+ ; (MSSP; i.e. enable i2c) interrupts
+ ; (temporary for this simple program)
+ bsf PIE1,5,0 ; enable USART receive interrupt (p85)
+ return
+
+;***************************************************************************(
+; FUNCTIONALITY
+; these routines actually glue things together to make something that
+; does something
+
+;----------------------------------------
+master_main
+; main program for master PIC
+
+ call led_green
+
+master_main_loop
+ goto master_main_loop
+
+
+;----------------------------------------
+slave_main
+; main program for slave PICs
+
+ call led_red
+
+slave_main_loop
+ call wait_for_i2c_interrupt ; wait for 1st (address) byte
+ call wait_for_i2c_interrupt ; wait for 2nd (data) byte
+ btg LATD,2,0 ; toggle colour of LED
+ goto slave_main_loop
+
+;----------------------------------------
+serial_rx_isr
+; what we actually do here is faff with I2C to start transmitting
+ bsf SSPCON2,SEN,0 ; i2c START
+ call wait_for_i2c_interrupt
+ movlw 0x41 ; transmit address 0100 0001
+ call wait_for_i2c_interrupt
+ btfss SSPCON2,ACKSTAT,0 ; check for ack from slave (=0), if no
+ goto panic ; then panic, else
+ movff RCREG,SSPBUF ; copy byte from serial to i2c buffer
+ call wait_for_i2c_interrupt
+ btfss SSPCON2,ACKSTAT,0 ; check for ack from slave (=0), if no
+ goto panic ; then panic, else
+ bsf SSPCON2,PEN,0 ; i2c STOP
+ call wait_for_i2c_interrupt
+
+;!!!fixme - next line is wrong, surely ?
+ goto master_main
+
+;***************************************************************************
+; SERIAL PORT
+
+;--------------------
+serial_setup
+; sets up the serial port, 9600 8N1 etc. as required by host
+; interrupt is enabled for reception but not transmission
; initial config - TXSTA register p181
bcf TXSTA,6,0 ; p181, set 8-bit mode
bsf SPBRG,7,0
bsf SPBRG,0,0
-
; interrupt set-up for serial receive
bcf IPR1,5,0 ; set to low-priority interrupt
+ return
+;***************************************************************************
+; FLASH ID LOCATIONS
-;----------------------------------------------------------------------------
-
-; copy PIC-dependent info out of flash memory
+;--------------------
+copy_per_pic_data
+; copies PIC-dependent info out of flash memory to RAM
+; see README.protocol
- movlw F_PIC_NO_U ; set table pointer to point to
- movwf TBLPTRU ; PIC number in flash
- movlw F_PIC_NO_H
+ movlw (F_PIC_NO >> 16) & 0xff ; set table pointer to point to
+ movwf TBLPTRU ; PIC number in flash
+ movlw (F_PIC_NO >> 8) & 0xff
movwf TBLPTRH
- movlw F_PIC_NO_L
+ movlw F_PIC_NO
movwf TBLPTRL
tblrd *+ ; read then increment pointer
; (now points to i2c control byte)
- movlb I2C_BUFF_PAGE ; ser BSR=i2c BSR (4)
+ banksel PIC_NO
movf TABLAT,0,0 ; move pic number into normal memory
movwf PIC_NO,1
movf TABLAT,0,0 ; move i2c_ctrl byte into normal memory
movwf I2C_CTRL,1
+; now have: PIC number in 400h, i2c control byte in 401h - see
+; RAM variables re i2c specific stuff, above
-; now have: PIC number in 400h, i2c control byte in 401h
-
-;----------------------------------------------------------------------------
+ return
-; i2c setup
+;***************************************************************************
+; I2C
-; NB to generate an address, take PIC_NO bits 4-0 and prepend 10
-; (i.e. all addresses are of the form 10xxxxx)
+;--------------------
+i2c_setup
+; sets up the I2C interface
- movlb I2C_BUFF_PAGE ; ser BSR=i2c BSR (4)
+; see also:
+; p68
+; p314
+; p 275 ID locs
-common_setup
+; To generate our I2C address, we take PIC_NO bits 4-0 and prepend
+; 0b10 (i.e. all addresses are of the form 0b10xxxxx)
+ banksel PIC_NO ; ser BSR=i2c BSR (4)
+; common to master and slaves:
bsf SSPSTAT,7,0 ; disable slew rate control
bcf SSPSTAT,6,0 ; disable SMBus specific commands
; (whatever that means)
bcf IPR1,SSPIP,0 ; make interrupt low priority
+; are we master or slave ?
+ btfss I2C_CTRL,I2C_CTRL_MASTER,1 ; test whether PIC is master
+ goto i2c_setup_if_master
+ goto i2c_setup_if_slave
-master_or_slave
-
- btfss I2C_CTRL,I2C_MASTER,1 ; test whether PIC is master
- goto pic_master_setup ; if so, set up as master
- goto pic_slave_setup ; else set up as slave
-
-
-pic_master_setup
-
+i2c_setup_if_master
movlw 0x08
movwf SSPCON1,0 ; set to master mode, clear top 4 bits
; Fosc=20MHz, currently want clock=50kHz
; => SSPADD=99
- goto switch_i2c_on
-
-
-pic_slave_setup
+ goto i2c_setup_endif_master_slave
+i2c_setup_if_slave
movlw 0x06
movwf SSPCON1,0 ; set to 7bit slave mode, clear top 4 bits
; (no extra start/stop interrupts)
; set slave address
- movlb I2C_BUFF_PAGE ; set BSR=i2c BSR (4)
+ banksel PIC_NO ; set BSR=i2c BSR (4)
movf PIC_NO,0,1 ; copy pic_no to W (000xxxxx)
iorlw 0x40 ; change top 3 bits t 010 (010xxxxx)
movwf SSPADD,0 ; move to slave address register
; (bit 7=0, bits 6-0=address)
- goto switch_i2c_on
-
-
-switch_i2c_on
-
+i2c_setup_endif_master_slave
bsf SSPCON1,5,0 ; enable I2C mode
+ return
-; p68
-; p314
-; p 275 ID locs
-
-
-;----------------------------------------------------------------------------
-
-; interrupt set-up
-
-; globally enable interrupts - p77
- bsf RCON,7,0 ; enable priority levels
- bsf INTCON,7,0 ; enable high-priority interrupts
- bsf INTCON,6,0 ; enable low-priority interrupts
- bcf PIE1,3,0 ; disable master synchronous serial port
- ; (MSSP; i.e. enable i2c) interrupts
- ; (temporary for this simple program)
- bsf PIE1,5,0 ; enable USART receive interrupt (p85)
-
-
-;****************************************************************************
-;****************************************************************************
-
-main
- movlb I2C_BUFF_PAGE ; ser BSR=i2c BSR (4)
- btfsc I2C_CTRL,I2C_MASTER ; check =master?, if so
- goto main_master ; goto master main routine
- goto main_slave ; elso goto slave main routine
-
-
-;----------------------------------------------------------------------------
-
-main_master
-
- call led_green
-main_loop_led
- goto main_loop_led
-
-
-;----------------------------------------------------------------------------
-
-main_slave
-
- call led_red
-
-slave_loop
- call wait_for_i2c_interrupt ; wait for 1st (address) byte
- call wait_for_i2c_interrupt ; wait for 2nd (data) byte
- btg LATD,2,0 ; toggle colour of LED
- goto slave_loop
-
-
-
-;****************************************************************************
-;****************************************************************************
-
-
-interrupt_high
- debug 'H'
- goto panic
-
-
-;****************************************************************************
-
-interrupt_low
-
-; check which interrupt.
- btfsc PIR1,SSPIF,0 ; check if MSSP interrupt generated, if so
-; goto interrupt_ms ; check whether master or slave
- goto panic ; nothing should generate these ATM....
- btfsc PIR1,5,0 ; check whether serial receive interrupt bit set
- ; if so
- goto serial_receive ; receive serial
- debug 'L' ; else panic
- goto panic
-
-
-interrupt_ms
- movlb I2C_BUFF_PAGE ; ser BSR=i2c BSR (4)
- btfsc I2C_CTRL,I2C_MASTER ; check =master?, if so
- goto interrupt_master ; goto master interrupt routine
- goto interrupt_slave ; elso goto interrupt_slave
-
+;----------------------------------------
+i2c_isr
+ banksel PIC_NO ; ser BSR=i2c BSR (4)
+ btfsc I2C_CTRL,I2C_CTRL_MASTER ; check =master?, if so
+ goto i2c_master_isr ; goto master interrupt routine
+ goto i2c_slave_isr ; elso goto interrupt_slave
-;----------------------------------------------------------------------------
-interrupt_master
+;--------------------
+i2c_master_isr
debug 'm'
goto panic
-;----------------------------------------------------------------------------
-interrupt_slave
+;--------------------
+i2c_slave_isr
debug 's'
goto panic
-;****************************************************************************
-
-serial_receive
- bsf SSPCON2,SEN,0 ; i2c START
- call wait_for_i2c_interrupt
- movlw 0x41 ; transmit address 0100 0001
- call wait_for_i2c_interrupt
- btfss SSPCON2,ACKSTAT,0 ; check for ack from slave (=0), if no
- goto panic ; then panic, else
- movff RCREG,SSPBUF ; copy byte from serial to i2c buffer
- call wait_for_i2c_interrupt
- btfss SSPCON2,ACKSTAT,0 ; check for ack from slave (=0), if no
- goto panic ; then panic, else
- bsf SSPCON2,PEN,0 ; i2c STOP
- call wait_for_i2c_interrupt
- goto main_master
-
-
+;----------------------------------------
wait_for_i2c_interrupt
+; polls the relevant bit until the I2C interrupt flag is set,
+; then returns. should not usually be used if I2C interrupts
+; are enabled, clearly.
+
+wait_for_i2c_interrupt_loop
btfss PIR1,SSPIF,0 ; check if interrupt set, if not, loop
- goto wait_for_i2c_interrupt
+ goto wait_for_i2c_interrupt_loop
+
bcf PIR1,SSPIF,0 ; clear interrupt bit
return
+;***************************************************************************
+; PER-PIC LED
-;****************************************************************************
+;----------------------------------------
led_green
bcf TRISD,2,0 ; make pin RD2 an output (DS100)
bsf LATD,2,0 ; set pin RD2 H (green)
return
+;----------------------------------------
led_black
bsf TRISD,2,0 ; make pin RD2 an input (i.e. set Z, black)
; (DS100)
return
+;----------------------------------------
led_red
bcf TRISD,2,0 ; make pin RD2 an output (DS100)
bcf LATD,2,0 ; set pin RD2 L (red)
return
+;***************************************************************************
+; GENERALLY-USEFUL SUBROUTINES;
+
+;----------------------------------------
panic
+; stops everything, makes LED red
+
debug 'x'
clrf INTCON,0 ; disable all interrupts EVER
debug 'y'
bcf PORTC,1,0 ; switch off booster
debug 'z'
call led_red
+
panic_loop
goto panic_loop