From PIC16F690 to PIC18F26K20 + The big function!

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
Attached is the filter coefficient file and a sample code

Please check if there's some error in the coding...

Note that all the filter coeficients will be stored as an '.inc' file and there will be a 'include filter_coefficients.inc' in the .asm file.

coefficients are labelled as follows, for instance in "f1_b11l":

f1: filter function1 (note there will be a couple)
b11: coefficient of x(n)
l: refers to the lower byte

Also note in the 'sample code ' file, this is not a template...this is so you can read and understande my code...

I will fill in values for coeffients later as im doing the coding first...

I want you to check if the "coefficient buffer initialization" is done properly, also i know I'll have to clear the sample buffer at some point...so check that as well...and i want to know if this should be in a subroutine or macro...

my goal is to write an efficient code written in a way to reduce the numer of cycle as much as possible...

Next post will be a piece of code for a macro that does this: z+= x*y , where z is 24-bit register, x and y are 10-bit number stored in two byte register.

Thanks for your comment!
 

Attachments

Last edited:

joeyd999

Joined Jun 6, 2011
5,234
Rich (BB code):
;---------------------------------------------------------------
; varialbles

coeff_buffer    equ  0x000	; coefficients buffer
sample_buffer	equ  0x020	; samples buffer



;---------------------------------------------------------------

;XX should this be a macro or subroutine??? 

; clearing sample buffer (020h - 033h)

reset	lfsr     FSR0,  0x020	; FSR0 points to the start of
				; sample buffer 

next	clrf     POSTINC0	; clear register pointed by FSR0 
				; then increment pointer
	movlw    0x033		;
	xorwf	 FSR0L,  w	; all buffer cleared?
	btfss	 STATUS, z	; skip if yes
	bra next		;
	bra reset		; reset to start of buffer


;---------------------------------------------------------------

;XX Note: this will be part of program initialization...

; coefficient buffer initialization

here1	lfsr     FSR1,  0x000	; FSR1 points to the start of
				; coefficient buffer

; loading coefficients of first of second order filter

	movlw	 f1_b11l	; load lower byte first
	movwf    POSTINC1	; and increment pointer
	movlw	 f1_b11m	; then load higher byte
	movwf    POSTINC1	; and increment pointer

	movlw	 f1_b12l	;
	movwf    POSTINC1	; 
	movlw	 f1_b12m	;
	movwf    POSTINC1	;

	movlw	 f1_b13l	;
	movwf    POSTINC1	; 
	movlw	 f1_b13m	;
	movwf    POSTINC1	;

	movlw	 f1_a11l	;
	movwf    POSTINC1	; 
	movlw	 f1_a11m	;
	movwf    POSTINC1	;

	movlw	 f1_a12l	;
	movwf    POSTINC1	; 
	movlw	 f1_a12m	;
	movwf    POSTINC1	;

; loading coefficients of second of second order filter

	movlw	 f1_b21l	;
	movwf    POSTINC1	; 
	movlw	 f1_b21m	;
	movwf    POSTINC1	;
	
	movlw	 f1_b22l	;
	movwf    POSTINC1	; 
	movlw	 f1_b22m	;
	movwf    POSTINC1	;

	movlw	 f1_b23l	;
	movwf    POSTINC1	; 
	movlw	 f1_b23m	;
	movwf    POSTINC1	;

	movlw	 f1_a21l	;
	movwf    POSTINC1	; 
	movlw	 f1_a21m	;
	movwf    POSTINC1	;

	movlw	 f1_a22l	;
	movwf    POSTINC1	; 
	movlw	 f1_a22m	;
	movwf    INDF1		; XXNOTICE CHANGE HERE!!!
	bra      here1		; point to start of buffer


;---------------------------------------------------------------
What a nice little infinite loop you have there, Eric, in the 'reset' routine! Also, be careful. Do you intend to clear adress 0x33 or not?

To make life a little easier, you may want to make a macro called 'movlf' that copies a literal to a file register. Actually, I use two, one for access ram, one for banked ram:

