1. 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.
    Dismiss Notice

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

Discussion in 'Microcontrollers' started by 3dluvr, Jan 5, 2008.

  1. kchriste

    kchriste New Member Forum Supporter

    Joined:
    Jul 23, 2006
    Messages:
    3,677
    Likes:
    47
    Location:
    Victoria BC, Canada
  2. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,161
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    I don't know why but when I copied the table from excel it got the last 2 values wrong.
    Code (text):

    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,4,6};
     
    Your 4 bit network should use the same data. Have you got the 1k on bit 3 down to the 8k on bit 0?

    Attached is the sine wave. As you can see it is approximate as it's only 4 bit but should become smoother with filtering.

    Mike.
     
    Last edited: Jul 6, 2008
  3. blueroomelectronics

    blueroomelectronics Well-Known Member

    Joined:
    Jan 21, 2007
    Messages:
    12,536
    Likes:
    170
    Location:
    Toronto, Canada
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. 3dluvr

    3dluvr Member

    Joined:
    Aug 25, 2003
    Messages:
    76
    Likes:
    0

    Thank you for the links about sine wave/dds generation, these ones I haven't seen before (I guess google sometimes misses the "good stuff"), and for the corrected sine wave table.

    And I did have the bits/resistors reversed in my 4-bit network so going "backwards" did the trick. I think prior I used a 20 value sine wave table which worked in the old direction and wasn't compatible with the new 32 value table. ;)

    I have taken two photos to show the sine wave from a R2R and 4-bit network via a RC filter (50ohm/10uF on both). You can see that the R2R has pretty jagged edges while 4-bit is smoother. This was taken in the DSO mode with avg of 2 on my PM 3394.

    So now, should I bother switching to timer1 or is that even possible, to achieve better control of the frequencies (resolution)?

    Also, where do I stick the LED flashing so that they flash in the right beat, the interrupt routine?

    I also wonder, is it possible to make a gradient transition between the two beats and how would that be done over a specified number of seconds? For example I would like to lower the beat from 11.1Hz to 6.4Hz in 60 seconds or 80 seconds.

    I know I have lots of questions and I truly appreciate your help. I want to see this through because I could've simply used an AVR chip and build the circuit as described. But then I would not have learned anything about PIC timers and interrupts as I did now. :)
     

    Attached Files:

  6. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,161
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    You can generate any frequency by calculating the value to add to position. The equation is,

    N = F*4.096

    So, if you want signal 1 to start at 11.1 Hz then you would calculate the step value as 11.1*4.096 = 45.

    In your ISR you could add a time count,
    Code (text):

    void interrupt(){
        position+=step;
        PORTB=SineTab[(position>>8)&31];
        position2+=step2;
        PORTA=SineTab[(position2>>8)&31];
        if(timeCount++==20){
            timeCount=0;
            hundreths++;
        }
    }
     
    In your main code you could then tween between values.

    Code (text):

    StartValue = (int)11.1*4.096; // start frequncy
    EndValue = (int)6.4*4.096;    // end frequency
    timeSteps = 8000;        // number of 100th of a second
    hundreths=0;             // clear 100th counter
    While(hundreths<timeSteps){
        //the following needs to use signed values and maybe floats
        step=(EndValue-StartValue)*hundreths/timeSteps+StartValue;
    }
     
    If this proves jittery then it would probably be worth switching from 24.8 to 16.16 fixed point calculations.

    Mike.
     
  7. 3dluvr

    3dluvr Member

    Joined:
    Aug 25, 2003
    Messages:
    76
    Likes:
    0
    Pommie these are excellent, thank you again!

    Would you please just clarify for me, we make the start/endvalue 8-bit with the cast and that's where you refer to the possible jitter? The 4.096 factor in the formula, where is that derived from?

    Then wouldn't the first value of step be 0 because we are starting with hundreths=0 and that would make an extra jitter as well?

    My StartValue/EndValue need to be the actual frequency2 (the one that changes), right? So if the previous value was 199.95Hz (819) I would move up to 204.65 (838) to get the final difference of 11.1Hz-6.4Hz=4.7Hz, from the base frequency1 of 211.91Hz (868)? :)

    I'm just not sure what you mean by switching from 24.8 to 16.16 (unsigned vs signed?). Right now step/step2 are long in my code.

    Could I also make this as a function instead of being part of the main code?

    I assume I will have to tie the LEDs in the interrupt then and drive them directly from there because I do not need the frequency1/frequency2 but their binaural difference? Or is there an easier way for that?

    I know, so many questions... :)
     
  8. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,161
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    Sorry, I assumed that int was 16 bit. If int is 8 bit then it should be changed to word/long or whatever is 16 bit.

    The first value of step would be StartValue as we add that to the tweened value.

    I should have said 16.16 instead of 8.8. What size is long 16 or 32 bit?

    The tween calculation could be a function but it would have to be called at least 1 every hundredth of a second. It can possible be slower as humans are not good at picking exact frequencies, too slow and it will sound stepped.

    For the LED, I would try,
    Code (text):

    void interrupt(){
        position+=step;
        TempB=SineTab[(position>>8)&31];
        PORTB=TempB;
        position2+=step2;
        TempA=SineTab[(position2>>8)&31];
        PORTA=TempA;
        if((TempA-7)+(TempB-7)>0){
            LED=1;
        }
        else{
            LED=0;
        }
        if(timeCount++==20){
            timeCount=0;
            hundreths++;
        }
    }
     
    Edit, missed a bit. The 4.096 is derived from the earlier formula. N=F * 500*10-6*32*256.
    4.096 = 500*10-6*32*256

    Mike.
     
    Last edited: Jan 8, 2008
  9. 3dluvr

    3dluvr Member

    Joined:
    Aug 25, 2003
    Messages:
    76
    Likes:
    0
    The int in CCS C is 8-bit (int8) while a long is an int16, long long being int32 and float being int32, all except of float are unsigned by default.

    What I was hoping to do is have a gradient function as well as a duration one, so that various "sessions" could be played out ie:

    play_tone(<freq>, <dur>);
    grad_tone(<freq1>, <freq2>, <dur>);
    play_tone(<freq>, <dur>);
    ....

    Or even better to have a scripted sequence table converted to suit this code from the output of Gnaural (http://gnaural.sourceforge.net/)...

    Here's an enhanced version of the Brain Machine for AVR, http://www.tahina.priv.at/electronics.html#index3h1 and if you peek in the source code the fellow uses DDS-generated audio and converts a session through Perl from Gnaural. I'd like to be able to do something like that ultimately in my project as well.

    Thank you.
     
    Last edited: Jan 9, 2008
  10. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,161
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    To make the code more functional you could do,
    Code (text):

    void PlayTone(long Freq1,long Freq2,long Duration){
    signed int32 work;
        hundreths=0;
        While(hundreths<Duration){
            work=Freq2-Freq1;
            work*=hundreths;
            work/=Duration;
            step=work+Freq1;
    }



    void main(){
        PlayTone(45,45,200);       //play 11.1Hz for 2 seconds
        PlayTone(45,868,500);      //increase to 211.9 over 5 seconds
        PlayTone(868,819,1000);    //play 211.9Hz and change to 200Hz over 10 seconds
    }
     
    I think it is better to precalculate the frequency values so as to avoid FP calculations. I added a 32 bit work variable to stop the possibility of overflow and make it signed.

    The page you linked to states that the LED should be lit when the 2 signals are out of phase. This can be easily done with,
    Code (text):

        if((TempA&8)!=(TempB&8)){
            LED=1;
        }
        else{
            LED=0;
        }
     
    I suspect that this will produce a beat of twice the required frequency as it effectively lights the LED on each half cycle and the earlier code may be better.

    Am I right in assuming that this code works as intended and you are getting the correct frequencies output? It's unusual to write code without any hardware to test it on and even more so for it to work.

    Mike.
     
    Last edited: Jan 9, 2008
  11. 3dluvr

    3dluvr Member

    Joined:
    Aug 25, 2003
    Messages:
    76
    Likes:
    0
    Sorry I should've been more clear about what binaural sounds do.

    We create a base frequency, let's say 200Hz and we play it into one ear. Then we create another frequency offsetted from the base by the desired brain wave frequency, let's say 211.1Hz, and play that into the other ear.

    The two tones "interfere" in the brain and their difference of 11.1Hz results in the entrainment signal that the brain will start syncing to and resonating in the Alpha state region.

    Beta is 13-30Hz, Alpha is 8-13Hz, Theta is 4-8Hz and Delta is 0-4Hz, the first is conscious and alert state, then dreamy, following subconscious and deep unconscious.

    By lowering the resonant frequency over a period of time we slowly relax the mind and go into deeper relaxation, of course it all depends what's the final goal. We do not want a subject to fall asleep by resonating in Theta and Delta continuously, so we add a bit of Beta to keep the person awake. :)

    So we really never play anything over 30-35Hz difference, and the 200Hz as base is only picked because it's more pleasant than 400Hz which sounds too high pitched.

    I have tested the output and I ran into a problem, a jitter or subharmonic noise of sort that occurs. I thought it was my MCU at first but then I swapped output_a and output_b and the same thing happens, so this has to do with TempB computation because TempA is just fine, nice constant tone.

    Code (text):

        position+=step;
        TempB=SineTab[(position>>8)&31];
        output_b(TempB);
        position2+=step2;
        TempA=SineTab[(position2>>8)&31];
        output_a(TempA);
     
    I have no clue why is this happening, I have tried outputting tones on PortA and PortB as per code above as well as both tones on PortB and it still occurs regarding TempB part.

    I am using an internal oscillator in the PIC, just trying to have as few components as possible since I'd like to mount the circuit onto the glasses like they did in the original project. Do you think that internal oscillator could be adding these noises because I'm running it at its max?

    I will try to record the sound through my soundcard and attach it so you can hear what I mean, it is rather distinct. Here's the full code so far in case you can see something I'm obviously unable to see, I took out the LED and gradient code until this noise issue is resolved:

    Code (text):

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

    int timeCount; //int = int8
    long TempA, TempB, step, step2, position, position2; //long = int16

    CONST unsigned int SineTab[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,4,6};

    #INT_TIMER2
    void interrupt(){
            position+=step;
            TempB=SineTab[(position>>8)&31];
            output_b(TempB);
            position2+=step2;
            TempA=SineTab[(position2>>8)&31];
            output_a(TempA);
    }

    void main(void) {
        setup_oscillator(OSC_8MHZ | OSC_INTRC);   // Use internal 8MHz oscillator
        set_timer2(0);
        setup_timer_2(T2_DIV_BY_4, 249, 1); // 500us = (1/8000000 * 4 * 4 * 249+1)

            step = 819; // changing frequency
        step2 = 868; // base frequency
        enable_interrupts(INT_TIMER2);
        enable_interrupts(GLOBAL);
       
        while(TRUE)
        {
            delay_us(1);
        }
       
            disable_interrupts(INT_TIMER2);
    }
     
    Many thanks!
     
  12. 3dluvr

    3dluvr Member

    Joined:
    Aug 25, 2003
    Messages:
    76
    Likes:
    0
    In follow-up, I have tried lowering the internal oscillator frequency to 4MHz without much luck.

    I am attaching the two sound recordings from PortA (output_good.mp3) and PortB (output_bad.mp3). What's even more bizzare is that the good sound is fairly uniform but a bit distorted, while the bad sound has jitters but it's quite clear?!?

    Both ports run identical 4-bit R-ladders (bit0-bit3: 8k2, 3k9, 2k, 1k tied together with 10k onto gnd) and identical RC filters (50ohm/10uF).

    I am at a loss...
     

    Attached Files:

  13. kchriste

    kchriste New Member Forum Supporter

    Joined:
    Jul 23, 2006
    Messages:
    3,677
    Likes:
    47
    Location:
    Victoria BC, Canada
    What happens when you output the same frequency (The good one) on both PortA and PortB? As they both the same or is PortB still bad?
     
  14. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,161
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    Is the noise tied to the step value. If you swap the 2 values (819 & 868) does the distortion move to the other channel?

    Mike.
     
  15. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,161
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    3dlvr,

    You say you have posted all your code but I don't see where you setup the ports. How do TRIS, ANSEL and CMCON get setup?

    Mike.
     
  16. 3dluvr

    3dluvr Member

    Joined:
    Aug 25, 2003
    Messages:
    76
    Likes:
    0
    Hello,

    I have found a reason for the distortions on the good channel - my bad desk illumination. I have mistakenly used a 1K resistor to the ground instead of 10K, in my 4-bit network on the PortA. That's fixed now...

    But the problem with the jitter on PortB is still there, I have tested the output of both TempA and TempB values to both ports.
    TempA (the good one) sounds just fine, but TempB has jitter on either and both ports regardless.

    The compiler uses "standard" mode for IO whereas pins are set to input or output every time they are used. It is less optimized way as it takes 1 byte for every port set to standard IO, but I'm not concerned with optimization right now.

    I did try using a "fixed" IO mode where I would declare the ports used in output in my pre-processor directives, but that made no difference, the jitter is still there coming from the TempB variable.

    I would rule out a bad MCU because the jitter happens on the PortA when redirected there, not just PortB. This must be something to do with the computation part of TempB then I just can't see what, it seems fairly straight forward?
     
  17. 3dluvr

    3dluvr Member

    Joined:
    Aug 25, 2003
    Messages:
    76
    Likes:
    0
    I have just tested something, put the step2 (base frequency) value to be the same, 819 as step (changing frequency) one.

    step2 was 868 before and now with 819 I get that jitter on the PortA as well.

    So the problem is with the FP calculations or something of sorts, dividing/multiplying with 868 is nice but with 819 is not and so the SineTab produces jitters.

    But now what? use float for all, then how would I round all this properly so it does not jitter?
     
  18. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,161
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    I can't understand how 819 can be a problem yet 868 isn't. The only thing I can think is that there is interference between the 119.9 frequency and the 2000Hz frequency.

    What happens with 818 and 820?

    Are you able to compile assembler as I could knock up a quick asm version to check it isn't a quirk of the compiler.

    Mike.
    Edit, a possible solution may be to switch to 250uS interrupts and hope the filter sorts out any problems.
     
  19. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,161
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    Another thought, how are you listening to the signals. If it's through your PC then the sample frequency could be interfering with the playback frequency. The glitch is very distinctive, can you see it on a scope?

    Mike.
     
  20. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,161
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    Just incase anyone fancies trying this in asm, here it is.
    Code (text):

                           
                    LIST    p=16F88
                    #INCLUDE <P16F88.INC>
                           
                    errorlevel -302
    ;------------------------------------------------------------------------------  
                           
                    __CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
                    __CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
                           
    ;------------------------------------------------------------------------------  
                           
                    CBLOCK  0x20            ; Sample GPR variable registers allocated contiguously
    Position:2              
    Step:2                  
    TempB                  
                    ENDC    
                           
    W_TEMP          EQU     0x7D            ; w register for context saving (ACCESS)
    STATUS_TEMP     EQU     0x7E            ; status used for context saving (ACCESS)
    PCLATH_TEMP     EQU     0x7F            ; variable used for context saving
                           
    ;------------------------------------------------------------------------------  
    ; RESET VECTOR          
    ;------------------------------------------------------------------------------  
                           
    RESET           ORG     0x0000          ; processor reset vector9
                    PAGESEL START
                    GOTO    START           ; go to beginning of program
                           
    ;------------------------------------------------------------------------------  
    ; INTERRUPT SERVICE ROUTINE  
    ;------------------------------------------------------------------------------  
                           
    ISR             ORG     0x0004          ; interrupt vector location
    ;         Context saving for ISR  
                    MOVWF   W_TEMP          ; save off current W register contents
                    MOVF    STATUS,W        ; move status register into W register
                    MOVWF   STATUS_TEMP     ; save off contents of STATUS register
                    MOVF    PCLATH,W        ; move pclath register into W register
                    MOVWF   PCLATH_TEMP     ; save off contents of PCLATH register
                           
    ;------------------------------------------------------------------------------  
    ; USER INTERRUPT SERVICE ROUTINE GOES HERE  
    ;------------------------------------------------------------------------------  
                           
                    movfw   Step            ;Position+=Step
                    addwf   Position,F
                    movfw   Step+1
                    btfsc   STATUS,C
                    addlw   1
                    addwf   Position+1,F
                    movlw   high GetSine    ;Prepare to get sine value
                    movwf   PCLATH
                    movfw   Position+1
                    andlw   .31             ;TempB=(Position>>8)&31
                    call    GetSine
                    movwf   TempB
                    movwf   PORTB
                           
                    bcf     PIR1,TMR2IF
                           
                           
    ;         Restore context before returning from interrupt  
                    MOVF    PCLATH_TEMP,W   ; retrieve copy of PCLATH register
                    MOVWF   PCLATH          ; restore pre-isr PCLATH register contents
                    MOVF    STATUS_TEMP,W   ; retrieve copy of STATUS register
                    MOVWF   STATUS          ; restore pre-isr STATUS register contents
                    SWAPF   W_TEMP,F
                    SWAPF   W_TEMP,W        ; restore pre-isr W register contents
                    RETFIE                  ; return from interrupt
                           
    ;------------------------------------------------------------------------------  
    ; MAIN PROGRAM          
    ;------------------------------------------------------------------------------  
                           
    START                  
                    bsf     STATUS,RP0
                    movlw   0xf0
                    movwf   TRISA
                    movwf   TRISB
                    movlw   b'00000111'     ;   disable comparator
                    movwf   CMCON
                    movlw   B'01110000'     ;select 8MHz clock
                    movwf   OSCCON
                    clrf    ANSEL           ;no ADCs
                    bcf     STATUS,RP0
                           
                    movlw   1<<TMR2ON|b'01'<<T2CKPS0
                    movwf   T2CON
                    bsf     STATUS,RP0
                    movlw   .249
                    movwf   PR2
                    bsf     PIE1,TMR2IE
                    bcf     STATUS,RP0
                           
                    movlw   low(.819)
                    movwf   Step
                    movlw   high(.819)
                    movwf   Step+1
                           
                    movlw   (1<<GIE|1<<PEIE|0<<TMR0IE|0<<INTE|0<<RBIE|0<<TMR0IF|0<<INTF|0<<RBIF)
                    movwf   INTCON          ;       enable Peripheral interupts
                           
                           
    ;------------------------------------------------------------------------------  
    ; PLACE USER PROGRAM HERE  
    ;------------------------------------------------------------------------------  
                           
                    GOTO    $
                           
                    org     0x0400
    GetSine         addwf   PCL,F
                    dt      .7,.8,.10,.11,.12,.13,.13,.14,.14,.14,.13,.13,.12,.11,.10,.8
                    dt      .7,.6,.4,.3,.2,.1,.1,.0,.0,.0,.1,.1,.2,.3,.4,.6
                           
                    END    
     
    Mike.
     
  21. 3dluvr

    3dluvr Member

    Joined:
    Aug 25, 2003
    Messages:
    76
    Likes:
    0
    I will try 818 and 820 and report here, as well as 250uS interrupts...
     

Share This Page