Go Down

Topic: Initializing timer registers on 16bit counter? (Read 29560 times) previous topic - next topic

thunderbird8791

Hi,

i have an Arduino board with an atmega328 and i use the arduino software version 17.
To generate a normal timer interrupt i tried to initialize the OCR1A register with a 16-bit hex-value for the 16-bit timer.

OCR1A = 0x0ABD;

My program didn't work right, so i made a check. I found out that the problem is that when i requested the value from the register

servoByteH = (int)servoPulse>>8;      // the high byte of OCR1A
servoByteL = (int)255&servoPulse;     // the low byte of OCR1A
Serial.println(servoByteH);
Serial.println(servoByteL);

The high-byte called OCR1AH was totally empty!

So is there a problem with the arduino software not being able to write into 16bit registers? Or what did i do wrong?
I know there's a TEMP register used for the high-byte to write into OCR1A simultaneously. But even though i loaded the <avr/interrupt.h> and the <avr/io.h> he doesn't know the expression TEMP.

Can you please help me how to access the 16bit register? Thanks!

Greets, ThunderBird

PaulS

From this code snippet, we can't see how servoPulse is defined or valued. Can you show a bit more of your code?

thunderbird8791

This is my code:

#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdlib.h>
#include <stdint.h>
#include "Wire.h"
#include "WiiChuck.h"

// ------------------ variable definitions ------------------------

uint16_t min16 = 540;//540;    // = 540uS min. pulse width - prescaler 8
uint16_t max16 = 2400;   // = 2400uS min. pulse width - prescaler 8
int servoPulse;
uint8_t angle;             // in degrees
boolean zButtonPressed = false;
boolean cButtonPressed = false;

WiiChuck chuck = WiiChuck();


void setup()   {
 
 TCCR1A |=(1<<WGM13)|(1<<COM1A1);  // using TIMER1, setting mode: PWM - phase and frequency correct mode

 TCCR1B |=(1<<CS11);     // defining the prescaler 8 --> f_cpu = prescaler*f_timer
 
 DDRB |= (1<<DDB1);      // setting Port B as output for PWM

 ICR1 = 0x5DC0;        // period length 24ms with prescaler 8 matches  ICR1 = 24000
 OCR1A = 0x0000;       // reseting PWM register
 pinMode(PORTB, OUTPUT);      // output for servo

 chuck.begin();      // initialising nunchuck
 chuck.update();
 Serial.begin(9600);      // debug serial output
}


void calcPulseWidth(uint16_t angleArg)
{
   unsigned long help = 0;
   if ( angleArg < 0) angleArg = 0;
   if ( angleArg > 180) angleArg = 180;
   angle = angleArg;
 
   help = ((long)(max16-min16))*((long)angle);
   servoPulse = (min16 + (uint16_t)(help/180L));
}



void loop()                    
{  
 chuck.update();
 zButtonPressed = chuck.buttonZ;
 cButtonPressed = chuck.buttonC;
 
  if(zButtonPressed == true)
  {
    calcPulseWidth(180);    // setting new servo pulse
    OCR1A = servoPulse;
    Serial.print("180°: ");
    Serial.println((int)OCR1A);
  }
  else
  {
    calcPulseWidth(0);    // setting new servo pulse
    OCR1A = servoPulse;
    Serial.print("0°: ");
    Serial.println((int)OCR1A);
  }
}


As you can see i tried to read what i wrote in the OCR1A register again and as it seems he only accesses the lower 8-bit of the OCR1A.

thunderbird8791

My problem is actually that i don't really know how to access the 16-bit registers.
And how can i check whether the compiler understands operations like:
int a = 1000;
OCR1A = a;
Because in assembler you have to put your high-byte first in the TEMP register and then after that when you load the low-byte into the OCR1A the high-byte gets into the OCR1A automatically.
But i don't know if this background stuff is all done by the compiler.

madworm

#4
Dec 24, 2009, 01:49 am Last Edit: Dec 24, 2009, 01:51 am by madworm Reason: 1
I have a very similar problem as well.

At first I thought it was the high/low/temp register order problem as described here. It should have been fixed for long.

I've checked that this code for example:

Code: [Select]
TCNT1 = 0xFFAA;

is turned into these assembler commands:

Code: [Select]
 TCNT1 = 0xFFAA;
 da:   e4 e8           ldi     r30, 0x84       ; 132
 dc:   f0 e0           ldi     r31, 0x00       ; 0
 de:   2a ea           ldi     r18, 0xAA       ; 170
 e0:   3f ef           ldi     r19, 0xFF       ; 255
 e2:   31 83           std     Z+1, r19        ; 0x01 (store HIGH byte first)
 e4:   20 83           st      Z, r18             ; (store LOW byte last)


And reading:

Code: [Select]
unsigned int timer1;
timer1 = TCNT1;

 timer1 = TCNT1;
 e6:   20 81           ld      r18, Z             ; (read LOW byte first)
 e8:   31 81           ldd     r19, Z+1        ; 0x01 (read HIGH byte last)
 ea:   3a 83           std     Y+2, r19        ; 0x02
 ec:   29 83           std     Y+1, r18        ; 0x01


