Fast PWM and UART interrupts

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

I have a working BIOS on my AVR that can be communicated with over the serial port.

I have implemented

ISR(USART_RXC_vect)
{
   char ReceivedByte;
   ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"
   put_in_serial_rec_buffer (ReceivedByte);
   if (ReceivedByte == 0x0D) handle_incoming_command();
}

that puts the received char in a ring buffer and calls handle_command() once an 0x0D is received. Works great! One of the received commands activates timer 1 in Fast PWM mode as follows:

        timer1SetPrescaler(3);
        TCNT1 = 0;
        ICR1 = 0x6000;
        OCR1A = 0x4000;
        OCR1B = 0x2000;

        sbi(TCCR1A,COM1A1);     // set OC1 on at COMP
        sbi(TCCR1A,COM1A0);     // reset OC1 at TOP
   
        // mode 14: OCR1A defines COMP; ICR1 defines TOP;                             
        // mode 15: OCR1B defines COMP; OCR1A defines TOP;
        cbi(TCCR1A,WGM10);      //  -
        sbi(TCCR1A,WGM11);      //  - Fast PWM
        sbi(TCCR1B,WGM12);      //  - Fast PWM
        sbi(TCCR1B,WGM13);      //  -
  
        sbi(TIMSK, OCIE1A);     // start generating the OC1 signal
signal

Once this thing is activated the AVR no longer receives commands over the serial port. Do two interrupts interfere here and render ISR(USART_RXC_vect) useless?

Thanks a lot in advance!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Let's see the ISR for the timer.

Which AVR model are you using?

Set the prescaler (which starts the timer) last in the setup sequence, or set all bits at once. You have the timer running in many intermediate modes with your sequence.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Let's see the ISR for the timer.

Which AVR model are you using?

Set the prescaler (which starts the timer) last in the setup sequence, or set all bits at once. You have the timer running in many intermediate modes with your sequence.

I have isolated the timer 1 code into

int main (void)
{
        DDRB = _BV (PB1);               /* PB1 is digital output */
        DDRB = _BV (PB2);               /* PB2 is digital output */
        
        timer1SetPrescaler( TIMER_CLK_DIV8 );
        TCNT1 = 0;
        ICR1 = 0x7040;
        OCR1A = 0xffff;
        OCR1B = 0x7fff;
        
        sbi(TCCR1A,COM1A1);     // set OC1 on at COMP
        sbi(TCCR1A,COM1A0);     // reset OC1 at TOP
        
        // mode 14: OCR1A defines COMP; ICR1 defines TOP;
        // mode 15: OCR1B defines COMP; OCR1A defines TOP;
        sbi(TCCR1A,WGM10);      //  -
        sbi(TCCR1A,WGM11);      //  - Fast PWM
        sbi(TCCR1B,WGM12);      //  - Fast PWM
        sbi(TCCR1B,WGM13);      //  -
        
        sbi(TIMSK, OCIE1A);
        sei();
        
        while (1)                       /* loop forever */
        {
        }
         
        return (0);
}

since the generated signal on OC1 is far from what I expect. Actually I get nothing right now. I have an ATMEGA8 running at 16MHz. What do you mean by ISR and intermediate modes?

Thanks a lot!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Let's see the ISR for the timer.

Oh, sorry! It's already a bit late here. My idea was that no ISR was necessray in "Fast PWM" mode. May be I am mistaken on that. My complete code now looks as follows:

ISR(TIMER1_COMPA_vect)
{
        DDRB = _BV (PB1);               /* PB1 is digital output */
}
        
ISR(TIMER1_OVF_vect)
{
         DDRB = _BV (PB1);               /* PB1 is digital output */
         
}
        
int main (void)
{
        DDRB = _BV (PB1);               /* PB1 is digital output */
        DDRB = _BV (PB2);               /* PB2 is digital output */
        
        timer1SetPrescaler( TIMER_CLK_DIV8 ); 
        TCNT1 = 0;
        ICR1 = 0xffff;
        OCR1A = 0xafff;
        OCR1B = 0x7fff;
  
        sbi(TCCR1A,COM1A1);     // set OC1 on at COMP
        sbi(TCCR1A,COM1A0);     // reset OC1 at TOP
        
//        sbi(TCCR1A,COM1B1);     // set OC2 on at COMP
//        sbi(TCCR1A,COM1B0);     // reset OC2 at TOP
            
        // mode 14: OCR1A defines COMP; ICR1 defines TOP;
        // mode 15: OCR1B defines COMP; OCR1A defines TOP;
        sbi(TCCR1A,WGM10);      //  -
        sbi(TCCR1A,WGM11);      //  - Fast PWM
        sbi(TCCR1B,WGM12);      //  - Fast PWM
        sbi(TCCR1B,WGM13);      //  -
            
        sbi(TIMSK, OCIE1A);
        sei();
             
        while (1)                       /* loop forever */
        {
        }

        return (0);
}

I get nothing on OC1. I had thi srunning two weeks ago and meant I got it to run with the code above. Obviously I am missing a bit!?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

What do you mean by ISR ...?

Quote:

sbi(TIMSK, OCIE1A);

You are enabling an interrupt source, but we have not yet seen an ISR -- Interrupt Service Routine; handler -- for that enabled interrupt source. If you have none, then (depending on your toolchain and the phase of the moon) your AVR is constantly resetting.

Quote:

What do you mean by ... intermediate modes?

