Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

100 hz tone on a PIC in C

Status
Not open for further replies.
That was my point. He really needs to work on terms. He may understand the concept but he's not conveying it.

In his new graphic for example, point 'C' should be 100% duty cycle and point 'H' should be 0% duty cycle. And without a reference on the X axis one might assume his transitions are occurring at regular intervals which would produce a sawtooth wave instead of a sine wave, that is if you can imply that "pwm increment" is actually "duty cycle".

Shoddy terms followed up with a shoddy graphic which includes more shoddy terms.

Mike, point H should be -5V; this is a sine wave after all.

All you are doing is gradually setting your PWM output level higher until you reach the maximum 5V value, then you're decrementing down in your PWM output level till you get to 0V; this is your positive peak cycle. Then, you decrement to 0V until -5V, then back to 0V again; this is your negative peak cycle. If you connect the dots, your making a sine wave, and 1% increments will make a more pure sine wave then 10%, etc.

In code in algorithm form would be similar to this:

Code:
	while(1)
	{
		// POSITIVE CYCLE (0° = 0V, 90° = 5V, 180° = 0V)
		for(i=0;i<100;i++)	// Rising edge in percentage
		{
			PWM_Output_Positive(i, freq);
		}
		for(i=100;i>0;i--)	// Falling edge in percentage
		{
			PWM_Output_Positive(i, freq);
		}
		
		//****************
		
		//NEGATIVE CYCLE, (180° = 0V, 270° = -5V, 360° = 0V)
		for(i=0;i>-100;i--)		// Falling edge in percentage
		{
			PWM_Output_Negative(i, freq);	// Negative falling edge of sine wave
		}
		for(i=-100;i<0;i++)		// Rising edge in percentage
		{
			PWM_Output_Negative(i, freq);	// Negative rising edge of sine wave
		}
	}
 
Last edited:
Good point. Perhaps a positive to negative DC/DC converter would work, but I haven't really looked into it.
 
Somebody gave me this, I have been trying to translate it so that I can understand.
I don't even know if this would work and I can't see how the 'Prescaller' (PRE1 ,2 3 and PREE) routines in the 'Chorus' function are even doing anything ( but that might be my shortcoming when it comes to understanding assembly).

Code:
;	TITLE	"CTCSS Encoder"

;	Assemble with MPASM
;   See below for wiring details
;
        LIST            P=16C84
        __CONFIG        3FF9H   ;XTAL OSC @4Mhz,NO WATCHDOG,PWRRST,NO CP.
        __IDLOCS        1234
	ERRORLEVEL      -302
;
CNT1	EQU	0CH	;PRESCALLER COUNTER REG 1
CNT2	EQU	0DH	;PRESCALLER COUNTER REG 2
CNT3	EQU	0EH	;PRESCALLER COUNTER REG 3
TONREG	EQU	0FH	;REGISTER FOR TONE NUMBER
PRES1	EQU	10H	;PRESCALLER STORE 1
PRES2	EQU	11H	;PRESCALLER STORE 2
PRES3	EQU	12H	;PRESCALLER STORE 3
PORTA	EQU	5
PORTB	EQU	6
TRISA	EQU	85H
TRISB	EQU	86H
OPTREG	EQU	81H
PC	EQU	2
PULLUP	EQU	7
STATUS	EQU	3
INTCON	EQU	0BH
GIE	EQU	7
RBIE	EQU	3
RBIF	EQU	0
RP0	EQU	5
OUT	EQU	2
PTT	EQU	7
;
	GOTO	START
	ORG	00C8
START	BSF	STATUS,RP0	;SELECT REG BANK 1
	BSF	TRISB,PTT	;SETUP RB7 AS INPUT FOR PTT
	BCF	TRISA,OUT	;SETUP RA2 AS OUTPUT FOR TONE
	BCF	OPTREG,PULLUP	;SET PULLUP RESISTORS ON PORTB
	MOVLW	0xFF		; SET PORTB AS I/Ps
	MOVWF	TRISB		; RB0-RB5 FOR SWITCH INPUTS
