Hello.
[001 The issue]
uint32_t micro_seconds = ((m<<8) + t) * (64/clockCyclesPerMicrosecond());
The issue occurs when this value overflows after reaching around 4,200,000,000. The expected behavior is that the value should overflow to 0 and then increment by 1(ex - 1,2,3,4), but instead, it stays stuck at 0.
when I print "(m<<8) + t", it increments correctly even after the overflow.
additionally, since "64/clockCyclesPerMicrosecond()" is constant double value, I can't understand why it result in 0.
I'm looking for the cause of this phenomenon, a solution is welcome, but it is not the primary focus of my question.
[002 Environment]
MCU : ATmega328p
IDE : Microchip Studio
System clock : 16MHz -> 14.7456MHz.
Reason for changing system clock : To reduce serial communication errors.
due to change in clock to 14.7456mhz, variables that were previously integers are now became real number, which is causing issues with the micros() and millis() function from the arduino library. So I'm editing the code.
[003 Source Code]
#define F_CPU 14745600UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h> // itoa
#define clockCyclesPerMicrosecond() ((double)F_CPU/1000000.0) // 14.7456
volatile uint32_t timer0_overflow_count = 0;
#define BAUDRATE 9600
void uart_write(int8_t data);
void uart_write_string(int8_t * data);
void uart_write_string(const char * data);
uint32_t micros();
int main(void)
{
/*
millis, micros()
*/
TCCR0A = TCCR0B = TIMSK0 = 0;
TCCR0A = 0; // PWM Mode X
TCCR0B = (1<<CS01) | (1<<CS00); // Normal Mode(CTC X)
TIFR0 &= ~(1<<TOV0);
TIMSK0 |= (1<<TOIE0);
/*
Serial
*/
UBRR0 = (F_CPU/(BAUDRATE*16UL)) - 1;
// UCSR0B |= (1<<TXEN0) | (1<<RXEN0) | (1<<RXCIE0);
UCSR0B |= (1<<TXEN0);
UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01); // Async, 8-bit data, 1 Stop bit, No Parity
/*
LED
*/
DDRB |= (1<<PORTB0);
PORTB &= ~(1<<PORTB0);
DDRD &= ~( (1<<PORTD5) | (1<<PORTD6) | (1<<PORTD7) );
PORTD |= (1<<PORTD5) | (1<<PORTD6) | (1<<PORTD7);
DDRB &= ~( (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3) | (1<<PORTB4) | (1<<PORTB5) );
PORTB |= (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3) | (1<<PORTB4) | (1<<PORTB5);
DDRC &= ~( (1<<PORTC0) | (1<<PORTC1) | (1<<PORTC2) | (1<<PORTC3) | (1<<PORTC4) | (1<<PORTC5) );
PORTC |= (1<<PORTC0) | (1<<PORTC1) | (1<<PORTC2) | (1<<PORTC3) | (1<<PORTC4) | (1<<PORTC5);
sei();
uint32_t previous_led_time = 0;
while (1)
{
uint32_t now = 0;
now = micros();
if(now - previous_led_time >= 10000000)
{
if(PORTB & (1<<PORTB0))
{
PORTB &= ~(1<<PORTB0);
previous_led_time = micros();
}
else
{
PORTB |= (1<<PORTB0);
previous_led_time = micros();
}
}
}
}
uint32_t micros()
{
uint32_t m;
uint8_t oldSREG = SREG;
cli();
m = timer0_overflow_count;
uint8_t t = TCNT0;
if((TIFR0 & (1<<TOV0)) && (t<255))
{
m++;
}
SREG = oldSREG;
uint32_t micro_seconds = ((m<<8) + t) * (64/clockCyclesPerMicrosecond());
char _buffer1[100];
uart_write_string("micros : ");
ultoa(micro_seconds, _buffer1, 10);
uart_write_string(_buffer1);
uart_write_string(" ");
char _buffer2[100];
uart_write_string("overflow count : ");
ultoa(m, _buffer2, 10);
uart_write_string(_buffer2);
uart_write_string(" ");
char _buffer3[100];
uart_write_string("m<<8 + t : ");
ultoa(((m<<8) + t), _buffer3, 10);
uart_write_string(_buffer3);
uart_write_string("\r\n");
return micro_seconds;
}
void uart_write(int8_t data){
while(!(UCSR0A & (1<<UDRE0)));
UDR0 = data;
}
void uart_write_string(int8_t * data)
{
while(*data != '\0')
{
uart_write(*data++);
}
}
void uart_write_string(const char * data)
{
while(*data != '\0')
{
uart_write(*data++);
}
}
ISR(TIMER0_OVF_vect)
{
timer0_overflow_count++;
}
after running the MCU for about 70 mins, micro_second value overflows after reaching around 4200000000 and becomes 0.
[004 Error Screenshots]
As shown in the screenshots, (m<<8) + t is incrementing correctly. (64 / clockCyclesPerMicrosecond()) is constant. so, result value should not be 0, but it is.
this is not accidental case, It happens consistently. I repeated the test 4 times.
before 70 mins, micros() function works fine.
[005 Experiment of uint32_t rollover]
Below is an experiment for checking uint32_t overflow behavior.
I confirmed that the uint32_t value increments by 1 after overflow. I could not find the situation that it became consistently 0 after overflow.
while (1)
{
uint32_t value_before_overflow = 4294967295;
char _buffer1[15] = {' '};
uart_write_string("value_before_overflow : ");
ultoa(value_before_overflow, _buffer1, 10);
uart_write_string(_buffer1);
uart_write_string("\r\n");
char _buffer2[15] = {' '};
uart_write_string("value plus 1 : ");
ultoa(value_before_overflow + 1, _buffer2, 10);
uart_write_string(_buffer2);
uart_write_string("\r\n");
char _buffer3[15] = {' '};
uart_write_string("value plus 2 : ");
ultoa(value_before_overflow + 2, _buffer3, 10);
uart_write_string(_buffer3);
uart_write_string("\r\n");
char _buffer4[15] = {' '};
uart_write_string("value plus 3 : ");
ultoa(value_before_overflow + 3, _buffer4, 10);
uart_write_string(_buffer4);
uart_write_string("\r\n");
char _buffer5[15] = {' '};
uart_write_string("value multipyling 2 : ");
ultoa(value_before_overflow * 2, _buffer5, 10);
uart_write_string(_buffer5);
uart_write_string("\r\n");
char _buffer6[15] = {' '};
uart_write_string("value multipyling 4 : ");
ultoa(value_before_overflow * 4, _buffer6, 10);
uart_write_string(_buffer6);
uart_write_string("\r\n");
char _buffer7[15] = {' '};
uart_write_string("value + 105 + multipyling 4 : ");
uint8_t plus = 105;
ultoa((value_before_overflow + plus) * 4, _buffer7, 10);
uart_write_string(_buffer7);
uart_write_string("\r\n");
char _buffer8[15] = {' '};
uart_write_string("((value << 8) + 105 * 4) : ");
ultoa(((value_before_overflow<<8) + plus) * 4, _buffer8, 10);
uart_write_string(_buffer8);
uart_write_string("\r\n");
char _buffer9[15] = {' '};
uart_write_string("((value << 8) + 105 * 4.1) : ");
ultoa(((value_before_overflow<<8) + plus) * 4.1, _buffer9, 10);
uart_write_string(_buffer9);
uart_write_string("\r\n");
char _buffer10[15] = {' '};
uart_write_string("((value << 8) + 105 * (64/13.8) : ");
ultoa(((value_before_overflow<<8) + plus) * (64/13.8), _buffer10, 10);
uart_write_string(_buffer10);
uart_write_string("\r\n");
char _buffer11[15] = {' '};
uart_write_string("((value << 8) + 105 * (64/14.7456) : ");
ultoa(((value_before_overflow<<8) + plus) * (64/14.7456), _buffer11, 10);
uart_write_string(_buffer11);
uart_write_string("\r\n");
#define clockCyclesPerMicrosecond() ((double)F_CPU/1000000.0)
char _buffer12[15] = {' '};
uart_write_string("( ((value<<8) + 105) * (64 / clockCyclesPerMicroseconds()) ) : ");
ultoa( ( ((value_before_overflow<<8) + plus) * (64 / clockCyclesPerMicrosecond()) ), _buffer12, 10);
uart_write_string(_buffer12);
uart_write_string("\r\n");
value_before_overflow = 3865411;
char _buffer13[15] = {' '};
uart_write_string("((3865411 << 8) + 105) / 4.1 : ");
ultoa( ( (value_before_overflow<<8) + plus ) / 4.1, _buffer13, 10);
uart_write_string(_buffer13);
uart_write_string("\r\n");
value_before_overflow = 3865478;
char _buffer14[15] = {' '};
uart_write_string("((3865478 << 8) + 105) / 4.1 : ");
ultoa( ( (value_before_overflow<<8) + plus ) / 4.1, _buffer14, 10);
uart_write_string(_buffer14);
uart_write_string("\r\n");
value_before_overflow = 3866000;
char _buffer15[15] = {' '};
uart_write_string("((3866000 << 8) + 105) / (64 / clockCyclesPerMicroseconds() ) : ");
ultoa( ( (value_before_overflow<<8) + plus ) / ( 64 / clockCyclesPerMicrosecond() ), _buffer15, 10);
uart_write_string(_buffer15);
uart_write_string("\r\n");
// result = ((m<<8) + t) * (64/clockCyclesPerMicrosecond());
/*
value plus 1 : 0
value plus 2 : 1
value plus 3 : 2
value multipyling 2 : 4294967294
value multipyling 4 : 4294967292
value + 105 + multipyling 4 : 416
((value << 8) + 105 * 4) : 4294966692
((value << 8) + 105 * 4.1) : 4294967295
((value << 8) + 105 * (64/13.8) : 4294967295
((value << 8) + 105 * (64/14.7456) : 4294967295
( ((value<<8) + 105) * (64 / clockCyclesPerMicroseconds()) ) : 4294967295
((3865411 << 8) + 105) / 4.1 : 241352528
((3865478 << 8) + 105) / 4.1 : 241356704
((3866000 << 8) + 105) / (64 / clockCyclesPerMicroseconds() ) : 228026000
*/
_delay_ms(3000);
}
}

