Generating white noise on a microcontroller
Originally posted June 3, 2009. Updated on May 11, 2019.
I recently finished a project for Dorel (makers of Safety 1st, Quinny, and other fine baby products) where I had to make a white noise generator using a microcontroller. It was a lot harder than I thought it would be. What didn't work: pre-computing a wavetable with 100-1000 random numbers. The human ear can detect aural patterns, and at an 8kHz sample rate, the 1000 number pattern repeats itself about 8 times per second which is noticeable. You hear a “sh-sh-sh-sh-sh-sh” sound pattern. I did discover what does work: a Linear Feedback Shift Register. (Wikipedia) Using a 16 bit LFSR you can get a 16*16 = 65535 pseudorandom number sequence. In my tests the pattern could not be detected, which is perfect. Please note that I did not do a FFT to determine whether the output noise whas flat in the frequency domain.
I implemented this on an MSP430F2618 using its internal DAC and the internal timer; though any MCU with a DAC could work. It uses TimerA0 to generate a constant period. When the timer interrupt occurs, the LFSR generates a new value and sends it out via the DAC.
Setting up the DAC:
void setupDac()
{
ADC12CTL0 = REF2_5V + REFON; // Internal 2.5V ref on
// Delay is 17mSec for caps to charge, [from code] 13600 at 1MHz, or 0x1A900 at 8MHz
// At 8000 cycles/Msec, need 0x21340 clock cycles to get 17mSec
// for safety, we'll delay a little longer, or 0x22000 clock cycles
#define SEVENTEEN_MS_AT_8MHZ 0x22000
for (long j = SEVENTEEN_MS_AT_8MHZ; j; j--); // Delay for needed ref start-up.
DAC12_1CTL = DAC12IR + DAC12AMP_5 + DAC12ENC + DAC12OPS; // Int ref gain 1, DAC12OPS = output select for DAC12_1 on P6.5
}
Setting up TimerA0. This sets up the interval between output samples.
void setupTimerA()
{
TACCTL0 = CCIE; // TACCR0 interrupt enabled
TACCR0 = 200; //starting interval, gets changed at first interrupt
TACTL = TASSEL_2 + MC_2; // SMCLK, up mode
}
And finally, the TimerA0 interrupt service routine (ISR). Every time period the DAC will output a new random number. This is the galois linear feedback shift register.
/*
* White Noise Generator
* Galois Linear Feedback Shift Register implementation
*/
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
TACCR0 += 0x3FF; // Timer interval, Add Offset to TACCR0
lsb = lfsr & 1; //Get lsb (i.e., the output bit).
lfsr >>= 1; //Shift register
if(lsb == 1) //Only apply toggle mask if output bit is 1.
lfsr ^= 0xB400u; //apply toggle mask, value has 1 at bits corresponding to taps, 0 else where.
DAC12_1DAT = lfsr;
}
That's it. In your main code, be sure that you enable interrupts, or else it won't work.