;

	BCF	STATUS,RP0	;SELECT REG BANK 0
	BCF	INTCON,GIE	;DISABLE GIE INTERUPTS
	BSF	INTCON,RBIE	;ENABLE PORT B CHANGE INTERUPT
	MOVF	TRISB,W		;READ DIP SWITCHES INTO W
	MOVWF	TONREG		;STORE IN 0F REG
	BCF	TONREG,6	; CLEAR BITS 6&7 AS
	BCF	TONREG,7	; THEY ARE IRRELAVANTS
	MOVF	TONREG,W	;RE-STORE INTO W
	ADDWF	TONREG,F	; MULTIPLY BY 3
	ADDWF	TONREG,F	; FOR TONE TABLE OFFSET
;
	MOVF	TONREG,W	;RELOAD TO W FOR OFFSET
	CALL	TONTBL		;GOTO TONE LOOKUP TABLE
	MOVWF	PRES1		;STORE FIRST VALUE FROM TABLE
;
	INCF	TONREG,F	;INCREMENT FOR NEXT TABLE VALUE
	MOVF	TONREG,W	;RELOAD W FOR OFFSET
	CALL	TONTBL		;GOTO TONE LOOKUP TABLE
	MOVWF	PRES2		;STORE SECOND VALUE FROM TABLE
;
	INCF	TONREG,F	;INCREMENT FOR NEXT TABLE VALUE
	MOVF	TONREG,W	;RELOAD W FOR OFFSET
	CALL	TONTBL		;GOTO TONE LOOKUP TABLE
	MOVWF	PRES3		;STORE THIRD VALUE FROM TABLE
;
;
RELOAD
	MOVF	PRES1,W		;LOAD W WITH PRESET FOR 1
	MOVWF	CNT1		;PRELOAD PRESCALLER 1
	MOVF	PRES2,W		;LOAD W WITH PRESET FOR 3
	MOVWF	CNT3		;PRELOAD PRESCALLER 3
	MOVF	PRES3,W		;LOAD W WITH PRESET FOR 2 READY
PTTCHK
	BTFSC	PORTB,PTT	;TEST PTT LINE
	GOTO	WAIT		;SLEEP TILL LOW
	CALL	CHORUS		;DO DIVIDER/PRESCALLER ROUTINE
;
OUTPUT
	BTFSS	PORTA,OUT	;TEST O/P HI OR LO AT PRESENT
	GOTO	HI		;IF LO GO SET HI
	GOTO	LO		;IF HI GO SET LO
;
HI
	NOP			;BALANCE OUT CYCLE TIME MARK&SPACE
	BSF	PORTA,OUT	;MAKE O/P HI
	GOTO	RELOAD		;RETURN TO START OF CYCLE
;
LO
	BCF	PORTA,OUT	;MAKE O/P LO
	GOTO	RELOAD		;RETURN TO START OF CYCLE
;
CHORUS
PRE1	
	DECFSZ	CNT1,F		;DECREMENT PRESCALLER 1
	GOTO	PRE2
	GOTO	FINE		;CALL FINE ADJUST SUBROUTINE
PRE2	MOVWF	CNT2		;RELOAD PRESCALLER 2
;
PREE	DECFSZ  CNT2,F		;DECREMENT PRESCALLER 2	
	GOTO	PREE		;GO BACK AND DECREMENT PRE2 TILL 00
;
	GOTO	PRE1		;GO BACK AND DECREMENT PRE1 TILL 00
;
FINE	DECFSZ	CNT3,F		;DECREMENT PRESCALLER 3
	GOTO	FINE		;GO BACK AND DECREMENT PRE3 TILL 00
	RETURN			;RETURN TO CHANGE O/P AND CHECK PTT ETC
;
WAIT    
	BCF	INTCON,RBIF	;CLEAR PORT B CHANGE INT FLAG
	SLEEP			;SLEEP AND WAIT FOR PTT
	NOP
	BCF	INTCON,GIE	;DISABLE GLOBAL INTERUPTS AGAIN 
	GOTO	PTTCHK		;CHECK IF PTT PASSES TEST