This is OK according to the datasheet.

Still when I want to use e.g. high_of(timer1), it is always 0! Something like:

Code: [Select]
Serial.println((timer1>>8)&0xFF,HEX);
• Upload doesn't work? Do a loop-back test.
• There's absolutely NO excuse for not having an ISP!
• Your AVR needs a brain surgery? Use the online FUSE calculator.
My projects: RGB LED matrix, RGB LED ring, various ATtiny gadgets...
• Microsoft is not the answer. It is the question, and the answer is NO!

madworm

#5
Dec 24, 2009, 02:07 am Last Edit: Dec 24, 2009, 02:34 am by madworm Reason: 1
This may be interesting as well:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=82847&highlight=tcnt1h

Edit: well only a little. Not much enlightenment there.
• Upload doesn't work? Do a loop-back test.
• There's absolutely NO excuse for not having an ISP!
• Your AVR needs a brain surgery? Use the online FUSE calculator.
My projects: RGB LED matrix, RGB LED ring, various ATtiny gadgets...
• Microsoft is not the answer. It is the question, and the answer is NO!

madworm

I've done some further checks with LEDs as indicators of HIGH/LOW bytes in a 16bit variable (unsigned int). I've also compiled it outside the Arduino IDE and the behaviour is the same. Still the assembler code looks ok as far as the order of reads/writes is concerned.

It boils down to this:

First I stop TIMER1 by setting the prescaler accordingly. Then it let the compiler write 0xFFFF into TCNT1 (16bit reg), assuming it knows how to do it correctly and instantly read it back into an uint16_t variable called timer1. I use 8 LEDs to show the LOW/HIGH byte contents of timer1 and print the results to serial as well.

The LOW byte content of timer1 is correct (0xFF) and 8 LEDs light up. The HIGH byte though is ZERO. If i replace the readout of TCNT1 into timer1 with just "timer1 = 0xFFFF", LOW/HIGH byte content of timer1 is correct and the 8 LEDs light up twice. The serial output is correct as well in this case.

So apparently reading TCTN1 into timer1 fails. Manually reading LOW/HIGH of TCNT1 using TCNT1L and then TCNT1H fails as well.

Code: [Select]
#include <util/delay.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>

void
setup (void)
{
 Serial.begin (9600);

 DDRD |= ((1 << PD5));            // PD5 output
 PORTD |= ((1 << PD5));      // RED anodes HIGH. All 8 anodes go to this pin
 DDRB = 0xFF;                  // all outputs
 PORTB = 0xFF;                  // all 8 cathodes HIGH --> OFF
}

void
loop (void)
{

 unsigned char sreg;
 volatile unsigned int timer1;
 unsigned char ctr;

 sreg = SREG;                  // store IRQ flags
 cli ();                  // all IRQs off
 TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10));      // stop timer1
 TCNT1 = 0xFFFF;            // set arbitrary value, should stay the same as timer1 is stopped
 timer1 = TCNT1;            // read it back. the compiler should know how to do a 16bit read
 //timer1 = 0xFFFF;            // use this instead of reading TCNT1 and it works as expected
 SREG = sreg;                  // restore IRQ flags

 for (ctr = 0; ctr <= 7; ctr++)
   {                        // show LOW byte of timer1
     if ((timer1 >> ctr) & 0x0001)
     {
       PORTB &= ~(1 << ctr);      // turn the LED on
     }
     else
     {
       PORTB |= (1 << ctr);      // turn the LED off
     }
   }
 _delay_ms (300);
 PORTB = 0xFF;                  // all off
 _delay_ms (300);

 for (ctr = 0; ctr <= 7; ctr++)
   {                        // show HIGH byte of timer1
     if ((timer1 >> (ctr + 8)) & 0x0001)
     {
       PORTB &= ~(1 << ctr);      // turn the LED on
     }
     else
     {
       PORTB |= (1 << ctr);      // turn the LED off
     }
   }
 _delay_ms (300);
 PORTB = 0xFF;                  // all off

 Serial.print ("timer1 (should be FFFF) : ");
 Serial.println (timer1, HEX);
 Serial.print ("timer1>>8 & 0xFF (high byte, should be FF): ");
 Serial.println ((timer1 >> 8) & 0xFF, HEX);
 Serial.print ("timer1 & 0xFF (low byte should be FF): ");
 Serial.println (timer1 & 0xFF, HEX);
 Serial.println ("-");
 delay (3000);
}


What the @$& is going on ?


BTW,

Happy Holidays everybody.
• Upload doesn't work? Do a loop-back test.
• There's absolutely NO excuse for not having an ISP!
• Your AVR needs a brain surgery? Use the online FUSE calculator.
My projects: RGB LED matrix, RGB LED ring, various ATtiny gadgets...
• Microsoft is not the answer. It is the question, and the answer is NO!

madworm

