pic18f assembly example 6 – discrete filter implementation
May 20, 2014
Leave a comment
;----------------------------------------------------------------------------------------------------------------------------- ; 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 goto Main ;Jump to Main immediately after a 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 bsf ADCON0,GO; start ADC conversion ; wait for hardware to reset GO bit - indicating ADRES SFR is ready to read adc_not_ready btfsc ADCON0, GO goto adc_not_ready clrf y movf y,W movf ADRESH,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' movwf ADCON1 ; set tad so that capacitor on ADC can fully charge - see pg129 movlw B'00100010' movwf ADCON2 clrf ADCON0; select analog channel 0; bsf ADCON0,ADON; //enable ADC 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
pic18f assembly example 5 – Sample ADC and pass through to DAC
May 20, 2014
Leave a comment
;----------------------------------------------------------------------------------------------------------------------------- ; 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 sent a DAC (MCP4921) via a SPI protocol. ; ; Test cicruit used is same as at http://eleceng.dit.ie/daq_lite/build.html ; ; In the main part of the code the value associated with analog channel 0 is monitored in a continuous loop. If the value ; is high then LATD2 is set high otherwise LATD2 is set low ; ; 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) ;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 goto Main ;Jump to Main immediately after a 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 y equ 0x20 ; y refers to a register at address 0x20 call micro_config ; configure pins,timers, ADC etc. main_loop nop ; Just wait for a timer interrupt to be triggered goto main_loop ;---------------------------------------------------------------------------------------------------------- ;-----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 bsf ADCON0,GO; start ADC conversion ; wait for hardware to reset GO bit - indicating ADRES SFR is ready to read adc_not_ready btfsc ADCON0, GO goto adc_not_ready movff ADRESH, 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' movwf ADCON1 ; set tad so that capacitor on ADC can fully charge - see pg129 movlw B'00100010' movwf ADCON2 clrf ADCON0; select analog channel 0; bsf ADCON0,ADON; //enable ADC 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
pic18f assembly example 4 – sample Analog Channel on timer interrupt
May 20, 2014
Leave a comment
;------------------------------------------------------------------------------------------------------------------------------; This assembly code sets up a timer interrupt to be triggered every millisecond on the pic18f4620. ; This assembly code uses a timer interrupt to read an analog channel every 1ms. ; When the interrupt service routine is called an ADC on channel 0 is performed. ; In the main part of the code the value associated with analog channel 0 is monitored in a continuous loop. If the value ; is high then LATD2 is set high otherwise LATD2 is set low ; ; by David Dorran (https://dadorran.wordpress.com) May 2014 ;------------------------------------------------------------------------------------------------------------------------------ ; TRY TO DO THE FOLLOWING IN ORDER TO DEVELOP YOUR PROGRAMMING SKILLS: ; 1. Change the sampling rate from 1kHz to 1Hz ;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. The micro jumps to 0x0000 on a reset goto Main ;Jump to Main immediately after a 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 an high priority interrupt is triggered jump to hi_isr org 0x0018 goto low_isr ;once an low priority interrupt is triggered jump to low_isr ;-------------------------------------------------------------------------- ; Main Program ------------------------------------------------------------- ;-------------------------------------------------------------------------- org 0x0100 Main goto pin_config ; configure the timers, ADC, IO pins, interrupts etc. main_loop ; This loop runs forever waiting for interrupts to be triggered ; if the analog input is greater than Vss/2 then turn on LATD2 else turn it off BTFSC ADRESH, 7 ; ADRESH stores the 8 most significant bits after a 10 bit ADC conversion. Just check the most significant bit to see if the value is 'large' or 'small' goto set_LATD2 goto reset_LATD2 set_LATD2 BSF LATD, 2 goto main_loop reset_LATD2 BCF LATD, 2 goto main_loop ;---------------------------------------------------------------------------------------------------------- ;-------configure pins, ADC,timers and interrupts on the pic18f4620 ----------------------------------- ;---------------------------------------------------------------------------------------------------------- pin_config ; configure LATD0-LATD3 as outputs and RD4-RD7 as inputs movlw 0xf0 movwf TRISD clrf LATD ; turn off all LATD output pins BSF LATD,1 ; turn on LATD1 ; 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' movwf ADCON1 ; set tad so that capacitor on ADC can fully charge - see pg129 movlw B'00100010' movwf ADCON2 clrf ADCON0; select analog channel 0; BSF ADCON0,ADON; //enable ADC 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 ; after the first trigger then set it up so that it is triggered at the rate you want 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 goto main_loop ;---------------------------------------------------------------------------------------------------------- ;-----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 BSF ADCON0,GO; start ADC conversion ; wait for hardware to reset GO bit - indicating ADRES SFR is ready to read adc_not_ready BTFSC ADCON0, GO goto adc_not_ready end_int retfie ;return and reset interrupts ;---------------------------------------------------------------------------------------------------------- ;-----Low Priority Interrupt Service Routine ------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------- org 0x0300 low_isr nop retfie ;return and reset interrupts end ; End of ASM code
Categories: pic18f, Uncategorized
pic18f assembly example 3 – an interrupt example
May 20, 2014
Leave a comment
;----------------------------------------------------------------------------------------------------------------------------- ; This assembly code uses a timer interrupt to set LATD2 high for 50ms then low for 50ms, repeatedly. ; ; by David Dorran (https://dadorran.wordpress.com) May 2014 ;--------------------------------------------------------------------- ; TRY TO DO THE FOLLOWING IN ORDER TO DEVELOP YOUR PROGRAMMING SKILLS: ; 1. Use the counter register set up in the code to change the on-off rate from 10Hz to 1Hz ;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 goto Main ;Jump to Main immediately after a 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 org 0x0018 goto low_isr ;-------------------------------------------------------------------------- ; Main Program ------------------------------------------------------------- ;-------------------------------------------------------------------------- org 0x0100 Main counter equ 0x10 ; counter refers to a register at address 0x10 clrf counter ; clear the counter ; configure LATD0-LATD3 as outputs and RD4-RD7 as inputs banksel TRISD movlw 0xf0 movwf TRISD clrf LATD ; turn off all LATD output pins bsf LATD,3 ; turn on LATD3 ; Set clock frequency (section 2 of the PIC18F4620 Data Sheet) ; Set Fosc = 8MHz, which gives Tcy = 0.5us movlw B'01110000' movwf OSCCON 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 ; after the first trigger then set it up so that it is triggered at the rate you want 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 loop nop; ;loop waiting for interrupts to be trigged goto loop ;-----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 timer overflow flag triggered every 500ms so will set timer to 0xffff - 1000 = 0x£CAF movlw 0xAF movwf TMR0L movlw 0x3C movwf TMR0H incf counter, f ; increment the counter ;invert LATD2 movlw 0x4 xorwf LATD,F end_int retfie ;return and reset interrupts ;-----Low Priority Interrupt Service Routine ------------------------------------------------------------- org 0x0300 low_isr nop retfie ;return and reset interrupts end ; End of ASM code
Categories: pic18f, Uncategorized
pic18f assembly example 2 – digital output high-low
May 20, 2014
Leave a comment
;----------------------------------------------------------------------------------------------------------------------------- ; This assembly code uses a timer to set LATD2 high for 50ms then low for 50ms, repeatedly ; ; by David Dorran (https://dadorran.wordpress.com) May 2014 ;------------------------------------------------------------------------------------------------------------------------------ ; TRY TO DO THE FOLLOWING IN ORDER TO DEVELOP YOUR PROGRAMMING SKILLS: ; 1. Change the on-off rate from 10Hz to 100 Hz ; 2. Change the on-off rate from 10Hz to 1.25Hz by changing the timer prescaler ;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 goto Main ;Jump to Main immediately after a reset ;-------------------------------------------------------------------------- ; Main Program ;-------------------------------------------------------------------------- org 0x0100 Main ; configure LATD0-LATD3 as outputs and RD4-RD7 as inputs movlw 0xf0 movwf TRISD clrf LATD bsf LATD,2 ; turn on LATD2 bsf LATD,3 ; turn on LATD3 ; Set clock frequency (section 2 of the PIC18F4620 Data Sheet) ; Set Fosc = 8MHz, which gives Tcy = 0.5us movlw B'01110000' movwf OSCCON ;------- Set up timer0 to increment every 1us ;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 ; after the first trigger then set it up so that it is triggered at the rate you want movlw 0xFF movwf TMR0L movlw 0xFF movwf TMR0H bcf INTCON, TMR0IF; reset timer overflow flag BSF T0CON, TMR0ON; turn on timer 0 loop ; check if the timer0 overflow flag has been set BTFSS INTCON, TMR0IF goto loop bcf INTCON, TMR0IF; reset timer overflow flag ;Could set up an interrupt to be generated whenever TMR0 goes from FFFFh to 0000h i.e. on overflow. ;The timer is incremented every 1us. ;Would like timer overflow flag triggered every 50ms so will set timer to 0xffff - 50000 = 0x3CAF movlw 0xAF movwf TMR0L movlw 0x3C movwf TMR0H ;invert LATD2 movlw 0x4 XORWF LATD,F goto loop END
Categories: pic18f, Uncategorized
pic18f assembly example 1 – digital output controlled by digital input
May 20, 2014
Leave a comment
;----------------------------------------------------------------------------------------------------------------------------- ; This assembly code monitors RD4 (pin 27) on the pic18f4620. If RD4 is high LATD2 (pin 21) is set high otherwise LATD2 is set low ; ; by David Dorran (https://dadorran.wordpress.com) May 2014 ;----------------------------------------------------------------------------------------------------------------------------- ; TRY TO DO THE FOLLOWING IN ORDER TO DEVELOP YOUR PROGRAMMING SKILLS: ; 1. change the digital input monitored to RD3 ; 2. Set LATD2 high if both RD3 and RD4 are high ;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 goto Main ;Jump to Main immediately after a reset ;NOTE: When an interrupt is triggered the micro jumps to 0x0008 for high priority and 0x0018 for low priority. For this reason you ;don't want to place any 'code' at these locations and this is why the you just jump to somewhere else in memory after a reset. ;If interrupts aren't used then there's no problem just moving the Main code to address 0x0000 ;-------------------------------------------------------------------------- ; Main Program ;-------------------------------------------------------------------------- org 0x0020 ;The main code section will be programmed starting from address 20H. Main ; configure D0-D3 as outputs and D4-D7 as inputs by modifying the TRISD register movlw 0xf0 movwf TRISD CLRF LATD ; turn off all the outputs main_loop BTFSS PORTD,4 ; if RD4 is set skip the next line goto turn_LATD2_off bsf LATD,2 ; turn on LATD2 - we wont reach this line in loop if RD4 is low goto main_loop turn_LATD2_off bcf LATD,2 ; turn on LATD2 goto main_loop END
Categories: pic18f, Uncategorized
cross correlation demo
April 25, 2014
2 comments
% A demonstration of cross correlation in action. % Hit the space bar during the demo to execute % % https://dadorran.wordpress.com/2014/04/25/cross-correlation-demo/ clc;close all a = [0.1 0.2 -0.1 4.1 -2 1.5 0 ]; b = [0.1 4 -2.2 1.6 0.1 0.1 0.2]; len = length(a); if(len ~= length(b)) error('vectors supplied must be the same length'); end figure set(gcf, 'position', [ 285 347 642 367]); max_amp = max([max(a) max(b)]); min_amp = min([min(a) min(b)]); plot_h = 0.25; text_h = 0.1; ax1 = subplot(2,1,1); pl1_line = plot(a); labels1 = text([1:len], a , num2str(a'), 'VerticalAlignment','bottom', ... 'HorizontalAlignment','right','fontsize',8); hold on; pl1_dot = plot(a,'r.'); xlim([1 len]) ylim([min_amp max_amp]) set(ax1,'position', [(1/3) 0.95-plot_h (1/3) plot_h]) set(ax1,'visible','off') ax2 = subplot(2,1,2); pl2_line = plot(b); labels2 = text([1:len], b , num2str(b'), 'VerticalAlignment','bottom', ... 'HorizontalAlignment','right','fontsize',8); hold on; pl2_dot = plot(b,'r.'); xlim([1 len]) ylim([min_amp max_amp]) set(ax2,'visible','off') set(ax2,'position', [(1/3) 0.9-plot_h*2 (1/3) plot_h]) str = ''; for k = 1: len str = [str '(' num2str(a(k)) ')(' num2str(b(k)) ') + ']; end str(end-1) = '='; str = [str num2str(sum(a.*b))]; r_ba = xcorr(a,b); corr_calc_text = annotation('textbox', [0 0.85-plot_h*2-text_h 1 text_h], 'linestyle','none','horizontalalignment','center' ,'string', {'correlation at zero lag is ' str}, 'fontsize', 8); annotation('textbox', [0.5 0.8-plot_h*2-text_h*2 1 text_h], 'linestyle','none','horizontalalignment','left' ,'string', sprintf('%.2f',r_ba(len)),'color','red', 'fontsize', 8); pause x_inc= (1/3)/(len-1); for k = 1:len-1 str = ''; for m = 1: len-k str = [str '(' num2str(a(m+k)) ')(' num2str(b(m)) ') + ']; end str(end-1) = '='; str = [str num2str(r_ba(len+k))]; set(corr_calc_text,'string', {['correlation at lag of ' num2str(k) ' is '] str}, 'fontsize', 8); set(ax2,'position', [(1/3)+k*x_inc 0.9-plot_h*2 (1/3) plot_h]) annotation('textbox', [0.5+x_inc*k 0.8-plot_h*2-text_h*2 1 text_h], 'linestyle','none','horizontalalignment','left' ,'string', sprintf('%.2f',r_ba(len+k)),'color','red', 'fontsize', 8); if(k ==1) pause annotation('textbox', [0.5 0.01 1 text_h], 'linestyle','none','horizontalalignment','left' ,'string', [' 0'] ,'color','blue', 'fontsize', 8); annotation('textbox', [0.001 0.01 1 text_h], 'linestyle','none','horizontalalignment','left' ,'string', ['Lag:'] ,'color','blue'); annotation('textbox', [0.5+x_inc*(len) 0.8-plot_h*2-text_h*2 1 text_h], 'linestyle','none','horizontalalignment','left' ,'string', ']' ,'color','red'); annotation('textbox', [0.5-x_inc*(len-1)-x_inc/2 0.8-plot_h*2-text_h*2 1 text_h], 'linestyle','none','horizontalalignment','left' ,'string', '[' ,'color','red'); annotation('textbox', [0.001 0.8-plot_h*2-text_h*2 1 text_h], 'linestyle','none','verticalalignment','middle','horizontalalignment','left' ,'string', {'Correlation' 'Sequence:'} ,'color','red'); end annotation('textbox', [0.5+x_inc*k 0.01 1 text_h], 'linestyle','none','horizontalalignment','left' ,'string', [' ' num2str(k)] ,'color','blue', 'fontsize', 8); pause end for k = 1:len-1 str = ''; for m = 1: len-k str = [str '(' num2str(a(m)) ')(' num2str(b(m+k)) ') + ']; end str(end-1) = '='; str = [str num2str(r_ba(len-k))]; set(corr_calc_text,'string', {['correlation at lag of ' num2str(-1*k) ' is '] str}, 'fontsize', 8); set(ax2,'position', [(1/3)-k*x_inc 0.9-plot_h*2 (1/3) plot_h]) annotation('textbox', [0.5-x_inc*k 0.8-plot_h*2-text_h*2 1 text_h], 'linestyle','none','horizontalalignment','left' ,'string', sprintf('%.2f',r_ba(len-k)),'color','red', 'fontsize', 8); annotation('textbox', [0.5-x_inc*k 0.01 1 text_h], 'linestyle','none','horizontalalignment','left' ,'string', [' ' num2str(k*-1)] ,'color','blue', 'fontsize', 8); pause end % Uncomment the next two lines if you would like to see a plot of the % correlation sequence % [corr_seq lags] = xcorr(a,b); % plot(lags,corr_seq) % xlabel('lags');ylabel('correlation measure');
Categories: matlab code, Uncategorized, youtube demo code
Matlab Fourier Demo
March 27, 2014
Leave a comment
% illustration of Fourier Theory using plots % % usage : fourier_demonstration(1) % a well defined signal % fourier_demonstration(2) % a segment of speech signal (download from https://www.dropbox.com/s/bw4dpf93xxz1lyb/speech_seg.wav) % fourier_demonstration(3) % a SQUARE WAVE % fourier_demonstration(4) % an impulse function fourier_demonstration(num) if(num==1) %sum of sinusoids as input t = 0:1/16000:1-1/16000; s1 = cos(2*pi*1*t);%cosw(16000,1, 16000, 0); s2 = cos(2*pi*5*t)*5;%cosw(16000,5, 16000, 0)*5; s3 = cos(2*pi*10*t + pi/2+0.56)*3;%cosw(16000,10, 16000, pi/2+0.56)*3; s4 = cos(2*pi*8*t+pi)*3;%cosw(16000,8, 16000, pi)*3; s5 = cos(2*pi*12*t+pi/2.2)*3;%cosw(16000,12, 16000, pi/2.2)*3; s6 = cos(2*pi*3*t+pi/2+0.4);%cosw(16000,3, 16000, pi/2+0.4)*3; sig = s1+s2+s3+s2+s3+s4; elseif(num==2) sig = wavread('speech_seg.wav')'; elseif(num==3) sig = [zeros(1,100) ones(1,100) zeros(1,100) ones(1,100) zeros(1,100) ones(1,100) zeros(1,100) ones(1,100) ]; sig = (sig-0.5)*2; else sig = [zeros(1, 200) 1 zeros(1,200)]; end sig = sig - mean(sig); N = length(sig); ft = fft(sig); ft(round(length(ft)/2)-2:end) = []; mags = abs(ft); phases = angle(ft); dc_mag = mags(1); mags(1) = []; phases(1) = []; [sorted_mags sorted_indices] = sort(mags,2); % sort in decending order sorted_mags = fliplr(sorted_mags); sorted_indices = fliplr(sorted_indices); sorted_phases = phases(sorted_indices); synth_op = zeros(1, N); sig = sig -dc_mag; n = [0:N-1]; % sample numbers ylim_max = max([ sorted_mags(1)/N*2 sig])*1.05; ylim_min = min([ -sorted_mags(1)/N*2 sig])*1.05; ylims = [ylim_min ylim_max ]; subplot(3,1,1) plot(sig) set(gca,'Xticklabel','', 'YLim', ylims) set(gca,'Xticklabel','') ylabel('Amplitude') xlabel('Time') title('Example Signal'); pause colors = 'rgkbm'; significant_freqs = find(sorted_mags > max(sorted_mags/100)); freq_vals_to_display = max(sorted_indices(1:length(significant_freqs))) +1 ; length(mags) for k = 1: length(mags) omega = 2*pi*(sorted_indices(k))/N; sinusoid = cos(n*omega+sorted_phases(k))*sorted_mags(k)/N*2 ; if(sorted_mags(k) < 10^-6) break end synth_op = synth_op + sinusoid; subplot(3,1,1) plot(sig) set(gca,'Xticklabel','', 'YLim', ylims) hold on plot(synth_op,'r') set(gca,'Xticklabel','') ylabel('Amplitude') xlabel('Time') if k ==1 title('Example signal and sinusoid shown in lower plot'); else title(['Example signal and ' num2str(k) ' sinusoids shown in middle plot added together.']); end hold off subplot(3,1,2) hold on plot(sinusoid,colors(rem(k,5)+1)) set(gca,'Xticklabel','') %set(gca, 'Ylim', [-max(mags)/N*2 max(mags)/N*2]) set(gca, 'Ylim', ylims) ylabel('Amplitude') xlabel('Time') subplot(3,1,3); ft_mag_vals = ones(1, freq_vals_to_display)*NaN; sorted_indices(k) if( sorted_indices(k) < freq_vals_to_display) ft_mag_vals(sorted_indices(k)) = sorted_mags(k)/N*2; end hold on %stem([0:freq_vals_to_display],[NaN ft_mag_vals],'^') hold off ylabel('Magnitude') xlabel('Normalised Frequency (1/(periods displayed))') pause %plot(sinusoid,colors(rem(k,6)+1)) end close all
Categories: matlab code, Uncategorized, youtube demo code
Correlation implemented in matlab
February 24, 2014
Leave a comment
This is the code shown at the end of http://youtu.be/_r_fDlM0Dx0
% Code to calculate the correlation measurement between to % two signals of length N samples % Available at https://dadorran.wordpress.com a = [2.1 3.8 -3.6 4.1 -2.9]; b = [-1.1 3.2 -3.6 2.2 -4.2]; N = length(a); % must be the same as length(b) corr_measure1 = 0; for n = 1:N mult_result = a(n)*b(n); corr_measure1 = corr_measure1 + mult_result; end disp(['The correlation measurement is ' num2str(corr_measure1)]) % Alternative concise implementation corrmeasure2 = sum(a.*b)
Categories: matlab code, Uncategorized, youtube demo code
Plotting Frequency Spectrum using Matlab
February 20, 2014
8 comments
This post shows a variety of ways of how to plot the magnitude frequency content of a discrete signal using matlab.
Contents
- Load Example Data
- Quick view of double-sided (two-sided) magnitude spectrum
- Double-sided magnitude spectrum with frequency axis (in bins)
- Single-sided magnitude spectrum with frequency axis in bins
- Single-sided magnitude spectrum with frequency axis in Hertz
- Single-sided magnitude spectrum with frequency axis normalised
- Single-sided magnitude spectrum – frequency in rads per sample
- Double-sided magnitude spectrum showing negative frequencies
- Single-sided magnitiude spectrum in decibels and Hertz
- Single-sided power spectrum in decibels and Hertz
- Single-sided power spectrum in dB and frequency on a log scale
Load Example Data
% download data from https://www.dropbox.com/s/n4kpd4pp2u3v9nf/tremor_analysis.txt signal = load('tremor_analysis.txt'); N = length(signal); fs = 62.5; % 62.5 samples per second fnyquist = fs/2; %Nyquist frequency
Quick view of double-sided (two-sided) magnitude spectrum
When roughly interpreting this data half way along x-axis corresponds to half the sampling frequency
plot(abs(fft(signal))) xlabel('Frequency (Bins - almost!)') ylabel('Magnitude'); title('Double-sided Magnitude spectrum'); axis tight
Double-sided magnitude spectrum with frequency axis (in bins)
fax_bins = [0 : N-1]; %N is the number of samples in the signal plot(fax_bins, abs(fft(signal))) xlabel('Frequency (Bins)') ylabel('Magnitude'); title('Double-sided Magnitude spectrum (bins)'); axis tight
Single-sided magnitude spectrum with frequency axis in bins
X_mags = abs(fft(signal)); fax_bins = [0 : N-1]; %frequency axis in bins N_2 = ceil(N/2); plot(fax_bins(1:N_2), X_mags(1:N_2)) xlabel('Frequency (Bins)') ylabel('Magnitude'); title('Single-sided Magnitude spectrum (bins)'); axis tight
Single-sided magnitude spectrum with frequency axis in Hertz
Each bin frequency is separated by fs/N Hertz.
X_mags = abs(fft(signal)); bin_vals = [0 : N-1]; fax_Hz = bin_vals*fs/N; N_2 = ceil(N/2); plot(fax_Hz(1:N_2), X_mags(1:N_2)) xlabel('Frequency (Hz)') ylabel('Magnitude'); title('Single-sided Magnitude spectrum (Hertz)'); axis tight
Single-sided magnitude spectrum with frequency axis normalised
Normalised to Nyquist frequency. Very common to use this method of normalisation in matlab
X_mags = abs(fft(signal)); bin_vals = [0 : N-1]; fax_norm = (bin_vals*fs/N)/fnyquist; % same as bin_vals/(N/2) N_2 = ceil(N/2); plot(fax_norm(1:N_2), X_mags(1:N_2)) xlabel({'Frequency (Normalised to Nyquist Frequency. ' ... '1=Nyquist frequency)'}) ylabel('Magnitude'); title('Single-sided Magnitude spectrum (Normalised to Nyquist)'); axis tight
Single-sided magnitude spectrum – frequency in rads per sample
X_mags = abs(fft(signal)); bin_vals = [0 : N-1]; fax_rads_sample = (bin_vals/N)*2*pi; N_2 = ceil(N/2); plot(fax_rads_sample(1:N_2), X_mags(1:N_2)) xlabel('Frequency (radians per sample)') ylabel('Magnitude'); title('Single-sided Magnitude spectrum (rads/sample)'); axis tight
Double-sided magnitude spectrum showing negative frequencies
See http://youtu.be/M1bLPZdNCRA for an explanation of negative frequencies
X_mags = abs(fftshift(fft(signal))); bin_vals = [0 : N-1]; N_2 = ceil(N/2); fax_Hz = (bin_vals-N_2)*fs/N; plot(fax_Hz, X_mags) xlabel('Frequency (Hz)') ylabel('Magnitude'); title('Double-sided Magnitude spectrum (Hertz)'); axis tight
Single-sided magnitiude spectrum in decibels and Hertz
X_mags = abs(fft(signal)); bin_vals = [0 : N-1]; fax_Hz = bin_vals*fs/N; N_2 = ceil(N/2); plot(fax_Hz(1:N_2), 10*log10(X_mags(1:N_2))) xlabel('Frequency (Hz)') ylabel('Magnitude (dB)'); title('Single-sided Magnitude spectrum (Hertz)'); axis tight
Single-sided power spectrum in decibels and Hertz
X_mags = abs(fft(signal)); bin_vals = [0 : N-1]; fax_Hz = bin_vals*fs/N; N_2 = ceil(N/2); plot(fax_Hz(1:N_2), 20*log10(X_mags(1:N_2))) xlabel('Frequency (Hz)') ylabel('Power (dB)'); title('Single-sided Power spectrum (Hertz)'); axis tight
Single-sided power spectrum in dB and frequency on a log scale
X_mags = abs(fft(signal)); bin_vals = [0 : N-1]; fax_Hz = bin_vals*fs/N; N_2 = ceil(N/2); semilogx(fax_Hz(1:N_2), 20*log10(X_mags(1:N_2))) xlabel('Frequency (Hz)') ylabel('Power (dB)'); title({'Single-sided Power spectrum' ... ' (Frequency in shown on a log scale)'}); axis tight
Categories: matlab code, Uncategorized, youtube demo code