;
;
;
;TONES TABLE AT 0008H
	ORG 0007H
TONTBL
	ADDWF	PC,F		; W+PC ->PC, JUMP DOWN TABLE
;
;
;							   16c84 PIN
;                                                    11  10  9   8   7   6
;				  TONE  FREQ        RB5 RB4 RB3 RB2 RB1 RB0
;
	DT	02,01,0x9D	; 000   1Khz TEST    0   0   0   0   0   0
	DT	86,82,10	; 001   67.0         0   0   0   0   0   1
	DT	81,85,10	; 002   69.4 *       0   0   0   0   1   0
	DT	84,80,0x0F	; 003   71.9         0   0   0   0   1   1
	DT	7F,85,0x0F 	; 004   74.4         0   0   0   1   0   0
	DT	7B,7C,0x0F	; 005   77.0         0   0   0   1   0   1
	DT	76,86,0x0F	; 006   79.7         0   0   0   1   1   0
	DT	72,82,0x0F	; 007   82.5	     0   0   0   1   1   1
	DT	74,8F,0x0E	; 008   85.4	     0   0   1   0   0   0
	DT	63,91,10	; 009   88.5	     0   0   1   0   0   1
	DT	5F,9A,10	; 010   91.5	     0   0   1   0   1   0
	DT	5B,47,11	; 011   94.8	     0   0   1   0   1   1
	DT	55,88,11	; 012   97.4	     0   0   1   1   0   0
	DT	56,49,11	; 013  100.0	     0   0   1   1   0   1
	DT	09,6E,0xB9 	; 014  103.5	     0   0   1   1   1   0
	DT	08,81,0xC9	; 015  107.2	     0   0   1   1   1   1 
	DT	0A,71,98	; 016  110.9	     0   1   0   0   0   0
	DT	0B,76,83	; 017  114.8	     0   1   0   0   0   1
	DT	0A,0xCA,83	; 018  118.8	     0   1   0   0   1   0
	DT	0A,9A,83	; 019  123.0	     0   1   0   0   1   1
	DT	09,0xF1,83	; 020  127.3	     0   1   0   1   0   0
	DT	08,93,9D	; 021  131.8	     0   1   0   1   0   1
	DT	07,94,0xB0	; 022  136.5	     0   1   0   1   1   0
	DT	07,7C,0xAD	; 023  141.3         0   1   0   1   1   1
	DT	07,85,0xA5	; 024  146.2         0   1   1   0   0   0
	DT	08,7B,89	; 025  151.4         0   1   1   0   0   1
	DT	08,80,83	; 026  156.7         0   1   1   0   1   0
	DT	07,0xA2,90	; 027  159.8 *       0   1   1   0   1   1
	DT	08,47,86	; 028  162.2         0   1   1   1   0   0
	DT	07,60,95	; 029  165.5 *       0   1   1   1   0   1
	DT	08,86,78	; 030  167.9         0   1   1   1   1   0
	DT	07,92,87	; 031  171.3 *       0   1   1   1   1   1
	DT	07,90,85	; 032  173.8         1   0   0   0   0   0
	DT	07,7D,85	; 033  177.3 *       1   0   0   0   0   1
	DT	09,5A,66	; 034  179.9         1   0   0   0   1   0
	DT	09,48,66	; 035  183.5 *       1   0   0   0   1   1
	DT	07,9E,78	; 036  186.2         1   0   0   1   0   0
	DT	08,0xBB,60	; 037  189.9 *       1   0   0   1   0   1
	DT	09,4C,60	; 038  192.8         1   0   0   1   1   0
	DT	08,9D,60	; 039  196.6 *       1   0   0   1   1   1
	DT	09,2F,60	; 040  199.5 *       1   0   1   0   0   0
	DT	07,82,70	; 041  203.5         1   0   1   0   0   1
	DT	07,76,70	; 042  206.5 *       1   0   1   0   1   0
	DT	07,66,70	; 043  210.7         1   0   1   0   1   1
	DT	07,6F,6A	; 044  218.1         1   0   1   1   0   0
	DT	06,0xA3,70	; 045  225.7         1   0   1   1   0   1
	DT	06,98,70	; 046  229.1 *       1   0   1   1   1   0
	DT	06,8A,70	; 047  233.6         1   0   1   1   1   1
	DT	06,72,70	; 048  241.8         1   1   0   0   0   0
	DT	04,0xC3,99	; 049  250.3         1   1   0   0   0   1
	DT	04,0xB9,99	; 050  254.1 *       1   1   0   0   1   0
	DT	05,1C,99	; 051  255   *       1   1   0   0   1   1
	DT	03,15,20	; 052  1750 Eu Tone  1   1   0   1   0   0
	DT	05,0F,10	; 053  1800 Eu Tone  1   1   0   1   0   1
	DT	01,84,9D	; 054  1200 Packet   1   1   0   1   1   0
	DT	02,13,30	; 055  2200 Packet   1   1   0   1   1   1
	DT	03,26,50	; 056  800   *       1   1   1   0   0   0
	DT	03,0F,50	; 057  900   *       1   1   1   0   0   1
	DT	02,3F,50	; 058  1100  *       1   1   1   0   1   0
	DT	03,36,20	; 059  1300  *       1   1   1   0   1   1
	DT	01,68,20	; 060  1500  *       1   1   1   1   0   0
	DT	03,09,20	; 061  2000  *       1   1   1   1   0   1
	DT	02,1A,20	; 062  2500  *       1   1   1   1   1   0
	DT	02,07,20	; 063  3500  *       1   1   1   1   1   1
