Copying Lower data type to higher data type

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

I have small issue in copying lower data type to higher data type in below code. I used nano board (atmega328P) and avr gcc compiler with codeblocks.

When I execute this code, I see 55 AA FF FF in terminal software. Somewhere some mistake it looks and unable to find it. Sorry, I am level 0 programmer and the question could be silly also.

Even I tried typecasting the line as below, but no use.

int32_t cnt=(int32_t)cnt2;

Complete code

#include <avr/io.h>
void USART0Init(void)
{
	// Set baud rate
	UBRR0H = (uint8_t)(UBRR_VALUE>>8);    // High Bye of the baud rate
	UBRR0L = (uint8_t)UBRR_VALUE;         // Low byte of the baud rate

	// Set frame format to 8 data bits,
	/// By default no parity and 1 stop bit
	// Another bit is taken from UCS register B. But not used.
	// By default character size is also 8 bit.

	UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);    // This is not needed since default setting is already 1.
	// But still it is better to initialize to 8 bit mode

	//Interrupt has to be disabled during initialization in case interrupt is used
	//cli();

	//enable transmission and reception Normal port functionality is now disabled
	UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
	//enable reception interrupt. This can be combined with previous step.
	// But keeping this separate so that disabling or enabling interrupt can be done easily.
	//UCSR0B |= (1<<RXCIE0)

	// Enable the Interrupt globally Interrupt based reception or transmission.
	// sei();       // SREG|=(1<<7);  Enable the global interrupt flag

}

// Return value is not used currently. For future use.

int8_t USART0SendByte(unsigned char dataTobeSent)
{
	//wait while previous byte is completed

	while(!(UCSR0A&(1<<UDRE0)))   // As long as not TRUE
	{
		// Do nothing

	}

	// Transmit the data
	UDR0 = dataTobeSent;
	return(0);
}

int main(void)
{
    USART0Init();
    int16_t cnt2=0xaa55;
    int32_t cnt=cnt2;
    uint8_t *ptr = (uint8_t*) &cnt;
    int8_t count;
    for(count=0;count<4;count++)
    {
        USART0SendByte(*ptr++);
    }
}

 

Last Edited: Thu. Sep 16, 2021 - 12:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My expectation was 55 AA 00 00. from where this FF FF is getting introduced?

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

Do you have access to a logic analyzer to look at the actual data stream as it comes into the board? That would tell you a lot.

 

Typecasting is no help if the data is incorrect!

 

Ooops, I am confused. Your code is sending data. Where do you observe those 4 characters? What happens if you do:

 

cnt = 0x0000aa55;

 

What happens if you use the debugger and put a breakpoint immediately after assigning cnt? What does the debugger show you as the hex value of cnt?

 

Ahh, for an int16_t, 0xaa55 is a NEGATIVE NUMBER. Assigning it to an int32_t will sign-extend that negative number into 0xffffaa55! Try making those uint16_t and uint32_t.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Thu. Sep 16, 2021 - 05:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh! I thought it is some silly mistake!! Thanks for your help

 

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

Its one of those type conversion things that catches me when I don't expect it. My rule is to never use a signed data type unless you REALLY need to handle negative values.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

 

It all started to go wrong here:

    int16_t cnt2=0xaa55;

In an int16_t (signed 16 bit) only 0x0000..0x7FFF are positive values. 0x8000..0xFFFF (in which range 0xAA55 sits) are negative numbers. If you think in decimal it may be clearer. The range of a signed 16 bit is -32768 .. +32767. 32767 is 0x7FFF. The very top bit of the upper byte is the "sign bit". In all the values 0x0000..0x7FFF bit 15 is 0 (so "positive"). In 0x8000..0xFFFF bit 15 is 1, so "negative".

 

So your:

    int16_t cnt2=0xaa55;

is exactly the same as if you wrote:

    int16_t cnt2 = -21931;

When you assign the 16 bit wide value to a 32 bit wide value it still needs to be -21391 in the wider variable which involves "sign extension". That basically says - if the sign bit in the thinner value is set then set all the bits above it in the wider value to 1 too. So 0xAA55 becomes 0xFFFFAA55 when extended to 32 bits. It is still the -21931 value you asked for at the very start.

 

