Генерация белого шума звуковой частоты с помощью Arduino Mini Pro

Для DIY, как запрограммировать Arduino Mini Pro действовать как генератор белого шума звуковой частоты?

Предположительно,

а) использование одного цифрового выходного вывода

б) подача на простой пассивный RC фильтр нижних частот для уменьшения внеполосного сигнала

c) подача на простой усилитель и динамик микросхемы IC класса D

d) поскольку это DIY и шум, lo-fi (не hi-fi) достаточно хорош.

Конкретные вопросы,

а) Как программировать?

б) Интересно, не лучше ли использовать для микросхемы act широтно - импульсный модулированный "цифроаналоговый преобразователь"?

в) Как рассчитать значение RC-фильтра (предположим, усилитель имеет высокий входной импеданс)?

Много лет назад я запрограммировал аппаратный чип звукового генератора как часть игрового автомата. Он имеет два регистра для управления амплитудой и "частотой" (полоса "белого" шума). Это сработало очень хорошо. Однажды я посмотрел на результат с помощью осциллографа, и он оказался квадратной волной. Я считаю, что он работает, в основном, используя вышеперечисленные принципы. Поскольку сейчас у меня нет чипа, я не могу его проверить. Теперь я хотел бы дублировать его функцию, используя Arduino в программном обеспечении.

, 👍8


2 ответа


Лучший ответ:

6

Я не знаю, нужно ли вам все это - просто Arduino, пьезодинамик и горшок, если вы хотите играть с громкостью.

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);  // Изменение этого значения изменяет частоту.
}
,

Прочитав ваш ответ, я провел некоторый поиск и понял, что ваш ответ основан на цитате из википедии: "псевдослучайная двоичная последовательность (PRBS), генерируемая регистром сдвига линейной обратной связи (LFSR)". Любые указатели на не чисто математическую (и простую) информацию о том, как выбрать "полином" (бит 24, 25, 29 и 31 в вашем примере), чтобы достичь свойства белого шума?, @EEd

В https://en.wikipedia.org/wiki/Linear_feedback_shift_register в статье приведены таблицы хороших полиномов для различных битовых сдвиговых регистров при http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf . Основное различие между хорошим многочленом и менее хорошим заключается в том, является ли длина цикла до его повторения как можно более длинной (2^n-1-максимум). http://users.ece.cmu.edu/~koopman/lfsr/index.html указывает на таблицу с 67 108 864 одинаково хорошими 32 - битными альтернативами., @Dave X

Я думаю, вам нужно отредактировать " unsigned long int newr;", чтобы он был "unsigned long int reg, newr;" (и добавить " const int speakerPin = 8;")., @Greenonline

Мне бы очень хотелось понять этот вопрос, но нет никакого объяснения тому, что здесь происходит. Я не знаю, что такое ключевое слово "reg", и что выполняют эти побитовые операции, и почему они просто не определены, поскольку они кажутся константами. На Arduino, как это лучше, чем использование "случайной" функции со значениями между минимальной и максимальной частотой?, @Danielle

Кроме того, регистр сдвига reg должен быть (а) определен как глобальный или статический и (б) присвоено ненулевое значение. Я использовал global: uint32_t reg = 0xfeedfaceUL ; . Решение в ответе Dave X работает без изменений., @6v6gt


5

Ответ Майка использует конфигурацию регистра сдвига линейной обратной связи Фибоначчи, но конфигурация регистра сдвига линейной обратной связи Галуа компактности может быть быстрее:

#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());
      }
     
}

Для 16 - битной реализации (быстрее, но только цикл 2^16) попробуйте:

#define LFSR_MASK 0x8016 
static uint16_t lfsr = LFSR_INIT;  /* 16 бит init, ненулевой */

Записано в более компактной форме:

#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());
      }
     
}

Для 8-битной реализации (быстрее, чем 32-бит, немного медленнее, чем 16, но только цикл 2^8) попробуйте:

#define LFSR_MASK 0x8e 

static byte lfsr = LFSR_INIT;  /* 8 бит init, ненулевой */

Где константы маски взяты из 8.txt и 16.txt файлы по адресу http://users.ece.cmu.edu/~koopman/lfsr/index.html

В https://github.com/hayguen/mlpolygen программа может искать полиномы обратной связи максимальной длины различных разрядов.

,

было бы здорово, если бы у вас было больше комментариев к этому коду, чтобы объяснить, что здесь происходит для людей, которые все еще учатся побитово. Я сбиваюсь с толку, потому что если "lfsr" определяется константой каждый раз, когда вызывается функция, а затем растрируется другой константой, то как это генерирует случайные числа? почему определение LFSR_MASK так сложно, если это просто константа? пожалуйста, помогите?, @Danielle

@dprogramz: В C ключевое слово "static" в объявлении защищает именно от того, о чем вы беспокоитесь-инициализация константой происходит только один раз при первоначальном объявлении функции, а изменение состояния "lfsr" поддерживается последовательными вызовами функции. Определение сдвига битов LFSR_MASK выглядит сложным, но его легче проверить, объяснить, изменить и поддерживать, чем константы типа 0b101000101000000000000000000000000ul Фактический выбор маски сложнее, поэтому см. Ссылки., @Dave X

Спасибо за ответ, теперь это имеет смысл. У меня было недопонимание использования статического ключевого слова, но теперь гораздо яснее, как оно функционирует, еще раз спасибо!, @Danielle