;
;					* Indicates non EIA standard tone
;
	ORG 2100H

        END


		All CTCSS tones are within EIA standard of .08% .
   		Use a 4Mhz crystal with 2x18pF capacitors. 
		PTT line is active LOW. PTT is RB7 Pin 13.
		OUTPUT tone is on RA2 Pin1 ,see Low pass filter diag.
		Lines RB0-RB5 can be fitted with DIP switches to set
		tone frequencies as per table above. A 1 on the table
		indicates the RB pin is logic HI . All RB port pins have
		been programmed with internal pullup resistors and will
		float HI if left open. The binary inputs are read only on
		power up and not each time the PTT is keyed.

		For CTCSS tones use a Low pass filter as shown here as the
		output waveform is square. RO is an padding resistor to
		allow for level adjustment typically 20k.


		RA2--------2k2----------RO----Output
		Pin1		  |
				  |
				  1uF
				  |
				  GND
 
Last edited:
Hi Baxter',

I believe that program is generating a square wave output. Is that what you want?

What PIC device are you using? If it has a PWM module and if you hang in there, I'll post you a C example.

Meanwhile, here are some comments from my first assembly language (DDS-PWM) CTCSS sine wave program from long ago. It only used a 64 element sine/pwm array and a 16 bit phase accumulator which only provided something like 0.119 Hz frequency resolution so I scrapped it for another version. This version did however have pseudo C code comments which may give you an idea or two.

Cheerful regards, Mike

