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.

Output two different sine waves with 16F88 and CCS C?

Status
Not open for further replies.

3dluvr

Member
Output two different sine waves with 16F88 in C?

Hello,

Here's what's been bothering me now for a week and perhaps someone could please help me out...

I saw this Brain Machine circuit (Make magazine #10, article here: https://www.electro-tech-online.com/custompdfs/2008/01/wp_brainmachine.pdf ) so I figured to build it. Since I'm a PIC and not an AVR guy I wanted to use what I have handy, a 16F88, thinking it's a fairly modern micro-controller.

In their circuit they use two AVR's built in timers that link to two oscillators (PWM) to generate different frequencies. Great, sounds pretty easy if only most PICs had more than one PWM (yes, I know there are some with two but they are in large packaging, and besides not handy).

My idea was, since there's only one PWM on most PICs to instead use two four bit sine wave ladders on port A or B or both, regardless (four bit sine wave example as seen here, towards bottom part of the page: **broken link removed** )

Well, the problem is that I can generate one just fine and the output is pretty decent going through and RC filter, but how do I generate two sine-waves at the same time, having two different frequencies, let's say 205Hz and 215Hz?

I have been trying all kinds of things with timers and interrupts, and to be honest I am no expert in writing for micro-controllers (presently using CCS C) so I'm stuck and hoping someone here could enlighten me?

Thank you in advance.
 
Last edited:
Did you watch the video demonstrating this thing?
Do you still want to build it? :D
 
Actually I watched author's presentation from the Chaos Communications Camp 2007 where he discussed brain entrainment and his device, which is nothing new really. But that's all beside the point, any ideas on how to achieve the above? :)
 
Can't you just add the two sine waves in software and output them to the one PWM channel?

Mike.
 
How would I do that?

The sounds need to be so called binaural tones, where one is slightly higher in frequency than the other, ie. 200Hz and 211.4Hz and their difference causes the entrainment of the brain to 11.4Hz.

My understanding is that they need to be outputted at the same time not one following the other, or the effect won't work...

Plus, the tones need to go to different ears, one channel is one tone and the other channel is the other tone, left and right earphone.
 
You could use two timers. One running at a (16 x 200Hz) rate and another at (16 x 211.4Hz) to trigger when to output a value from of your 4bit sine lookup table.
 
But could the output be simultaneous and independent from each other? If I use the higher nibble of port B for left channel and lower nibble for the right channel? Could you please mock-up an example for me?

In reality one channel/tone is always static and does not change, it's a fixed frequency of let's say 200Hz. The other channel would change depending on which brainwave range user wishes to entrain to (Delta, Theta, Alpha, Beta).

Then there are the LEDs that would need to be pulsed at that entrain frequency (11.1Hz for example for Alpha as used in the Brain Machine) along with the tones being produced.

Thanks.
 
But could the output be simultaneous and independent from each other?
Yes, if you used interrupts for both timers.
If I use the higher nibble of port B for left channel and lower nibble for the right channel?
You can do that, though it would be easier if you used separate ports. To use the same port, you would have to read/modify/write to the port in your ISR without disturbing the "other" bits.
Could you please mock-up an example for me?
Sorry I don't have CCS C as a compiler or any C compiler for the 16F series.
In reality one channel/tone is always static and does not change, it's a fixed frequency of let's say 200Hz.
I would assign that channel to the timer with the least number of bits (TMR0 or 2) and use Timer 1 and the CCP for the variable tone channel.
Then there are the LEDs that would need to be pulsed at that entrain frequency
They could be slaved off the TMR0 or 2 interrupt using a software counter to set the flash rate.
 
Last edited:
Thank you again for the direction pointers, I think I understand now (sort of). :)

I still have to figure out the interrupts and timers (noob) but using two different ports is not an issue since the chip will do nothing else but produce tones and flash LEDs.
 
I didn't realise you needed the 2 signals to be separate. A possible solution would be to use a fixed interrupt of say 500uS and a 32 byte sine lookup table and simply step through the table each interrupt, then you will get a frequency of 1/(500*10-6*32) Hz = 62.5Hz. If we add 2 to our lookup table position then we will get a frequency of 2/(500*10-6*32) = 125Hz. If we want 211.4 Hz then we need to add 211.4*500*10-6*32 = 3.3824 which is not a very nice number. However, if we multiply 3.3824 by 256 we get 868 which is much nicer. If we add this number to a 16 bit variable and then shift the variable right 8 places, we can use the resultant value as our lookup value to get 211.4Hz.