Rich (BB code):
movlf	macro	freg,literal	;move literal to file reg (USE ARAM)
	movlw	literal
	movwf	freg,0
	endm

movlfb	macro	freg,literal	;move literal to file reg (use BSR)
	movlw	literal
	movwf	freg,1
	endm
Also, your constants are hard coded (as equates). This is fine. But you can store them in program space as a table, and then copy them into memory using a TBLRD *+ in a loop. Makes for tighter code.

As a matter of fact, you don't really need to copy them to RAM. Just access them through TBLRD when you need them.

And when you define them, use DW (define word). This way you don't need to break your 16 bit values into two bytes manually. The assembler will do it for you.

Along the same lines, you can use 16 bit values in your constant definitions. Use the HIGH and LOW operators to break out the individual bytes. Saves much time.

All for now. Good luck.
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
"What a nice little infinite loop"...HAHAHA...LOL you are so right!!
I didn't realise it...

But thanks for those tips...I will consider that and repost as soon as I can....

Those stupid mistakes are due to the fact that I'm Not too familiar with everything...but step by step I'll get there..
 
Last edited:

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
Do you intend to clear adress 0x33 or not?
matter fact I wanted to clear 20 bytes starting from address 020h so the answer to your question will be YES
So i think the code will be as shown below, right?

Rich (BB code):
               lfsr     FSR0,  0x020         ; FSR0 points to the start of
                                             ; sample buffer 
next         clrf     POSTINC0             ; clear register pointed by FSR0 
                                            ; then increment pointer
               movlw    0x033               ;
               xorwf  FSR0L,  w             ; all buffer cleared?
               btfss  STATUS, z            ; skip if yes
               bra next                 ; loop
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
Attached is part of the 'filter1_IIR4' fuction that uses a couple of macros...the algorithm of this fuction is described in post #32

I only wrote the code for the first two coefficients because I need to know if I'm writing it well so I can carry on to the end.

So basically, the coefficient is loaded into two 8-bit registers, the sample is loaded in another two 8-bit registers then the 'mult_accum' macro multiply them and accumulate the product result.

My first concern is about the 'current' sample from ADC (ADRESH:ADRESL), this needs to be temporarely stored somewhere so that the different filter functions can process it...so i was wondering if storing it in a two 8-bit registers (like i did in the code) is the way of doing it or there's a best way?

Also, the loading of the coefficient from the program space into the registers and... is the programming ok?

regards !
 

Attachments

MMcLaren

Joined Feb 14, 2010
861
matter fact I wanted to clear 20 bytes starting from address 020h so the answer to your question will be YES
So i think the code will be as shown below, right?

Rich (BB code):
               lfsr     FSR0,  0x020         ; FSR0 points to the start of
                                             ; sample buffer 
next         clrf     POSTINC0             ; clear register pointed by FSR0 
                                            ; then increment pointer
               movlw    0x033               ;
               xorwf  FSR0L,  w             ; all buffer cleared?
               btfss  STATUS, z            ; skip if yes
               bra next                 ; loop
Not quite... You're testing FSR0L immediately after it has been post-incremented so if you want to clear location 0x33 you really need to test for FSR0L == 0x34. There are lots of different ways to do it...

Regards, Mike

Rich (BB code):
        lfsr    0,0x33          ; 0x20 + 19
clr1    clrf    POSTDEC0        ; clear 0x20..0x33
        btfsc   FSR0L,5         ; done (FSR0L = 0x1F)?
        bra     clr1            ; no, branch (loop), else
Rich (BB code):
        lfsr    0,0x1F          ; start address minus 1
        movlw   d'20'           ; number of bytes to clear
clr2    clrf    PLUSW0          ; clear 0x20..0x33
        decfsz  WREG,W          ; done? yes, skip, else
        bra     clr2            ; loop (do another)
Rich (BB code):
        lfsr    0,0x20          ; start address
        movlw   d'20'           ; use WREG for counter
clr2    clrf    POSTINC0        ; clear 0x20..0x33
        decfsz  WREG,W          ; done? yes, skip, else
        bra     clr2            ; loop (do another)
 
