From PIC16F690 to PIC18F26K20 + The big function!

joeyd999

Joined Jun 6, 2011
5,283
OK! I can do that...but what would be the problem or the difficulty by storing those coefficients as they are and having only one mult_accum routine?

Do you mean the output will have to be sign tested first then a flag will indicate wheter it positve or negative and if negative then that value will be converted into its 2s complement, right? Lemme know if I got that right please...
You must remember that the PIC18 multiplier only works with unsigned bytes. Normally you would do the following:

1) Test 1st operand. If it is negative, 2s complement it *and remember it was negative*.

2) Test 2nd operand. 2s complement if necessary, and remember

3) Multiply

4) if either oper1 or oper2 was negative (but not both), 2s complement the result.

So you have at most 3 2s complement operations to perform with each multiplication! This consumes an extra 2+n instruction cycles per multiplication, where n is the number of bytes of the operand (or result).

I am suggesting that you keep your numbers positive, eliminating the need for the extra 2s complements, and use flags to keep track of +/-. You will still need to test signs and figure the sign of the result, but you will save time overall.

You can still have one mulacc routine. Just have it check the flags and automatically decide to add or subtract the unsigned values. Remember that the output of mulacc may be a signed negative number! You will still need to complement this and set a flag for future multiplies (but keep the accumulator signed, as you'll need it for future add/subs.

BTW, this is one good example of where asm excels over C. You can fine-tune your math for the fastest speed possible *for a particular application*. You don't have that option when writing something like result+=a*b in C.
 

joeyd999

Joined Jun 6, 2011
5,283
What do you think of the T3CON configuration? (Timer1 assignment to CCP2 module)
Is my code correct? If not please show me how to do it!

Sometimes doing simple stuff gets more complicated than doing complicated stuff...
Use the simulator w/stopwatch. You'll know if you did it right or not.
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
I would like to store all those 7 arrays of coefficients in RAM using the 'move double literal to file register (BSR)' macro...

So I guess I should do that during initialisation, right? Coz I was thinking of creating an inc. file but guess I can't use instruction set in a inc. File right?

Hope I spoke my mind right...

Thanks!
 

joeyd999

Joined Jun 6, 2011
5,283
I would like to store all those 7 arrays of coefficients in RAM using the 'move double literal to file register (BSR)' macro...

So I guess I should do that during initialisation, right? Coz I was thinking of creating an inc. file but guess I can't use instruction set in a inc. File right?
Of course you can! The asm doesn't know file types (neither does C!). i use .inc/.asm as a matter of convenience. .inc stores definiitions, .asm stores code. No reason why it cannot be mixed and matched, except for readability/maintainability.
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
Attached is the way I'm planning to store the coefficients in a inc. file...

I'm just showing for the first filter and if I'm doing it ok...I'll carry on with the rest...I'm starting from address 0x100 of RAM

In the attachement of post #76 I changed the sign of a11, a12, a21, a22 (as in the equation there is a subtraction) so I can use only mult_accum with addition...but now as ima be using addition for +ve coefficients and subtraction for -ve one, I leave the original sign given by matlab!

Thanks for da comments!
 

Attachments

Last edited:

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
I'm trying to understand how the 'gettim' routine works and having a lil hard time...
It should be easier, looking at da code, but not yet...

How often the 'gettim' routine in main loop gets executed?
Why timer0 gets disable when reading time?

But I understand well what timer0 does...just increment a regiter and save the bits that have changed after incrementing the timer register and it makes sense!

But also shouldn't _timer register get cleared at some point?

Thx
 
Last edited:

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
LOL!:D

After playing with a pen and a piece of paper by actually doing some binary arithmetic...I've understood very well how 'gettim' works...I mean tc1ms - tc131ms! This is brilliant!!! I'm wondering how you came up with that idea?? *thinkin#

But what happens when/ after bit 7 is set? _timer gets cleared or what?

Thx!
 
Last edited:

joeyd999

Joined Jun 6, 2011
5,283
LOL!:D

After playing with a pen and a piece of paper by actually doing some binary arithmetic...I've understood very well how 'gettim' works...I mean tc1ms - tc131ms! This is brilliant!!! I'm wondering how you came up with that idea?? *thinkin#

But what happens when/ after bit 7 is set? _timer gets cleared or what?

Thx!
No, _timer just counts continuously sequentially every 1.024ms, then rolls over from 255 to 0. I developed the concept over 20 years ago. Just seemed obvious at the time.

The reason for disabling the interrupt when reading the _timer and _tmrchg is so that things don't get corrupted if a TMR0 rollover happens to occur while reading the timer in the main loop.

Notice also that _tmrchg 'accumulates' bits until it is read out. This is so that if you happen to miss a bit change (due to a long main loop), you will still capture the bit the next pass.
 

joeyd999

Joined Jun 6, 2011
5,283
Attached is the way I'm planning to store the coefficients in a inc. file...

I'm just showing for the first filter and if I'm doing it ok...I'll carry on with the rest...I'm starting from address 0x100 of RAM

In the attachement of post #76 I changed the sign of a11, a12, a21, a22 (as in the equation there is a subtraction) so I can use only mult_accum with addition...but now as ima be using addition for +ve coefficients and subtraction for -ve one, I leave the original sign given by matlab!

Thanks for da comments!
No, I would not do it this way. If you really want to copy the constants from ROM to RAM, I would do it in a loop using TBLPTR to point to a table in program memory and an FSR to point to RAM. Do a tblrd *+ for each byte followed by movff tablat,postinc0 (or 1 or 2).

Tomorrow, I will post some code for you that will do this, including automatically setting external sign flags that can be used in your mulacc routine.
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
No, _timer just counts continuously sequentially every 1.024ms, then rolls over from 255 to 0.
Ok but what happens after _timer register has value 256?
Coz for instance if you have to check something every 33ms, how does it work coz when 33ms has elasped there should be some reset system somewhere for next 33ms and what if you have to check different timing...?

The reason for disabling the interrupt when reading the _timer and _tmrchg is so that things don't get corrupted if a TMR0 rollover happens to occur while reading the timer in the main loop.

Notice also that _tmrchg 'accumulates' bits until it is read out. This is so that if you happen to miss a bit change (due to a long main loop), you will still capture the bit the next pass.
I thought as much and I understand that but will still think it...
So How often the 'gettim' routine in main loop gets executed? Every 1.024ms or what? I'm asking coz I wana know how often _tmrchg gets cleared so make sure if my arithmetic was correct...
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
No, I would not do it this way. If you really want to copy the constants from ROM to RAM, I would do it in a loop using TBLPTR to point to a table in program memory and an FSR to point to RAM. Do a tblrd *+ for each byte followed by movff tablat,postinc0 (or 1 or 2).

Tomorrow, I will post some code for you that will do this, including automatically setting external sign flags that can be used in your mulacc routine.
Ok thanks! I tried using tblrd*+ in a post in this thread but had a problem separating lower and higher byte of the coefficient from program space!

And as for the array for the samples, I think the size of it should be eight 2-byte coz the sample coming from ADRESH:ADRESL does not need to be saved but will be processed straight from it and when updating the samples array it will be copied to the address for 'x(n-1) sample and the same applies for the output sample y(n)...

But also we have to remember that the outputs of the 1st second order filter are inputs of the 2nd second order filter as 1st second order + 2nd second order= 4th order.

I ll try posting some code I combled for mult_accumadd...
 

joeyd999

Joined Jun 6, 2011
5,283
Ok but what happens after _timer register has value 256?
Coz for instance if you have to check something every 33ms, how does it work coz when 33ms has elasped there should be some reset system somewhere for next 33ms and what if you have to check different timing...?
256??? Eric, sorry, but I am not going to answer this question. I will leave it to you to figure out. Hint: use the simulator and see how 8 bit numbers behave.


I thought as much and I understand that but will still think it...
So How often the 'gettim' routine in main loop gets executed? Every 1.024ms or what? I'm asking coz I wana know how often _tmrchg gets cleared so make sure if my arithmetic was correct...
gettim gets executed once each iteration of the main loop. It doesn't matter how long/short the main loop is, as long as the the *average* loop time is less then the highest resolution timer bit you are actually going to use in the main loop *or* you don't mind dropping a timer hit here and there.
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
256??? Eric, sorry, but I am not going to answer this question. I will leave it to you to figure out. Hint: use the simulator and see how 8 bit numbers behave.
Oopss!! LOL...Sorry for my dumb question then...guess I've not understood the concept totaly...I do all my posting using my iPhone...so it very hard to work properly

gettim gets executed once each iteration of the main loop. It doesn't matter how long/short the main loop is, as long as the the *average* loop time is less then the highest resolution timer bit you are actually going to use in the main loop *or* you don't mind dropping a timer hit here and there.[/QUOTE]

OK! Got to keep reading until I understand it 100% or else I ll process my key in the interrupt or else I ll a 'copy n paste' for now and will try understanding it again later!

Thanks!
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
256??? Eric, sorry, but I am not going to answer this question. I will leave it to you to figure out. Hint: use the simulator and see how 8 bit numbers behave.
OMG! I can't believe I said that earlier...an 8-bit register can never contain value of 256!!! When it reaches 255 and after incrementing it again it will rollover to 0....no need simulator...just common sense!!! that was a serious mistake I made....guess I'm getting tired with this thread!

gettim gets executed once each iteration of the main loop. It doesn't matter how long/short the main loop is, as long as the the *average* loop time is less then the highest resolution timer bit you are actually going to use in the main loop *or* you don't mind dropping a timer hit here and there.
Yeah...I'm starting to get the whole picture...
 

joeyd999

Joined Jun 6, 2011
5,283
Ok, I promised you some code. This bit of code will move 10 2-byte integers from program memory to ram. While doing so, it will automatically convert negative coefficients to positive ones, and set a series of flags to indicate plus or minus for each coefficient for use later in your mulacc routine.

First, here is how the coefficients are defined in ROM:

Rich (BB code):
; filter1_IIR4, frequency interval: 150Hz - 400Hz [0.075 0.2]

; 17    -25    17    -460    246    2498    -4900    2498    -469    247
 
filter1	dw	17,-25,17,-460,246,2498,-4900,2498,-469,247
Notice I am storing them as signed integers. I am going to let the CPU automatically extract the sign information during the copy from ROM to RAM. This makes data entry easier later if you have to change coefficients.

Here are some general purpose registers that the code will use:

Rich (BB code):
;Bank 0/Access RAM 0x000 - 0x05F (96 bytes available)

	cblock	0x000

	temp0				;general temporary storage
	temp1

	bitcnt				;general  purpose bit/byte counter

	endc
And here is your ram bank with 2 bytes of sign flags followed by 10 integers for storing coefficients:

Rich (BB code):
;Bank 1 0x100 - 0x1FF (256 bytes available)

	cblock	0x100

	f1flags:2			;flflags.b = 1 if coef is negative
	filcof1:10*2			;the actual coefficients
	
	endc


Each bit in f1flags will be 0 if the corresponding coefficient is positive, or 1 if negative.

And here is the code to copy rom to ram and set the sign flags:

Rich (BB code):
;load filter constants into RAM and set sign flags

	BANKSEL	f1flags				;work in bank 1

	movlf	temp0,1				;set initial bit for sign flag setting
	clrf	temp1

	clrf	f1flags,1			;clear p/m coefficient flags
	clrf	f1flags+1,1			;   (10 bits = 2 bytes)

	movlf	tblptrl,low filter1		;tablat points to constants in ROM
	movlf	tblptrh,high filter1

	lfsr	0,filcof1-1			;fsr0->byte before coefficient table for filter 1

	movlf	bitcnt,10			;10 constants to copy to ram

cpromram

	tblrd	*+				;get low byte of coefficient
	movff	tablat,preinc0			;copy to ram

	tblrd	*+				;get high byte of coefficient
	movff	tablat,preinc0			;copy to ram

	bbc	tablat,7,0,cprrnn		;no sign processing if positive

	comf	postdec0,f			;negate word (make it positive)
	negf	postinc0
	skpnc
	incf	indf0,f

	movfw	temp0				;set negative flag for this coefficient
	iorwf	f1flags,f,1
	movfw	temp1
	iorwf	f1flags+1,f,1

cprrnn	clrc					;rotate p/m flags
	rlcf	temp0,f	
	rlcf	temp1,f

	djnz	bitcnt,cpromram			;do for 10 constants
Notice I used some of my macros. You will need to include my macro.inc file for this to assemble properly. Use the simulator to help you understand how it works.

Here is the output of the simulator:

Rich (BB code):
 Address Symbol Name     Value     

  100  F1FLAGS   00000001 01001010
  102  FILCOF1                17
  104  0x104                  25
  106  0x106                  17
  108  0x108                 460
  10A  0x10A                 246
  10C  0x10C                2498
  10E  0x10E                4900
  110  0x110                2498
  112  0x112                 469
  114  0x114                 247
BTW, yes, you can just use movdf for each constant, and set the appropriate flags manually, but that wouldn't be nearly as much fun, and the final code will not be as flexible as it could be.

Enjoy.
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
Ok, I promised you some code. This bit of code will move 10 2-byte integers from program memory to ram. While doing so, it will automatically convert negative coefficients to positive ones, and set a series of flags to indicate plus or minus for each coefficient for use later in your mulacc routine.
This is just brilliant!!! And I like the 'automatic' flags setting, it makes life much easier...

First, here is how the coefficients are defined in ROM:

Rich (BB code):
; filter1_IIR4, frequency interval: 150Hz - 400Hz [0.075 0.2]
 
; 17    -25    17    -460    246    2498    -4900    2498    -469    247
 
filter1    dw    17,-25,17,-460,246,2498,-4900,2498,-469,247
Notice I am storing them as signed integers. I am going to let the CPU automatically extract the sign information during the copy from ROM to RAM. This makes data entry easier later if you have to change coefficients.
This is awesome!!! *this makes data entry easier later if you have to change coefficients* Oh yes...and I'm thinking of changing that already...lol

Here are some general purpose registers that the code will use:

Rich (BB code):
;Bank 0/Access RAM 0x000 - 0x05F (96 bytes available)
 
    cblock    0x000
 
    temp0                ;general temporary storage
    temp1
 
    bitcnt                ;general  purpose bit/byte counter
 
    endc
And here is your ram bank with 2 bytes of sign flags followed by 10 integers for storing coefficients:

Rich (BB code):
;Bank 1 0x100 - 0x1FF (256 bytes available)
 
    cblock    0x100
 
    f1flags:2            ;flflags.b = 1 if coef is negative
    filcof1:10*2            ;the actual coefficients
 
    endc


Each bit in f1flags will be 0 if the corresponding coefficient is positive, or 1 if negative.



perfect!!!

And here is the code to copy rom to ram and set the sign flags:

Rich (BB code):
;load filter constants into RAM and set sign flags
 
    BANKSEL    f1flags                ;work in bank 1
 
    movlf    temp0,1                ;set initial bit for sign flag setting
    clrf    temp1
 
    clrf    f1flags,1            ;clear p/m coefficient flags
    clrf    f1flags+1,1            ;   (10 bits = 2 bytes)
 
    movlf    tblptrl,low filter1        ;tablat points to constants in ROM
    movlf    tblptrh,high filter1
 
    lfsr    0,filcof1-1            ;fsr0->byte before coefficient table for filter 1
 
    movlf    bitcnt,10            ;10 constants to copy to ram
 
cpromram
 
    tblrd    *+                ;get low byte of coefficient
    movff    tablat,preinc0            ;copy to ram
 
    tblrd    *+                ;get high byte of coefficient
    movff    tablat,preinc0            ;copy to ram
 
    bbc    tablat,7,0,cprrnn        ;no sign processing if positive
 
    comf    postdec0,f            ;negate word (make it positive)
    negf    postinc0
    skpnc
    incf    indf0,f
 
    movfw    temp0                ;set negative flag for this coefficient
    iorwf    f1flags,f,1
    movfw    temp1
    iorwf    f1flags+1,f,1
 
cprrnn    clrc                    ;rotate p/m flags
    rlcf    temp0,f    
    rlcf    temp1,f
 
    djnz    bitcnt,cpromram            ;do for 10 constants
BEAUTIFUL!!!:D

I love this code and have a few questions though...

Remember I cobbled some code using tblrd*+ then I got confused and left that approach...
my confusion was due to this line of the datasheet: " the program memory space is 16 bits wide, while the data RAM space is 8 bits wide."

Then I was thinking if I'm using 'dw' so when I use 'tblrd*+' I thought it would increment the pointer to the next word which is absolutely not true!!! because datasheet also says "a read from program memory is executed one byte at a time" so it doesn't matter whether one uses 'dw' or 'db' with 'tblrd*+' as it will read one byte at a time into TABLAT...
I've understood that now...

by writing:

Rich (BB code):
movlf tblptrl,low filter1 ;tablat points to constants in ROM
movlf tblptrh,high filter1
I guess you making 'tblptr' pointing to the first element of filter1 (17) but in what case you would also add 'movlf tblptru, upper filter1'??

Rich (BB code):
bbc tablat,7,0,cprrnn ;no sign processing if positive
Guess here, tablat contains the high byte, 7 is the sign bit and if this bit is clear it jumps to cprrnn...
BWT, why you've named that label so?

why is it that the carry flag is checked here after an operation?
Also I don't really understand the meaning of rotate through carry, what does it really mean? and what's the difference with rotate with no carry? I know what rotate is (left or right) but that 'carry' thing confusing me a bit...

SO this piece of code will have to be store as it is in an .inc file, right?
So far I'll have two .inc file (macros and coefficient)...Well I understand this code at 75%...
I still have to understand that part where you make the negative word positive with the 'comf' and 'negf' and the code under 'cprrn'....
well I know what it is doing but still have to picture it properly...

Notice I used some of my macros. You will need to include my macro.inc file for this to assemble properly. Use the simulator to help you understand how it works.
Yes! Ok!

Here is the output of the simulator:

Rich (BB code):
 Address Symbol Name     Value     
 
  100  F1FLAGS   00000001 01001010
  102  FILCOF1                17
  104  0x104                  25
  106  0x106                  17
  108  0x108                 460
  10A  0x10A                 246
  10C  0x10C                2498
  10E  0x10E                4900
  110  0x110                2498
  112  0x112                 469
  114  0x114                 247
OMG!!!

BTW, yes, you can just use movdf for each constant, and set the appropriate flags manually, but that wouldn't be nearly as much fun, and the final code will not be as flexible as it could be.
You are just right...but I'm happy that you saying my method would work too...but yours is definately the best!

Oh Yes!! I am REALLY enjoying this...

Even if I don't acheive what I'm trying to do I'll still be Happy as I've learnt a lot with regard to PIC programming in this thread...I can now use stuffs I've leant here to do other things...:)

Thanks a million!
 

joeyd999

Joined Jun 6, 2011
5,283
...so it doesn't matter whether one uses 'dw' or 'db'...
Yes, it does matter. 'db' works at the byte level. 'dw' works at the word level.

Generally, this is not an issue, *except* when you have an odd number of bytes in a 'db' line, followed by another db line. In this case, the high byte of the last value will be set to 0, and the next db will occur at the low byte of the next word.

...but in what case you would also add 'movlf tblptru, upper filter1'??
Again, the datasheet is your friend. Notice you have no program rom above 0xffff. Therefore, tblptru will always be 0. Just make sure you clear it at the start of your program (during initialization), and never touch it again.

Rich (BB code):
bbc tablat,7,0,cprrnn ;no sign processing if positive
Guess here, tablat contains the high byte, 7 is the sign bit and if this bit is clear it jumps to cprrnn...
BWT, why you've named that label so?
cprrnn = CoPy Rom to Ram Not Negative

why is it that the carry flag is checked here after an operation?
Because I have to do something with the result, and I cannot change things in ROM. So I copy ROM to RAM first, check if it is plus or minus, then complement the RAM copy if necessary.

Also I don't really understand the meaning of rotate through carry, what does it really mean? and what's the difference with rotate with no carry?
rrcf: C -> register -> C
rlcf: C <- register <- C

rrncf: register.0 -> register -> register.7
rlncf: register.0 <- register <- register.7

rrcf/rlcf are 9 bit shifts, using carry as an intermediary bit.
rrncf/rlncf are 8 bit shifts, the carry is not affected.


SO this piece of code will have to be store as it is in an .inc file, right?
Not preferably. I reserve .inc files for definitions, not code. Any file that resolves into actual instructions should be called .asm. Again, the assembler doesn't care...but it makes things more maintainable as you know what kind of file to look for when changing things.

I still have to understand that part where you make the negative word positive with the 'comf' and 'negf' and the code under 'cprrn'....
well I know what it is doing but still have to picture it properly...
Step through it with the simulator looking at what happens with different values. Hopefully it will become apparent.
 

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
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.
Talking about 'daylight' if I understand well what you guys mean by that...I'll say given that I sample at 4Hkz --> my daylight is 250us --> all the DSP processing must take less than 250us for each sample...

I recently had a discussion on 'general electronic chat' , thread 'Capacity ID' and it was said that by limiting the frequency at 2000Hz I will have problems with the 'consonants' meaning words with same vowels but different consonants will be confused...

I also did a little google about that...yes consonants have higher frequency compared to vowels....range ~1800hz to 2500hz or more for important consonants....

So now I'm considering 3000Hz as my highest frequency --> sampling rate will now be 6Khz...in my circuit I'm going to use a LPF to limit the highest frequency to ~3000Hz....I was thinking of sampling at 8KHz but that would give me only 125us for my DSP processing....

Therefore I'll change my coeficients as well....ima consider frequencies from 150Hz to ~ 2900Hz with an interval of 400 so it will aways be 7 filters...but all will aslo depend on how much time a filter take to execute...anyway changing coefficients as much as I want is no a problem as Joeyd999 provided a solution for that!:)

Also in the equations, there's four '-ve' signs:

y1 = b11 x(n) + b12 x(n-1) + b13 x(n-2) - a11 y1(n-1) - a12 y2(n-2)
y2 = b21 x(n) + b22 x(n-1) + b23 x(n-2) - a21 y1(n-1) - a22 y2(n-2)

I will first transform the above equation to:

y1 = b11 x(n) + b12 x(n-1) + b13 x(n-2) + (-a11 y1(n-1)) + (-a12 y2(n-2))

y2 = b21 x(n) + b22 x(n-1) + b23 x(n-2) + (-a21 y1(n-1)) + (-a22 y2(n-2))

So what I'm tryna say is that I will change the sign of the last two coefficients for the two equations first before using them in the coefficient .inc file so my first post with all the 7 arrays of coefficients was correct....but there will still be negative coefficient and Joeyd999 code will still apply without problem

Thanks!
 
Last edited:

Thread Starter

Eric007

Joined Aug 5, 2011
1,158
So far I only have one pushbutton...

But now I'm thinking of either adding another pushbutton or using the same pushbutton with two functionalities...
what I mean is for instance a 'single press' would something and a 'double press' would do something else!

Why do I need two pushbutton? because I first want to pre-record a few words (3 to 5) into MCU using a pushbutton (I'm actually training my MCU here) and then I want to recognised them using another one!

So those pre-recorded words should not be volitale....now a question is raised! where should I store those pre-recorded words? RAM? ROM? or EEPROM?

Also, How would you modify your 'getkey' routine to control 2 or more pushbuttons? or how would you modify it for a sinle pushbutton with two functions like I explained abobe!

Using a single pushbutton would be so fun but it ain't my level yet:)

Regards, Eric
 
Top