Of course if your test value have been 0x55AA instead you wouldn't have seen this. 0x55AA is +21930 so when extended to 32 bit it would be 0x000055AA because the sign bit was not set in the original so will not be sign extended in the wider value. In fact this is true for all values in the 0x0000 .. 0x7FFF (0 .. 32767) range. But as soon as you set the SIGNED 16 bit value to something in the 0x8000..0xFFFF range you are really setting a negative value in the -32768 .. -1 range and it will continue to be negative when promoted to 32 bits (by the upper two bytes being set to 0xFFFF which moves the sign bit all the way up to bit 31).

 

PS interesting summary here: http://www.c-jump.com/CIS77/CPU/...

 

One thing that also tells you is that in an int8_t (not uint8_t by the way!) the range is -128 .. +127 with 0x00..0x7F (the values that don't have bit 7, the sign bit set) being the positive values 0 .. +127 but any value 0x80 .. 0xFF will be negative. So if you had even used:

    int8_t cnt2 = 0xB7;

then the 32 bit extension of this would have been 0xFFFFFFB7 which continues to hold the -73 value this starts with.

 

Stolen from: https://en.wikipedia.org/wiki/Signed_number_representations#Two's_complement :

Compare the signed/unsigned interpretation of the same bit patterns once you pass 0b01111111 (127)

 

Last Edited: Thu. Sep 16, 2021 - 09:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joneewheelock wrote:
lower data type to higher data type

you mean a smaller to a larger data type?

 

C has standard behaviours here - called the "Usual arithmetic conversions":

 

https://en.cppreference.com/w/c/language/conversion

 

it's not specific to AVR - it's a general C thing.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
My rule is to ...
operate linters

#include <stdint.h>

void USART0Init(void);
int8_t USART0SendByte(unsigned char dataTobeSent);

int main(void)
{
    USART0Init();
    int16_t cnt2=0xaa55;
//  int16_t cnt2=(int16_t)0xaa55;
    int32_t cnt=cnt2;
    uint8_t *ptr = (uint8_t*) &cnt;
    int8_t count;
    for(count=0;count<4;count++)
    {
        USART0SendByte(*ptr++);
    }
}

 

int16_t cnt2=0xaa55;

produced a loss of information warning of 16b to 15b.

 


PC-lint Plus Online Demo - Gimpel Software - The Leader in Static Analysis for C and C++ with PC-lint Plus

Gimpel On-Line Message Reference Manual (1/3 page for 569)

PC-lint Plug-in - Developer Help (Microchip Technology)

 

"Dare to be naïve." - Buckminster Fuller

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

joneewheelock wrote:

void USART0Init(void)
{
	// Set baud rate
	UBRR0H = (uint8_t)(UBRR_VALUE>>8);    // High Bye of the baud rate
	UBRR0L = (uint8_t)UBRR_VALUE;         // Low byte of the baud rate

	// Set frame format to 8 data bits,
	/// By default no parity and 1 stop bit
	// Another bit is taken from UCS register B. But not used.
	// By default character size is also 8 bit.

	UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);    // This is not needed since default setting is already 1.
	// But still it is better to initialize to 8 bit mode

	//Interrupt has to be disabled during initialization in case interrupt is used
	//cli();

	//enable transmission and reception Normal port functionality is now disabled
	UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
	//enable reception interrupt. This can be combined with previous step.
	// But keeping this separate so that disabling or enabling interrupt can be done easily.
	//UCSR0B |= (1<<RXCIE0)

	// Enable the Interrupt globally Interrupt based reception or transmission.
	// sei();       // SREG|=(1<<7);  Enable the global interrupt flag

}

I thought it began to go wrong with the above, should be:

void USART0Init(void)
{
    //Interrupt has to be disabled during initialization in case interrupt is used

   // do this first before changing any registers
    cli();
    
    // Set baud rate
    UBRR0 = UBRR_VALUE;         // compiler knows how to talk to 16 bit registers

 

    // Set frame format to 8 data bits,
    /// By default no parity and 1 stop bit
    // Another bit is taken from UCS register B. But not used.
    // By default character size is also 8 bit.

    // use = here to set all bits of the register
    UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);    // This is not needed since default setting is already 1.

 

    //enable transmission and reception Normal port functionality is now disabled
    UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);

 

    // Enable the Interrupt globally Interrupt based reception or transmission.
    // do this step in main(), just before entering forever loop
    // sei();       // SREG|=(1<<7);  Enable the global interrupt flag

}

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

If you want something you've never had...

...you must be willing to do something you've never done!

Lets go Brandon!

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

Thanks for your nice explanation. "Learn from mistake" is what I did today. I was really not aware of this sign extension and could understand this good concept.

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

