7

For DIY, how to program Arduino Mini Pro to act as an Audio Frequency White Noise generator?

Presumably,

a) using one Digital Output pin

b) feed to a simple passive RC low pass filter to reduce out of band signal

c) feed to a simple class-D IC chip audio amplifier and speaker

d) since it is DIY and noise, lo-fi (not hi-fi) is good enough.

Specific questions,

a) How to program?

b) Wonder if it is better to use to chip act a Pulse Width Modulated 'digital to analogue converter'?

c) How to calculate value for RC filter (assume amplifier has high input impedance)?

Many years ago, I have programmed a hardware sound generator chip as part of a game machine. It has two registers for amplitude and 'frequency' control (band of 'white' noise). It worked very well. I once looked at the output using oscilloscope and it was square wave. I believe that it works, basically, using the above principles. Since I do not have the chip now, I cannot verify it. Now, I would like to duplicate its function using Arduino in software.

2 Answers 2

5

I don't know that you need all that - just an Arduino and a piezo speaker and a pot if you want to play with the volume.

byte speakerPin = 1;

void generateNoise() {
  static unsigned long int reg = 0x12345678; //seed
  unsigned long int newr;
  unsigned char lobit;
  unsigned char b31, b29, b25, b24;
  b31 = (reg & (1L << 31)) >> 31;
  b29 = (reg & (1L << 29)) >> 29;
  b25 = (reg & (1L << 25)) >> 25;
  b24 = (reg & (1L << 24)) >> 24;
  lobit = b31 ^ b29 ^ b25 ^ b24;
  newr = (reg << 1) | lobit;
  reg = newr;
  digitalWrite (speakerPin, reg & 1);
  delayMicroseconds (50);  // Changing this value changes the frequency.
}
5
  • After reading your answer, I did some searching and realize that your answer is based on, quoted from wikipedia, "pseudo-random binary sequence (PRBS) generated by a linear feedback shift register (LFSR)". Any pointers to non-pure-maths (and simple) information on how to choose the "polynomial" (bit 24, 25, 29 and 31 in your example) so as to achieve the white-noise property?
    – EEd
    Dec 28, 2014 at 18:29
  • The en.wikipedia.org/wiki/Linear_feedback_shift_register article points to tables of good polynomials for different bit-length shift registers at xilinx.com/support/documentation/application_notes/xapp052.pdf . The main difference between a good polynomial and a less good one is whether the length of the cycle until it repeats is as long as possible (2^n-1 is the max). users.ece.cmu.edu/~koopman/lfsr/index.html points to a table with 67,108,864 equally good 32 bit alternatives.
    – Dave X
    Nov 24, 2015 at 15:10
  • I think you need to edit unsigned long int newr; to be unsigned long int reg, newr; (and add const int speakerPin = 8;) Jun 22, 2018 at 0:12
  • I'd really like to understand this anwser, but there is no explanation as to whats going on here. I don't know what the "reg" keyword is, nor what these bitwise operations are accomplishing, or why they aren't just defined, as they appear to be constants. On an Arduino, how is this better than using the "random" function with the values between the min and max frequency?
    – Dani
    Feb 4, 2019 at 22:03
  • Also the shift register reg must be (a) defined as either a global or as static and (b) assigned an non-zero value. I used global: uint32_t reg = 0xfeedfaceUL ; . The solution in the answer by Dave X works without changes.
    – 6v6gt
    May 12, 2019 at 9:25
5

Mike's answer uses a Fibonacci Linear Feedback Shift Register configuration, but the compactness Galois Linear Feedback Shift Register Configuration could be faster:

#define speakerPin 8

unsigned long lastClick;

void setup() {
  // put your setup code here, to run once:
   pinMode(speakerPin,OUTPUT);
   lastClick = micros();   
}


