Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[PIC] Using a PIC to generate square wave sweep tones (audio)

Status
Not open for further replies.

JDW_

Junior Member level 2
Joined
Jul 12, 2016
Messages
20
Helped
0
Reputation
0
Reaction score
2
Trophy points
1,283
Activity points
1,672
First some background. I began learning PIC assembly programming in the 1990s, self-taught from Microchips documents. I have various PICs, a PM3 programmer, and still use MPLAB 8.x. I do not know C. I understand the merits of a debugger but do not have one, nor have I needed one to date. So I would like to proceed with the understanding that I have no debugger. I've only written rather simplistic programs on PICs, so while I am not a novice I would be hesitant to call myself a pro.

I am trying to design a controller for a 12V siren (for an alarm system). The controller will output square waves from the PIC using 2 outputs that are 180-deg out of phase with each other (inverted), both outputs fed through an H-BRIDGE amp to an 8-ohm speaker (to get maximum volume). I do NOT need Sine waves, Sawtooth, etc. I only need to output Square waves. Frequency range will be as low as 300Hz to as high as 2650Hz. I intend to use a DIP switch with the PIC to allow selection of various tones such as these (all 50% duty):

  • 500Hz to 1200Hz sweep over 400msec. (repeating)
  • 1000Hz & 800Hz alternating, each 250msec. (repeating)
  • 500Hz to 2650Hz sweep over 150msec., then 2650Hz to 900Hz sweep over 100msec. (repeating)
  • 1050Hz to 2650Hz sweep over 150msec., then 2650Hz to 1050Hz over 150msec. (repeating)
  • 280us(HI),280us(LO) (1786Hz) for 4 cycles then 225us(HI),225us(LO) (2222Hz) for 14 cycles repeating for 280ms, then 280ms off (repeating)

If memory allows, I will ultimately create 16 different selectable tones (fixed frequency and sweeps).

I create my sweep tones in Audacity using its Generate > Chirp… feature. But I want to know how I can recreate these sweep tones on a PIC.

I first started toying with an old 16F84 but couldn't figure out how to implement sweeps, so I posted in another forum about this. It was recommended that I consider using the NCO in the PIC16F150x series, which also has CWG to generate an inverse waveform (180 degrees out of phase) of my primary NCO output, which is needed for use with my H-BRIDGE amplifier. I ordered samples of the 16F1503 and 16F1508, and right now I am trying to figure out the assembly code required.

I have been told that interrupts would likely be needed in this application, even on the 16F150x-series PICs with NCO. In fixed Duty Cycle mode, the NCO Accumulator overflow occurs every 1/2 cycle, at which time the half-cycle output will change. Accumulator overflows appear in NCO1CON-bit5 (NxOUT), which can be polled. Furthermore, PIR2-bit2 is set to HI on an overflow, even when interrupts are disabled, and this bit too could be polled.

I have also been told that a look-up table could be used. But those who mentioned "interrupts and tables" to me are versed in C-code, not assembly, and no one offered me any example assembly code to study, hence my post here.

I don't understand precisely how a look-up table, if truly required, would be used to determine NCO adjustments required to generate sweep tones. I created my sweep tones in Audacity (quite easily via its Generate > Chirp… feature), but must I use Audacity to manually measure each pulse width, from start to finish in my sweep, to know how many pulses and at what width would fit in a given sweep time window on my PIC? Surely there must be a faster and easier way, which is no doubt why I was advised to use an NCO-capable PIC to me in the first place. Perhaps if I knew the algorithm that Audacity uses to create its tonal sweeps, I could then implement the PIC version of that. For truly, my aim is simply to use a PIC to reproduce sweep tones that I have created I Audacity.

All said, if someone versed in PIC Assembly could show me some example code for the 16F150x and the NCO, that's likely all the study I would need to up and running. For example, on the PIC16F150x, when running the HFINTOSC at 16MHz, could anyone offer me example Assembly code that would use the NCO to smoothly sweep from 500Hz to 1200Hz in approximately 400msec, with the NCO output ending in a LO? To see an example of how that is accomplished in Assembly on a PIC16F150x would be very helpful and a good learning experience for me.

