Help on millis code in Atmel Studio not working

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

I try to create millis code using Timer0 on ATTiny861. It is not working. Can someone help to detect where is the error.  The code can build without error.

 

*
 * GccApplication6.c
 * ATTiny861 test the millis functions in Atmel Studio 7
 * using Timer0
 * Fuse setting L:62, H:DF, E: FF
 */

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>


// Store value of millisecond since uC started
volatile unsigned long millis_value;

unsigned long PreviousMillis = 0;
unsigned long CurrentTimeInterval = 3000; //1000 = 1 sec


// This function is used to get millis_value
unsigned long millis()
{
	unsigned long m;
	
	// Disable interrupts while we read millis_value or we might get an
	// inconsistent value (e.g. in the middle of a write to millis_value)
	asm ("cli");
	m = millis_value;
	asm ("sei");
	
	return m;
}



// Timer 0 output compare interrupt service routine
ISR(TIMER0_OVF_vect)
{
	// Increment millisecond every 1ms
	millis_value++;
}




int main(void)
{
    DDRB = 0xFF; 
	
	/* Set timer 0 on CTC mode that will compare match every 1ms */
	// Timer/Counter 0 initialization
	// Mode: CTC top=OCR0A
		
	// TCCR0A = TCW0 | ICEN0 | INCC0 | ICES0 | ACIC0 | - | - | CTC0
	//            0  |  0    |    0  |     0 |  0    | 0 | 0 | 1
 	TCCR0A = 0x01;
	
	// TCCR0B = |- | -| - | TSM | PSR0 | CS02 | CS01 | CS00 
	//          | 0|0 | 0 |  0  | 0    | 0    |  1   | 1    
	//divide by 64
	TCCR0B = 0x03;
	
	TCNT0L = 0x00;
	
	// 0CR0A value = (0.001 s x Fcpu/N) - 1
	// 
	OCR0A = 0x7C;//DEC IS 124, (0.001 x 8M/64)-1

	/* Enable compare match interrupt for timer 0 */
	// Timer(s)/Counter(s) Interrupt(s) initialization
	TIMSK = 0x02;//TOIE0 IS SET
	
	
	/* Enable global interrupt */
	asm("sei");


	while (1)
	{
		unsigned long currentMillis = millis();
		unsigned long elapsedTime = currentMillis - PreviousMillis;
		if (elapsedTime >= CurrentTimeInterval) {//if Time lapsed
        PORTB |= (1 << PB4);//led on
        _delay_ms(1000);
		PORTB &= ~(1 << PB4);//led off		
     	_delay_ms(1000);
		PreviousMillis = millis();}
	}
	
	
}

Kindly help. Appreciated.

This topic has a solution.
Last Edited: Tue. Nov 24, 2020 - 09:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have an overflow isr, but have the time configured to stop on output compare match, so it won’t ever overflow.

there’s a separate output compare isr...

 

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

Put your code into the AS7.0 Simulator.

 

Observe which interrupt flags are set and cleared.   e.g. OCFA, OVF, ...

 

Read the exact times that are reported by the Simulator.

Modify the program to make it work.

 

Oh,  I strongly advise using the  official Atmel BIT_NAMES when accessing Special Function Registers e.g.

	// TCCR0B = |- | -| - | TSM | PSR0 | CS02 | CS01 | CS00 
	//          | 0|0 | 0 |  0  | 0    | 0    |  1   | 1    
	//divide by 64
	TCCR0B = (1 << CS01) | (1 << CS00);   //use BIT_NAMES in expression

You may have noticed that some bits are grouped together.   I personally prefer to say:

	TCCR0B = (3 << CS00);   //use BIT_NAMES in expression

David.

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

It is working now the error is due to TIMSK, it shall be set with OCIE0A.  I have one more question which confusing to me due to my stupidity.  

 

This Timer 0 I wanted to have millis working in 1 millis sec.   I have the Timer1 which i need it to work in PLL mode.  The fuse I need to set to PLL Clock and CKDIV by 8 have to be untick. I did not use external crystal. 

At this PLL mode fuse, what is my F_CPU for the Timer0 ?   'Will I still get 1 millis sec in millis ?  

 

    

 

 

 

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

F_CPU will be 16MHz

 

If you are ever in doubt,  write a 1 second Blinky.

 

The AS7.0 Simulator will probably tell you too.   But requires you to set all the fuses correctly in the Simulator.

Note that you can run ATtiny85 Timers at 64MHz (as far as I can remember)

 

David.

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

 

Anytip where can i learn more about simulator in AS7.  I face quite a lots of strange stuff like variable Value in simulator is unknown location and some variable show optimised away.  

 

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

Single step in the DisAssembler view.

 

Many variables are held in registers.   AS7.0 is not very good at displaying variables held in registers.    You have to notice which registers are holding what.   And observe the register value.

 

