OCR1A and OCR1B cannot be used at the same time

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

Hello guys,

 

Good day.  May I ask for your advise on the PWM topic of the ATMega328P.  I was trying some codes on the ATMega328P Arduino where I wanted to enable both PB1 (OCR1A) and PB2 (OCR1B) at the same time.  OCR1A output is a variable PWM that is controlled by a potentiometer.  OCR1B output is a fixed 8kHz PWM which can be enabled by a toggle switch.  I found out that they cannot both be activated at the same time.  I need to disable either one of them in order to make the other work.

 

I have these 3 questions:

1. Is this expected as they are sharing the same register?

2. Is there a way to enable the 2 outputs as I wanted to?

3. Also I found out that this code below needed to be placed inside an ISR loop.  If I put it inside the while(1) loop it does not work as intended.

May I ask what is the reason why this code does not work when inside the while(1) loop?

 

 

{

             if(PINB&(1<<0))                                            

{

             OCR1B= 77.5;                           

             }

             else

             {

                    OCR1B= 250;                             //disable OCR1B. 

             }

       }

 

Thank you for your time.

 

#include <avr/io.h>					
#define F_CPU 16000000
#include <util/delay.h>
#include <avr/interrupt.h>

void ADC_init()
{
    ADMUX=(1<<REFS0);
    ADCSRA|=(1<<ADEN)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);		//enable ADC/  enable ADC interrupt
    sei();									                            //enable global interrupt
}

void Variable_PWM()								
{	TCCR1A=(1<<COM1A1)|(1<<COM1A0)|(1<<WGM11)|(1<<WGM10);		//set to Fast PWM 10bit TOP/ Set OC1A/OC1B on Compare Match, clear  OC1A/OC1B at BOTTOM (inverting mode)
    TCCR1B=(1<<WGM12)|(1<<CS11)|(1<<CS10);				        //set prescaler to 64
}										                        //OCR1A


void PWM_init()
{
TCCR1A=(1<<COM1B1)|(1<<COM1B0)|(1<<WGM11);			//Set OC1A/OC1B on Compare Match, clear OC1A/OC1B at BOTTOM (inverting mode)
TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11);				//set to Fast PWM TOP is ICR1/  set prescale to 8
ICR1=250;								            //OCR1B
}

                        


int main(void)
{
    DDRD=0x7F;						//pins0,1,2,3,4,5 and 6 are outputs
    DDRB=0x0E;						//pins 1,2 and 3 are outputs
    DDRC=0X24;						//pins 2 and 5 are outputs
    
    ADC_init();
    Variable_PWM();
    PWM_init();
    
    ADCSRA|=(1<<ADSC);
    
    while (1)
    {	
    }
}


ISR(ADC_vect)
{
    {
        if(PINB&(1<<0))							
        {
        OCR1B= 77.5;					
        }
        else
        {
            OCR1B= 250;					//disable OCR1B.  
        }
    }
    
    
    switch(ADMUX&(0x07))
    {
        case 0:						
        if((ADC>512)&&(ADC<819))			//set threshold from 2.5V to 4V
        {
            PORTD|=(1<<2);			    	//enable PortD pin2
            PORTC&=~(1<<2);				    //disable PortC pin2
        }
        else if(ADC>=819)					
        {
            PORTC|=(1<<2);			    	//enable PortC pin2
            PORTD&=~(1<<2);			    	//disable PortD pin2
        }		
        else
        {
            PORTD&=~(1<<2);				    //disable PortD pin2
            PORTC&=~(1<<2);			    	//disable PortC pin2
        }
        break;
        
        case 1:
        {
            OCR1A=1023-ADC;					
        }
        break;
        
        case 3:						
        if(ADC>=512)
        {
            PORTC|=(1<<5);
        }
        else
        {
            PORTC&=~(1<<5);
        }
        break;
        
    }
    
    ADMUX=((ADMUX&(0x07))+1);
    ADMUX&=0x07;
    ADMUX|=(1<<REFS0);
    ADCSRA|=(1<<ADSC);
}




 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void Variable_PWM()								
{	TCCR1A=(1<<COM1A1)|(1<<COM1A0)|(1<<WGM11)|(1<<WGM10);		//set to Fast PWM 10bit TOP/ Set OC1A/OC1B on Compare Match, clear  OC1A/OC1B at BOTTOM (inverting mode)
    TCCR1B=(1<<WGM12)|(1<<CS11)|(1<<CS10);				        //set prescaler to 64
}										                        //OCR1A