/* initialize with any 32 bit non-zero  unsigned long value. */
#define LFSR_INIT  0xfeedfaceUL
/* Choose bits 32, 30, 26, 24 from  http://arduino.stackexchange.com/a/6725/6628
 *  or 32, 22, 2, 1 from 
 *  http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
 *  or bits 32, 16, 3,2  or 0x80010006UL per http://users.ece.cmu.edu/~koopman/lfsr/index.html 
 *  and http://users.ece.cmu.edu/~koopman/lfsr/32.dat.gz
 *  or generate a one with https://github.com/hayguen/mlpolygen
 */  
#define LFSR_MASK  ((unsigned long)( 1UL<<31 | 1UL <<15 | 1UL <<2 | 1UL <<1  ))

unsigned int generateNoise(){ 
  // See https://en.wikipedia.org/wiki/Linear_feedback_shift_register#Galois_LFSRs
   static unsigned long int lfsr = LFSR_INIT;  /* 32 bit init, nonzero */
   /* If the output bit is 1, apply toggle mask.
                                    * The value has 1 at bits corresponding
                                    * to taps, 0 elsewhere. */
                                
   if(lfsr & 1) { lfsr =  (lfsr >>1) ^ LFSR_MASK ; return(1);}
   else         { lfsr >>= 1;                      return(0);}
}


void loop() {
      /* ... */
      if ((micros() - lastClick) > 50 ) { // Changing this value changes the frequency.
        lastClick = micros();
        digitalWrite (speakerPin, generateNoise());
      }
     
}

For a 16 bit implementation (faster, but only a cycle of 2^16) try:

#define LFSR_MASK 0x8016 
static uint16_t lfsr = LFSR_INIT;  /* 16 bit init, nonzero */

Written in a more compact form:

#define speakerPin 8

#define LFSR_INIT 0xfeed /* non-zero seed value for generateNoise() */
#define LFSR_MASK 0x8016 /* magic number from http://users.ece.cmu.edu/~koopman/lfsr/ */

unsigned long lastClick;

void setup() {
  // put your setup code here, to run once:
   pinMode(speakerPin,OUTPUT);
   lastClick = micros();   
}


unsigned int generateNoise(){ 
   // Return 1 bit of noise using a Galois Linear Feedback Shift Register
   // See https://en.wikipedia.org/wiki/Linear_feedback_shift_register#Galois_LFSRs

   static uint16_t lfsr = LFSR_INIT; 
                                
   if(lfsr & 1) { lfsr =  (lfsr >>1) ^ LFSR_MASK ; return(1);}
   else         { lfsr >>= 1;                      return(0);}
}


void loop() {
      /* ... */
      if ((micros() - lastClick) > 50 ) { // Changing this value changes the frequency.
        lastClick = micros();
        digitalWrite (speakerPin, generateNoise());
      }
     
}

For an 8 bit implementation (faster than 32 bit, slightly slower than 16, but only a cycle of 2^8) try:

#define LFSR_MASK 0x8e 

static byte lfsr = LFSR_INIT;  /* 8 bit init, nonzero */

Where the mask constants are from the 8.txt and 16.txt files at http://users.ece.cmu.edu/~koopman/lfsr/index.html

The https://github.com/hayguen/mlpolygen program can search for maximum length feedback polynomials of various bit sizes.

3
  • 1
    be great for this to have more comments on this code to explain whats going on here for people still learning bitwise. I'm getting throw off because if "lfsr" is defined by the constant every time the function is called and then bitwised by another constant, how is this generating random numbers. why is the definition for LFSR_MASK so complex if its just a constant? please help?
    – Dani
    Feb 4, 2019 at 22:11
  • @dprogramz: In C, the static keyword in the declaration protects against just what you are worried about--the initialization with the constant only happens once on the initial declaration of the function, and the changing lfsr state is maintained through successive calls to the function. The bit-shifting definition of LFSR_MASK looks complicated, but it is easier to check, explain, change, and maintain than constants like 0b10100010100000000000000000000000UL The actual selection of the mask is more complicated, so see the refs.
    – Dave X
    May 13, 2019 at 3:53
  • 1
    Thanks for the reply, that makes total sense now. I had a misunderstanding of the use to the Static keyword, but it's much more clearer now how it functions, thanks again!
    – Dani
    May 20, 2019 at 15:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.