Problems getting Sinewave Generator to output full bandwidth
Hello,
In a previous post where I was asking about the availabilty of a simulator, I alluded to the fact that I would also post my "mostly working" problem program to a different thread.· Before I do that, let me provide a little background into what this code is expected to do.· As a first venture into programming the Prop in assembly, I wanted to do something relatively simple.· I took the PWM demo code and modified it to operate as a sinewave generator.· I stored·360 8-bit values for one cycle of a DC offset sinewave into a look-up table in the COG.· This gives me 1 degree resolution per entry.· The theory is that the fixed frequency PWM signal is used to reproduce the sine wave by dynamically updating how often the duty cycle changes.· Pretty straighforward stuff I would think.· By altering how many PWM pulses, or "samples" as I call them in my code, pass between retrieving the next value or "step" from the look-up table I am able to adjust the frequency of the sine wave.· So far the code works mostly as expected.· I can generate low tones from 1 Hz "pops" up to 1KHz tones.· However, attempting to go beyond 1KHz gives problems·in the form of if a tone is generated at all it sounds the exact same pitch as the 1KHz tone.· The theoretical maximum output frequency of this setup should be the PWM frequency / #steps per cycle of Sinewave or PWM Freq / 360 in this case.· Since I am using a PWM frequency of 1MHz I should have no problem completely covering the audio band but as I said, I peak at 1KHz.
There are a few other quirks about my program that I feel I should mention.· First, increasing the PWM frequency above 1MHz results in no tone being generated.· With the Prop running at 80MHz, it should have no problem generating a 1MHz PWM signal right?· Second, when doing the bounds checking for the look-up table, I am XORing the index variable with 361 and if the result is zero, then clearing the index.· In theory, the result of the bounds checking (index XOR 361) should require the "nr" switch so that the value in "index" is unchanged.· However, for some reason, my code will not work with the "nr" switch present.
· So, this tells me that index is counting as 0, 361, 1, 360, 2, 359, etc...but from the sound of the tones produced, this is not the case.· This is why a simulator would be handy, I could watch how the look-up table was being indexed and I might be able to figure this out easier.
Anyway, now that I have talked everyone's ear off, let me post my code and see if anyone can make heads or tails of it.
I will also attach the source file so that you can download and load it straight into Propeller Tool.· The only dependancy it has is the "FullDuplexSerial" object.
Please let me know if you see anything or can help me get debugging information from the generator to the serial port.
Thanks in advance,
Shawn
p.s.· I am using the Propeller Demo Board.
In a previous post where I was asking about the availabilty of a simulator, I alluded to the fact that I would also post my "mostly working" problem program to a different thread.· Before I do that, let me provide a little background into what this code is expected to do.· As a first venture into programming the Prop in assembly, I wanted to do something relatively simple.· I took the PWM demo code and modified it to operate as a sinewave generator.· I stored·360 8-bit values for one cycle of a DC offset sinewave into a look-up table in the COG.· This gives me 1 degree resolution per entry.· The theory is that the fixed frequency PWM signal is used to reproduce the sine wave by dynamically updating how often the duty cycle changes.· Pretty straighforward stuff I would think.· By altering how many PWM pulses, or "samples" as I call them in my code, pass between retrieving the next value or "step" from the look-up table I am able to adjust the frequency of the sine wave.· So far the code works mostly as expected.· I can generate low tones from 1 Hz "pops" up to 1KHz tones.· However, attempting to go beyond 1KHz gives problems·in the form of if a tone is generated at all it sounds the exact same pitch as the 1KHz tone.· The theoretical maximum output frequency of this setup should be the PWM frequency / #steps per cycle of Sinewave or PWM Freq / 360 in this case.· Since I am using a PWM frequency of 1MHz I should have no problem completely covering the audio band but as I said, I peak at 1KHz.
There are a few other quirks about my program that I feel I should mention.· First, increasing the PWM frequency above 1MHz results in no tone being generated.· With the Prop running at 80MHz, it should have no problem generating a 1MHz PWM signal right?· Second, when doing the bounds checking for the look-up table, I am XORing the index variable with 361 and if the result is zero, then clearing the index.· In theory, the result of the bounds checking (index XOR 361) should require the "nr" switch so that the value in "index" is unchanged.· However, for some reason, my code will not work with the "nr" switch present.
· So, this tells me that index is counting as 0, 361, 1, 360, 2, 359, etc...but from the sound of the tones produced, this is not the case.· This is why a simulator would be handy, I could watch how the look-up table was being indexed and I might be able to figure this out easier.Anyway, now that I have talked everyone's ear off, let me post my code and see if anyone can make heads or tails of it.
CON _clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
pwm_freq = 1_000_000
obj
serial : "fullduplexserial"
var
long paramater
long cog
pub main | ticks, temp
serial.start(31,30,0,115200) 'start the serial terminal
waitcnt(cnt + clkfreq) 'pause 1 second
serial.str(string("Please Enter Frequency : ")) 'prompt for output frequency
repeat 'forever loop
ticks := serial.rxcheck 'get response
case ticks 'parse response
"S","s" : stop 'stop the tone
serial.str(string("Cog Stopped"))
serial.tx($0D)
serial.str(string("Press 'G' to restart or enter a new frequency."))
serial.tx($0D)
"G","g" : if start 'start the tone
serial.str(string("Cog Started Successfully"))
serial.tx($0D)
serial.str(string("Press 'S' to stop or enter a new frequency."))
serial.tx($0D)
$0D : paramater := pwm_freq / (temp * 360) 'X Hz Tone * 360 Steps / Cycle = X Steps/Sec
temp := 0 'PWM Freq samples/Sec * 1Sec / X Steps = Y Samples/Step
serial.str(string("Frequency accepted.")) 'update new tone frequency
serial.tx($0D)
serial.str(string("Press 'G' to start or 'S' to stop or enter a new frequency."))
serial.tx($0D)
$30..$39 : temp := (temp * 10) + (ticks - $30) 'build numeric representation of entered
pub start : success
stop
success := cog := cognew(@entry,@paramater) + 1
pub stop
cogstop(cog~ - 1)
dat
entry mov dira, diraval 'set APIN to output
mov ctra, ctraval 'establish counter A mode and APIN
mov frqa, #1 'set counter to increment 1 each cycle
mov time, cnt 'record current time
add time, period 'establish next period
and index, #$00000000 'clear look-up table index
add index, sine - 4 'initialize look-up table index
:loop
mov value, index 'get next pulse width
rdlong freq, par 'get up to date sample count
cmpsub count, freq wz 'test for # samples. if #samples = samples/step count then
if_z add index, #1 'incriment step count
xor index, #$00000169 wz 'check for 360 steps ***DOES NOT WORK WITH NR FLAG EVEN THOUGH IT NEEDS TO BE SET
if_nz xor index, #$00000169 'if 360 steps, wrap back to zero
waitcnt time, period 'wait until next period
add count, #1 'incriment sample counter
neg phsa, value 'back up phsa so that it
jmp #:loop 'loop for next cycle
diraval long |< 10 'APIN=10
ctraval long %00100 << 26 + 10 'NCO/PWM APIN=10
period long 80_000_000 / pwm_freq 'PWM Period = (_clkfreq / PWM Freq)
time res 1
value res 1
count res 1
index res 1 'index into Sine Look-Up Table
freq res 1 'Sets frequency of output'd sine wave
sine
long 127, 129, 131, 134, 136, 138, 140, 143, 145, 147, 149, 151, 154, 156, 158, 160, 162, 164, 167
long 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 200, 202, 204
long 206, 208, 209, 211, 213, 214, 216, 218, 219, 221, 222, 224, 225, 226, 228, 229, 231, 232, 233
long 234, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 246, 247, 248, 249, 249, 250, 251
long 251, 252, 252, 253, 253, 253, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
long 255, 254, 254, 254, 253, 253, 253, 252, 252, 251, 251, 250, 249, 249, 248, 247, 246, 246, 245
long 244, 243, 242, 241, 240, 239, 238, 237, 236, 234, 233, 232, 231, 229, 228, 226, 225, 224, 222
long 221, 219, 218, 216, 214, 213, 211, 209, 208, 206, 204, 202, 200, 199, 197, 195, 193, 191, 189
long 187, 185, 183, 181, 179, 177, 175, 173, 171, 169, 167, 164, 162, 160, 158, 156, 154, 151, 149
long 147, 145, 143, 140, 138, 136, 134, 131, 129, 127, 125, 123, 120, 118, 116, 114, 111, 109, 107
long 105, 103, 100, 098, 096, 094, 092, 090, 087, 085, 083, 081, 079, 077, 075, 073, 071, 069, 067
long 065, 063, 061, 059, 057, 055, 054, 052, 050, 048, 046, 045, 043, 041, 040, 038, 036, 035, 033
long 032, 030, 029, 028, 026, 025, 023, 022, 021, 020, 018, 017, 016, 015, 014, 013, 012, 011, 010
long 009, 008, 008, 007, 006, 005, 005, 004, 003, 003, 002, 002, 001, 001, 001, 000, 000, 000, 000
long 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 001, 001, 001, 002, 002, 003
long 003, 004, 005, 005, 006, 007, 008, 008, 009, 010, 011, 012, 013, 014, 015, 016, 017, 018, 020
long 021, 022, 023, 025, 026, 028, 029, 030, 032, 033, 035, 036, 038, 040, 041, 043, 045, 046, 048
long 050, 052, 054, 055, 057, 059, 061, 063, 065, 067, 069, 071, 073, 075, 077, 079, 081, 083, 085
long 087, 090, 092, 094, 096, 098, 100, 103, 105, 107, 109, 111, 114, 116, 118, 120, 123, 125, 127
I will also attach the source file so that you can download and load it straight into Propeller Tool.· The only dependancy it has is the "FullDuplexSerial" object.
Please let me know if you see anything or can help me get debugging information from the generator to the serial port.
Thanks in advance,
Shawn
p.s.· I am using the Propeller Demo Board.

Comments
The RES directive does not reserve any real space it will only declare a named symbol at that address. Use LONG instead or put your RES directives at the end of your table.
BTW: Why you did not use the internal SINE table ?
Since this was·supposed to be more of a learning experiment and not a "useful" project , I wanted to break it down one small step at a time.· My initial read through the Prop. manual gave me the impression that there were some complex manipulations required to retrieve the values from the internal table.··It·seemed much more·straightforward·to just include·my own table.· Plus, it gave me an opportunity to play with different addressing schemes in Prop. assembly.· Really, doing it this way offered no·benefit other than it simplified the coding, this was after all my first attempt at writing assembly for the Prop.
I'll come back later with my findings after I get an oppertunity to try your suggestions.
Shawn