View Full Version : concatenating 4 bytes into one pseudo 32 bit counter?
HankMcSpank
- 26th August 2011, 11:05
So having read up about DDS to get sufficient frequency accuracy, it seems most people typically use a 24 bit or 32 bit accumuator.
Every interrupt a fixed number/value is added to the number held in the accumulator - the accumulator is basically a 32 bit counter.
Can this be kludged in picbasic - concatenate 4 bytes into one 'pretend' 32 bit counter - then add a fixed value to it everytime an interrupt happens?
Here's what one guy does in his assembly interrupt routine for a 16f628 PIC (original source - http://www.g4jnt.com/PIC_DDS.zip )...
ints ;Using divide by prescalar, Interupt service runs at at Fosc / 200
; when set to count 25 before overflowing, ie every 50 clocks = sampling rate
bcf INTCON , T0IF ;reset the interrupt flag
bsf TESTSTRB ;Spare instruction needed to make up 8 clocks total, up to the point of reloading TMR0
;So use it to pulse a test point
movlw d'235' ; Load in value to count up to 256 FROM (= NNN)
movwf TMR0 ;Timer restarts counting 2 cycles after here.
;Up to this point takes 8 clocks from Interrupt trigger
;So NNN = 256 - [Wanted count] + 8 / Prescale
; ie = 256 - 25 + 8 / 2 = 235
;Below here is not time-critical, but must be less than than about 42 clocks total
; so the whole interrupt takes < 50 clocks and doesn't overrun.
movf D3, W ;First output the D/A value from the LAST pass though so there is
call SineTable ; no jittering due to variable timing of the addition routine
movwf PORTB
movf F0, W ;Now add the frequency data stored in F3/2/1/0 to D3/2/1/0
addwf D0
btfss STATUS, C ;Add lowest order byte and check for and carry at each stage
goto Add1Done ; that could ripple right through to MS byte
incf D1
btfsc STATUS, Z
incf D2
btfsc STATUS, Z
incf D3
Add1Done
movf F1, W ;Add the next byte
addwf D1
btfss STATUS, C
goto Add2Done
incf D2
btfsc STATUS, Z
incf D3
Add2Done
movf F2, W ;... and the next
addwf D2
btfsc STATUS, C
incf D3
movf F3, W ;MS Byte now added in. If it has overflowed, its is exactly what we want
addwf D3 ;D0-3 are now updated ready for next pass through, with D3 only sent to D/A converter.
OutInt
bcf TESTSTRB ;Reset the Test Point
retfie ;This interrupt takes a max of 35 clocks for worst case addition, so does leave
; a bit of scope for extra tasks or faster sampling.
....it doesn't mean a lot to me, but the gist of it seems to be add the fixed number to the lowest byte, then when that byte fills up, add a 'carry bit' to the second byte of the four ....rinse repeat until the the whole 32 bits are full, then start again from zero.
If it can be donekludged in Picbasic, would anyone be gracious enough to get me started?!!
If it can't be done, I guess I could try and work out what bits of his code to put in PICBasic ASM interrupt plus other supporting code! (that'll be me gone for a few months!)
HankMcSpank
- 26th August 2011, 12:09
Straying off a little here, on my journey towards having accurate sine waves under precise frequency control, here's the 16f1828's 5 bit DAC pin outputting a sawtooth - won't win any fidelity awards, but very simple to do at least!..
http://img825.imageshack.us/img825/259/sawf.jpg (http://imageshack.us/photo/my-images/825/sawf.jpg/)
pedja089
- 26th August 2011, 12:23
Why not use LONG variable? PBP will do everything for you.
Before LONG I use something like this
A var byte
B var byte
C var byte
etc
main:
GOTO main
Init: ' start value of each byte
A=0
B=0
etc...
RETURN
Inc:
A=A+1
IF A=0 THEN
B=B+1
IF B=0 THEN
C=C+1
IF C=0 THEN
D=D+1
ENDIF
ENDIF
ENDIFA is lowest byte, D is highest.
A,B,C,D is 0 only on overflow, so if lowest byte overflow(IT go from 255 to 0) you need to add 1 to second byte.
For example if you want to calculate actual value of counter you need to do it like this:
32bitResult=D*2^24+C*2^16+B*2^8+A
Hope that I understand what exactly you need.
EDIT:
Sorry for long, you use 16F. I didn't use 16F since LONG variable is added to PBP...
HankMcSpank
- 26th August 2011, 13:06
Excellent, it was this bit that is all I needed...
Inc:
A=A+1
IF A=0 THEN
B=B+1
IF B=0 THEN
C=C+1
IF C=0 THEN
D=D+1
ENDIF
ENDIF
ENDIF
obvious when you see someone else type it!
I then just use the value of the final variable (D) as lookup towards extracting a value in a preloaded 256 byte array - great stuff, I'll have a dabble with this later - many thanks!
pedja089
- 26th August 2011, 13:17
I'm glad that I helped.
HenrikOlsson
- 26th August 2011, 13:23
Hi,
I'm afraid it's not quite THAT easy :-(
Yes, it works OK as long as the value you add to the accumulator is 1 but what about when you want to add 2? Well, then A goes 0, 2, 4, 6....252, 254, 1 which means that B won't increment.
And what about adding a value larger than 255?
Of course it can be done in PBP but it involves a little bit more.
/Henrik.
pedja089
- 26th August 2011, 14:02
I use similar code for numbers greater then 1.
Something like this:
A VAR WORD
B,C,D,Add VAR BYTE
INC:
A=A+Add
IF A>255 THEN
A=A-255
B=B+1
IF B=0 THEN
C=C+1
IF C=0 THEN
D=D+1
ENDIF
ENDIF
ENDIF
HenrikOlsson
- 26th August 2011, 23:28
Hi,
Here's a piece of code working with a 32bit accumulator, the lookuptable from the code Hank linked to and the resulting waveform plotted in Excel with addvalues of:
1) 512500
2) 128500
3) 22825
LSW VAR WORD ' Least significat word of accumulator
MSW VAR WORD ' Most significatn word of accumulator
ADDL VAR WORD ' Least significant work of value to add
ADDH VAR WORD ' Most significant word of value to add
Out VAR BYTE ' This is the actual output from the lookup table
Temp VAR WORD
OverFlow VAR BIT ' Gets set when 32bit accumulator overflows.
i VAR WORD
Init:
LSW = 0
MSW = 0
AddL = 500
AddH = 2000 ' 50*256+500=768500
Pause 3000
Main:
For i = 1 to 1000
OverFlow = 0
TMR1H = 0
TMR1L = 0
Gosub Add
Gosub GetValue
HSEROUT["Count: ", DEC4 i, " MSW: ", DEC5 MSW, " LSW: ", DEC5 LSW, " Overflow: ", BIN Overflow, " Out: ", DEC Out, " Ticks: ", DEC5 TMR1H*256+TMR1L, 13]
Pause 5
NEXT
END
Add:
T1CON = 1 ' This is just used to measure the execution time
Temp = LSW ' Remember least significant word
LSW = LSW + ADDL ' Add low word
If LSW < Temp Then ' Did we wrap around/overflow?
MSW = MSW + 1 ' Increment high word
If MSW = 0 Then OverFlow = 1 ' Did we overflow high word?
ENDIF
Temp = MSW ' Remember high word
MSW = MSW + ADDH ' Add high word
If MSW < Temp Then ' Did we wrap around/overflow?
OverFlow = 1 ' Set flag
ENDIF
T1CON = 0
RETURN
GetValue:
Lookup MSW.HighBYTE, [$80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A 5,$A8,$AB,$AE,$B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,_
$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8,$DA,$DC,$DE,$E0,$E 2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5,$F6, $F7,$F8,$F9,$FA,$FB,$FC,$FC, _
$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$F F,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7,$F6, $F5,$F3,$F2,$F0,$EF,$ED,$EC, _
$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC,$DA,$D8,$D5,$D3,$D 1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3,$B0, $AE,$AB,$A8,$A5,$A2,$9F,$9C, _
$98,$95,$92,$8F,$8C,$89,$86,$83,$7F,$7C,$79,$76,$7 3,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51,$4F, $4C,$49,$46,$43,$40,$3E,$3B, _
$38,$36,$33,$31,$2E,$2C,$2A,$27,$25,$23,$21,$1F,$1 D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A,$09, $08,$07,$06,$05,$04,$03,$03, _
$02,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0 0,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08,$09, $0A,$0C,$0D,$0F,$10,$12,$13, _
$15,$17,$19,$1B,$1D,$1F,$21,$23,$25,$27,$2A,$2C,$2 E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C,$4F, $51,$54,$57,$5A,$5D,$60,$63, _
$67,$6A,$6D,$70,$73,$76,$79,$7C],Out
RETURN
The add routine works with two words instead of four bytes and takes ~86 cycles normally but 121 cycles when it overflows. Not the most effecient code for sure but it does seem to work. If you're running at 32Mhz and interrupting at 20kHz that's still 400 cycles between interrupts.
5918
/Henrik.
HankMcSpank
- 27th August 2011, 00:40
Wow Henrik...what can I say (thank heavens for folks like you & others on here)
I won't pretend to understand it all (once numbers start going above 256 values "woah, there goes scary stuff!").
re this bit...
Init:
LSW = 0
MSW = 0
AddL = 500
AddH = 2000 ' 50*256+500=768500
Not understanding the bolded bits - can you please let me know what you're doing there?
I'm trying to see how/where I would enter what in DDS terms is called the tuning word (essentially the number that gets added to the accumulator each interrupt?
Also, I was hoping (eventually) that the tuning word would arrive serially (either being calculated manually or sent from another pic)....the maths are going to be a bit troubling...to glean the tuning word to set the required output frequency, it's
required frequency/interrupt rate * accumulator size
so for a wanted frequency of 4971Hz involving a 32 bit accumulator & and say an interupt rate of 20,000Hz
(4971/20000) * 4294967296
...that's gonna be a challenge in an 8 bit PIC?
HenrikOlsson
- 27th August 2011, 06:01
Hi,
You have the 32bit accumulator built up by the two WORDS, LSW and MSW. Then you have the 32bit value which gets added to the accumulator each iteration (each interrupt in your case) and that's the ADDL and ADDH words. Perhaps it's the invalid comment that's causing confusion....?
Lets take a 16bit WORD as an example.
myValue VAR WORD
myValue = 12345
Now, the WORD, which is two bytes holds the value 12345, if you'd look at the high byte of that word its value would be 48 and the value in the low byte would be 57. Why? Because 48*256+57=12345
Same thing with our accumulator and "adder value" but this time we're working with two WORDS.
ADDL = 500
ADDH = 2000
2000 * 65536 + 500 = 512500 which is the value getting added to the accumulator each time. In your example, 4971/20000*2^32 the, value to add the accumulator each time is 1067514121 or ADDH=16288, ADDL=64753. Or, which might be easier expressed in hex: 3FA0F909, see there's your two words, ADDH=$3FA0, ADDL=$F909
Let me know if you try it on some real hardware.
/Henrik.
HankMcSpank
- 27th August 2011, 11:26
Thanks Henrik....excellent stuff.
So, using your code where I have say a 20khz interrupt rate in place, would it be the extract below that goes into the actual 'accumulator addition & lookup' interrupt subroutine?
Add: ' start of interrupt interrupt routine?
Temp = LSW ' Remember least significant word
LSW = LSW + ADDL ' Add low word
If LSW < Temp Then ' Did we wrap around/overflow?
MSW = MSW + 1 ' Increment high word
If MSW = 0 Then OverFlow = 1 ' Did we overflow high word?
ENDIF
Temp = MSW ' Remember high word
MSW = MSW + ADDH ' Add high word
If MSW < Temp Then ' Did we wrap around/overflow?
OverFlow = 1 ' Set flag
ENDIF
'
'
Lookup MSW.HighBYTE, [$80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A 5,$A8,$AB,$AE,$B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,_
$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8,$DA,$DC,$DE,$E0,$E 2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5,$F6, $F7,$F8,$F9,$FA,$FB,$FC,$FC, _
$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$F F,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7,$F6, $F5,$F3,$F2,$F0,$EF,$ED,$EC, _
$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC,$DA,$D8,$D5,$D3,$D 1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3,$B0, $AE,$AB,$A8,$A5,$A2,$9F,$9C, _
$98,$95,$92,$8F,$8C,$89,$86,$83,$7F,$7C,$79,$76,$7 3,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51,$4F, $4C,$49,$46,$43,$40,$3E,$3B, _
$38,$36,$33,$31,$2E,$2C,$2A,$27,$25,$23,$21,$1F,$1 D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A,$09, $08,$07,$06,$05,$04,$03,$03, _
$02,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0 0,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08,$09, $0A,$0C,$0D,$0F,$10,$12,$13, _
$15,$17,$19,$1B,$1D,$1F,$21,$23,$25,$27,$2A,$2C,$2 E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C,$4F, $51,$54,$57,$5A,$5D,$60,$63, _
$67,$6A,$6D,$70,$73,$76,$79,$7C],Out
@ INT_RETURN
HankMcSpank
- 27th August 2011, 11:44
Henrik, rather than have two threads running, if you don't mind I'll cut/paste your input over to the other thread http://www.picbasic.co.uk/forum/showthread.php?t=14213&p=106921#post106921 , which perhaps has a more appropriate title for the content we are now discussing (allowing others to find it easier)
Powered by vBulletin® Version 4.1.7 Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.