If we also want 200Hz then 200*500*10-6*32 = 3.2. 3.2*256=819

The actual frequencies due to rounding errors would be,
868/(500*10-6*32*256) = 211.9
819/(500*10-6*32*256) = 199.9

Our interrupt would look something like,
(generic C)
Code:
void interrupt(){
    position+=868;
    PORTB=SineTab[(position>>8)&31];
    position2+=819;
    PORTA=SineTab[(position2>>8)&31];
}

or, if you want to use just port B,

void interrupt(){
    position+=868;
    Temp=SineTab[(position>>8)&31]<<4;
    position2+=819;
    PORTB=Temp|SineTab[(position2>>8)&31];
}

You could of course calculate the values in the background and put them in a variable.

Hope that makes sense.

Mike.
edit typo and changes nS to uS as pointed out by kcriste.
 
Last edited:
Thank you for the calculation, it seems better than mine...

Here is what I've done so far (yesterday night). I used timer0 and timer2, did not want to mess with the 16bit values in timer1 yet so my freq2 adjustment is rather crude, steps of 4 or so Hz.

Code:
#include <16F88.h>
#fuses INTRC_IO,NOWDT,NOPROTECT,NOLVP,PUT,CCPB3
#use delay(clock=8000000)

int i, j;

CONST unsigned int SINE_WAVE[30] = {
0b00010000, 0b10010000, 0b01010000, 0b11010000, 0b00110000, 0b10110000, 
0b01110000, 0b11110000, 0b01110000, 0b10110000, 0b00110000, 0b11010000, 
0b01010000, 0b10010000, 0b00010000, 0b11100000, 0b01100000, 0b10100000, 
0b00100000, 0b11000000, 0b01000000, 0b10000000, 0b00000000, 0b10000000, 
0b01000000, 0b11000000, 0b00100000, 0b10100000, 0b01100000, 0b11100000};

#INT_RTCC
void main_wave() {	
	if (i++ == 29)
		i = 0;
	output_b(SINE_WAVE[i]);
}

#INT_TIMER2
void TIMER2_isr() {	
	if (j++ == 29)
		j = 0;
	output_a(SINE_WAVE[j]>>4);
}

void main(void) {
	i = 0;
	j = 0;
	setup_adc_ports(NO_ANALOGS);    // Turn off analogue inputs
	setup_oscillator(OSC_8MHZ | OSC_INTRC);   // Use internal 8MHz osc 
	set_timer0(0);

	setup_counters( RTCC_INTERNAL, RTCC_DIV_1 | RTCC_8_BIT);
        // about 258.7Hz
	
        setup_timer_2( T2_DIV_BY_4, 65, 1 );
        // 65 gives 250.8Hz

        enable_interrupts(INT_TIMER0);
        enable_interrupts(INT_TIMER2);
	enable_interrupts(GLOBAL);
	
	while(TRUE)
	{
		delay_ms(1);
	}
   
        disable_interrupts(INT_RTCC);
}

Any comments on improving the above are welcome.

Also, where do I stick the LED pulsing now in, so they pulse at the binaural beat (the frequency difference)?
 
Last edited:
I don't understand your sine table. It seems to go 1, 9, 5, 13, 3, 11 .... which doesn't look very sinusoidal.

A quick play with excel gives a 20 byte sign table as,
7,9,11,13,14,14,14,13,11,9,7,5,3,1,0,0,0,1,3,5,7,9,11,13,14,14,14,13,11,9
Where,
7 represents 0
0 represents -1
14 represents +1

As for the binaural beat, I would guess that the LED should be on if one wave form is the opposite sign to the other waveform, but I suspect this would be twice the frequency. Maybe when A>0 and B<0 then LEN is on will work.

Mike.
 
The sine table is for a 4-bit based resistor network: 1k, 2k, 3k9, 8k2 which seems to work pretty good here (at least according to my scope) :)

Should I just tie the LEDs to the PWM instead of the one or the other timer, but then I'm afraid that pulsing would be off because the PWM and the timers would not be in the right sync?