I've started a thread on avrfreaks.net with basically the same question. Maybe some of the experts there know what the problem is.
• Upload doesn't work? Do a loop-back test.
• There's absolutely NO excuse for not having an ISP!
• Your AVR needs a brain surgery? Use the online FUSE calculator.
My projects: RGB LED matrix, RGB LED ring, various ATtiny gadgets...
• Microsoft is not the answer. It is the question, and the answer is NO!

mem

#8
Dec 27, 2009, 06:11 am Last Edit: Dec 27, 2009, 06:45 am by mem Reason: 1
Hi madworm,

I see what you are trying to do but that test does not duplicate real world use of the timer. Have you checked the datasheet to see if reading TCNT1 is valid when the timer is not running?

Perhaps try a test with the timer running and set to the slowest count possible and poll TCNT1 as follows :
Code: [Select]

void setup()
{
   TCCR1A = 0;             // normal counting mode
   TCCR1B = _BV(CS10) |  _BV(CS12);      // set prescale to 1024
   TCNT1 = 0;              // clear the timer count
   Serial.begin(9600);
}

void loop()
{
 sreg = SREG;                  // store IRQ flags
 cli ();                  // all IRQs off
 timer1 = TCNT1;            // read it back
 SREG = sreg;                  // restore IRQ flags
 Serial.print(millis());
 Serial.print(",");
 Serial.println(timer1);
}


You should see timer1 increase by however many ticks of  64us it takes to do the loop. You should see the timer value and the millis value correspond  (multiply the timer value by .064 to get milliseconds)

madworm

#9
Dec 27, 2009, 07:07 am Last Edit: Dec 27, 2009, 07:08 am by madworm Reason: 1
Yes it is valid.

I plugged my test code into some other piece of code I have, which also uses timer1, and it works there. It precharges timer1 to some value very close to 0xFFFF to increase the frequency of the overflow interrupt.

What is the difference ? As you may very well already know, timer1 can be a 16bit counter, but doesn't have to be. Anyhow, it only works as it should when the WGM10 bit of TCCR1A is zero.

Now the question arises is why WGM10 is not 0, although the datasheet claims the default value for TCCR1A is 0.
• Upload doesn't work? Do a loop-back test.
• There's absolutely NO excuse for not having an ISP!
• Your AVR needs a brain surgery? Use the online FUSE calculator.
My projects: RGB LED matrix, RGB LED ring, various ATtiny gadgets...
• Microsoft is not the answer. It is the question, and the answer is NO!

mem

>Anyhow, it only works as it should when WGM10 = 0.

For normal counting mode, you need WGM10 set to 0.

>Now the question arises is why WGM10 is not 0, although the datasheet claims the default value for TCCR1A is 0.

Its not in the default normal counting mode because the Arduino startup code initializes the timers for PWM. If you want a particular mode you should always explicitly set all relevant registers.

madworm

#11
Dec 27, 2009, 07:34 am Last Edit: Dec 27, 2009, 07:43 am by madworm Reason: 1
Ungh. In this case the arduino security blanket sucks.

Is there a way to selectively disable arduino glue when not needed ? If all the analogWrite stuff is never called at all for example, don't mess with the timer registers ? But there's also delay and so on. Maybe it's time to move on. I guess the arduino ide just can't provide a more open way of "include this, configure that, but exclude ...". Maybe an 'advanced mode' with e.g. a PWM.h include for the timers and so on. If the core were split up like that, more experienced users could decide what they really need.
• Upload doesn't work? Do a loop-back test.
• There's absolutely NO excuse for not having an ISP!
• Your AVR needs a brain surgery? Use the online FUSE calculator.
My projects: RGB LED matrix, RGB LED ring, various ATtiny gadgets...
• Microsoft is not the answer. It is the question, and the answer is NO!

mem

#12
Dec 27, 2009, 12:13 pm Last Edit: Dec 27, 2009, 12:14 pm by mem Reason: 1
Quote
Ungh. In this case the arduino security blanket sucks.


I think the problem may be more with your expectations than with Arduino  ;)
Saying it sucks because there is no simple way to cut holes in the security blanket is missing the point of Arduino - its purpose is the security blanket.

I wouldn't disagree that Arduino hardware initialization would benefit from being more flexible, but supporting more boards would IMO be a higher priority than disabling Arduino functionality for experienced programmers.

Anyway, there is nothing stopping you from modifying the initialization code in wiring.c to make it do what you want. And if you create a board variant in the hardware/cores directory for the hacked core, you can switch between the standard version and your customized core as needed.


Have fun

Doro

I am trying to set the TTCR1B resister and get a strange result


void setup()
{
   char s[4];
   Serial.begin(9600);
   
   TCCR1B |= (0 << CS12)|(1 << CS11)|(0 << CS10); // Should clear bit 0 and 2 and set bit 1.
   
   Serial.println("=======================");
   snprintf(s, 4, "%d", TCCR1B);
   Serial.println(s);  // (The result I get is a decimal 3 insted of a decimal 2)
   Serial.print("=======================");

}

void loop()
{
   
}

The expected result is a 2 and I get 3

Go Up