Truncating Data for use with OCR0A (PWM)

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void Set_Motor_Speed(void){
	uint8_t temp=0;
	temp = PIND;	
	temp &= 0xA0;	
	switch(temp){
		case 0: 
			duty_cycle = 255;	
	soft_start_duty_cycle = duty_cycle * 0.8;	
			break;
		case 32: 
			duty_cycle = 230;	
	soft_start_duty_cycle = duty_cycle * 0.8;	
			break;
		case 128: 
			duty_cycle = 204;	
	soft_start_duty_cycle = duty_cycle * 0.8;
			break;
		case 160: 
			duty_cycle = 179;	
	soft_start_duty_cycle = duty_cycle * 0.8; 
			break;
		default : 
			duty_cycle = 255;	
	soft_start_duty_cycle = duty_cycle * 0.8;
			break;
	}
}

If I try to compile the following, it compiles with no errors or warnings, however my program usage jumps from 8.8% to 46% and my data usage jumps from .9% to 27%. So something is going on that I do not understand. soft_start_duty_cycle and duty_cycle are both declared as volatile global uint8_t because they are used to change OCR0A in an interrupt. Since I only need unsigned integer values for OCR0A, I want the result truncated since after the decimal is of no important for my application here. How do I truncate the result of the multiplications without using such an extreme amount of program and data space? I tried type cast duty_cycle and 0.8 as float, then moved the result into soft_start_duty_cycle, hoping it would truncate without this issue, but that did not work.

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

You need to remove the use of floats entirely. That 0.8 causes the floating point library to invite itself into your program. You can use integer math such as

val2 = ((unsigned int)val1*8)/10;

(make sure val1*8 can fit into an unsigned int, otherwise you'll need to go to an unsigned long)

Or

val2 = val1 - val1/5;

Or

val2 = (val1>>1)+(val1>>2)+(val1>>4);

(this actually gives a multiplier of 0.5+0.25+0.0625 = 0.8125 - is that close enough?)

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

Ok, thanks, I actually just found that information on a C++ forum, that took care of it.

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

kk6gm wrote:

val2 = ((unsigned int)val1*8)/10;

(make sure val1*8 can fit into an unsigned int, otherwise you'll need to go to an unsigned long)


Just to correct the record, the above should be

val2 = ((unsigned int)val1*4)/5;

That allows for twice as big a val1 before the intermediate value (val1*4) overflows. Of course if val1 is an 8-bit value then a 16-bit intermediate could never overflow with a multiplier of 4, but in the more general case where val1 is 16-bits, or where the multiplier is some other number, it pays to reduce the fraction as I did here.