If base freq is ~258.7 and second freq ~250.8 the difference is 7.9Hz and that would be the proper pulsing for the LEDs, but does that mean that they need to be on the whole time of those 7.9Hz (126.58227ms) or half the time 3.95Hz (63.29113ms) ?!

Thanks
 
Pommie said:
Code:
void interrupt(){
    position+=868;
    PORTB=SineTab[(position>>8)&31];
    position2+=819;
    PORTA=SineTab[(position2>>8)&31];
}

or, if you want to use just port B,

void interrupt(){
    position+=868;
    Temp=SineTab[(position>>8)&31]<<4;
    position2+=819;
    PORTB=Temp|SineTab[(position2>>8)&31];
}

I'm trying to understand and implement the code above, where should we increase the position/position2 and how do we check we are at the end of the sine table to reset the position/position2?

Am I to presume you are using timer1 which is 16-bit, but you said 500ns which is 0.5us, so at 8MHz that's 1/4 of the instruction cycle or T1_DIV_BY_4 ?
 
You won't be able to implement an interrupt at a 500ns rate on a PIC running at 8Mhz which gives you a 500ns instruction cycle. So Pommie's DDS method, while very elegant, won't work for you.
EDIT: OOpps. I see he really meant 500uS not 500ns!!!! So it will work at that rate:
868/(500*10-6*32*256) = 211.9
 
Last edited:
3dluvr said:
I'm trying to understand and implement the code above, where should we increase the position/position2 and how do we check we are at the end of the sine table to reset the position/position2?
The instruction
position+=868;
increments the position. In effect, all the values that we are using are multiplied by 256 and so to get the actual position in the table we have to do position>>8. As out table is 32 entries long then we can simply use the bottom 5 bits of the divided value. Hence,

PORTB=SineTab[(position>>8)&31];

(position>>8)&31 divides position by 256 and keeps only the bottom 5 bits.

Am I to presume you are using timer1 which is 16-bit, but you said 500ns which is 0.5us, so at 8MHz that's 1/4 of the instruction cycle or T1_DIV_BY_4 ?
I don't understand the syntax of the compiler you are using. At 8MHz an interrupt is required every 1000 cycles. With this in mind, I would use timer 2 with a prescaller of 4 and a PR2 value of 249.

Mike.
 
kchriste said:
You won't be able to implement an interrupt at a 500ns rate on a PIC running at 8Mhz which gives you a 500ns instruction cycle. So Pommie's DDS method, while very elegant, won't work for you.
EDIT: OOpps. I see he really meant 500uS not 500ns!!!! So it will work at that rate:

Ooops, you are correct, I did mean uS and not nS. I'll edit the original so as not to confuse others. Thanks.

Mike.
 
Aah that makes more sense, because to get 500ns I would have to have a 0x100000 prescaler factor at my 8MHz, or something like that for a 16-bit timer.

I see mention of a 32 byte sine table, where do I find one that works on a 4-bit based resistor network?

And are we talking about a 8-bit or 16-bit timer (I see that *256 there which suggests 8-bit). I'm rather confused on the issue because I thought that using a 16-bit timer for the 2nd frequency (the one that changes) would give better control over it?

Thanks.
 
I assumed you would use a R2R converter. A suitable table for this DAC is,
Code:
CONST unsigned int SINE_WAVE[32] = {
    7,8,10,11,12,13,13,14,14,14,13,13,12,11,10,8,
    7,6,4,3,2,1,1,0,0,0,1,1,2,3,7,7};

There is only 1 timer required with this method and as timer2 is the simplest to use, that is the one I would choose.

Mike.
 
I have tried both R2R and 4-bit network (1,2,4,8k) and the 4-bit seems to give nicer sine waves, but I will definitely try R2R again in this configuration.

It's because the RC filter makes the sine wave much smoother than with the R2R, not sure how to make the RC filter right for the R2R network, I get saw instead of sine.

Could you please generate a sine table for me for 4-bit network, or tell me how to do it myself?

What kind of control over frequency (step wise) can I expect with the timer2 being used as an 8-bit one?

Thanks again.

P.S. I just tried R2R and I see periodic jumps in my saw wave, the whole wave moves up or down on my scope, is this because of the interrupt or the RC filter? 4-bit network never exhibited this but then again I did not tried it with using the new timer2 setup?
 
Last edited:
Status
Not open for further replies.

Latest threads

Back
Top