Thank you.
 

Try to use DDS AD9850 or similar. No difference which controller to use. Direct syntesis with PIC is impossible with your requiments.
 

Hi,

I´m not experienced with PIC, therefore some general answers only.

You know PIC is a digital microcontroller, therfore you can´t do a soft sweep like with analog systems.
There will be steps, jumps in frequency.
The more steps per time, the smaller the frequency steps, the more "soft" it will be.

The next thing is how to distribute the steps.
What does that mean?
With music (wich is somehow adjusted to the human hearing) one talks about octaves.
One octave means doubling the frequency: 440Hz, 880Hz, 1760Hz... this equals an exponential function

With an NCO solution one produce linear frequency steps. 100Hz, 150Hz, 200Hz ....
With PWM one produces linear steps in time, but this means f = 1/t steps, wich is an hyperbolic function.
(far away from the exponential function, therefore it needs the highest effort in software)

But there is no need to exactely follow the exponential function. You just have to be satisfied with the sweep.

With PWM there ma be two possible solutions (depending on microcontroller hardware)
* Either you use the waveform generation mode. But usually whan you change the frequency (timing) you have to adjust a second register to keep duty cycle at 50%
* or you use the "toggle output" mode. Then the PWM frequency just needs to be twice the expected audo frequency.
In either case I recommend to "adjust" the parameters synchrounously to the PWM frequency.

The NCO is less critical and usually it enables better frequency resolution, but with higher jitter (which is no problem for your application).

Klaus
 

I think it can be done although there will inevitably some jumps (steps) in the spectrum.

Using a PIC with PWM output would be easiest. Set the PWM ratio to 50% to produce a square wave then load the frequency value into PR2 and Timer 2. Using PWM will allow the square wave to run continuously from hardware so the PIC program only has to update the registers to change the frequency. If you are familar with the 16F84, I suggest 'upgrading' to a 16F1827 or 16F1847 which are pin compatible but are faster (up to 32MHz with internal clock), have more memory and peripherals inside them.

Brian.
 

Better make an DAC based on 8x resistors and reproduce sound from raw data stored in external flash or eeprom.
Anyway, you will redesign cheap chinease audio player))))
 

Generating frequency modulated square waves is a completely different from replaying Audacity waveforms.

Like betwixt I believe that any processor with hardware timer output can generate the intended tones with acceptable quality for an alarm siren. The "only" problem is to write suitable software.

I also believe that insisting on assembler programming will narrow the available help. But is it a big thing to translate algorithms described in C to assembler?
 

Gentlemen,

Thank you for the replies.

