How to configure TIMERS in Atmega328p through Arduino IDE with AVR-GCC code?

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

Hello,

 

 

I'm working to test the timings of DH11 received 40-bits data, and wanted to implement my Arduino Uno TIMER0 for this purpose.

 

I thought I configured the TIMER0 as I wanted but I think it's not! I learned in the datasheet that I just need to enable CS00 to activate TIMER0

 

Then I tried to change the prescaler bit for my test condition, but the same result. Then I cleared TCCR0B and surprisingly the LCD also shows random numbers like other selecting bits.

 

This is my test code, I also tried a delay instead of the while loop.

 

TCNT0=0;
while(m<100)m++;
num = TCNT0;
itoa(num,str_arr,10);
LCD_string(str_arr);
_delay_ms(1000);
clr_dis();  
_delay_ms(1000);

 

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

Arduino uses Timer0 for things like millis() so you might want to switch to Timer1 (16-bit)

or Timer2 (8-bit).

 

Your code doesn't show any changes to the prescaler, but it's probably "working" anyway

because the Arduino code has turned Timer0 on already.

 

--Mike

 

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

Deep inside the Arduino install, there is a file, called "wiring.c". Inside this file, there is a function, called "init()", that is called before "setup()", and configures the timers. Here is the source:

 

void init()
{
	// this needs to be called before setup() or some functions won't
	// work there
	sei();
	
	// on the ATmega168, timer 0 is also used for fast hardware pwm
	// (using phase-correct PWM would mean that timer 0 overflowed half as often
	// resulting in different millis() behavior on the ATmega8 and ATmega168)
#if defined(TCCR0A) && defined(WGM01)
	sbi(TCCR0A, WGM01);
	sbi(TCCR0A, WGM00);
#endif

	// set timer 0 prescale factor to 64
#if defined(__AVR_ATmega128__)
	// CPU specific: different values for the ATmega128
	sbi(TCCR0, CS02);
#elif defined(TCCR0) && defined(CS01) && defined(CS00)
	// this combination is for the standard atmega8
	sbi(TCCR0, CS01);
	sbi(TCCR0, CS00);
#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
	// this combination is for the standard 168/328/1280/2560
	sbi(TCCR0B, CS01);
	sbi(TCCR0B, CS00);
#elif defined(TCCR0A) && defined(CS01) && defined(CS00)
	// this combination is for the __AVR_ATmega645__ series
	sbi(TCCR0A, CS01);
	sbi(TCCR0A, CS00);
#else
	#error Timer 0 prescale factor 64 not set correctly
#endif

	// enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
	sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
	sbi(TIMSK0, TOIE0);
#else
	#error	Timer 0 overflow interrupt not set correctly
#endif

	// timers 1 and 2 are used for phase-correct hardware pwm
	// this is better for motors as it ensures an even waveform
	// note, however, that fast pwm mode can achieve a frequency of up
	// 8 MHz (with a 16 MHz clock) at 50% duty cycle

#if defined(TCCR1B) && defined(CS11) && defined(CS10)
	TCCR1B = 0;

	// set timer 1 prescale factor to 64
	sbi(TCCR1B, CS11);
#if F_CPU >= 8000000L
	sbi(TCCR1B, CS10);
#endif
#elif defined(TCCR1) && defined(CS11) && defined(CS10)
	sbi(TCCR1, CS11);
#if F_CPU >= 8000000L
	sbi(TCCR1, CS10);
#endif
#endif
	// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
	sbi(TCCR1A, WGM10);
#endif

	// set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
	sbi(TCCR2, CS22);
#elif defined(TCCR2B) && defined(CS22)
	sbi(TCCR2B, CS22);
//#else
	// Timer 2 not finished (may not be present on this CPU)
#endif

	// configure timer 2 for phase correct pwm (8-bit)
#if defined(TCCR2) && defined(WGM20)
	sbi(TCCR2, WGM20);
#elif defined(TCCR2A) && defined(WGM20)
	sbi(TCCR2A, WGM20);
//#else
	// Timer 2 not finished (may not be present on this CPU)
#endif

#if defined(TCCR3B) && defined(CS31) && defined(WGM30)
	sbi(TCCR3B, CS31);		// set timer 3 prescale factor to 64
	sbi(TCCR3B, CS30);
	sbi(TCCR3A, WGM30);		// put timer 3 in 8-bit phase correct pwm mode
#endif

#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */
	sbi(TCCR4B, CS42);		// set timer4 prescale factor to 64
	sbi(TCCR4B, CS41);
	sbi(TCCR4B, CS40);
	sbi(TCCR4D, WGM40);		// put timer 4 in phase- and frequency-correct PWM mode	
	sbi(TCCR4A, PWM4A);		// enable PWM mode for comparator OCR4A
	sbi(TCCR4C, PWM4D);		// enable PWM mode for comparator OCR4D
#else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */
#if defined(TCCR4B) && defined(CS41) && defined(WGM40)
	sbi(TCCR4B, CS41);		// set timer 4 prescale factor to 64
	sbi(TCCR4B, CS40);
	sbi(TCCR4A, WGM40);		// put timer 4 in 8-bit phase correct pwm mode
#endif
#endif /* end timer4 block for ATMEGA1280/2560 and similar */	

#if defined(TCCR5B) && defined(CS51) && defined(WGM50)
	sbi(TCCR5B, CS51);		// set timer 5 prescale factor to 64
	sbi(TCCR5B, CS50);
	sbi(TCCR5A, WGM50);		// put timer 5 in 8-bit phase correct pwm mode
#endif

#if defined(ADCSRA)
	// set a2d prescaler so we are inside the desired 50-200 KHz range.
	#if F_CPU >= 16000000 // 16 MHz / 128 = 125 KHz
		sbi(ADCSRA, ADPS2);
		sbi(ADCSRA, ADPS1);
		sbi(ADCSRA, ADPS0);
	#elif F_CPU >= 8000000 // 8 MHz / 64 = 125 KHz
		sbi(ADCSRA, ADPS2);
		sbi(ADCSRA, ADPS1);
		cbi(ADCSRA, ADPS0);
	#elif F_CPU >= 4000000 // 4 MHz / 32 = 125 KHz
		sbi(ADCSRA, ADPS2);
		cbi(ADCSRA, ADPS1);
		sbi(ADCSRA, ADPS0);
	#elif F_CPU >= 2000000 // 2 MHz / 16 = 125 KHz
		sbi(ADCSRA, ADPS2);
		cbi(ADCSRA, ADPS1);
		cbi(ADCSRA, ADPS0);
	#elif F_CPU >= 1000000 // 1 MHz / 8 = 125 KHz
		cbi(ADCSRA, ADPS2);
		sbi(ADCSRA, ADPS1);
		sbi(ADCSRA, ADPS0);
	#else // 128 kHz / 2 = 64 KHz -> This is the closest you can get, the prescaler is 2
		cbi(ADCSRA, ADPS2);
		cbi(ADCSRA, ADPS1);
		sbi(ADCSRA, ADPS0);
	#endif
	// enable a2d conversions
	sbi(ADCSRA, ADEN);
#endif

	// the bootloader connects pins 0 and 1 to the USART; disconnect them
	// here so they can be used as normal digital i/o; they will be
	// reconnected in Serial.begin()
#if defined(UCSRB)
	UCSRB = 0;
#elif defined(UCSR0B)
	UCSR0B = 0;
#endif
}

 

