i have a problem using timer 1 fast pwm prescaler 64 tinkercad

i have to start with a 10% duty cycle and i have to increase it by 10% after 4 seconds, when it reaches 90% duty it has to go to 10% again. (period 100 hz 10 ms) i´m testing it in tinkercad and it doesn´t work, it oscilates between 10% and 20%. in this code i dont ask when it reaches 90% duty because it doesn´t reach 90% duty.

volatile uint32_t ul_contador = 0;
#define pulsador 5

void setup()
{

 pinMode(pulsador,INPUT);

}








void InitPWM1(){
 cli();
    OCR1A =2500; //64 Preescaler
    OCR1B =250; 
    TCCR1B |= (1<<WGM12)|(1<<WGM13);
    TCCR1A |= (1<<WGM10)|(1<<WGM11);

    
    //Timer 1 Preescaler 1
      TCCR1B &=~(1<<CS12);
      TCCR1B |= (1<<CS11);
      TCCR1B |=(1<<CS10);
      TCCR1A |= (1<<COM1B1);
      TCCR1A &=~ (1<<COM1B0); 
      TIMSK1 |= (1<<OCIE1A);    
 sei();
}
int main( void){
   DDRB |= (1<< DDB2);



   InitPWM1();
   while(1)
    {
  
  
                        
    }   
    
    

} 



ISR(TIMER1_COMPA_vect){
   ul_contador++;
    //PORTB |=(1 << PB2);
    if (ul_contador >= 400){
    
        OCR1A =2500; 
        OCR1B =OCR1B+250;
        ul_contador=0;
        PORTB ^= ( 1<< PB2 );
    }

}
1 Like

Why not use setup() and loop(), not main?

No need to constantly update OCR1A, set it once.

Its wise to reset TCNT1 to zero once you've changed the OCR1x registers.

I think it works as is except pin 10 isn't changing - change

        PORTB ^= ( 1<< PB2 );

to

        PORTB ^= PB2 ;

to fix that.
, not PB2 to fix that.
Not sure what PB2 is, I suspect its a bit number not a bitmask

Fine that you use Code Tags :slight_smile:

Can you add some comments to the timer initialization at the right place?
There remain undefined bits in the control registers which you never set nor clear.

Setting OCR1A (TOP) is not required in the ISR, may cause strange effects.

OCR1B increments beyond 2500 so that no more match is detected until it wraps around to 0.

This should toggle PB2 once with each change of the duty cycle, but it may do nothing if the waveform generator is activated for PWM. For testing purposes I'd use a different pin, not affected by the timer.

volatile uint32_t ul_contador = 0;
#define pulsador 5

void setup()
{
  pinMode(pulsador, INPUT);
  DDRB |= (1 << DDB2);
  InitPWM1();
}

void loop() {

}

void InitPWM1() {
  cli();
  OCR1A = 2500; //64 Preescaler
  OCR1B = 250;
  TCCR1B |= (1 << WGM12) | (1 << WGM13);
  TCCR1A |= (1 << WGM10) | (1 << WGM11);
     
  //Timer 1 Preescaler 1
  TCCR1B &= ~(1 << CS12);
  TCCR1B |= (1 << CS11);
  TCCR1B |= (1 << CS10);
  TCCR1A |= (1 << COM1B1);
  TCCR1A &= ~ (1 << COM1B0);
  TIMSK1 |= (1 << OCIE1A);    
  sei();
}

ISR(TIMER1_COMPA_vect) {
  static byte step = 1;
  ul_contador++;
  if (ul_contador >= 400) {
    step++;
    OCR1B += 250;
    ul_contador = 0;
    if ( step >= 10 ) {
      step = 1;
      OCR1B = 250;
    }
    TCNT1 = 0;   
  }
}

Now that you mention this: what are all the register bit names mentioned in the data sheet - bit numbers (for sbi/cbi?) or masks for integer and/or? Where is that convention documented?

Obrigado!
Serial.println(PB2); should reveal truth :slight_smile:
PORTB ^= (1<<PORTB2);

I conected an oscilator in PB2 so it shows the duty cycle there.

Updating TCNT1 in the ISR is not required, perhaps calling for trouble. See the data sheet what the FastPWM mode does in hardware with the timer registers and output pins.

I'd use a variable or constant for TOP:

const uint16_t T1TOP = 2500;
...
  OCR1A = T1TOP;
...
  OCR1B += 250;
  if (OCR1B>=T1TOP) ...

I think you need to set TCNT1 after setting OCR1A in setup(), since that is the value of TOP, not when OCR1B changes as that's not TOP. If TCNT1 happened to be >= 2500 when setup() runs, TCNT1 will be spending a while incrementing all the way round to 0xFFFF and wrapping back to 0.

BTW when setting timer registers don't just tweak some individual bits, set all the register bits to definite values - otherwise you are risking mixing your new configuration with existing configuration, which could lead to obscure failures in some circumstances you haven't seen yet.

volatile uint32_t ul_contador = 0;
const uint16_t T1TOP = 2500;
#define pulsador 5

void setup()
{
pinMode(pulsador, INPUT);
DDRB |= (1 << DDB2);
InitPWM1();
}

void loop() {

}

void InitPWM1() {
cli();
OCR1A = T1TOP; //64 Preescaler
OCR1B = 250;
TCCR1B |= (1 << WGM12) | (1 << WGM13);
TCCR1A |= (1 << WGM10) | (1 << WGM11);

//Timer 1 Preescaler 1
TCCR1B &= ~(1 << CS12);
TCCR1B |= (1 << CS11);
TCCR1B |= (1 << CS10);
TCCR1A |= (1 << COM1B1);
TCCR1A &= ~ (1 << COM1B0);
TIMSK1 |= (1 << OCIE1A);
sei();
}

ISR(TIMER1_COMPA_vect) {
static byte step = 1;
ul_contador++;
if (ul_contador >= 400) {
step++;
OCR1B += 250;
ul_contador = 0;
if (OCR1B>=T1TOP) {
step = 1;
OCR1B = 250;
}
TCNT1 = 0;
}
}

tried this in thinkercad, still not working, it oscilates between 10% and 20%.

My code works flawlessly in the simulation. What have you changed?