Go Down

### Topic: Call random() from interrupt service routine? (Read 1 time)previous topic - next topic

#### JimG

##### Oct 11, 2011, 06:07 pm
I've got a program that responds to AC line zero crosses (an isolated detector drives an Uno D2 pin low at each crossing).  What's the best way to make sure there is a "fresh" pseudo-random number available to the interrupt 0 handler each time it gets called?

Jim

#### Grumpy_Mike

#1
##### Oct 11, 2011, 06:35 pm
It is best to keep interrupt service routines short but nothing to stop you calling the random function.

Random coupled with mains does make me worried, what are you doing?

#### JimG

#2
##### Oct 11, 2011, 11:43 pm
I am experimenting with different methods of integral cycle resistive load control using zero cross SSR's.

Presently, I am using predefined on/off patterns, at half-wave resolution (120Hz), for different output levels.  This works pretty well, but I want to see if random patterns further reduce the flicker.

The general idea is to generate a random number from 0 to 100.  Compare it to the desired duty cycle (75%, for example).  If the random number is less than 75, then the SSR signal is made high and the next half wave becomes an "on" wave.  If the random number is greater than 75, then the next half wave is an "off" wave.  Over a period of a second or two, you would expect something near the desired 75% duty cycle.

Jim

#### jwatte

#3
##### Oct 12, 2011, 02:38 amLast Edit: Oct 12, 2011, 02:39 am by jwatte Reason: 1
If you don't call random() from any other part of your code, it should be safe.

However, you probably should just set a flag in the interrupt handler, and pick that flag up in the main() function, and do all your random stuff there, including preparing the next random number.

Also, you can easily generate your own random number genrator using a linear congruential generator, which will be good enough for the use you're suggesting (in fact, a simple table of size 256 might be good enough!)

If your problem is flicker, though, then that won't get better with randomness IMO. You're probably better off just settling on a N:M quantization of on/off distribution. A random distribution might make the noise you may hear from certain fixtures less static, though, and thus maybe make it slightly less unpleasant. It's better to have no noise, though :-)

Here's a function I would use to figure out whether the next is "on" or "off":

Code: [Select]
`char curVal;char ratioN; // N >= 0, N <= Mchar ratioM; // M > 0, M <= 127bool getOn() {  curVal += N;  if (curVal >= 0) {    curVal -= M;    return true;  }  return false;}`

Also, you probably don't need 100 steps of control -- try 20-30 steps of control for a pretty smoothly varying system, especially if you're driving things like incandescent lights.

#### JimG

#4
##### Oct 12, 2011, 04:58 am
Many thanks for the suggestions.  I now have some research to do  :~

Jim

#### JimG

#5
##### Oct 13, 2011, 02:29 am
The N:M quantization works very well.  Thanks again.

I ended up using a resolution of 100 (i.e. M = 100), though I agree that a lower resolution would have probably worked just as well.   Using 100 makes the duty cycle more easily understood by humans.  No real penalty for overkill in this situation.

The short algorithm you posted replaces probably a hundred lines of my old code  :0

Jim

#6
##### Oct 16, 2011, 08:13 am

One small suggestion.  The language standards do not specify if char is signed or unsigned.  Best to be explicit or use a platform independent type...

Quote
int8_t curVal;
int8_t ratioN; // N >= 0, N <= M
int8_t ratioM; // M > 0, M <= 127

bool getOn() {
curVal += N;
if (curVal >= 0) {
curVal -= M;
return true;
}
return false;
}

#### nickgammon

#7
##### Oct 16, 2011, 08:18 am
See:

http://arduino.cc/en/Reference/Char

Quote
The char datatype is a signed type, meaning that it encodes numbers from -128 to 127.
Please post technical questions on the forum, not by personal message. Thanks!

#8
##### Oct 16, 2011, 09:38 am

When I wrote that, I was thinking of the C(++) language standards and future boards.  After reflection I will re-characterize the suggestion as "irrelevant" rather than "small".

#### JimG

#9
##### Oct 16, 2011, 03:26 pmLast Edit: Oct 16, 2011, 03:30 pm by JimG Reason: 1
FWIW, I implemented it using int16_t.  I'm going to change it to int8_t, I think.

All is not completely well, however. This code, along with code to perform a phase angle delay using timer1, lives inside an ISR for int 0 (I have an external zero cross detector).

In the main loop of the program, I watch for changes on A0 and A1 using analogRead. There are 1-turn pots on those pins. I change the values of ratio_N and phase delay used in the ISR based on the analog readings.

Seemingly randomly, the Arduino locks up once in a while when turning the pots. Way too much code to post here, and could also be related to trying to update an I2C LCD panel too quickly with the changing analog readings (although the same code works perfectly in other applications that do not have the interrupt handler).

Jim

code is here if you are interested.

#### jwatte

#10
##### Oct 17, 2011, 05:29 am

The short algorithm you posted replaces probably a hundred lines of my old code  :0