Problems getting Sinewave Generator to output full bandwidth
sstandfast
Posts: 39
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