void PWM_init()
{
TCCR1A=(1<<COM1B1)|(1<<COM1B0)|(1<<WGM11);			//Set OC1A/OC1B on Compare Match, clear OC1A/OC1B at BOTTOM (inverting mode)
TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11);				//set to Fast PWM TOP is ICR1/  set prescale to 8
ICR1=250;								            //OCR1B
}

You're loading different values into TCC1A and TCCR1B! Obviously only the last one is the one that sticks!

 

You can have two pwm outputs - but they have to be the same frequency. Have another read of the datasheet in relation to the timers and pwm modes.

 

Also, your ADC code does not make much sense - why have an interrupt for it if the isr doesn't do anything with the adc result? You REALLY are making it hard for yourself - you don't need any interrupts at this point in time.

 

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

in addition what do you think is the actual value written to OCR1B in this case:

 

        {
        OCR1B= 77.5;					
        }

 

OCR1A and OCR1B can be used at the same time, but they share the same timer. So they will have the same frequency. You can change the duty cycle on both, but they always will have the same frequency.

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

Kartman wrote:

void Variable_PWM()								
{	TCCR1A=(1<<COM1A1)|(1<<COM1A0)|(1<<WGM11)|(1<<WGM10);		//set to Fast PWM 10bit TOP/ Set OC1A/OC1B on Compare Match, clear  OC1A/OC1B at BOTTOM (inverting mode)
    TCCR1B=(1<<WGM12)|(1<<CS11)|(1<<CS10);				        //set prescaler to 64
}										                        //OCR1A


void PWM_init()
{
TCCR1A=(1<<COM1B1)|(1<<COM1B0)|(1<<WGM11);			//Set OC1A/OC1B on Compare Match, clear OC1A/OC1B at BOTTOM (inverting mode)
TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11);				//set to Fast PWM TOP is ICR1/  set prescale to 8
ICR1=250;								            //OCR1B
}

You're loading different values into TCC1A and TCCR1B! Obviously only the last one is the one that sticks!

 

You can have two pwm outputs - but they have to be the same frequency. Have another read of the datasheet in relation to the timers and pwm modes.

 

Also, your ADC code does not make much sense - why have an interrupt for it if the isr doesn't do anything with the adc result? You REALLY are making it hard for yourself - you don't need any interrupts at this point in time.

 

 

Thank you sir for the hint.  I deleted some codes which are not relevant.  I just found out that using _delay_ms() function, which I did not show on the code,  can only be used practically with ISR otherwise I get some problems.

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

meslomp wrote:

in addition what do you think is the actual value written to OCR1B in this case:

 

        {
        OCR1B= 77.5;					
        }

 

OCR1A and OCR1B can be used at the same time, but they share the same timer. So they will have the same frequency. You can change the duty cycle on both, but they always will have the same frequency.

 

Thank you for the hint sir appreciate your input.  This should be a whole number not a fraction.

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

ryan2019 wrote:
I just found out that using _delay_ms() function, which I did not show on the code,  can only be used practically with ISR otherwise I get some problems.
Well it is interruptible - if that's what you mean? As such it can only be guaranteed to give the published duration if interrupts are prevented while it is being used. This is why, in reality, embedded applications almost never use non-timer based, software delay loops. It's fine if you want to flash and LED as a test or something:

int main(void) {
    DDRB = 0xFF;
    while(1) {
        PORTB ^= 0xFF;
        _delay_ms(100);
    }
}

but it's not much use for anything but this kind of thing.