Data Truncation/Conversion Issues

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

I know this is going to be a silly question here. But for some reason I am stuck and unable to proceed.

In the code below, I am unable to set PWM based on the data sent from terminal. I want to set %PWM and max I can enter is 100%. Seems to be a small issue in data type/conversion. I type casted one of them, but still not able to get correct result. If I send 0x64 (100%), I get back 0x00 from controller and if I send 0x1E from terminal, I get back 0x01. If I send 0x10, I get back 0x02.

If I comment the line 

data = (uint8_t)((uint16_t)data*OCR2Acnt)/100;

and send upto 0x1E, everything works fine.

 

Second question: 

What is the value of var1=255+ 1; if the var1 is uint8_t in AVR GCC? In which document I can find the rules for overflow?

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#define USART_BAUDRATE 9600
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
void USART0Send_Byte(unsigned char dataTobeSent);
void USART0Init(void);
void initTimer2_ForFastPWM(uint8_t OCRA_cnt);

void initTimer2_ForFastPWM(uint8_t OCRA_cnt)
{
    //
    DDRD|=1<<PD3;
	TCCR2A = (1<<COM2B1) | (1<<WGM21) | (1<<WGM20);
	TCCR2B = (1<<WGM22)|(1<<CS22)|(1<<CS21)|(1<<CS20);  // Prescaler
	OCR2A = OCRA_cnt;
}

void USART0Send_Byte(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;
}


void USART0_Init(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

	UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
    UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
}

int main (void)
{
    USART0Init();
    uint8_t OCR2Acnt=31;
    initTimer2_ForFastPWM(OCR2Acnt);
    OCR2B=10;
    while(1)
    {
         if(UCSR0A & (1<<RXC0))
        {
            uint8_t data;
            data=UDR0;
            // Convert percentage to count
            if(data<=100)
            {
              data = (uint8_t)((uint16_t)data*OCR2Acnt)/100;
              OCR2B=data;
              USART0Send_Byte(data);
            }
        }
    }
    return(0);
}

I am using avr8-gnu-toolchain with atmega328P.

This topic has a solution.

Last Edited: Tue. Dec 19, 2017 - 11:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lets leave the casting out of the picture.  What were you expecting from the line

 

data = (data * OCR2Acnt) / 100;

​An example only to aid discovery, the obtainable values of data1 are minimum = 0 and maximum = 31, is this what you expected?  Step through this in the simulator and see what it does.

	volatile uint8_t OCR2Acnt = 31, data, data1;

	while (1) {
		// Convert percentage to count
		for (data = 0; data <= 100; data++)
		{
			data1 = (data * OCR2Acnt) / 100;
		}

	}

 

 

 

 

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Wed. Dec 20, 2017 - 12:37 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yes, I am expecting only 0 to 31 because OCR2A is set to 31 and OCR2B cannot be more than 31.

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

Add 1 to 255 = 0
The result is truncated to fit into the specified storage.

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

 In which document I can find the rules for overflow?

Probably in Basic Numerical Mathematics. 

 

A pot overflows when it it is full of water and we add another spoon to it.

A number overflows when it holds the maximal possible value and we add 1.

 

4-bit number overflows from 1111 to 0   (15 -> 0)

8-bit number overflows from 11111111 to 0   (255 -> 0)

and so on

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

joneewheelock wrote:

yes, I am expecting only 0 to 31 because OCR2A is set to 31 and OCR2B cannot be more than 31.

Perfect, now we all know what "still not able to get correct result" is.  I had already surmised that.

 

EDIT: removed truncation comment

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Wed. Dec 20, 2017 - 12:39 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 I send 0x1E from terminal, I get back 0x01. If I send 0x10, I get back 0x02.

  data = (uint8_t)((uint16_t)data*OCR2Acnt)/100;

 

wrong parenthesis, should be

 

data = (uint8_t) ((uint16_t)data*OCR2Acnt/100);   

 

 

Edit:

The original command works if you remove the cast (uint8_t)

 

data = ((uint16_t)data*OCR2Acnt)/100;

Last Edited: Tue. Dec 19, 2017 - 07:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So in the end, now that the OP has been given the answer, the percentage points at which data will increment by one are; 

0x04, 0x07, 0x0a, 0x0d, 0x11, 0x14, 0x17, 0x1a, 0x1e, 0x21,

0x24, 0x27, 0x2a, 0x2e, 0x31, 0x34, 0x37, 0x3b, 0x3e, 0x41,

0x44, 0x47, 0x4b, 0x4e, 0x51, 0x54, 0x58, 0x5b, 0x5e, 0x61,

0x64

 

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Wed. Dec 20, 2017 - 12:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh Thank you. It was the parenthesis that caused the problem. My intention was

data = (uint8_t)((uint16_t)data*OCR2Acnt/100);

But noticed that typecasting is not needed.

I was initially thinking that (data*OCR2Acnt) gets truncated when product crosses 255 (that is the reason why I asked what is 255+1 in AVR GCC in case there is a compiler dependency). But noticed that truncation does not happen during multiplication and gets promoted to 2 byte value first and then gets divided by 100 and finally result it may get truncated depending on LHS data type. So following code works fine:

data = data*OCR2Acnt/100;

 

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

Glad it works.

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Wed. Dec 20, 2017 - 12:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joneewheelock, one small piece of advice to add here, when you write your code use spaces and parentheses.  Spaces to delineate terms and operations, parentheses to group them to show the intent of the code.

(not covering grouping for order of operations here - that is another topic altogether)

 

It will make your code easier to read and maintain.

 

So for instance your

data = data*OCR2Acnt/100;

might be better written as 

data = (data * OCR2Acnt) / 100;

 

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Wed. Dec 20, 2017 - 01:10 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Pedantic moment: Overflow isn't defined for signed types in C. Unsigned types always work modulo TYPE_MAX+1. Since uint8_t can represent 0-255, anything converted to uint8_t is mod 256.

 

And as a general piece of advice: Just always use the parentheses. Even if they're not changing precedence, they make it clearer what your intent was.

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

Yes I agree for two suggestions on parenthesis usage. Depending on operator precedence is definitely a bad practice. For example: if (UCSR0A & 1<<RXC0).