Code:
;
;  unsigned int accu = 0;       // phase accumulator
;  unsigned int tone = 0;       // tone phase offset
;  unsigned char angle = 0;     // sine array index
;
;  #define lo(x) (char)((x)&255)
;  #define hi(x) (char)((x)/256)
;
;  #define r08 const rom unsigned char
;
;  r08 sine[] = { 128,140,152,165,176,188,198,208,
;                 218,226,234,240,245,250,253,254,
;                 255,254,253,250,245,240,234,226,
;                 218,208,198,188,176,165,152,140,
;                 128,115,103,090,079,067,057,047,
;                 037,029,021,015,010,005,002,001,
;                 000,001,002,005,010,015,021,029,
;                 037,047,057,067,079,090,103,115 };
;
;  void interrupt()             // 128-usec timer 2 interrupts
;  { pir1.TMR2IF = 0;           // clear timer 2 interrupt flag
;    accu += tone;              // bump phase accumulator
;    angle = hi(accu)>>2;       // upper 6 bits for sine index
;    ccpr1l = sine[angle];      // duty cycle for next period
;  }                            //
;
;  void main()
;  { cmcon0 = 7;                // comparator off
;    ansel = 0;                 // analog off, all digital I/O
;    trisio = 0b00111011;       // GP2 output, all others input
;    osccon = 0b01110000;       // set INTOSC to 8-MHz
;    while(!osccon.HTS);        // wait for oscillator stable
;    pie1 = 1<<TMR2IE;          // enable timer 2 interrupts
;    pr2 = 256 - 1;             // 128-usec period (7812.5 Hz)
;    ccpr1l = 0;                // set initial 0% duty cycle
;    ccp1con = 0b00001100;      // --00---- duty cycle b1..b0
;                               // ----1100 pwm mode, active hi
;    intcon.GIE = 1;            // enable global interrupts
;    intcon.PEIE = 1;           // enable peripheral interrupts
;    t2con = 1<<TMR2ON;         //
;
;    tone = 65536/(78125/915);  // phase offset for 91.5 Hz
;
;    while(1)                   //
;    {                          //
;    }                          //
;  }                            //
;
 
Last edited:
Hi Baxter',
I believe that program is generating a square wave output. Is that what you want?

No, I don't want a square wave. I thought that it was generating a square wave but wasn't 100% sure. Thanks.
I don't know what PIC I am using yet because I didn't know how to go about this. Now that I know I can use PWM and have some ideas, I will choose my PIC accordingly.
 
Hi Baxter',
What PIC device are you using? If it has a PWM module and if you hang in there, I'll post you a C example.
Mike, I'm just not figuring out how to get a sine wave. I'll take you up on your offer. I haven't chosen a pic yet, so it can be one with PWM. I have not done a lot of pic programming and I am definitely still a novice. I appreciate your help.
 
Mike,
I found something that may help me understand how to do this. Let me sort through it and if it doesn't work I'll let you know.
Thanks
 
Well, I think I am dense. I am having a lot of trouble figuring this one out. I had hoped to find a simple example out there so that I could look at it and learn. No such luck. There are lots of PWM sine examples, most in ASM and the ones in C are pretty complex. It sounded like it is suppose to be a fairly simple thing.
I have really hesitated asking, but could someone post a simple PWM sine wave generator example in C. Thanks in advance. I think it would be very beneficial to all of us newbie PIC people.
 
can any1 tell me how does it ACTUALLY work? I mean, pin can only give 1 or 0 and how to achieve
digitalpwmsinewaveexample-png.48277
? Voltage can't be changed and PWM can only give heights of A or E (in picture). if you can, please give any frequency generator on assembly. and 1 more question. must I directly connect pin to loudspeaker? which transistor should I use?
 
Well, I think I am dense. I am having a lot of trouble figuring this one out. I had hoped to find a simple example out there so that I could look at it and learn. No such luck. There are lots of PWM sine examples, most in ASM and the ones in C are pretty complex. It sounded like it is suppose to be a fairly simple thing.
I have really hesitated asking, but could someone post a simple PWM sine wave generator example in C. Thanks in advance. I think it would be very beneficial to all of us newbie PIC people.

The listing in post #25, if you were to turn it into a BoostC program, is pretty much a complete solution that will generate a 91.5-Hz sine wave.

I understand that the DDS concept may not be very easy to understand.

Hang in there. I'll have more time to play when this semester is over in two weeks...

Happy Holidays!

Mike
 
Last edited:
can any1 tell me how does it ACTUALLY work? I mean, pin can only give 1 or 0 and how to achieve
I have not figured out how to do it in C but I do understand the concept, I think.
It has to do with voltage averaging. If over the time of 100 milliseconds (as an example) the pin is on all the time the average voltage would be 5v. If it is off then on and so on 50 of the 100 milliseconds the duty cycle would be 50% and the voltage average 2.5v. If you varied the duty cycle so that at the bottom of the sine you had few on periods (less voltage) and more as you worked up the sine wave hill (more voltage), the average voltage would look like a sine wave. When you add a simple RC (resistor/capacitor) filter, the sine can look pretty good on a scope. This or this might help a bit more. I just have to figure out how to get C to do it, but I hope this helps you.
 
