Имитатор шума прибоя на attiny13
Автор ARV   
21.09.2008 г.

Только не спрашивайте - зачем! Так уж вышло, что сначала возникла необходимость в нем, а когда был получен результат - необходимость пропала. Ну не пропадать же добру!? Возможно, кому-то пригодится в качестве игрушки, а кому-то алгоритм пригодится... В общем, вот что вышло.

 

Задача.

Сделать генератор, имитирующий шум прибоя. То есть плавно нарастающий шипящий звук, затем так же плавно, но медленнее, спадающий до нуля. Через некоторое время тишины процесс повторяется. (В перспективе планировалось сделать регулируемой скорость «волн» и даже сделать ее случайной - но это требование отпало).

Алгоритм.

Прежде всего, пришлось решить проблему генерации шипения. Наиболее точно шипению соответствует белый шум, т.е. сигнал, спектр которого «плоский» и «бесконечный». Получение такого сигнала средствами микроконтроллера - нетривиальная задача.

В общем случае, белый шум достаточно неплохо имитируется последовательностью прямоугольных импульсов случайной длительности, т.е. можно его получить при помощи ШИМ со случайным заполнением. Главное - получение этого самого «случайного» заполнения.

Как известно, аппаратно последовательность импульсов псевдослучайной длительности, реализуется при помощи регистра сдвига с обратными связями через элементы «исключающее или», т.е. примерно по такой схеме:

Функциональная схема генератора случайной последовательности
Функциональная схема генератора случайной последовательности

Число триггеров в регистре сдвига - чем больше, тем более длинной получается неповторяющаяся последовательность импульсов на выходе элемента XOR. Число входов элемента XOR и номера триггеров, с выхода которых на этот элемент подаются сигналы, определяют «полином генератора», т.е., если не вдаваться в математические глубины, характер последовательности. Данная схема имеет одну особенность - если начальное состояние триггеров окажется нулевым - схема работать не будет.

Реализация регистра сдвига элементарно реализуется оператором сдвига, элемент XOR - само собой соответствующим оператором, а «полином» - соответствующей константой, биты которой определят и номера разрядов, с которых берутся обратные связи.

 

Чтобы сгенерированный таким образом шум плавно нарастал по громкости, надо определиться и с характером этого процесса. Я избрал, как наиболее универсальный, табличный способ задания «огибающей», т.е. массив, числа в котором являются масштабными коэффициентами, на которые будут умножаться случайные числа, ну а само произведение будет задавать коэффициент заполнения ШИМ.

Итак, алгоритм вырисовывается следующий: получаем случайное число при помощи нашего «генератора», умножаем его на очередной коэффициент из массива огибающей, результат записываем в регистр управления ШИМ-генератора. Через некоторое время эти действия повторяем - в результате наш белый шум будет промодулирован огибающей.

Аппаратная поддержка.

В микроконтроллере attiny13 имеется всего один аппаратный таймер, который будет использоваться для организации выборки значений огибающей из массива. Модуль захвата-сравнения этого таймера будет настроен на режим ШИМ, причем оба его канала (А и В) на работу в противофазе. Это сделано для того, чтобы иметь на выходе 2 противофазных сигнала, что позволит либо увеличить громкость звукоизлучателя (если он будет подключаться непосредственно к портам контроллера), либо организовать «световые» эффекты, подключив звукоизлучатель к одному выходу, а к другому - светодиод (тогда в такт «волнам» плавно будет вспыхивать и светодиод).

Программа.

Исходный текст получившейся программы на Си (WinAVR) приведен ниже.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#define POLY (0xA001) /* полином генератора случайных чисел */
// от этого полинома зависит «тембр» шума

// константы, задающие характер шума
#define WN_TEMPO 250 /* «темп» прибоя */
#define WN_FLAT 20 /* длительность «вершины» волны :) */
#define WN_FALL 4 /* сила замедления спада волны */
#define WN_PAUSE 250 /* длительность паузы между волнами */

#define SZ 128 /* размер массива огибающей */

//уровень громкости шума
volatile register unsigned char volume asm("r10");

// массив огибающей - 1/4 периода синуса
PROGMEM unsigned char amp[SZ]= {
253, 250, 247, 243, 240, 237, 234, 231, 228, 225, 222, 218, 215, 212, 209, 206,
203, 200, 197, 194, 191, 188, 185, 182, 179, 176, 173, 170, 167, 164, 161, 158,
155, 152, 149, 147, 144, 141, 138, 135, 133, 130, 127, 124, 122, 119, 116, 114,
111, 109, 106, 104, 101, 99, 96, 94, 91, 89, 86, 84, 82, 79, 77, 75,
73, 71, 68, 66, 64, 62, 60, 58, 56, 54, 52, 50, 49, 47, 45, 43,
41, 40, 38, 36, 35, 33, 32, 30, 29, 27, 26, 25, 23, 22, 21, 19,
18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 8, 7, 6, 6, 5,
4, 4, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0};

