Go Down

Topic: truncated 16bit timer registers (Read 3436 times) previous topic - next topic

lubko

hello,
i found a strange bug with setting 16bit registers.
i tried to use hardware pwm on arduino micro / 32u4 chip.
ubuntu 15.10, arduino ide 1.6.5, as well as arduino 1.6.6

i tried to set 16 bit register, but only the lower 8 bits were applied.
for some reason, setting ICR1 works, but setting OCR1A and OCR1B does not.

setting these registers in pure C, worked, but then i could not use serial port. (didn't know how to)
i tried experimenting with extern "C" {} but that did not work either.

after some trial and error i found a way how to set my OCR1B register.
it works this way:

OCR1BH = 2;
ICR1 = 999;
OCR1BL = 0;

setting 16bit value into ICR1 works without any issues.
for OCR1B you have to first set the upper byte as documented, and afterwards commit with the lower byte.
this however does not work without inserting another instruction in between.
anything else leads to failure, setting only the lower byte.

Pete9

I ran in to the same problem. Assumed it was a know issue, but google doesn't throw up anything relevant

Appears the counter timer unit must be running in order to write to the high byte of two 16-bit compare registers (OCR1A and OCR1B)

By default, no clock source is selected in TCCR1B and the timer is off. Seemingly this effects the process of high byte access of those 16-bit registers

Setting the clock prescaler before writing to the compare registers solves the issue (bits CS12:0 in TCCR1B)

You might have to experiment on how the prescaler effects things as I run Timer 1 with no prescaler (CS12:0 = 0x01). The timer 1 clock is directly linked to the system clock and to program execution

Place your assignment to OCR1B as the last step in configuring the timer unit and you should be in business

Pete9

I need to correct that. I did some experimenting to nail things down. Oddly it's setting the WGM13 bit that does the magic, not the clock select bits. Not sure what's going on there.

Set the WGM13 bit in register TCCR1B and you'll be able to write a 16 bit value directly to the compare registers

TCCR1B = _BV(WGM13);
OCR1B = 0x0200;

Coding Badly


How are you two determining that the write is not working?


Pete9

#4
Dec 21, 2015, 11:35 am Last Edit: Dec 22, 2015, 03:33 pm by Pete9
I have timer 1 configured to produce a split-phase PWM on its 2 outputs. I'm driving some high-powered relays and need to spread the current draw

Compare unit A is set to produce a pulse at the bottom end of the count

Compare unit B is set to produce a pulse at the high end of the count

The timer is run in mode 14, with the upper limit of the count defined by ICR1

This gives two PWM outputs, running on a cycle time of 50us, with phase-shifted pulses of 5uS on time, either side of counter reset

Until finding lubko's workaround, I had problems getting output B to produce the desired waveform. I could see on the scope, the pulse on output B was not following the value written to the compare unit. It stayed towards the low end of the count

Reading OCR1B back showed only the low byte was valid. The high byte stayed at 0x00. Splitting the access in to two 8-bit writes and even coding the access in assembler proved unsuccessful (byte order as per datasheet)

Here's my code. If I print the value of OCR1B after it's run I get 0x02d0 and the output waveforms are correct

If I remove the workaround (setting of WGM13 bit), I find OCR1B is 0x00d0 and the output waveform is wrong


void configurePWM()
{
  PRR = PRR & ~_BV(PRTIM1);            // Enable Counter/Timer Unit 1
  TCCR1B = _BV(WGM13);                // <-- Workaround to enable access to 16 bit compare registers

  OCR1A = PWM_COMPARE;                // Output A compare register
  OCR1B = PWM_TOP - PWM_COMPARE;      // Output B compare register
  ICR1 = PWM_TOP;                     // Set upper limit of count
  TCNT1 = 0;                          // Reset the count

  TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(WGM11);  // Split phase. A active at low count. B active at high
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);                   // Fast PWM, mode 14. No clock prescaler
}

lubko

@pete: thanks for another tip

@cody: i grew tired of being in blind, so i got me multimeter which can measure freq, ut61e.
i am measuring frequency and duty cycle on the pin 10 of arduino micro.
the duty cycle would be 50% when ok, ~25% on failure.

there is another pwm mode, which instead of ICR1 uses OCR1A for top, setting frequency.
in this case not only duty cycle, but also the frequency would be affected by the truncated register.
(easier to measure, if you can not measure duty)