First, you never >>set<< the timer control registers to a value; rather, you are assuming that they are 0. Yes, they are set to 0 at AVR reset. What if your program runs away? What happens next week if you call two different timer setup routines in your app?


        timer1SetPrescaler( TIMER_CLK_DIV8 ); // the timer is started in mode 0
        TCNT1 = 0; // oops; timer count reset, but it keeps climbing...
        ICR1 = 0x7040;
        OCR1A = 0xffff; // remember still mode 0 ...
        OCR1B = 0x7fff; 
       
        sbi(TCCR1A,COM1A1);     // set OC1 on at COMP  now, pins might be connected to timer
        sbi(TCCR1A,COM1A0);     // reset OC1 at TOP
       
        // mode 14: OCR1A defines COMP; ICR1 defines TOP;
        // mode 15: OCR1B defines COMP; OCR1A defines TOP;
        sbi(TCCR1A,WGM10);      //  -  // now in mode 1
        sbi(TCCR1A,WGM11);      //  - Fast PWM // now in mode 3
        sbi(TCCR1B,WGM12);      //  - Fast PWM // now in mode 7
        sbi(TCCR1B,WGM13);      //  - // now in mode 15
       

And compare match probably already occurred when you started the timer, so you are going to get a spurious interrupt.

Quote:

Actually I get nothing right now.

I do not see the pins being made outputs.

Compare to:

TCNT1 = 0;
ICR1 = 1234;
OCR1A = 9876;
OCR1B = 5432;
TIFR = (1 << OCIE1A);
TIMSK = (1 << OCIE1A); // or |=
TCCR1A = (1 << WGM10) | (1 << WGM11) | (1 << COM1A1) | (1 << COM1A0);
  // note as your comments say it makes no sense to look for an output on OCR1A pin in mode 15--won't work
TCCR1B = (1 << WGM12) | (1 << WGM13) | (1 << CS11); // starts the timer, from a fully initialized and stopped state 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

Compare to:

TCNT1 = 0;
ICR1 = 1234;
OCR1A = 9876;
OCR1B = 5432;
TIFR = (1 << OCIE1A);
TIMSK = (1 << OCIE1A); // or |=
TCCR1A = (1 << WGM10) | (1 << WGM11) | (1 << COM1A1) | (1 << COM1A0);
  // note as your comments say it makes no sense to look for an output on OCR1A pin in mode 15--won't work
TCCR1B = (1 << WGM12) | (1 << WGM13) | (1 << CS11); // starts the timer, from a fully initialized and stopped state 

Oh man! I should get a bit more sleep! Thanks for the hint with the superfluous OCIE1A enabling and the ISR reset problematic. The follwoing code works perfectly:

        sbi(DDRB,PB1);                  /* digital output */
        sbi(DDRB,PB2);                  /* digital output */
        sbi(DDRB,PB5);                  /* digital output */
        sbi(DDRD,PD7);                  /* digital output */
 
        sbi (UCSRB, RXCIE);
        sei();
            
       TCCR1A = 0;
        TCCR1B = 0;
        TCNT1 = 0;
        ICR1 = 0x7000;
        OCR1A = 0x6000;
        OCR1B = 0x3000;
            
        sbi(TCCR1A,COM1A1);     // set OC1 on at COMP
        sbi(TCCR1A,COM1A0);     // reset OC1 at TOP
        sbi(TCCR1A,COM1B1);     // set OC2 on at COMP
        sbi(TCCR1A,COM1B0);     // reset OC2 at TOP
          
        // mode 14: (PWM on OC1A and OC1B)
        //   OCR1A defines COMP for PB1 (OC1A)
        //   OCR1B defines COMP for PB2 (OC1B)
        //   ICR1 defines TOP for both lines
        //   
        // mode 15: (PWM on OC1B only)
        //    OCR1B defines COMP
        //    OCR1A defines TOP
        //
        cbi(TCCR1A,WGM10);      //  -
        sbi(TCCR1A,WGM11);      //  - Fast PWM
        sbi(TCCR1B,WGM12);      //  - Fast PWM
        sbi(TCCR1B,WGM13);      //  -
        
        timer1SetPrescaler( TIMER_CLK_DIV8 );   // prescaler

Thanks!!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

one final question regardingthis timer issue. One of the serial command has the purpose of stopping the PWM on PB1 (OC1A). I do this as follows:

// stop timer 1 fast PWM action
timer1SetPrescaler( 0 ); // stop timer 1
cbi(PORTB,PB1);
cbi(PORTB,PB2);

The action stops but the signal on PB1 is high after this!? I guess it takes some time for the background mechanic to actually stop the timer and for whatever reason it gets modified by the dying timer after my cbi(PORTB,PB1) statement. Any idea what I can do to effectively shut the PIN down to LOW?

Thnaks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
sbi(DDRB,PB1);                  /* digital output */ 
sbi(DDRB,PB2);                  /* digital output */ 
sbi(DDRB,PB5);                  /* digital output */ 

(3x2 cycles=6 clck cycles) You are wasting instructions and clock cycles every time you do things like these.

DDRB |= (1<<PB5 | 1<<PB2 | 1<<PB1)

(2 clck cycles) does the same thing in 1/3 of the time.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try this: set port pin to output and set it low BEFORE initing the pwm. Then later, when disabling the pwm, the 'pwm override' of that output will be removed, exposing a lo output pin?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
Try this: set port pin to output and set it low BEFORE initing the pwm. Then later, when disabling the pwm, the 'pwm override' of that output will be removed, exposing a lo output pin?

I am doing PORTB = 0 after setting DDRB direction bits. But thi smakes no difference. I still see the high after disbaling PWM!?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have to set the COM1xx bits to 0 to restore

Quote:
Normal port operation, OC1A/OC1B disconnected.

/Lars