Last edited:

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
Yes there are many ways of doing that...I also wrote one like your first as well but with POSTINC0 and I had to test 2 times FSR0...

OK, between the two which one will take less time to execute?
Also why you just don't write 0x033 or 33h BUT 0,0x33??? I'm Not familiar with this writing style....

I will post the routine for my 'mult_accum' macro soon for correction...

Thanks Mike!
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
I think I saw my mistake...so in my code I just have to change movlw 0x033 to 0x034, right? And it makes perfect sense...

But I'll just go for your third alternative...

That's why I like to post first then you guyz can correct me...I don't like being provided with code without even trying...but trying myself I learn more and with your correction I understand even better....
 

MMcLaren

Joined Feb 14, 2010
861
Ohk! And "lfsr FSR0, 0x020" is also another way of writing the same thing, right?
Yep, using "FSR0" appears to work. I always used "0" or "1" because that's what is shown in the "Instruction Set Summary" in the Datasheet.

<added>

ok, this description of the 'f' parameter in the Datasheet suggests why both forms work;

f = 8-bit Register file address (00h to FFh) or 2-bit FSR designator (0h to 3h).
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
PIC18F26K20 has two Vss and one Vdd...should I ground the two Vss or just choose one?

Also PIC18F46K20 has two Vdd ans two Vss...
Should I ground the two Vss and connect the two Vdd to +5V or what?

As for the ICSP connection...PGM pin, what to do with it? Coz with PIC16F690, I was using only the 5 pins for ICSP...

Regard, Eric
 
Last edited:

thatoneguy

Joined Feb 19, 2009
6,359
You connect all Vss pins to GND, and the Vdd pins to Power, they are sometimes split up so wires don't need to be crossed over other ties to part of the die, and sometimes used as heatsinks.

For programming, it's the same as with the 16F690, If you have a PICKit 2/3 it will recognize and program it, as long as you have the pins connected.

MCLR, Vdd, Vss, PGC, and PGD,

PGC and PGD are often RB6 and RB7, but double check the datasheet on the device, as those pins will sometimes change on newer model PIC's. It's in the datasheet under programming.
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
Attached is an incomplete piece of code!

What I'm trying to write is a program that starts taking samples when a push button is pressed. And as soon as 2000 samples have been recorded, ADC must be turned off and wait for next press.

I tried writing something as it can be seen attached...
My first problem is that I'm not too sure how to implement the 'as soon as 2000 samples have been recorded, stop ADC part'...I would like help (ideas or pseudo code) on this one so i can code it properly...

Also, I would like you guyz to check my 'config fuses' as i just borrowed it from MMclaren...lol:D and I think there should be slight changes as this is another application...I marked 'XXX' where i believe change should be made...

I set ADC interrupt as 'high priority' as I think this is the main interrupt...hope I did it well and its interrupt just reads the result into a register curr_sampl:curr_sampm so that all the filter functions can process it...

Another problem I have is HOW to set the sampling rate (4Khz= 250us)...
my understanding is that Tad (time to convert one bit) is different than the sampling time! And given that a sample should be taken each 250us, this means that there's no more need for acquisition time as 250us is more than enough for that thing to charge up for next conversion that why I set ADC to have a manual acquisition time...

I also think that high and low priority interrupts can share the same 'restore context and return', am i correct?

Notice that Macros and subroutines section still need to be filled in but that for later...I need to have the structure of the program for now!

Thanks for all your comments
 

Attachments

joeyd999

Joined Jun 6, 2011
5,234
I'll post more later, but 3 things pop out at me:

1. In your A/D interrupt (high priority), use the fast return stack (RETFIE FAST). This way, you can save some cycles using the hardware context save. See the datasheet.

2. Do you need exactly 4Mhz? Look at the datasheet regarding the "special event trigger".