First, please understand I do not need anything fancy. I am not trying to reproduce a SIN wave. I only need a square wave output. Why? Because I only want MAXIMUM VOLUME via H-BRIDGE amp to an 8Ω speaker, for use in an alarm system. In other words, this isn't about GREAT SOUND. This is about generating a close approximation of buzzing tones (fixed tones and sweeping tones) to blast one's ear drums via an alarm speaker. The output amplitude will be fixed, and the pulse widths will vary (sounds like PWM, but it's not).

To better understand what I mean, please download Audacity, then click the Generate menu, then choose Chirp... Set Waveform to Square, make your Start frequency to be 500 and your end 1200, Amplitude = 1 & 1, Interpolation = Linear, and Duration = 00 00 00.400s. Then click the Transport menu and choose Loop Play. If you can't hear anything, make sure your volume is up high enough, and/or check the Audacity preferences, playback device. Now you can see and hear one of the sweep tones I am trying to reproduce. It's a square wave, as you can see. It shouldn't take a DAC or dedicated DDS chip seeing that the PIC16F150x-series has an NCO. I simply do not know the best means of implementing this SWEEP in Assembly on a PIC, hence my post here.

Another reason I don't think it is "impossible on a PIC," because when I posted in another forum, the PIC geniuses there said it would be "extremely hard to do on a PIC16F84" (which is what I was playing with at the time), but most of them agreed that doing it with a Numerically Controlled Oscillator (i.e., DDS) inside the PIC16F150x-series would be "easier" (i.e., definitely possible). But they all program in C and none of them supplied example code at all. When I first started learning PICs back in 1996, I had a huge book from Microchip that was about 3 inches thick. What helped me learn PIC Assembly wasn't examining the data sheets alone but reviewing all those glorious code examples! As such, I learn by example, and so I posted in this forum in hopes of finding someone (a) well-versed in PIC Assembly and (b) someone who has programmed the 16F150x-series NCO, such that some example code could be tossed around among us.

Here's an Assembly example of using the PIC16F1503 to produce a FIXED output of 504Hz:

Code:
	list p=16f1503 			; define processor 
	
#include	p16f1503.inc    ; processor specific variable definitions

 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _BOREN_OFF & _CLKOUTEN_OFF
 __CONFIG _CONFIG2, _WRT_OFF & _STVREN_OFF & _LVP_OFF

 errorlevel -302

	ORG		0x000           ; Reset vector.
	goto	Main                    ; Go to main program.
	ORG		0x004           ; Interrupt vector location 
                                        ; NOTE: W, STATUS, BSR, FSTs & PCLATH are saved 
                                        ; automatically in Shadow regs (Bank 31)
	retfie                          ; Return from interrupt.
	
Main
	banksel	OSCCON			; Switch to Bank 1.
	movlw	b'01111000'		; Internal OSC = 16 MHz
	movwf	OSCCON
	btfss	OSCSTAT, HFIOFR	        ; Internal OSC running?
	goto	$-1                     ; No, loop until running.
	btfss	OSCSTAT, HFIOFS	        ; OSC stable?
	goto	$-1 			; No, loop until stable.

; Initialize
	banksel	ANSELA			; Switch to Bank 3.
	clrf 	ANSELC			; PORTC = Digital I/O.
	clrf	ANSELA			; PORTA = Digital I/O.
                                        ; NOTE: 16F1503 has only A & C while 16F1508 has B too.
	banksel	OPTION_REG		; Switch to Bank 1.
	movlw	b'11000010'		; Pull-ups Off, TMR0 Prescaler = 1:8
	movwf	OPTION_REG
	movlw	b'11111101'		; RC1 output (pin 9)
	movwf 	TRISC			; PORTC setup
	movlw	b'11101111'		; RA4 output (pin 3)
	movwf	TRISA			; PORTA setup

	banksel	APFCON			; Switch to Bank 2.
	clrf 	APFCON			; NCO1 function is on RC1 (pin 9).
	
	banksel NCO1CLK			; Switch to Bank 9.
	movlw	b'01100000'		; NCO output pulse 8 clocks, clock source HFINTOSC (16 MHz).
	movwf	NCO1CLK
	clrf	NCO1ACCU		; Clear the Accumulator.
	clrf	NCO1ACCH
	clrf	NCO1ACCL
	clrf	NCO1INCH		; High Incr. register = 0 (must come before NCOINCL).
	movlw	b'10000100'		; Low Incr. register = 66d
	movwf	NCO1INCL		; Fovrflw = (NCO Clock x Incr.Value)/(2^20)=1008 /2=504Hz squarewave
	movlw	b'11010000'		; Enable NCO(b7), enable O/P, O/P low, O/P active high, fixed DC.
	movwf	NCO1CON
Loop	
	goto	Loop			; Do nothing else.
	end

Generating fixed signal output is easy with the NCO. But what I am trying to do is SWEEP.

Any further thoughts and specific code examples using the NCO would be greatly appreciated.

Thank you.
 

An almost perfect sweep can be performed by using a PIC hardware timer in pwm mode with 50 % duty cycle, reloading timer period and pulse duration after each pulse in the interrupt. The respective period ramp can be either loaded from a look-up table or calculated incrementally on the fly. Or simply toogle the pulse output in the timer interrupt. This adds quite a bit of jitter to the tone frequency but also simplifies operation.

Basic 16F84 has only a 8 bit timer, so the beep has be generated mostly in software. If the processor has no other job than generating the tones, it would be still be able to do it.
 

An almost perfect sweep can be performed by using a PIC hardware timer in pwm mode with 50 % duty cycle, reloading timer period and pulse duration after each pulse in the interrupt. The respective period ramp can be either loaded from a look-up table or calculated incrementally on the fly. Or simply toogle the pulse output in the timer interrupt. This adds quite a bit of jitter to the tone frequency but also simplifies operation.

Conceptually, I somewhat follow you, but do you have an assembly code example? I have Googled for days and can't find even a single example of PIC Assembly accomplishing that. Lots of people talk about it, but there is no code. Or if someone out there did do it, they haven't shared the code. Seems rather strange since I would think that audio sweeps wouldn't be so rare.

Also consider that the aim is to produce multiple tones and sweeps. The fixed tones are easy on any PIC, but the sweeps are what gets me. It's easy to do a sweep with no time limit, but the time window is critical and will change. See my opening post for a short sample of sweeps I would be implementing. There is a start frequency, end frequency, and then the time window within which the sweep must begin and end.

Surely there must be a way to automate the calculations of pulse widths, right? I shouldn't need to be forced to manually calculate the pulse widths and how many pulses for each and every sweep. Surely there must be a way to simplify the job, right?
 

Hi,

Why you need a code?

It´s not that difficult to write your own.
* adjust the generated frequency from time to time.

Depending on used hardware or mode you should do this either with a fixed timing or synchronized to output signal.

Example:
if you want a linear frequency sweep from 500Hz to 1200Hz.
Then decide how many steps you want. --> let´s assume 10 steps (= 11 frequencies)
Each step is (UpperFreq - LowerFreq) / steps = (1200Hz -500Hz) / 10 = 70Hz
So the frequencies are: 500-570-640-710-780-850-920-990-1060-1130-1200 (all in Hz)
Now find the corresponding setup values (for PWM or NCO ...)
Generate a table with the 11 setup values.

Then decide in what time you want to go from 500Hz to 1200Hz ---> let´s say 1s
Within 1s you have 10 steps, so it is obvious to set a new value every 1s/10 = 0.1s.

Every 0.1s take the next value from the table and set (with it) the new frequency.
If you reach the top of the table, then go back to the bottom and back to the top...

Klaus
 

I started learning PICs back in 1996. I had the 2-inch thick Embedded Control Handbook dated 1994/1995 at the time. How did I learn? By reading through all that example code. Some folks are geniuses by birth. That's not me.

You assumed 10 steps, but why?

I have been measuring each pulse in the the 500Hz to 1200Hz in 400ms sweep created in Audacity. So far, I am half way finished (at 952Hz right now). The frequency jumps started off at about 5Hz at the beginning, but now (at 952Hz) each jump in frequency is 20Hz. And the number of times the same frequency repeats increases as the frequency increases. At 500Hz the repeats were about 2 on average but now that I am at 952Hz the repeats are at 6. In other words, I would get 6 cycles at 909Hz, followed by 6 cycles at 930Hz, etc.

Why analyze the entire waveform so closely in Audacity? To see how Audacity creates its perfectly smooth sweep, and then to consider how to accomplish the same on a PIC.

That's why I said it isn't so simple as one might think, at least, to create a smooth transition from the start to end frequency, within the given time window.
 

Hi,

You assumed 10 steps, but why?
Just to have any value as an example.

I could have used 2, 5, 20, 100 or any othr value.

--> YOU need to decide how many steps you want to use. Nobody of us can tell you.

***
A simple chart with time in X and frequency in Y is all you need.
My opinion: Don´t over-analyze the Audacity signals. Choose your own solution .. depending on your hardware, software effort and meaningful results.

Klaus
 

I'm a bit confused by the CWG setup on the PIC16F150x-series. The datasheet gives a very specific sequence that should be followed, but I discovered that the C-code generated by the MPLAB Code Configurator doesn't follow that sequence!

Here is some basic Assembly code I wrote (note that it's preliminary and not complete), which follows "26.11 Configuring the CWG" in the PIC16(L)1508/9 datasheet. Refer specifically to the "CWG Setup" section inside my assembly code below.

Code:
[syntax=asm]	list 	p=16f1508 		; Define processor 
	
#include	p16f1508.inc    	; Standard variable definitions

;               +---u---+
;          Vdd <|1    20|> Vss
;          RA5 <|2    19|> RA0/ICSPDAT
;          RA4 <|3    18|> RA1/ICSPCLK
; MCLR/Vpp/RA3 <|4    17|> RA2
;    CWG1A/RC5 <|5    16|> RC0
;    CWG1B/RC4 <|6    15|> RC1
;          RC3 <|7    14|> RC2
;          RC6 <|8    13|> RB4
;          RC7 <|9    12|> RB5
;          RB7 <|10   11|> RB6
;               +-------+

; --------------------------------------
; Configuration Bits & Error Level
; --------------------------------------
 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _BOREN_OFF & _CLKOUTEN_OFF
 __CONFIG _CONFIG2, _WRT_OFF & _STVREN_OFF & _LVP_OFF

 errorlevel -302			; Suppress assembler Messages pertaining to banks, so be careful!
 
; --------------------------------------
; Core Registers (present in all Banks)
; --------------------------------------
; INDF0 & INDF1
; PCL
; STATUS
; FSR0L & FSR0H & FSR1L & FSR1H
; BSR
; WREG
; PCLATH
; INTCON 
; --------------------------------------

	ORG		0x000		; Reset vector.
	goto		Main		; Go to main program.
	ORG		0x004		; Interrupt vector location 
                 			; NOTE: W, STATUS, BSR, FSTs & PCLATH are saved 
					; automatically in Shadow regs (Bank 31)
	retfie				; Return from interrupt.
	
Main
; ---------[ Interrupts ]-------------------------------------------
	movlw	b'01000000'		; Interrupts disabled, PEIE bit6 enabled
	clrf	INTCON			; INTCON is a Core Register (in all banks)
	banksel	PIE2			; [Bank 1]
	movlw	b'00000100'		; NCO1IE = 1 (enabled) for NCO Interrupts
	movwf	PIE2
	banksel	PIR2			; [Bank 0]
	clrf	PIR2			; Peripheral Inter. Flags (NCO1IF bit2 = 1 on Accum.Overflow)
; ---------[ Oscillator Setup ]--------------------------------------
	banksel	OSCCON			; [Bank 1]
	movlw	b'01111000'		; Internal OSC = 16 MHz
	movwf	OSCCON
	btfss	OSCSTAT, HFIOFR		; Internal OSC running?
	goto	$-1             	; No, loop until running.
	btfss	OSCSTAT, HFIOFS		; OSC stable?
	goto	$-1 			; No, loop until stable.
; ---------[ Ports & I/O Setup ]------------------------------------
	banksel	PORTA			; [Bank 0] Initialize Ports.
	clrf	PORTA
	clrf	PORTB
	clrf	PORTC
	banksel	LATA			; [Bank 2] Clear data Latches.
	clrf	LATA
	clrf	LATB
	clrf	LATC
	banksel	ANSELA			; [Bank 3] Setup I/O.
	clrf	ANSELA			; PORTA = Digital I/O.
	clrf 	ANSELB			; PORTB = Digital I/O. (NOTE: missing on 16F1503)
	clrf 	ANSELC			; PORTC = Digital I/O.
	banksel	OPTION_REG		; [Bank 1]
	movlw	b'11000010'		; Pull-ups Off, TMR0 Prescaler = 1:8
	movwf	OPTION_REG
	movlw	b'11111111'		; Make CWG1A & CWG1B Inputs for now. (See CWG Setup below.)
	movwf 	TRISC			;  PORTC 
	movlw	b'00000000'		; All outputs
	movwf	TRISB			;  PORTB 
	movlw	b'11101111'		; RA4 the only Output (pin 3)
	movwf	TRISA			;  PORTA 
; ---------[ CWG Setup ]------------------------------------------
; The sequence below follows "26.11 Configuring the CWG" in the PIC16(L)F1508/9 datasheet:
; http://ww1.microchip.com/downloads/en/DeviceDoc/40001609E.pdf
; CWG1A (RC1) & CWG1B (RC0) are configured as Inputs for now.
	banksel	CWG1CON0		; [Bank 13]
	bcf	CWG1CON0, 7		; Clear GxEN (bit 7) to Disable CWG.
	movlw	.20			; Put 20d into "CWG Rising DEAD-BAND Count Register"
	movwf	CWG1DBR			;  (20 clock cycles = 1.25us @ HFINTOSC=16MHz)
	movlw	.20			; Put 20d into "CWG Falling DEAD-BAND Count Register"
	movwf	CWG1DBF
	movlw	b'10000000'		; GxASE(b7)=1, GxARSEN(b6)=0
	movwf	CWG1CON2		;  (CWG in Shutdown State, Auto-Restart Disabled)
	movlw	b'10100110'		; CWG1A & CWG1B outputs LO on Shutdown, Source = NCO1
	movwf	CWG1CON1
	movlw	b'01100001'		; Leave GxEN=0, CWG1A/B to Output pins, Normal Polarity, HFINTOSC
	movwf	CWG1CON0
	bsf	CWG1CON0, 7		; Set GxEN (bit 7) to Enable CWG.
	banksel	TRISC			; [Bank 1]
	movlw	b'11111100'		; RC0=CWG1B, RC1=CWG1B (set to Outputs)
	movwf 	TRISC			;  PORT C
	banksel	CWG1CON2		; [Bank 13]
	bcf	CWG1CON2, 7		; Clear GxASE to start the CWG.
; ---------[ NCO Setup ]------------------------------------------
	banksel	APFCON			; [Bank 2]
	clrf 	APFCON			; NCO1 function is on RC1 (NCO not using pin. See NC01CON below.)
	banksel NCO1CLK			; [Bank 9]
	clrf	NCO1CLK			; NxPWS n/a due to fixed DC, clock source HFINTOSC (16 MHz).
	clrf	NCO1ACCU		; Clear the Accumulator (UPPER->HI->LO).
	clrf	NCO1ACCH
	clrf	NCO1ACCL
	clrf	NCO1INCH		; HI Incr.Value = 0  (NCO1INCH must execute before NCOINCL)
	movlw	.66			; LO Incr.Value = 66d
	movwf	NCO1INCL		;  Foverflow = (NCO Clock x Incr.Value)/(2^21)=504Hz square-wave
	movlw	b'10010000'		; Enable NCO, Disable Out-pin, Polarity=LO(inverted), Fixed D.C.
	movwf	NCO1CON			;  Bit5 (NxOUT) is 1 when NCO output is HI, 0 when LO.
; ------------------------------------------------------------------
Loop	
	goto	Loop			; Do nothing else.
	end [/syntax]

And here is the C code generated by MPLAB Code Configurator:

Code:
[syntax=c]void CWG1_Initialize(void)
{
    // Set the CWG to the options selected in MPLAB(c) Code Configurator

    // Writing to CWGxCON0, CWGxCON1, CWGxCON2, CWGxDBR & CWGxDBF registers

    // G1IS NCO1OUT; G1ASDLA driven_low; G1ASDLB driven_low; 
    CWG1CON1 = 0xA6; //10100110

    // G1ASDSC2 disabled; G1ASDSC1 disabled; G1ARSEN enabled; G1ASDSCLC2 disabled; G1ASDSFLT disabled; G1ASE no_auto_shutdown; 
    CWG1CON2 = 0x40; //01000000

    // CWG1DBR 20to21_counts; 
    CWG1DBR = 0x14; //00010100

    // CWG1DBF 20to21_counts; 
    CWG1DBF = 0x14; //00010100
    
    // G1EN enabled; G1POLB normal_polarity; G1OEB enabled; G1POLA normal_polarity; G1OEA enabled; G1CS0 HFINTOSC; 
    CWG1CON0 = 0xE1; //11100001
}

void CWG1_LoadRiseDeadbandCount(uint8_t dutyValue)
{
    // Writing 6 bits of rising dead band count into CWGxDBR register
    CWG1DBR = dutyValue;
}

void CWG1_LoadFallDeadbandCount(uint8_t dutyValue)
{
    // Writing 6 bits of rising dead band count into CWGxDBF register
    CWG1DBF = dutyValue;
}

void CWG1_AutoShutdownEventSet()
{
    // Setting the GxASE bit of CWGxCON2 register
    CWG1CON2bits.G1ASE = 1;
}

void CWG1_AutoShutdownEventClear()
{
    // Clearing the GxASE bit of CWGxCON2 register
    CWG1CON2bits.G1ASE = 0;
}[/syntax]

NOTE: I used *.txt filename extensions (instead of *.asm and *.c) to make the text readable in-browser.

I am not a C programmer, but I assume each line of C code executes sequentially as the assembly code does. If so, then clearly the C code doesn't match the datasheet sequence. Note that my assembly code does match the specified CWG setup sequence (in the datasheet).

Obviously, I would appreciate hearing from someone with experience getting the CWG to work. Is it not important to follow the datasheet sequence for CWG setup? Also, should my NCO code come before or after the CWG code (in Assembly)?

Thank you.
 

Wonder why you switched to CWG (complementary waveform generator) now? It's primarly intended for power electronics applications. I don't see a particular purpose related to your application.
 

Wonder why you switched to CWG (complementary waveform generator) now? It's primarly intended for power electronics applications. I don't see a particular purpose related to your application.

I am using a PIC to generate tones that will be outputted via H-BRIDGE to an 8-ohm speaker. An H-BRIDGE requires 2 complementary (180° out of phase) outputs to be fed into it. The CWG module of the PIC provides those outputs with no major programming required on my part.

The NCO module of the PIC feeds the CWG with the desired output square wave. The non-inverted output of the CWG is basically a clone of the NCO output, and then the CWG provides a second output that is inverted with respect to the non-inverted output. The H-BRIDGE does its amplification magic, and the end result is a blaring siren blast.

If anyone else would happen to have comments pertaining to my previous post, I certainly would be appreciative.

Thank you.
 

Sorry, I didn't notice that you mentioned H-bridge control already in your first post.

I would expect the MPLAB code configurator to work unless there are clear indications that it's faulty. Presumedly the initialization sequence can be varied without problems. Debugging the code interactively would be my favorite way to find out.
 

FvM,

Thank you for your reply.

I actually posted about my project in the Microchip forums (a horribly buggy forum, by the way), and also on PICLIST. Despite my having posted actual ASM code, no one offered or pointed me to any existing Assembly code. I guess no one these days programs in Assembly anymore, or at least, not with PICs that use the NCO or CWG.

In Microchip's forum, one person replied to me as follows:

"the datasheets are better than any code generator [Microchip] has released."

Which is basically saying to me, "don't trust the C code, go with what's written in the datasheet." Well, the PIC16F1508/9 datasheet doesn't give Assembly code examples for the NCO or CWG, only a text description. I patterned my ASM after that textual guidance in the datasheet, but I am unsure if its correct.

I don't have a debugger, but even if I did, just because my code works in one set of instances doesn't mean its recommended or correct. That's why I've been looking for other Assembly language programs out there which use the 16F150x-series and the NCO with CWG. I just can't find any code like that, and I've been looking a long time, hence my post here in this forum.

I'd like to add that I found errors in Microchip's own YouTube video on the NCO. (Go to the YouTube site and type "microchip nco" as keywords. You'll see it's entitled "Microchip Self-Paced Training - Numerically Controlled Oscillator (NCO).") There's a discrepancy in that video at 14:43 and again at 15:46 pertaining to NxPOL. The video says just the opposite of what the datasheet says. Since the video's entire purpose is to train the viewer on how to use the NCO, that error is quite glaring indeed.

I've opened 2 separate cases with Microchip support, asking about CWG setup in Assembly, among other things; but so far they have not offered me a reply.

So if there is anyone here in this forum who has actually programmed a PIC16F150x chip's NCO with CWG using Assembly language, I would love to see a snippet of your code showing the initialization sequence and how you have your code ordered. I would find actually assembly code significantly more helpful than just a couple sentences of general guidance.

Thank you.
 

I don't have a debugger, but even if I did,
You previously stated you were using MPLAB which has an excellent debugger, do you have the simulator selected for debugging?

I program both in C and asm but your task seems to be heading off at a tangent instead of concentrating on the real task. The NCO probably isn't the best answer to creating tones although it certainly can. I can't see why you are bothering to produce complimentary outputs when it can be done in hardware with two components either. You appear to be over-complicating things.

What you need is a tone generator, trying to do it by toggling pins in software isn't really an option when you are also doing something else. Almost all PICs have PWM generators in them which are ideal for this purpose because they run without software intervention after their intial configuration.

You also have a requirement to store the start frequency, finish frequency and a stepping rate betwen them. You can calculate the frequency steps by simply subtracting the two frequencies to find their difference then working out how big the increments or decrements have to be to sweep in the desired time. You can use timer interrupts to trigger the steps at consistent rates.

For example 500Hz to 1200Hz in 400mS:
Frequency shift is 1200-500 = 700 Hz.
700Hz in 400mS = 1.75Hz per mS
in 400mS (685 steps) you end up with 500 + (685 * 1.75) = 1198.75Hz. That's probably close enough to 1200Hz!

Set up the PWM to produce 500Hz, set up a timer interrupt at 1mS intervals, add 1.75Hz to the frequency 400 times and you get 500Hz to 1198.75Hz tone sweep.

Brian.
 

Brian, I simply don't have a PICKIT3. Instead, I use a PM3 programmer. So what I was saying is that I don't have an in-circuit debugger.

Heading off on a tangent depends on who you ask. I only began using a 16F1508 chip because that was strongly recommended to me by some of the gurus over at Microchip's forum. I was originally fiddling with an old 16F84, and when I told them that they laughed me to scorn and educated me on the NCO of newer PICs. They specifically told me the NCO would be the better approach for my sweep objective, over and above PWM (although the 16F1508 has a PWM module and NCO module too). Of course, none of them were any help with Assembly code since they all seem to program in C alone over there. And despite having recommended the chip and NCO to me, no one was forthcoming with code either. Suffice it to say, I moved on to other forums such as this one to see if I could get more specific advice, especially ASM code snippets.

I am using the CWG because, in theory anyway, it easily gives me the two outputs I need for my H-Bridge. The CWG1A output basically clones the NCO output and sends that to RC5 (pin5), while CWG1B is inverted (180° out of phase) and outputs on RC4 (pin6). Why add cost and complexity to the circuit when you can generate the normal and inverted outputs via the PIC?

Thank you for the specific advice on using the PWM module of the PIC instead of the NCO. And I certainly can give that a try. However, I did notice that when I create sweeps in Audacity or TwistedWave, the way the same frequency repeats across the entire sweep range is not consistent. For example, at the 500Hz end, the same frequencies repeat on average of 3 times. But at the higher frequency end, they repeat 5 or 6 times. I guess that makes sense seeing the pulse widths at the high end are narrower. Perhaps they do that to make the sweep sound smoother to the ear? Not sure. It's just an observation.

Now with regard to my previous post in this thread though, I was speaking about INITIALIZATION SETUP exclusively. If you read that post again, you can see I was talking about setup of the CWG. I know you feel an external approach is better, but if I can get the CWG up and running internal to the PIC, I think it's perfectly fine, since it will read the NCO or even the PWM module and output the normal and inverted outputs automatically (if the initial setup is right).
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top