tags:

views:

918

answers:

4

Im using C language for a PIC18F to produce tones such that each of them plays at certain time-interval. I used PWM to produce a tone. But I don't know how to create the intervals. Here is my attempt.

#pragma code               // 
void main (void)
{

 int i=0;
    // set internal oscillator to 1MHz 
    //OSCCON = 0b10110110;              // IRCFx = 101
    //OSCTUNEbits.PLLEN = 0;                //PLL disabled

    TRISDbits.TRISD7 = 0;

    T2CON  = 0b00000101;     //timer2 on
    PR2    = 240;
    CCPR1L = 0x78;
    CCP1CON= 0b01001100;



LATDbits.LATD7=0;
Delay1KTCYx(1000);

while(1);
}
A: 

In Windows you can use the Beep function in kernel32:

    [DllImport("kernel32.dll")]
    private static extern bool Beep(int frequency, int duration);
Jeff Kotula
and in your car, you can use the horn.... (unfortunately, he's on an embedded processor).
AShelly
No need to get snarky. I just misunderstood the context of the question...
Jeff Kotula
+1 for the good laugh.
Nate
+6  A: 

When I'm doing embedded programming, I find it extremely useful to add comments explaining exactly what I'm intending when I'm set configuration registers. That way I don't have to go back to the data sheets to figure out what 0x01001010 does when I'm trying to grok the code the next time I have to change it. (Just be sure to keep the comments in sync with the changes).

From what I can decode, it looks like you've got the PWM registers set up, but no way to change the frequency at your desired intervals. There are a few ways to do it, here are 2 ideas:

  • You could read a timer on startup, add the desired interval to get a target time, and poll the timer in the while loop. When the timer hits the target, set a new PWM duty cycle, and add the next interval to your target time. This will work fine, until you need to start doing other things in the background loop.
  • You could set timer0's count to 0xFFFF-interval, and set it to interrupt on rollover. In the ISR, set the new PWM duty cycle, and reset timer0 count to the next interval.

One common way of controlling timing in embedded processes looks like this:

int flag=0;
void main()
{
  setup_interrupt(); //schedule interrupt for desired time.
  while (1)
  {  
     if (flag)
     {  
        update_something();
        flag = 0;
     }
  }

Where does flag get set? In the interrupt handler:

void InterruptHandler()
{
  flag = 1;
  acknowledge_interupt_reg = 0;
}

You've got all the pieces in your example, you just need to put them together in the right places. In your case, update_something() would update the PWM. The logic would look like: "If it's on, turn it off; else turn it on. Update the tone (duty cycle) if desired"

There should be no need for additional delays or pauses in the main while loop. The goal is that it just runs over and over again, waiting for something to do. If the program needs to do something else at a different rate, you can add another flag, which is triggered completely independently, and the timing of the two tasks won't interfere with each other.

AShelly
+1  A: 

Thanks for the reply,AShelly. My answers to your questions :

1) Yes,the PWM is working.But without timer & interrupt(or maybe other means),it only produces a constant flat sound.

2) Yes,the pulses from PWM pin to produce sound is also connected to RA4/TOCKI pin.I have set T0 as counter mode.

3) I have fixed it =)

4) Yup,I know.It doesn't matter how long the interval is for now.

5 & 6 ) erm because I was trying to off the PWM pin for the interval purpose..

7) Yes,I did already set the PR2.

Below is my updated code...I got this error message "Halting build on first failure as requested."

/** I N C L U D E S **************************************************/
#include "p18f46k20.h"
#include "delays.h"
#include "12 CCP PWM.h"  // header file
#include "08 Interrupts.h"

/** V A R I A B L E S *************************************************/
#pragma udata   // declare statically allocated initialized variables


/** D E C L A R A T I O N S *******************************************/

void PWM(void);
void timer0(void);

//----------------------------------------------------------------------------
//Low priority interrupt vector

#pragma code InterruptVectorLow = 0x18
void
InterruptVectorLow (void)
{
  _asm
    goto InterruptHandlerLow
  _endasm
}


//-----------------------------------------------------------------------------


#pragma code               // declare executable instructions

void main (void)
{



    OSCCON = 0b10110110;              //Device enters idle mode on SLEEP instruction
               // set internal oscillator to 1MHz
               // Device is running from the internal oscillator
               // HFINTOSC frequency is stable
               // Internal oscillator block


    OSCTUNEbits.PLLEN = 0;                //PLL disabled

    RCONbits.IPEN=1;          //Enable priority levels on interrupts

    //INTCON2bits.INTEDG0=0;      // interrupt is falling edge
    //INTCONbits.INT0IE=1;       // enable  interrupt
    INTCONbits.GIEL = 1;      // enable Low-priority interrupt(which this program is using)
    INTCONbits.GIEH = 1;               //enable global interrupts
    INTCONbits.TMR0IE=1;         //enable the TMR0 overflow interrupt


    PORTA=0x04;         // ON RA4 for TOCKl pin to input pulses from PWM pin (for T0's counter purpose)


do
{

PWM();

}while(1);

}

void PWM (void)
{
    TRISDbits.TRISD7 = 0;      //PWM-pin ON
    T2CON  = 0b00000101;      //timer2 on 
    PR2    = 240;
    CCPR1L = 0x78;
    CCP1CON= 0b01001100;

    timer0();

    while (INTCONbits.TMR0IF==0);    //wait for TMR0 to roll over 


}     




//---------------------------------------------------------------------------
void timer0(void)
{

    T0CON=0b10100010;    //enable timer0
            //timer is configured as 16-bits counter
            //transition on TOCKl pin
            //increment on low-high transition on T0CKl pin
            //set prescaler     
            //1:8 prescaler

    TMR0H = 0;                   // clear timer - always write upper byte first
    TMR0L = 0;



}


//-------------------------------------------------------------------------
// Low priority interrupt routine

#pragma code
#pragma interrupt InterruptHandlerLow
void 
InterruptHandlerLow ()
{    
    if (INTCONbits.TMR0IF)
    {
        // stop the PWM for a period of time

        INTCONbits.TMR0IF=0;       //clear roll-over interrupt flag
    }
}
twiart
Hi update your question with a update section instead of answer, and timerinterrupts/ticks is what you are looking for.
Johan
A: 

EDIT:
I'm now confused about what you are trying to accomplish. Do you want a series of pulses of the same tone (on-off-on-off)? Or do you want a series of different notes without pauses (do-re-me-fa-...)? I had been assuming the latter, but now I'm not sure.


After seeing your updated code, I'm not sure exactly how your system is configured, so I'm just going to ask some questions I hope are helpful.

  1. Is the PWM part working? Do you get the initial tone? I'm assuming yes.
  2. Does your hardware have some sort of clock pulse hooked up to the RA4/T0CKI pin? If not, you need T0 to be clock mode, not counter mode.
  3. Is the interrupt being called? You are setting INT0IE, which enables the external interrupt, not the timer interrupt
  4. What interval do you want between tone updates? Right now, you are getting 0xFFFF / (clock_freq/8) You need to set the TMR0H/L registers if you want something different.
  5. What's on LATD7? and why are you clearing it? Are you saying it enables PWM output?
  6. Why the call to Delay()? The timer itself should be providing the needed delay. There seems to be a disconnect about how to do timing. I'll expand my other answer
  7. Where are you trying to change the PWM frequency? Don't you need to write to PR2? You will need to give it a different value for every different tone.


"Halting build on first failure as requested."

This just says you have some syntax error, without showing us the error report, we can't really help.

AShelly