3. Consider that it may be simpler to continuously run the A/D than switch it on/off for every button press. There is no real cost associated with this unless you need absolutely all of your instruction cycles available to you during the time when you aren't converting. Use a counter initialized at the number of conversions required when the button is pressed, and when it reaches 0, do something with the acquired data.
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
1. In your A/D interrupt (high priority), use the fast return stack (RETFIE FAST). This way, you can save some cycles using the hardware context save. See the datasheet.
Ok! That ‘retfie fast’ in this case will only be for the high priority interrupt and the low priority can just use the normal ‘restore context routine’ right? Also is there a comma between retfie and fast?

2. Do you need exactly 4Mhz? Look at the datasheet regarding the "special event trigger".
Heuh… I think you are talking about my sampling rate here, if so then its rather 4Khz (not 4Mhz) as the highest frequency of interrest is 2000Hz so sampling rate should be double…so yeah I need a 4khz exact for sampling rate.

I will read on “special even trigger” thanks

3. Consider that it may be simpler to continuously run the A/D than switch it on/off for every button press. There is no real cost associated with this unless you need absolutely all of your instruction cycles available to you during the time when you aren't converting.
Yeah! You got it…actually all the filter function (there are 7 in total…check main program in attachement) need to be done executing before next (new) sample comes!!! That means the time taken by all 7 filter function MUST be less than 250us…that why I set my clock at 64Mhz! so no need to stored the sample…the ADC temporarily store each sample so each function can process the sample from there instead of ADRESH:ADRESL

Use a counter initialized at the number of conversions required when the button is pressed, and when it reaches 0, do something with the acquired data.
Actually im interrested at 2000 samples because with a sample rate of 4khz, 2000 samples correspond to 0.5second and remember a mic is connected to the ADC line (RB0) and as automatic voice detection is a bit complicated for now (will implement it on the improvement of the system), that is why I have included a pushbutton so I can say a word as soon as a press the button…

So when the initialized number (which is 2000) reaches o, the ADC should stop taking samples!!!

Hope I explained every thing I intend to do…
 
Last edited:

joeyd999

Joined Jun 6, 2011
5,234
4MHz, 4Khz...what's the difference??? :D

Here is another thing I do for each project re config bit:

At the tail end of each Microchip supplied .inc file, i.e. "p18f26k20.inc", there is an exhaustive list of the config bits for that particular part.

I copy that section into a file called "config.inc", and modify it so that I can easily set or clear bits just by commenting out lines. In addition, i can include conditional assembly directives so that bits are set differently depending on the build I am doing. For instance, if i am using the ICD, I #define ICD, and that automatically turns code protection off and 'debug' mode on. A normal build reverses this.

I've attached a sample file that I created for a project using a PIC18F24K22. Take a look.
 

Attachments

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
I made a few changes in the prevoius attachement:

- 'retfie, fast' for high priority interrupt
- Timer0 configuration + pushbutton processing
- Timer1 configuration
- CCP2 module configuration for special event trigger BUT I'm not too sure how to assign Timer1 to CCP2 module, I know it is done with T3CON (T3CCP<2:1> bits) but..can i get help on this one?

- CCP2 pin assignment in the 'config fuse' section

Any other configuration missing for special event trigger?

- I switch on timer1 with the push button press (getkey routine) and switch it off when 2000 conversion have been made (done in high priority isr)...i used two 8-bit register to count 2000 ADC interrupts...if this not how it is supposed to be done...please provide a better way!

Is everything well configured?? I mean all the bits for those registers for my application?

- As for the 'config fuses' I'm still a bit confused with the ones I marked with an '--XX' for instance why configuring WDTPS while WDTEN is off?, 'BORV and BOREN' I don't know how to set them up as I don't understand their role plus datasheet has different values from the ones posted earlier...as for FOSC, I think i should go for 'INTI067' but not sure