Quite honestly,  it is easier to program in C.   Make a few notes on paper.    And believe that C statements are executed correctly.

 

You will learn with experience.

Place Breakpoints in strategic places.   Then run till the Breakpoint is hit.    Observe the Processor cycles and execution time.

 

For example.   Place a Break in your 1ms ISR().    Run to Break.   Zero the cycle counter.  Run till next Break.    Read the us/ms/s value.  Read the cycles.  

 

If you want to play with super fast PWM,  Write and enable a Compare ISR().   Set Breakpoint, ...

 

You will learn that there are some things to avoid.   e.g. single-step a _delay_ms() sequence

And some places are handy to Break.    Inserting the odd asm("nop") is a useful way to set a place for Break.

 

David.

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

My millis() code is working but during the build, there is still two warning but build successful..  These are the warning I hilighted in the below picture.t 

 

 

Is this two warning dangerous and could cause some problem ?

 

Attached is my code, Kindly help. 

 

* millis.c
 * ATTiny861 test the millis functions in Atmel Studio 7
 * using Timer0 at PLL, the F_CPU will be in 16MHz
 * Fuse setting L:D1, H:D7, E: FF
 */

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <util/atomic.h>
#include <avr/interrupt.h>


// Store value of millisecond since uC started
volatile unsigned long millis_value;
unsigned long interval_value;
unsigned long elapsed_time;

// This function is used to get millis_value
unsigned long millis()
{
	unsigned long m;
	uint8_t oldSREG = SREG;
	// Disable interrupts while we read millis_value or we might get an
	// inconsistent value (e.g. in the middle of a write to millis_value)
	ATOMIC_BLOCK(ATOMIC_FORCEON){
	cli();
	m = millis_value;
	SREG = oldSREG;
	
	return millis_value;
	}
}


// Timer 0 output compare interrupt service routine
ISR(TIMER0_COMPA_vect)
{
	// Increment millisecond every 1ms
	millis_value++;	
		
}


void init_millis()
{
    
	/* Set timer 0 on CTC mode that will compare match every 1ms */
	// Timer/Counter 0 initialization
	// Mode: CTC top=OCR0A
	// TCCR0A = (1<<TCW0) if use 16 bit mode
		
	// TCCR0A = TCW0 | ICEN0 | INCC0 | ICES0 | ACIC0 | - | - | CTC0
	//            0  |  0    |    0  |     0 |  0    | 0 | 0 | 1
 	TCCR0A |= 1<< CTC0;
	
	// TCCR0B = |- | -| - | TSM | PSR0 | CS02 | CS01 | CS00 
	//          | 0|0 | 0 |  0  | 0    | 0    |  1   | 1   //64    
	//if 16MHz divide by 64 will get 249
	//if 8MHz divide by 64 since we only use 8 bits, cs0=1 and cs1 =1
	//if 1MHz devide by 8 since we only use 8 bits, cs01 =1
	TCCR0B |= (1<<CS00) | (1<<CS01) ;
	
	TCNT0L = 0x00;
	
	// 0CR0A value = (0.001 s x Fcpu/N) - 1
	// if 16MHz set at 254 (FE) fuse setting on PLL
	// if 8MHz set at 124 (7C) 
	OCR0A = 0xFE;//DEC IS 254
    
	/* Enable compare match interrupt for timer 0 */
	// Enable Compare A Interrupt(s) initialization
	TIMSK |= 1 << OCIE0A;//OCIE0A IS SET
	
	/* Remember to Enable global interrupt by using sei */
}
	
int main(){
	DDRB = 0xFF;
	init_millis();
    sei();  
	unsigned long previous_millis = 0 ;
	while(1)
	{
		PORTB |= (1<<PB4);
		unsigned long current_millis = millis();
		elapsed_time = current_millis - previous_millis;
    if (elapsed_time > 1000)
	{
    PORTB &= ~(1<<PB4);
     _delay_ms(100);    
    previous_millis = millis();
	}
	}
  return 0;
}

 

 

 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hamisu wrote:

unsigned long millis()
{
	unsigned long m;
	// Disable interrupts while we read millis_value or we might get an
	// inconsistent value (e.g. in the middle of a write to millis_value)
	ATOMIC_BLOCK(ATOMIC_FORCEON)

        {

	m = millis_value;

	}
return m;
}

 

The ATOMIC_BLOCK does the cli()/sei()for you. Did you not read the instructions?

I've applied some fixes so that should keep the compiler happy.

In retrospect, do you understand what the compiler was telling you?

 

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

Thank you Kartman. The error all gone.  You are genious !

 

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

Yes, it’s a cross I must bear.
However, what did you learn? Do you now understand why the compiler gave you those warnings? Spend some to understand these and the use of atomic-block. This will build your knowlege and skill.