but i assume even without external measuring, if i had read TCCR1B after setting it, i would get a mismatch, which i could indicate via serial or blinking led.
(but that is just my assumption, i have not tried that)

------ this works ------
#include <avr/io.h>
#include <util/delay.h>

#define BLINK_DELAY_MS 500

int main (void)
{
/* set most pins for output*/
DDRC = 0xFF;
DDRB = 0xFF;

ICR1 = 999; //freq
OCR1B = 500; //duty

TCCR1A = 0;
TCCR1B = 0;

TCCR1A |= _BV(WGM11);
TCCR1B |= _BV(WGM12) | _BV(WGM13);
//Clear OCnA/OCnB/OCnC on compare match when up-counting. Set OCnA/OCnB/OCnC on compare match when down-counting.
TCCR1A |= _BV(COM1B1);
//prescake 1x
TCCR1B |= _BV(CS10);

//disable all timer1 inreupts, not needed, just to be sure
TIMSK1 = 0;

while(1) {
 /* set pin 5 high to turn led on */
 //PORTB |= _BV(PORTB5);
 _delay_ms(BLINK_DELAY_MS);

 PORTC ^= 0xFF;

 /* set pin 5 low to turn led off */
 //PORTB &= ~_BV(PORTB5);
 _delay_ms(BLINK_DELAY_MS);
}
}
--------- end ----------

----- this fails ------
void setup() {
 // put your setup code here, to run once:
DDRC = 0xFF;
DDRB = 0xFF;


ICR1 = 999;
OCR1B = 500;

TCCR1A = 0;
TCCR1B = 0;
//FastPWM ICRA = TOP
//TCCR1A |= _BV(WGM11) | _BV(WGM10);
TCCR1A |= _BV(WGM11);
TCCR1B |= _BV(WGM12) | _BV(WGM13);
//Clear OCnA/OCnB/OCnC on compare match when up-counting. Set OCnA/OCnB/OCnC on compare match when down-counting.
TCCR1A |= _BV(COM1B1);
//prescake 1x
TCCR1B |= _BV(CS10);

//disable all timer1 inreupts, not needed, just to be sure
TIMSK1 = 0;
}

void loop() {
 // put your main code here, to run repeatedly:

}
----- end ---------

------ this works again ----
void setup() {
  // put your setup code here, to run once:
 DDRC = 0xFF;
 DDRB = 0xFF;


OCR1BH = 1;
ICR1 = 999;
OCR1BL = 244;

TCCR1A = 0;
TCCR1B = 0;
//FastPWM ICRA = TOP
//TCCR1A |= _BV(WGM11) | _BV(WGM10);
TCCR1A |= _BV(WGM11);
TCCR1B |= _BV(WGM12) | _BV(WGM13);
//Clear OCnA/OCnB/OCnC on compare match when up-counting. Set OCnA/OCnB/OCnC on compare match when down-counting.
TCCR1A |= _BV(COM1B1);
//prescake 1x
TCCR1B |= _BV(CS10);

//disable all timer1 inreupts, not needed, just to be sure
TIMSK1 = 0;
}

void loop() {
  // put your main code here, to run repeatedly:

}
------ end ------

lubko

I have updated arduino IDE to 1.6.7

The problem is still there, but my workaround does not seem to help anymore.

The dependency found by Pete9, still works.

... which saved me, thanks.

ckevar

Hi,
A little bit late. I'll be using the this doc by PJRC to explain. I am using the 32u4 of the lilypad usb and realize that all TIMERs starts from the very beginning since they are enabled by Power Reduction Register PRR0 and PRR1 (p.46), Watch out with PRR0 cause if you desable Timer0, delay() function wont work.

the PWM by hardware of TIMER1 and TIMER3 comes in 8, 9, and 12 bits and other modes. By default the register Mode of Operation TCNT3A (p.131)  is set as 8-bit PWM (there is no documentation about this) so that's why you can't write anything in any HIGH register.

After choosing the mode of operation you will be able to load anything in HIGH Registers. The following lines of code show how to set up a Normal Mode for TIMER3

Code: [Select]
   PRR1 &= ~(1 << PRTIM3); // Timer3 Enabled p.46
   /**** Timer3 Settings ****/
  TCCR3A &= 240;      // Normal mode p131
  GTCCR |= 129;         // Syncronizing timer to change prescaler p90
  TCCR3B = 2;            // Change Prescaler (1MHz) p133
  GTCCR &= 126;       // Clearing synchronization p90

Go Up