This means the timer registers are not in the default states, you have to configure them again. Alternatively, you can delete everything inside the .ino file, that is, setup() and loop(), leave it completely empty. Then create a new .cpp file in the project, where you will do the actual programming in standard C++ without Arduino "stuff" trying to help you.

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

avr-mike wrote:

Arduino uses Timer0 for things like millis() so you might want to switch to Timer1 (16-bit)

or Timer2 (8-bit).

 

Your code doesn't show any changes to the prescaler, but it's probably "working" anyway

because the Arduino code has turned Timer0 on already.

 

--Mike

 

 

LOL, what a mistake! I forgot to include the timer init function :) so sorry.

 

But when I included it in the setup function, it worked and also I can control TIMER0 too.

 

Here's my code:

void setup() {
  I2C_init();
  LCD_Init();
  TCCR0B = 0x02;
}

void loop() {
TCNT0=0;
_delay_us(100);
num = (TCNT0/2);
itoa(num,str_arr,10);
LCD_string(str_arr);
_delay_ms(1000);
clr_dis();
}

 

With this setup, the Atmega is running at 16MHz, so instruction cycle should be 62.5ns, prescaler is Fosc/8 so the timer should run at 0.5us

 

Measuring 100us delay, I should get 200, which is what I have right now on the LCD, dividing the timer result by 2 I should get 100!

 

 