// прерывание по переполнению таймера - генерация огибающей
ISR(TIM0_OVF_vect){

static unsigned char i=SZ-1, n=0, k=0;
static unsigned char mode = 0;

if(++n == WN_TEMPO) // громкость меняется не каждое прерывание!!!
// в зависимости от режима меняется поведение

switch(mode){

// "фронт волны" ------------------------------------------------
case 0: if(i > (SZ-1)){

// если весь массив перебрали - вкл. следующий режим
mode = 1;
i = 0;
k = 0;
break;
};
// извлекаем уровень огибающей
n=0;
volume = pgm_read_byte(&amp[i--]);
break;

// "вершина волны"-----------------------------------------------
// просто держим некоторое время уровень неизменный шума

case 1: if(++k != WN_FLAT) break;

else{
// после паузы вкл.след.режим
mode=2;
i = 0;
n=0;
k = 0;
break;
};

// "спад волны"_-------------------------------------------------
case 2: if(++k != WN_FALL) break;

// скорость спада меньше нарастания
k=0;
if(i > (SZ-1)){
// весь массив перебрали - вкл. следующий режим
mode = 3;
i = 127;
n=0;
k = 0;
break;
}
// извлекаем уровень огибающей
n=0;
volume = pgm_read_byte(&amp[i++]);
break;

// "пауза" -----------------------------------------------------
// просто некоторое время ничего не делаем

case 3: if(++k != WN_PAUSE){ break;}

// после истечения паузы - начинаем сначала
mode = 0;
n=0;
break;

};

}

int __attribute__((naked)) main(void) {

unsigned int poly = POLY;
unsigned int word = 0xFFFF;

union{

unsigned long L;
unsigned char b[4];
} R;

DDRB = (1<<PB0)|(1<<PB1); // оба выхода ШИМ 

// настройка таймера
TCCR0A = 0b10110011; // Fast-PWM, выходы А и В в противофазе
TCCR0B = 0b00000001; // без предделителя

// настройка прерываний
TIMSK0 = (1<<TOIE0); // по переполнению таймера
sei(); // разрешаем глобально

// главный цикл
for(;;){

// генерируем очередное случайное число
if(word & 0x0001)

word=word>>1 ^ poly;

else

word=word>>1;

// умножаем его на уровень громкости (т.е. модулируем)
R.L = (unsigned long)word * volume;

// старший байт результата используется для ШИМ
// так как результат (word)*(byte) не более 3-х байт,
// старшим будет 3-й, а не 4-й байт

OCR0A = R.b[2];
OCR0B = R.b[2];

}

}

Собственно говоря, я надеюсь, что комментарии в тексте позволят разобраться в программе. Заострю внимание лишь на нескольких нюансах.

Для функции main() используется атрибут naked - это позволяет сэкономить несколько байт памяти программ, что для attiny13 с его килобайтом памяти - совсем не лишнее.

 

При модуляции используется умножение 16-битного числа на 8-битное. По правилам Си результат будет приведен к наибольшему размеру из сомножителей, т.е. к 16 битам, что нас не устраивает из-за потери старших битов. Поэтому используется явное приведение типа первого множителя к unsigned long. C другой стороны, нас интересует только старший байт результата (который будет третьим из четырех - см. комментарии в тексте). Чтобы наиболее просто обратиться к третьему байту результата использована переменная-объединение (union) R. Такой подход позволяет сэкономить несколько байт крохотной памяти программ по сравнению с первым приходящим на ум способом получения того же результата (L>>16) & 0xFF.

Итоги.

Программа проверялась на макетной плате, к контроллеру подключались звукоизлучатели из китайских игрушек - один с сопротивлением катушки 16 ом, другой - 60. На мой «вкус» - шум вполне похож на шум волны, но несколько утомляет однообразием: слишком ритмичный... Хотя, возможно, кому-то наоборот понравится засыпать под этот ритм... Схема, ввиду ее примитивности, не приводится, надеюсь, номера выводов питания и выходов каналов А и В для ШИМ сумеют найти среди аж 8-и выводов attiny13 все желающие повторить это чудо.

Константы, определяющие ритм прибоя, достаточно просто заменяются на соответствующие переменные, которые можно менять в программе либо по случайному закону (при помощи того же генератора случайных чисел), либо при помощи каких-то кнопок. Реализация этой части алгоритма пусть будет на совести тех, кого этот «микро-проект» заинтересует.

 


Добавить в любимые (10) | Просмотров: 144528

  Коментарии (5)
 1 Написал(а) Настя, в 09:06 22.09.2008
Очень интересная математика. 
И полезность, если включить 
функцию "фантазия" очевидна 
(в вопросах шифрования и защиты).
 2 Написал(а) ARV, в 12:10 22.09.2008
можно в принципе и рейтинг статье поставить, если понравилась :)
 3 Написал(а) Igor, в 08:27 23.04.2009
такую штуку делают и продают за большие деньги спецмализированнные фирмы для комнат переговоров, типа зашумление пространства чтобы помешать подслушиванию в звуковом диапазоне... 
А вот интересно бы было сделать звук капель дождя стучащих по подоконнику\\отливу окна на улице, и чтобы частота капель и интенсивность была случайной :) 
а вообще мне понравилась задумка с прибоем. нестандартно и просто. спасибо.
 4 Написал(а) manoj, в 23:56 30.09.2017
Hello ARV 
How are you?.... I cant still access the vk.com. 
I need to reinstall the windows... 
 
Mis you lot...
 5 Написал(а) Николай, в 18:22 16.10.2020
Знаю что тема давно умерла, но именно сейчас, с приближением старости, появилась надобность в шумелке, которая будет душить посторонние звуки, мешающие, уже очень чуткому сну. Летом спасал шуршащий вентилятор, дующий в открытое окно и кроме успокаивающего шелеста, увеличивал приток свежего воздуха в квартиру. Пришла осень и вентилятору пора на покой. Но без его шелеста, я очень плохо сплю. Вот и задумал собрать шумелку. Буду очень благодарен, если мне в этом помогут. У меня есть всякие контроллеры и программаторы для пик и авр. Но в программировании, я дуб дерево. Мне нужна схема и НЕХ файл. Остальное я могу

Только зарегистрированные пользователи могут оставлять коментарии.
Пожалуйста зарегистрируйтесь или войдите в ваш аккаунт.