making code faster: PWM registers instead of analogWrite?

I've written a library that controls a rgb ledstrip.
I'm looking for optimization so that the time to change the color gets faster.
The code that needs optimization is goes as follows:

void RGB::setFromHSV(double hue, double saturation, double value){
	double RGB[3]; // to store the 3 values of R, G & B

	//H = hue, S = saturation (saturation), V = Value(blackness) http://en.wikipedia.org/wiki/HSL_and_HSV

	_H = hue;
	_S = saturation;
	_V = value;
	double f, p, q, t;
	if (_H > 359) _H = 0.0;
	_H = constrain(_H, 0.0, 360.0);
	int Hi = floor((_H / 60));
	f = _H / 60.0 - (Hi);
	p = (_V * (1.0 - _S));
	q = (_V * (1.0 - f * _S));
	t = ((_V * (1.0 - (1 - f) * _S)));

	switch (Hi) {
	case 0:
		RGB[0] = _V;
		RGB[1] = t;
		RGB[2] = p;

		break;
	case 1:
		RGB[0] = q;
		RGB[1] = _V;
		RGB[2] = p;

		break;
...(other cases similar)...
	default:
		RGB[0] = 0;
		RGB[1] = 0;
		RGB[2] = 0;

	}
	RGB[0] = round((RGB[0] * 255.0));
	RGB[1] = round((RGB[1] * 255.0));
	RGB[2] = round((RGB[2] * 255.0));

	_r = RGB[0];
	_g = RGB[1];
	_b = RGB[2];
	writeLeds();
}
void RGB::writeLeds(){
	analogWrite(_rPin, _r);
	analogWrite(_gPin, _g);
	analogWrite(_bPin, _b);
}

Now for the optimization: I'm thinking that the most efficient way to speed things up, is to change the analogWrite to the method using the PWM registers directly?

I'm limited to the pins 9,10 & 11 due to hardware restrictions (pcb is already made etc...) I'm using the arduino nano.
Also, I think I shouldn't change the timer registers, because I'm also using a RTC and LCD and delay() functions?

Will changing the pwm method help me to run the code faster? and if so, how do I edit the duty cycle on those 3 pins without changing frequency?
Other ways to speed up the code are welcome too :slight_smile:

You've got floating point arithmetic, and you're worrying about a optimising a few register writes?

Ward123:
I'm thinking that the most efficient way to speed things up,

Not much point trying to speed things up until you have figured out exactly what part of your code is slowing it down.

For instance, have you written a short program to time how long it takes to do analogWrite() ?

...R

float divisions are really expensive on an Arduino, multiplication is way faster.

_H / 60

==>
_H * 0.0166666667;

twice


p = (_V * (1.0 - _S));
q = (_V * (1.0 - f * _S));
t = ((_V * (1.0 - (1 - f) * _S)));

can be expressed simpler by finding common sub-expressions
first expand the formulas and detect the reusable parts

p = _V - _V * _S;
q = _V - _V * f * _S;
t = _V - _V * _S + _V * f * _S;

determine 2 common parts

float a = _V * _ S;
float b = a * f; // b = V * f * S

p = _V - a;
q = _V - b;
t = p + b;

so the original 5 mul + 4 add(sub) ===> 2 mul + 3 add(sub) should be ~2x faster.

Does this help?

very minor one but as the first line already checks the upper bound of constrain,
only the lower bound need to be checked.

if (_H > 359) _H = 0.0;
_H = constrain(_H, 0.0, 360.0);

===>

if (_H > 359) _H = 0.0;
if (_H < 0) _H = 0.0;

(OK, might be only 0.5 usec)

Also, I think I shouldn't change the timer registers, because I'm also using a RTC and LCD and delay() functions?

You are using delay() and looking to speed up your program. Hmm. Here's a clue by four.

Now for the optimization: I'm thinking that the most efficient way to speed things up, is to change the analogWrite to the method using the PWM registers directly?

That will make little, if any, difference. analogWrite just sets the PWM registers. The PWM is then done by hardware.

There is a little mucking around translating the requested pin to a register, but after that, it is plain sailing (example):

				// connect pwm to pin on timer 1, channel A
				sbi(TCCR1A, COM1A1);
				OCR1A = val; // set pwm duty

and if so, how do I edit the duty cycle on those 3 pins without changing frequency?

That can be done if you want to. There is a trade-off between available frequencies and the range of duty-cycles available.

As an example, if you need the counter to count to 4 to give you a desired frequency, there can be only 4 possible duty cycles: 0, 1, 2, 3.

I have a page on timers.

Well, thank you guys.
It's just that i'm looking for a way to learn more about code optimization. There must be a first time for everything. :wink:

I'm going to time the differences for each change and I'll let you guys know :slight_smile:

Also, in my main loop i'm updating the 'current time' from my RTC:

DateTime now = rtc.now();
 uur = now.hour();
 minuut = now.minute();

I'm suspecting that it would be alot faster to call this code only once every 500th time the main loop gets called, for instance:

if(loopcount==500) {
 loopcount = 0;
 DateTime now = rtc.now();
 uur = now.hour();
 minuut = now.minute();
}
loopcount++;

edit
@robtillaart:
My code took +-150 µs; the changes you proposed, made it 67µs faster :slight_smile:
@Nick Gammon:
Indeed, the 3 analogWrite's together take only 40µs; so it seems like there is little to improve on that part.

I changed my main loop so that the code that reads the current time from the rtc is runned only once every 500th loop too.
after all, the result is that the main loop runs at 1780Hz instead of 530Hz. quite an improvement I'd say. (:

Ward123:
I'm suspecting that it would be alot faster to call this code only once every 500th time the main loop gets called, for instance:

Or call it once every 10 seconds?

...R

Or every hour? Who cares what the time is when you are flashing an LED strip?

 uur = now.hour();
 minuut = now.minute();

Especially if you are only interested in the time to the nearest minute.