EDIT: Another interesting performance aspect I noticed is that implementing:

while(m<100){m++;}

Instead of

_delay_us(100);

Changes the results, I get 50 timer results with the while loop instead of the actual 100 for the _delay_us.

 

So the incrementing process takes an extra CPU clock, I think AVR-GCC _delay_us/ms functions apply timers for their functionality.

Last Edited: Sat. Aug 18, 2018 - 03:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

Deep inside the Arduino install, there is a file, called "wiring.c". Inside this file, there is a function, called "init()", that is called before "setup()", and configures the timers. Here is the source:

 

This means the timer registers are not in the default states, you have to configure them again. Alternatively, you can delete everything inside the .ino file, that is, setup() and loop(), leave it completely empty. Then create a new .cpp file in the project, where you will do the actual programming in standard C++ without Arduino "stuff" trying to help you.

 

First of all thank you very much for pointing for me about the source file where the timers settings are.

 

Also, I'm so happy with your advice of writing my own C++ code in the Arduino IDE! That's really impressive :)

 

I'm currently trying to learn the basics of AVR-GCC skills. And a good thing is that I can write my C code in Arduino IDE. Does that mean according to your strategy, I can develop a C sketch? Yes, I can :)

 

I got 1478 bytes instead of 1804 bytes, that's really good thanks a lot :)

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

I've done my DH11 timings test and the results are impressive!

 

I got the precise timings as expected, I couldn't get the same results with my PIC18F4550, I don't know why.

 

The PIC18F4550 worked well for measuring the IR sensor VS1838b received bits, but didn't with the DH11! I don't know why.

 

This is my code:

void DH11_init(void)
{
  _delay_ms(2000);
  TCCR0B = 0x02;	// enable timer0
}

void DH11_read(uint8_t *bits_timing)
{
	uint8_t i;
	DDRB = 0xFF;		            // Pin0 output
	PORTB &= ~(1<<PB0);
	_delay_ms(18);
	PORTB |= (1<<PB0);
	DDRB = 0xFE;                        // Pin0 input

	TCNT0=0;
	while((PINB & (1<<PINB0)));        // measuring these pulses for testing purposes
	bits_timing[0]=TMR0_MSK;

	TCNT0=0;
	while(!(PINB & (1<<PINB0)));       // DH11 LOW 80us response
	bits_timing[1]=TMR0_MSK;

	TCNT0=0;
	while((PINB & (1<<PINB0)));        // DH11 HIGH 80us response
	bits_timing[2]=TMR0_MSK;	

	for (i=3;i<43;i++)
    {
        while(!(PINB & (1<<PINB0)));       // wait for 0 leading bit
        TCNT0=0;
        while(PINB & (1<<PINB0));          // high bit 0 is 24us or 1 is 70us
        bits_timing[i] = TMR0_MSK;
    }
}

 

Last Edited: Sat. Aug 18, 2018 - 03:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:
I learned in the datasheet that I just need to enable CS00 to activate TIMER0 Then I tried to change the prescaler bit for my test condition, but the same result.

It sure would be quite helpful if you >>showed<< the code for what you did.

 

There is an extensive thread and discussion this week, that will give you much information.

https://www.avrfreaks.net/forum/...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Sat. Aug 18, 2018 - 04:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're right in that code I didn't include the init function which has the timer settings. But I modified that mistake in the after posts.

 

Also thanks for telling me about the thread, it's an honor for me :)