0
Offline
Newbie
Karma: 0
Posts: 3
Automobilino
|
 |
« on: December 13, 2009, 08:47:46 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Offline
Brattain Member
Karma: 311
Posts: 35470
Seattle, WA USA
|
 |
« Reply #1 on: December 13, 2009, 09:12:14 am » |
From this code snippet, we can't see how servoPulse is defined or valued. Can you show a bit more of your code?
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 3
Automobilino
|
 |
« Reply #2 on: December 13, 2009, 12:01:14 pm » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 3
Automobilino
|
 |
« Reply #3 on: December 13, 2009, 01:05:00 pm » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
"The old Europe"
Offline
Edison Member
Karma: 0
Posts: 2003
Bootloaders suck!
|
 |
« Reply #4 on: December 23, 2009, 07:49:13 pm » |
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: TCNT1 = 0xFFAA; is turned into these assembler commands: 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: 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: Serial.println((timer1>>8)&0xFF,HEX);
|
|
|
|
« Last Edit: December 23, 2009, 07:51:16 pm by madworm »
|
Logged
|
• 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!
|
|
|
|
"The old Europe"
Offline
Edison Member
Karma: 0
Posts: 2003
Bootloaders suck!
|
 |
« Reply #5 on: December 23, 2009, 08:07:15 pm » |
|
|
|
|
« Last Edit: December 23, 2009, 08:34:34 pm by madworm »
|
Logged
|
• 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!
|
|
|
|
"The old Europe"
Offline
Edison Member
Karma: 0
Posts: 2003
Bootloaders suck!
|
 |
« Reply #6 on: December 24, 2009, 08:18:08 am » |
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. #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.
|
|
|
|
|
Logged
|
• 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!
|
|
|
|
"The old Europe"
Offline
Edison Member
Karma: 0
Posts: 2003
Bootloaders suck!
|
 |
« Reply #7 on: December 26, 2009, 11:23:46 pm » |
I've started a thread on avrfreaks.net with basically the same question. Maybe some of the experts there know what the problem is.
|
|
|
|
|
Logged
|
• 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!
|
|
|
|
London
Offline
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #8 on: December 27, 2009, 12:11:31 am » |
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 : 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)
|
|
|
|
« Last Edit: December 27, 2009, 12:45:59 am by mem »
|
Logged
|
|
|
|
|
"The old Europe"
Offline
Edison Member
Karma: 0
Posts: 2003
Bootloaders suck!
|
 |
« Reply #9 on: December 27, 2009, 01:07:34 am » |
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.
|
|
|
|
« Last Edit: December 27, 2009, 01:08:32 am by madworm »
|
Logged
|
• 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!
|
|
|
|
London
Offline
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #10 on: December 27, 2009, 01:15:34 am » |
>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.
|
|
|
|
|
Logged
|
|
|
|
|
"The old Europe"
Offline
Edison Member
Karma: 0
Posts: 2003
Bootloaders suck!
|
 |
« Reply #11 on: December 27, 2009, 01:34:57 am » |
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.
|
|
|
|
« Last Edit: December 27, 2009, 01:43:59 am by madworm »
|
Logged
|
• 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!
|
|
|
|
London
Offline
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #12 on: December 27, 2009, 06:13:29 am » |
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
|
|
|
|
« Last Edit: December 27, 2009, 06:14:26 am by mem »
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 3
Arduino rocks
|
 |
« Reply #13 on: January 24, 2011, 12:51:13 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
|