The listing in post #25, if you were to turn it into a BoostC program, is pretty much a complete solution that will generate a 91.5-Hz sine wave.
Mike
Thanks. I thought that one said it only generated a very low frequency, but I see it was the resolution frequency (I should learn to read). I got it confused so didn't look too hard at it. Sorry. I'll have a look at it and see what I can come up with.

Thanks. Happy Holidays to you as well.
 
Last edited:
The listing in post #25, if you were to turn it into a BoostC program, is pretty much a complete solution that will generate a 91.5-Hz sine wave.

I understand that the DDS concept may not be very easy to understand.

Mike

Mike,
1)Is that DDS?
2) Am I mistaken or is that just the first bit of the code.
 
(1) Yes, that's DDS (direct digital synthesis), or perhaps more correctly, an NCO (numerically controlled oscillator).
(2) Add a few lines to setup config fuses and that code will produce a 91.5-Hz tone. Want a 254.1-Hz tone?
Code:
tone = 65536/(78125/2541);  // phase offset for 254.1-Hz
 
Last edited:
Well I'm not use to Assembly and it looks to me like you have a 'while 1' at the bottom that doesn't go any where. While 1 what. There is no code in the while loop. Did the whole thing get posted, would you mind looking at #25 and checking that it's all there?
 
I built a BoostC program from those post 25 comments for you. It's "untested" but it compiles fine.

You asked for example code that generates a tone and that's what this program does. The tone is generated entirely in the background in the (dds 'engine') ISR and that's why the 'main' program ends in an endless loop.

If you can hang on until I'm finished with finals in a couple weeks, I should be able to provide you with a higher performance version.

Happy Holidays.

Mike

Code:
/********************************************************************
 *                                                                  *
 *  Project: CTCSS old                                              *
 *   Source: CTCSS_old.c                                            *
 *   Author: Mike McLaren, K8LH                                     *
 *     Date: 26-Nov-10                                              *
 *  Revised: 26-Nov-10                                              *
 *                                                                  *
 *  Old 12F683 DDS(PWM) CTCSS Sine Wave Tone Generator Demo'        *
 *                                                                  *
 *                                                                  *
 *      IDE: MPLAB 8.56 (tabs = 4)                                  *
 *     Lang: SourceBoost BoostC v7.01, Lite/Free version            *
 *                                                                  *
 ********************************************************************/

#include <system.h>

#pragma DATA _CONFIG, _MCLRE_OFF, _WDT_OFF, _INTOSCIO

unsigned int accu = 0;       // phase accumulator
unsigned int tone = 0;       // tone phase offset
unsigned char angle = 0;     // sine array index

#define lo(x) (char)((x)&255)
#define hi(x) (char)((x)/256)

#define r08 const rom unsigned char

r08 sine[] = { 128,140,152,165,176,188,198,208,
               218,226,234,240,245,250,253,254,
               255,254,253,250,245,240,234,226,
               218,208,198,188,176,165,152,140,
               128,115,103, 90, 79, 67, 57, 47,
                37, 29, 21, 15, 10,  5,  2,  1,
                 0,  1,  2,  5, 10, 15, 21, 29,
                37, 47, 57, 67, 79, 90,103,115 };

void interrupt()             // 128-usec timer 2 interrupts
{ pir1.TMR2IF = 0;           // clear timer 2 interrupt flag
  accu += tone;              // bump phase accumulator
  angle = hi(accu)>>2;       // upper 6 bits for sine index
  ccpr1l = sine[angle];      // duty cycle for next period
}                            //