To be clear: on a platform with 16-bit ints, 0xaa55 is an unsigned int.

Outside the common range, converting unsigned to signed is implementation-defined.

AVRs are twos-complement machines and avr-gcc does a sensible thing:

it just copies bytes.

The result is a negative number.

On any platform with both, converting from int16_t to int32_t is value-preserving.

In this case, a negative number is preserved.

Not coincidentally, its value is 0x2a55-0x8000 and (uint32_t)cnt==0xFFFFaa55.

Moderation in all things. -- ancient proverb

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

ka7ehk wrote:

Its one of those type conversion things that catches me when I don't expect it. My rule is to never use a signed data type unless you REALLY need to handle negative values.

Exactly.  For the OP, I will offer my little 8-bit mantra.  Apply it to every piece of data in your program.

 

Never use floating point when integer will do.

Never use 32 bits when 16 bits will do.

Never use 16 bits when 8 bits will do.

Never use signed when unsigned will do.

Last Edited: Fri. Sep 17, 2021 - 02:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But also, be aware of hidden implicit promotion of data types, which can also change the sign of a value if you're not aware of what's happening: https://www.avrfreaks.net/forum/...

 

Neil

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

kk6gm wrote:
Never use 8 bits when 8 bits will do.

You mean 16 when 8 will do?

 

This is all good for 8-bit processors, but possibly not so much for larger widths (eg, ARM 32 bits) ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

kk6gm wrote:
Never use 8 bits when 8 bits will do.

You mean 16 when 8 will do?

 

This is all good for 8-bit processors, but possibly not so much for larger widths (eg, ARM 32 bits) ...

Yes, thanks, I fixed the 8/16 bit error.  And I did describe this as my 8-bit mantra.  For 32-bit CPUs, the 8/16/32 performance issues largely go away (but the RAM use issues remain - an array of values 0 < x < 200 stored as 32-bit integers is still wasteful, and the smallest 32-bit parts may have as little as 1k of RAM).

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

kk6gm wrote:
and the smallest 32-bit parts may have as little as 1k of RAM).
an instance of such :

picorv32/picosoc at master · cliffordwolf/picorv32 · GitHub (RISC-V)

[mid-page]

The SRAM is just a small scratchpad memory (default 256 words, i.e. 1 kB).

 

"Dare to be naïve." - Buckminster Fuller

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

Never use signed when unsigned will do.

I agree. Lot of string functions make use of 'char' types. char is implementation defined. How do we handle this? Let us say I have written code to read data from UART and then use some string functions like atoi or itoa (for transmitting). I use all uint8_t data type for ring buffers etc. Though I did not face any issues in passing uint8_t to char type, is such code is portable or maintainable in future?

Last Edited: Sat. Sep 18, 2021 - 01:39 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

My preference is to use char for characters, as the C-gods intended. Yes, char will work for 8-bit numeric values, but why get confused. Example:

 

Suppose that your UART receives '9' = 0x39 and you convert it to its integer value of 9 = 0x09. Now. try to keep from getting confused if you use uint8_t for your receive buffer. What is in it, characters or their numeric equivalent?

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Sat. Sep 18, 2021 - 05:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joneewheelock wrote:
Lot of string functions make use of 'char' types. char is implementation defined. How do we handle this?

You only use plain 'char' for things which are characters - for everything else, use uint8_t or int8_t.

 

ADDENDUM

 

In other words, consider that you have three distinct types:

  1. char - for characters only;
  2. uint8_t - for unsigned numbers, or bitfields, etc;
  3. int8_t - for signed numbers.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Sat. Sep 18, 2021 - 09:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Use char for characters. Use int8_t or uint8_t for anything else in 8 bits. If you insist on using just "char" for arithmetic be sure to put the word signed or unsigned on the front. 

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

joneewheelock wrote:
How do we handle this?
cast

#include <stdint.h>

void out(char val);

int main() {
    int8_t road = -1;
    uint8_t gtx = 0;
    out((char) gtx);
    out(road);
}

 


PC-lint Plus Online Demo - Gimpel Software - The Leader in Static Analysis for C and C++ with PC-lint Plus

Gimpel On-Line Message Reference Manual (713)

 

"Dare to be naïve." - Buckminster Fuller

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


And if you do use 'char', make sure you know what your compiler will make of it...

 

 

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

If you stick to using 'char' strictly for characters only, then you don't have to worry about this.

 

use uint8_t or int8_t (as appropriate) for everything else.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...