All these register configurations giving me a headache now...:(

Thanks for your comments!
 

Attachments

joeyd999

Joined Jun 6, 2011
5,234
I made a few changes in the prevoius attachement:
- 'retfie, fast' for high priority interrupt
Eric, may I suggest in the future that you try to assemble your code prior to posting. You will then see things like:

RETFIE,FAST

will give you an error and

RETFIE FAST

will not.

Also, read the datasheet! It is your best friend. For instance:

5.1.3 FAST REGISTER STACK
A fast register stack is provided for the Status, WREG
and BSR registers, to provide a “fast return” option for
interrupts. The stack for each register is only one level
deep and is neither readable nor writable. It is loaded
with the current value of the corresponding register
when the processor vectors for an interrupt. All interrupt
sources will push values into the stack registers.
The values in the registers are then loaded back into
their associated registers if the RETFIE, FAST
instruction is used to return from the interrupt.
Therefore, you do not need to save Status, WREG, or BSR at the start of your high priority interrupt! This will save you some instruction cycles.

Also, I am going to assume you only need one hi-p interrupt (the A/D), so get rid of the interrupt flag checking and the GOTO ADC_int code. Just run the A/D int processing immediately. As long as you have the ADIP bit set, *and no other*, flag checking does nothing for you except waste instruction cycles. (But you still need to clear the flag!)

- Timer0 configuration + pushbutton processing
Please go back to the 'fourled' code and consider using my example of using a system clock driven by TMR0. There is no need to do pushbutton processing inside an interrupt. Your main loop can do this easily by checking the timer change flag that indicates about 16ms. One instruction, done.

- Timer1 configuration
- CCP2 module configuration for special event trigger BUT I'm not too sure how to assign Timer1 to CCP2 module, I know it is done with T3CON (T3CCP<2:1> bits) but..can i get help on this one?
Like I said in an earlier post, use macros and operators to make your code easier to read and maintain, for example:

Rich (BB code):
	movlf	CCPR2L,low 4000
	movlf	CCPR2H,high 4000
You can even make a 'double literal' macro, and simplify this even further:

Rich (BB code):
movdf	macro	reglo,literal

	movlw	low literal
	movwf	reglo
	movlw	high literal
	movwf	reglo+1

	endm

...

	movdf	CCPR2L,4000
I've attached a file of a set of standard macros I use for each 18F project.

T3CON is self-explanatory. Take a guess, and see if it works.

- CCP2 pin assignment in the 'config fuse' section

Any other configuration missing for special event trigger?

- I switch on timer1 with the push button press (getkey routine) and switch it off when 2000 conversion have been made (done in high priority isr)...i used two 8-bit register to count 2000 ADC interrupts...if this not how it is supposed to be done...please provide a better way!
Again, process the pushbutton in the main loop, not in the interrupt.

May I suggest you use 2048 conversions instead of 2000? This way, you only need to test bit 4 of the high byte of the counter (counting up from zero) to determine when you're done. Two benefits: saves instruction cycles and, later, when your trying to compute an FFT (which I know you will!), an FFT generally works with datasets of 2^n samples.

Unless you have power issues, don't bother starting/stopping timer 1. Let it free run, and let the A/D run continuously. Use a flag triggered by the push button in the main loop to activate the DSP code in the A/D interrupt, and let the A/D interrupt clear the flag when the number of required samples are complete. This is *much* easier and less prone to problems in the long run.

Also, you must *guarantee* that your signal processing completes its task for each sample prior to the next sample. I strongly recommend doing your DSP *within* the A/D interrupt code. Generally, this is a *bad* idea, but in this case it will be necessary! Just make sure you have enough "daylight", as ErnieM calls it, to process your main loop often enough.

Is everything well configured?? I mean all the bits for those registers for my application?
Answer: does it work??? If so, then yes.

- As for the 'config fuses' I'm still a bit confused with the ones I marked with an '--XX' for instance why configuring WDTPS while WDTEN is off?, 'BORV and BOREN' I don't know how to set them up as I don't understand their role plus datasheet has different values from the ones posted earlier...as for FOSC, I think i should go for 'INTI067' but not sure

All these register configurations giving me a headache now...:(
For now, don't worry about the WDT, BOR, Code Protection, etc.. Just turn them off. Theses are 'production' features, and you don't need them for firmware development.

If you are going to use the internal oscillator, remember that there will be variation in your clock, and therefore, sampling rate. This *may* cause issues wrt speech recognition if that is what your are still trying to do.

Thanks for your comments!
You're welcome! :)
 

Attachments

Last edited:
Top