void main()
{ cmcon0 = 7;                // comparator off
  ansel = 0;                 // analog off, all digital I/O
  trisio = 0b00111011;       // GP2 output, all others input
  osccon = 0b01110000;       // set INTOSC to 8-MHz
  while(!osccon.HTS);        // wait for oscillator stable
  pie1 = 1<<TMR2IE;          // enable timer 2 interrupts
  pr2 = 256 - 1;             // 128-usec period (7812.5 Hz)
  ccpr1l = 0;                // set initial 0% duty cycle
  ccp1con = 0b00001100;      // --00---- duty cycle b1..b0
                             // ----1100 pwm mode, active hi
  intcon.GIE = 1;            // enable global interrupts
  intcon.PEIE = 1;           // enable peripheral interrupts
  t2con = 1<<TMR2ON;         //

  tone = 65536/(78125/915);  // phase offset for 91.5 Hz

  while(1)                   //
  {                          //
  }                          //
}
 
Last edited:
Thanks, I am in no rush. This will give me a chance to look through it and understand how this is done. I would certainly appreciate the higher performance version as well as it will let me see how you improve on the performance. Thanks again.
 
Here's the high performance version (24-bit Phase Accumulator) which requires an 8.388608-MHz crystal.

Add an array of phase offset (dds tuning word) values for the 50 CTCSS tones. Add code for a display (7-segment or LCD) if desired. Add code for push button switches, jumpers, or rotary encoder, to select tone frequency.

Happy Holidays.

Mike

Code:
/********************************************************************
 *                                                                  *
 *  Project: CTCSS 12F683                                           *
 *   Source: CTCSS_12F683.c                                         *
 *   Author: Mike McLaren, K8LH                                     *
 *     Date: 26-Nov-10                                              *
 *  Revised: 26-Nov-10                                              *
 *                                                                  *
 *  High Performance 12F683 DDS(PWM) CTCSS Tone Generator Demo      *
 *  with 24-bit Phase Accumulator (8.388608-MHz crystal)            *
 *                                                                  *
 *      IDE: MPLAB 8.56 (tabs = 4)                                  *
 *     Lang: SourceBoost BoostC v7.01, Lite/Free version            *
 *                                                                  *
 ********************************************************************/

#include <system.h>

#pragma DATA _CONFIG, _MCLRE_OFF, _WDT_OFF, _HS_OSC

#pragma CLOCK_FREQ 8388608      // using an 8.388608-MHz crystal

//--< function prototypes >------------------------------------------
//--< type definitions >---------------------------------------------

typedef unsigned char u08;
typedef unsigned int u16;
typedef unsigned long u32;

#define r08 rom char*

//--< variables >----------------------------------------------------

u32 accum = 0;                  // phase accumulator
u32 phase = 0;                  // phase offset (DDS tuning word)

r08 sinetbl =   { 100,102,104,107,109,112,114,117,119,121,
                  124,126,129,131,133,135,138,140,142,144,
                  147,149,151,153,155,157,159,161,163,165,
                  167,168,170,172,174,175,177,178,180,181,
                  183,184,185,187,188,189,190,191,192,193,
                  194,194,195,196,197,197,198,198,198,199,
                  199,199,199,199,200,199,199,199,199,199,
                  198,198,198,197,197,196,195,194,194,193,
                  192,191,190,189,188,187,185,184,183,181,
                  180,178,177,175,174,172,170,168,167,165,
                  163,161,159,157,155,153,151,149,147,144,
                  142,140,138,135,133,131,129,126,124,121,
                  119,117,114,112,109,107,104,102, 99, 97,
                   95, 92, 90, 87, 85, 82, 80, 78, 75, 73,
                   70, 68, 66, 64, 61, 59, 57, 55, 52, 50,
                   48, 46, 44, 42, 40, 38, 36, 34, 32, 31,
                   29, 27, 25, 24, 22, 21, 19, 18, 16, 15,
                   14, 12, 11, 10,  9,  8,  7,  6,  5,  5,
                    4,  3,  2,  2,  1,  1,  1,  0,  0,  0,
                    0,  0,  0,  0,  0,  0,  0,  0,  1,  1,
                    1,  2,  2,  3,  4,  5,  5,  6,  7,  8,
                    9, 10, 11, 12, 14, 15, 16, 18, 19, 21,
                   22, 24, 25, 27, 29, 31, 32, 34, 36, 38,
                   40, 42, 44, 46, 48, 50, 52, 55, 57, 59,
                   61, 64, 66, 68, 70, 73, 75, 78, 80, 82,
                   85, 87, 90, 92, 95, 97 };

