Home > pic18f, Uncategorized > pic18f assembly example 6 – discrete filter implementation

## pic18f assembly example 6 – discrete filter implementation

```;-----------------------------------------------------------------------------------------------------------------------------
; This assembly code sets up a timer interrupt to be triggered every millisecond on the pic18f4620.
; When the interrupt service routine is called an ADC on channel 0 is performed and this value  is used as an
;    input to the discrete system given by the difference equation y[n] = x[n]-0.3x[n-1]+0.3y[n-1]-0.75y[n-1]
;    The output of the discrete system is then sent to a  DAC (MCP4921) via the SPI protocol.
;
; Test cicruit used is same as at http://eleceng.dit.ie/daq_lite/build.html
;
; This code is just an educational example and no attempt has been made to optimise it in any way. Use it whatever way you want but I don't accept any responsibility for any problems this code might cause you.
;
; NOTE 1: Only the most significant 8 bits of the 10-bit ADC are used. Therefore the
;    the ADC values used are in the range of 0-255.
; NOTE 2: When implementing the discrete system the ADC values are interpreted as a signed signal which varies between 0.9922 (=127/128) and -1.
;    An ADC value of 255 is interpreted as a signal value of 0.9922; an ADC value of 0 is interpreted as a signal value of -1 ;
;    an ADC value of 128 is interpreted as a signal value of 0; An ADC value of 127 is interpreted as a signal value of -0.0078 (1/128)
; NOTE 3: The pic18f does not have a floating point unit and signed decimal values are represented in and 8-bit, fixed point, 2's complement format.
;    A signal value of 0.9922 is stored as a binary value of '01111111' in memory
;    A signal value of -1 is stored as a binary value of '10000000'  in memory
;    A signal value of 0.5 is stored as binary value of '01000000' in memory
;    A signal value of -0.5 is stored as binary value of '11000000' in memory
;    A signal value of 0.75 is stored as binary value of '01100000' in memory
;    A signal value of -0.75 is stored as binary value of '10100000' in memory
;    A signal value of 0.0078 is stored as binary value of '00000001' in memory
;    A signal value of -0.0078 is stored as binary value of '11111111' in memory
;
; by David Dorran (https://dadorran.wordpress.com) May 2014
;-----------------------------------------------------------------------------------------------------------------------------

;configure the  assembler directive 'list' so as to set processor to 18f4620 and set the radix used for data expressions to decimal (can be HEX|DEC|OCT)
list p=18f4620, r=DEC
#include <p18f4620.inc>
; configure the micro so that the watchdog timer is off, low-voltage programming is off, master clear is off and the clock works off the internal oscillator
config WDT=OFF, LVP=OFF, MCLRE=OFF, OSC=INTIO67
;The org directive tells the compiler where to position the code in memory
org 0x0000 ;The following code will be programmed in reset address location i.e. This is where the micro jumps to on reset

;NOTE: When an interrupt is triggered the micro jumps to 0x0008 for high priority and 0x0018 for low priority.
org 0x0008
goto hi_isr ;once a high priority interrupt is triggered jump to hi_isr
org 0x0018
goto low_isr ;once a low priority interrupt is triggered jump to low_isr

;--------------------------------------------------------------------------
; Main Program -------------------------------------------------------------
;--------------------------------------------------------------------------
org 0x0100 ; the following code is placed at address 0x0100 in program memory (Flash memory)
Main
; store the filter variables in a contiguous block in RAM starting at address 0x000
cblock 0x000
; y_1 = y[n-1]; y_2 = y[n-2]; x_1 = x[n-1]; x_2 = x[n-2]
x, y,a1,a2,b0,b1,b2, var1,var2, x_1, y_1, x_2, y_2
endc

; set up b and a coefficients of the discrete system (y[n] = x[n]-0.3x[n-1]+0.3y[n-1]-0.75y[n-1])
movlw .127
movwf b0 ; approx = 1 (exact value is 0.9922)
movlw B'11011010'
movwf b1 ; approx = -0.3 (exact value is -0.3047)
movlw B'00000000'
movwf b2 ; = 0
movlw B'00100110'
movwf a1 ; = approx 0.3 (exact value is 0.3047)
movlw B'10100000'
movwf a2 ; = -0.75

; intialise the 'past value' variables to zero
clrf y_1
clrf y_2
clrf x_1
clrf x_2

call micro_config ; configure pins,timers, ADC etc.

main_loop
nop ; Just wait for a timer interrupt to be triggered
goto main_loop

;----------------------------------------------------------------------------------------------------------
;-------multiply accumulate - multiply var1 by var2 and accumulate result in y   --------------------------
;------   this routine is used a lot to filter the signal -------------------------------------------------
;----------------------------------------------------------------------------------------------------------
mac
; pic18f only has 8X8 unsigned multiply - the following code analyses the result of an 8X8 unsigned multiply to produce a signed result
movf var1, W
mulwf var2 ; var1 * var2 -> PRODH:PRODL (var1 and var interpreted as     btfsc var2, 7 ; Test Sign Bit (NOTE: if var2 is negative (2's complement representation) then its unsigned interpretation is 256-var2 and then the result stored in PROD is var1*(256-var2) = var1*256-var1*var2
btfsc var2, 7 ; Test Sign Bit
subwf PRODH, F ; PRODH = PRODH - var1 (equivalent to PROD = PROD - var1*256)
movf var2, W
btfsc var1, 7 ;Test Sign Bit
subwf PRODH, F ; PRODH = PRODH - var2

; The two most significant bits of PROD are sign bits so shift PRODH to the left and grab the most significant bit of PRODL to get the 8 most significant useful bits.
rlncf PRODH,0 ; W contains PRODH shifted to the left by one
movwf var2 ; using var2 as it is available (not for any other reason)
bsf var2, 0
BTFSS PRODL,7 ; set the most significant bit of PRODL as the least signifcant in W
bcf var2, 0
movf var2,W

addwf y,f ; accumulate the result of multiplication in y. Note not handling overflow. Would also be better to accumulate those 7 bits discarded after the multiplication and round afterwards for a more accurate result

return

;----------------------------------------------------------------------------------------------------------
;-----High Priority Interrupt Service Routine -------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------
org 0x0200
hi_isr
btfss INTCON, TMR0IF
goto end_int
bcf INTCON, TMR0IF; reset interrupt
;An interrupt has been set up to be generated whenever TMR0 goes from FFFFh to 0000h i.e. on overflow.
;Would like the timer overflow flag triggered every 1ms so will set timer to 0xffff - 1000 = 0xFC17
movlw 0x17
movwf TMR0L
movlw 0xFC
movwf TMR0H

clrf y

movf y,W

addlw -128 ; account for dc offset. The input values from ADRESH will be in range 0-255. These values represent a signal that varies betwen 1 and -1: the ADC value of 255 is mapped to 1 and ADC value of 0 is mapped to -1; ADC value of 128 is mapped to 0.

movwf x
movwf var1
movff b0,var2
call mac ; multiply var1 by var2 and accumulate the result in y (mac - multiply accumulate)

movff x_1, var1
movff b1, var2
call mac;

movff x_2, var1
movff b2, var2
call mac;

movff y_1, var1
movff a1, var2
call mac;

movff y_2, var1
movff a2, var2
call mac

; update past values of the inputs and outputs to use on next iteration of the discrete system
movff y_1, y_2
movff y, y_1
movff x_1, x_2
movff x, x_1

movf y,W
addlw .128 ; account for dc offset
movwf y
goto spi_write ;write contents of y to SPI device
end_int
retfie ;return and reset interrupts

;----------------------------------------------------------------------------------------------------------
;-------configure pins, ADC,timers and interrupts on the pic18f4620    -----------------------------------
;----------------------------------------------------------------------------------------------------------
micro_config
; configure LATD0-LATD3 as outputs and RD4-RD7 as inputs
movlw 0xf0
movwf TRISD
clrf LATD ; turn off all LATD output pins

; SPI configuration ------------------------------------------------------------
clrf TRISC
;bcf TRISC,3 ; pin 3 on PORTC used as SPI clock and should be configured as an output to operate in master mode
;bcf TRISC,5 ; pin 5 on PORTC used for SPI serial data out (SDO) and should be configured as an output
movlw B'10110000' ; set up SPI control register see pg 163 for details
movwf SSPCON1;
bsf SSPSTAT,CKE; data transmitted on rising edge
; END SPI config ----------------------------------------------------------

; Set clock frequency (section 2 of the PIC18F4620 Data Sheet)
; Set Fosc = 8MHz, which gives Tcy = 0.5us
movlw B'01110000'
movwf OSCCON

; all channels set up for ADC - no particular reason why
movlw B'00000001'

; set tad so that capacitor on ADC can fully charge - see pg129
movlw B'00100010'

clrf ADCON0; select analog channel 0;

bcf INTCON, GIE ;Disable global interrupts
bcf T0CON, TMR0ON; turn off timer 0
bsf INTCON, TMR0IE; Enable TIMER0 interupt

;set up 1:2 prescaler so that TMR0 SFR is incremented every 2 Tcy  i.e. 1us
bcf T0CON,0
bcf T0CON,1
bcf T0CON,2

bcf T0CON, T0CS; use internal instruction cycle clock
bcf T0CON, T08BIT; use 16 bit mode
bcf T0CON, PSA; turn on prescaler

; setup so that timer0 overflow is triggered quickly initially
; after the first trigger then set it up so that it is triggered at the rate you want in the high_isr routine
movlw 0xFF
movwf TMR0L
movlw 0xFF
movwf TMR0H

bsf INTCON2, TMR0IP; ; Set the timer0 interrupt up as high priority
bsf INTCON, GIE ;Enable global interrupts
bsf T0CON, TMR0ON; turn on timer 0
return

;----------------------------------------------------------------------------------------------------------
;-----WRITE contents in y to DAC (MCP4921) using SPI ------------------------------------------------------
;----------------------------------------------------------------------------------------------------------
spi_write
;two bytes of data sent to spi daq (a 12-bit dac). First nibble of first byte is dac config settings; second nibble is
; the most significant 4 bits of 12bit numerical value. The second byte contains the 8 least signifcant bits of the 12-bit value

bcf LATD, LATD1; // Select SPI DAC chip

movf SSPBUF, W ;WREG reg = contents of SSPBUF (this clears BF) which will be set again after the data is transmitted
; load SSPBUFF with spi dac config data to be sent to SPI device
; First nibble of first byte sent to dac is dac config settings; second nibble is
; the most significant 4 bits of 12bit numerical value
swapf y, f
movlw 0x0f
andwf y, W ; W no holds 4 most signicant bits in lower nibble

iorlw 0x70 ; these are configuration bits for spi dac
movwf SSPBUF

spi_wait1
btfss SSPSTAT, BF ;Has data been received (transmit complete)?
goto spi_wait1 ;No

movf SSPBUF, W ;WREG reg = contents of SSPBUF (this clears BF) which will be set again after the data is transmitted
; load SSPBUFF with y data to be sent to SPI device

movlw 0xf0
andwf y, W ;
movwf SSPBUF

spi_wait2
btfss SSPSTAT, BF ;Has data been received (transmit complete)?
goto spi_wait2 ;No

bsf LATD, LATD1; // Select SPI DAC chip
goto end_int

;----------------------------------------------------------------------------------------------------------
;-----Low Priority Interrupt Service Routine -------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------
org 0x0300
low_isr
nop ; put whatever you would like to do on low priority interrupt here
retfie ;return and reset interrupts

end ; End of ASM code
```
Categories: pic18f, Uncategorized