The AtMega32 goes strange in an arithmetic statement.

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

In Atmega32, I am writing a code for a function that creates a delay using tmers when the user gives it a number representing the milliseconds he wants as a time delay.

Bool delay(uint32 ms_time)
 {
	Bool local_errorState = 1;
	uint32 local_countsNo;
	f32 count_unit;

	count_unit = ((f32)1 / F_CPU ) / 1000;

	//local_countsNo = (uint32)(ms_time / count_unit);

	if(( (ms_time) / (count_unit) ) <= MAX_8bit_COUNT )
	{
		TIM2_TCNT2 = MAX_8bit_COUNT - local_countsNo;
		WRITE_REG(TIM2_TCCR2, 1<< CS20);

		while( (TIM_TIFR & (1 << TOV2)) == GPIO_LOW );

		WRITE_REG(TIM2_TCCR2, GPIO_LOW);
		SET_BIT(TIM_TIFR, TOV2);
    }

I am not asking about how to write this function, above is just a part of a long code. I can handle the logic of the code.

Bool is a synonym for unsigned char, uint32 is a synonym for unsigned long, and f32 is a synonym for float.

F_CPU equals 12.

While single-stepping the code that calls this function on a number e.g 500, the code goes to disassembly tab in Atmel Studio 7 when it encounters 'count_unit = ((f32)1 / F_CPU ) / 1000;' and does nothing, it is stuck in this statement.

And this code executed on hardware generates a much longer delay, and the delay is the same even if the function argument is changed. 

Usually, Atmel Studio 7 goes to the disassembly tab after returning from the main function.

I think the trouble is in the arithmetic operations in the AVR.

I need clarification about what is going on.

This topic has a solution.
Last Edited: Tue. Jul 23, 2019 - 09:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try setting F_CPU to 12000000 instead of 12 if your clock speed is 12MHz.

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

Which AVR8 ??


What is MAX_8bit_COUNT set to ? (255 ?)


The time delay will probably not change because local_countsNo has not been set to anything.

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

AhmedH wrote:
when it encounters 'count_unit = ((f32)1 / F_CPU ) / 1000;' and does nothing
Why would you expect that statement to "do" anything? If, as you say F_CPU=12 I can tell you now that this is 8.3333333333333333333333333333333e-5. I know it, the compiler knows it - nothing will be done at run time to calculate this. When used the compiler will simply load the float value for 0.000083333333 into machine registers (that is 0x38aec33e). The first time this is used is during:

(ms_time) / (count_unit)

Unless ms_time is invariable and already known at compile time (in which case the compiler would pre-calculate this too) then the compiler will use 0x38aec310 as one parameter and the ms_time value promoted to float as the other parameter and will pass these to a floating div function.

 

If I build:

#define F_CPU 12

#include <avr/io.h>

typedef uint8_t Bool;
typedef uint32_t uint32;
typedef float f32;

#define MAX_8bit_COUNT 200

Bool delay(uint32 ms_time)
{
	f32 count_unit;

	count_unit = ((f32)1 / F_CPU ) / 1000;

	if(( (ms_time) / (count_unit) ) <= MAX_8bit_COUNT )
	{
		PORTB = 0x55;
		return 1;
	}
	return 0;
}

int main(void)
{
	asm("nop");
	
}

as a cut down test program I find:

000000c6 <delay>:
{
	f32 count_unit;

	count_unit = ((f32)1 / F_CPU ) / 1000;

	if(( (ms_time) / (count_unit) ) <= MAX_8bit_COUNT )
  c6:	82 d0       	rcall	.+260    	; 0x1cc <__floatunsisf>
  c8:	2e e3       	ldi	r18, 0x3E	; 62
  ca:	33 ec       	ldi	r19, 0xC3	; 195
  cc:	4e ea       	ldi	r20, 0xAE	; 174
  ce:	58 e3       	ldi	r21, 0x38	; 56
  d0:	15 d0       	rcall	.+42     	; 0xfc <__divsf3>
  d2:	20 e0       	ldi	r18, 0x00	; 0
  d4:	30 e0       	ldi	r19, 0x00	; 0
  d6:	48 e4       	ldi	r20, 0x48	; 72
  d8:	53 e4       	ldi	r21, 0x43	; 67
  da:	0c d0       	rcall	.+24     	; 0xf4 <__cmpsf2>
  dc:	18 16       	cp	r1, r24
  de:	24 f0       	brlt	.+8      	; 0xe8 <delay+0x22>
	{
		PORTB = 0x55;
  e0:	85 e5       	ldi	r24, 0x55	; 85
  e2:	85 b9       	out	0x05, r24	; 5
		return 1;
  e4:	81 e0       	ldi	r24, 0x01	; 1
  e6:	08 95       	ret
	}
	return 0;
  e8:	80 e0       	ldi	r24, 0x00	; 0
}
  ea:	08 95       	ret

The first rcall is converting the input ('ms_time') from u32 to f32. You then see 38 AE C3 3E being loaded into a register group that are then used in the call to _divsf3(). This is exactly as I predicted.

 

So unless the code you are looking at does something dramatically different in the Asm this is working exactly as expected.

 

By the way do not use the name "F_CPU" for your frequency value. There is a convention in AVR code (esp avr-gcc) that "F_CPU" will always be the CPU speed in Hertz not in MegaHertz so you will likely have very confusing results if you use the same name for something that is MHz. Maybe call yours "F_CPU_M" or something similar to make the distinction and avoid confusion.

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

It seems that the trouble is not in the 

count_unit = ((f32)1 / F_CPU ) / 1000;

statement, it works fine.

I discarded all my defined symbolic constants, and used only numbers to clarify everything for myself, and wrote everything in main.

When I write this to create a half-second delay:

f32 count_unit =   (1 / (f32)12) *1024  / 1000;

uint16 local_countsNo = 500 / count_unit;

uint16 val = 65536 - local_countsNo;	

TCNT1L = val;
TCNT1H = (val >> 8);

TCCR1B |= ((1 << CS10) | (1 << CS12))

while( (TIFR & (1 << TOV1)) == GPIO_LOW );

TCCR1B = 0x00;

TIFR |= (1 << TOV1);

The same long delay occurs, that has the same duration whether I use 500 or any other number.

And the single stepping drives me to the disassembly tab in Atmel Studio 7  when it encounters this

'uint16 local_countsNo = 500 / count_unit;' 

 

Surprisingly, when I write the following code in which I made the calculations on my own, and in the program I just just load the TCNT1L, and TCNT1H with the proper values, the code works well and generates a half-second delay that can be modified by changing the number 500.

It seems that the  division in 'uint16 local_countsNo = 500 / count_unit;' is the problem, and I am overwhelmed now.

 

TCNT1L = 59677;
TCNT1H = (59677 >> 8);

TCCR1B |= ((1 << CS10) | (1 << CS12));

while( (TIFR & (1 << TOV1)) == 0 );

TCCR1B = 0x00;

TIFR |= (1 << TOV1);

 

Last Edited: Tue. Jul 23, 2019 - 01:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

If I build:

void delay(void)
{
	f32 count_unit =   (1 / (f32)12) *1024  / 1000;

	uint16 local_countsNo = 500 / count_unit;

	uint16 val = 65536 - local_countsNo;

	TCNT1L = val;
	TCNT1H = (val >> 8);
}

the code generated is:

000000c6 <delay>:

	uint16 local_countsNo = 500 / count_unit;

	uint16 val = 65536 - local_countsNo;

	TCNT1L = val;
  c6:	8d e1       	ldi	r24, 0x1D	; 29
  c8:	80 93 84 00 	sts	0x0084, r24	; 0x800084 <__TEXT_REGION_LENGTH__+0x7e0084>
	TCNT1H = (val >> 8);
  cc:	89 ee       	ldi	r24, 0xE9	; 233
  ce:	80 93 85 00 	sts	0x0085, r24	; 0x800085 <__TEXT_REGION_LENGTH__+0x7e0085>
  d2:	08 95       	ret

So val is 0xE91D. If you want to know how the compiler got that value make the intermediates "volatile" then:

	volatile f32 count_unit =   (1 / (f32)12) *1024  / 1000;
  da:	8e e3       	ldi	r24, 0x3E	; 62
  dc:	93 ec       	ldi	r25, 0xC3	; 195
  de:	ae ea       	ldi	r26, 0xAE	; 174
  e0:	bd e3       	ldi	r27, 0x3D	; 61
  e2:	89 83       	std	Y+1, r24	; 0x01
  e4:	9a 83       	std	Y+2, r25	; 0x02
  e6:	ab 83       	std	Y+3, r26	; 0x03
  e8:	bc 83       	std	Y+4, r27	; 0x04

shows that the result of that was 0x3DAEC33E which you can plug into: 

 

https://www.h-schmidt.net/FloatConverter/IEEE754.html

 

which shows that to be 0.08533333. Next comes:

	volatile uint16 local_countsNo = 500 / count_unit;
  ea:	29 81       	ldd	r18, Y+1	; 0x01
  ec:	3a 81       	ldd	r19, Y+2	; 0x02
  ee:	4b 81       	ldd	r20, Y+3	; 0x03
  f0:	5c 81       	ldd	r21, Y+4	; 0x04
  f2:	60 e0       	ldi	r22, 0x00	; 0
  f4:	70 e0       	ldi	r23, 0x00	; 0
  f6:	8a ef       	ldi	r24, 0xFA	; 250
  f8:	93 e4       	ldi	r25, 0x43	; 67
  fa:	1f d0       	rcall	.+62     	; 0x13a <__divsf3>
  fc:	86 d0       	rcall	.+268    	; 0x20a <__fixunssfsi>
  fe:	78 87       	std	Y+8, r23	; 0x08
 100:	6f 83       	std	Y+7, r22	; 0x07

and while you could try to work your way through that it's much easier to simply run it in the simulator where at 0x00fe the value in R23:R22 is 0x16E3 which is 5859. So far, so good, 500 / 0.0853333 is indeed 5859. I imagine it's the next bit that will be most "interesting"...

	volatile uint16 val = 65536 - local_countsNo;
 102:	8f 81       	ldd	r24, Y+7	; 0x07
 104:	98 85       	ldd	r25, Y+8	; 0x08
 106:	91 95       	neg	r25
 108:	81 95       	neg	r24
 10a:	91 09       	sbc	r25, r1
 10c:	9e 83       	std	Y+6, r25	; 0x06
 10e:	8d 83       	std	Y+5, r24	; 0x05

So that is taking the 0x16E3 value and NEGating it. At 0x0106 the R25:R24 value is 0xEA1D then the SBC makes it 0xE91D and that is the value (59677) that is written into the TCNT registers. 1D to TCNT1L and E9 to TCNT1H.

 

What I have noticed is that in the AS7 simulator for mega32 that there's something wrong with the display of TCNT1H. I see it do an OUT 2D, R25 which should write it but I don't see the value in TCNT1H updated to reflect this?

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

AhmedH wrote:

When I write this to create a half-second delay:

f32 count_unit =   (1 / (f32)12) *1024  / 1000;

uint16 local_countsNo = 500 / count_unit;

uint16 val = 65536 - local_countsNo;	

TCNT1L = val;
TCNT1H = (val >> 8);

TCCR1B |= ((1 << CS10) | (1 << CS12))

while( (TIFR & (1 << TOV1)) == GPIO_LOW );

TCCR1B = 0x00;

TIFR |= (1 << TOV1);

From the datasheet:

To do a 16-bit write, the High byte must be written before the Low byte. For a 16-bit read, the
Low byte must be read before the High byte.

 

Hopefully the compiler will correct this miss ordering of the register write values, but could explain why changing the value does not change the time delay!

When writing 16 bit registers, I let the compiler do the work and simply do:

AhmedH wrote:

TCNT1 = val;

The compiler knows the correct order to do this operation!

Jim

 

 

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

@ clawson  It seems that my code works fine. What should I do to make it really execute on my hardware ? 

Should I use another IDE, or anything else? 

Do you have a solution to it?

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


AhmedH wrote:
Do you have a solution to it?
Jim just gave you the solution. Either:

	TCNT1H = (val >> 8);
	TCNT1L = val;

or

	TCNT1 = val;

(for my money the latter seems infinitely preferable). In either case...

 

That has put the desired 0xE91D into TCNT1

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

To reset the overflow flag:
TIFR = (1<< TOF1);
Do not do |= !!!

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

Kartman wrote:
To reset the overflow flag: TIFR = (1<< TOF1); Do not do |= !!!

 

I used 

TIFR |= (1 << TOV1)

and everything works very well in all aspects, thank you.

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

Thanks all, everything works very well now.

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

There are possible side effects. You may not encounter them now, but may hit them when you least expect it. Do a search - its been discussed here many times. Ignore at your own peril!

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

Kartman wrote:
There are possible side effects. You may not encounter them now, but may hit them when you least expect it. Do a search - its been discussed here many times. Ignore at your own peril!

 

Why your language seems tough? I will do a search and will appreciate your hint.

 

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

don’t take ‘my ignore at your own peril’ comment too literally.
However, using |= is not good for clearing an interrupt flag. If more than one interrupt flag is set, then you will end up clearing more than one bit.