//--< defines >------------------------------------------------------
//--< isr >----------------------------------------------------------

/*
 *  12F/16F1xxx DDS-PWM CTCSS Access Tone Generator Notes
 *  =====================================================
 *
 *  using an 8.388608 MHz crystal
 *
 *    Tcy = 1 / 8388608 * 4 = 476.837158203125 nsecs
 *
 *  using a 200 cycle PWM period provides a DDS frequency of
 *
 *    Fdds = 1 / (200 Tcy) = 10,485.76 Hz
 *
 *  frequency resolution using a 24 bit phase accumulator is
 *
 *    Fres = Fdds / 2^24
 *    Fres = 10485.76 / 16777216 = 0.000625 Hz
 *
 *  dds tuning word (phase offset) can be calculated a couple
 *  different ways.  since the dds frequency resolution (Fres)
 *  is basically 0.01-Hz divided by 16, you can calculate the
 *  phase offset by mulitplying desired Fout output frequency
 *  by 1600.  the phase offset for an Fout of 254.1-Hz is;
 *
 *    phase = (Fout  * 100) * 16
 *          = (254.1 * 100) * 16 = 406560
 *
 *    phase =  25410 << 4 = 406560
 *
 *  or you can also calculate phase offset like this (yuch!);
 *
 *    phase = Fout / Fres
 *          = 254.1 / 0.000625 = 406560
 *
 *    phase = Fout * 2^24 / Fdds
 *          = 254.1 * 16777216 / 10485.76 = 406560
 *
 *  the highest CTCSS frequency (254.1 Hz) will produce the
 *  smallest number of sine wave D/A output points per cycle;
 *
 *    INT(10485.76 / 254.1) = 41 output points per cycle
 *
 */

void interrupt()                // 200-cycles (10485.76-Hz)
{ pir1.TMR2IF = 0;              // clear TMR2 interrupt flag
  accum += phase;               // add phase offset to accum
  ccpr1l = sinetbl[accum>>16];  // sine duty cycle value for
}                               // next PWM period

//--< main >---------------------------------------------------------

void main()
{
  cmcon0 = 7;                   // comparator off, digital I/O
  ansel = 0;                    // a2d module off, digital I/O
  trisio = 0b00111011;          // GP2 output, all others input
 /*
  *  setup PWM module for 200 cycle interrupts (10485.76-Hz with
  *  an 8.388608-MHz clock)
  *
  */
  ccp1con = 0b00001100;         // 00001100
                                // --00---- DC1B<1:0>, duty cycle b1:b0
                                // ----1100 CCP1M<3:0>, pwm mode
  t2con = 0b00000000;           // 00000000
                                // -0000--- TOUTPS<3:0>, postscale 1
                                // -----0-- TMR2ON, timer off
                                // ------00 T2CKPS<1:0>, prescale 1
                                // for 476.837158203125 nsec 'ticks'
  pr2 = 200-1;                  // for 95.367431640625 usec interrupts
  ccpr1l = 0;                   // 0% duty cycle
  pie1 = 1<<TMR2IE;             // enable Timer 2 interrupts
  pir1 = 0;                     // clear all peripheral interrupt flags
  intcon.PEIE = 1;              // enable peripheral interrupts
  intcon.GIE = 1;               // enable global interrupts
  t2con.TMR2ON = 1;             // turn TMR2 on

  phase = 25410<<4;             // phase offset for 254.1-Hz tone

  while(1);                     // loop forever
}
 
Hi Mike,

Nice piece of code. I assume that a 20MHz crystal could be used instead, it's just that the phase calculation would be more complex.

BTW, I think that you are using a 16 bit offset into the sine table. ccpr1l = sinetbl[accum>>16];

Mike.
 
Status
Not open for further replies.

Latest threads

Back
Top