Привет друзья!
Генератор - штука нужная! Это факт номер один! А факт номер два - у меня его до сих пор нет! Серьезно! Хотя потребность в нем возникает с завидным постоянством. Как-то выкручиваюсь! Если уж совсем надо - собираю из подручных средств то на 555-м таймере, а то и на транзисторе с кварцем. А то, было дело, настраивал коротковолновый приемник прямого преобразования при помощи генератора, собранного на логических элементах и осциллографа. Ничего, до сих пор работает!
Вот примеры моего кустарного генераторостроения.
Слева генератор прямоугольных импульсов на таймере NE555 с 4-мя диапазонами, регулировкой частоты и скважности. В середине генератор меандра со стабилизатором напряжения и плавной регулировкой частоты от 50 кГц до 20 МГц на логике 74HC00. Это с помощью него я приемники настраиваю! Справа - гармонический кварцевый генератор на 1 МГц (дроссель фильтра извлечен для нужных дел).
Так бы все и продолжалось, пока дело не дошло до обработки сигналов при помощи замечательных (в смысле, мощных и дешевых) китайских микроконтроллеров. В прошлый раз, настраивая усилитель на этом контроллере, я пробовал компьютерный генератор использовать, но уж больно шумный сигнал был на выходе.
Ну и пришла, значит, мне идея побаловать себя очередным приборчиком. Тем более, что имеется платформа для решения задачи вполне себе подходящая. Я конечно имею ввиду микроконтроллер CH32V003J4M6, который в последнее время стал прямо-таки моим источником вдохновения!
Итак, делаем функциональный генератор с возможностью расширения функционала!
Постановка задачи и немного теории
Когда в ваши руки попадает микроконтроллер, то запросы растут как снежный ком! Идея формирования произвольного сигнала будоражит душу и заставляет выдумывать какие-то глупые колебания, которые вам точно не понадобятся! Начинал я вполне традиционно: синус, прямоугольник, треугольник, пила... Это точно все нужно? Хм... Потом появился запрос на белый шум. Ладно, это вещь полезная иногда. Потом я вспомнил про генератор качающейся частоты (ГКЧ), а потом вообще подумал про возможность графического редактора любого сигнала. В общем, помаявшись пару дней творческой дурью, но взяв себя в руки, я все же решил, что в 95,3 процентов случаев мне нужен обычный синус! А как-же остальное? А нормально! Понадобится - добавим!
Еще одна важная деталь - частотный диапазон. Тут не буду лукавить. Мои насущные задачи, связанные с цифровой обработкой сигналов, требуют генератора звукового диапазона. Более высокочастотные сигналы наш малыш CH32 обработать в реальном времени все равно не сможет, как не сможет и синтезировать высокочастотный сигнал хорошего качества (расчеты ниже). Значит, от 0 до 20, а лучше 30 килогерц (чтобы изменения в АЧХ улавливать).
Итак, создаем законченный прибор - генератор гармонических колебаний звукового диапазона с возможностью расширения функционала на китайском микроконтроллере CH32М003J4M6.
Общий подход к формированию аналоговых сигналов при помощи цифровых микроконтроллеров состоит из нескольких этапов:
- Создание модели сигнала в памяти устройства;
- Генерация прямоугольного сигнала с широтно-импульсной модуляцией (ШИМ);
- Фильтрация полученного сигнала.
Поехали!
Создание модели гармонического (синусоидального, если кого-то пугает слово "гармонический") сигнала.
Самый простой способ - использовать библиотечную функцию sin(x). Действительно просто, но и затратно с точки зрения вычислительных ресурсов. Дело в том, что вычисление тригонометрических функций связано с их представлением в виде рядов Тейлора. Высокая точность представления требует вычисления большого количества членов ряда. А каждый член ряда это множество операций умножения. В результате наш микроконтроллер будет полностью загружен только арифметическими задачами. Не надо нам такого!
Поэтому, второй способ - табличный. В этом случае опорные отсчеты сигнала рассчитываются заранее и хранятся в массиве. Вариант отлично подходит для случаев, когда не требуется слишком высокая точность. У нас как раз такой!
Теперь, что с точностью. Прикидываем. Максимальная тактовая частота контроллера CH32V003 - 48 МГц. Если предположить, что нас устроит 8-разрядное представление уровня сигнала, то потребуется 2^8 = 256 уровней ШИМ-сигнала. Значит базовая частота ШИМ равна 48000000 / 256 = 187500 Гц. Тогда на представление верхней частоты звукового диапазона - 20 кГц придется 187000 / 20000 = 9,3 отсчета сигнала. Много это или мало? Попробуем изобразить.
Как видно, 9 точек уже неплохо передают форму синусоиды в случае кусочно-линейной интерполяции (соединили точки прямыми). Здесь следует учесть, что особенности обработки сигналов в фильтрах низких частот сделают этот график более плавным. В общем, будем считать, что такая частота нас вполне устроит. Меньше явно не хотелось бы.
В свою очередь, 256 уровней квантования по напряжению тоже вполне достаточны. Конечно, их бы не хватило для качественного представления, например, речи или музыки. Не хватили бы потому, что громкость сигнала там меняется (этот параметр называется динамический диапазон) и на тихие участки будет приходится меньше уровней. Но, в нашем случае, синусоида имеет один уровней, а значит все 256 уровней будут использоваться постоянно. Отлично! С параметрами определились.
Теперь, что делать с изменением частоты? Ведь не будем же мы хранить свою синусоиду для каждой частоты, которая нам может понадобится? Разумеется нет! Достаточно будет одной, но сформированной относительно точно. Например так.
Здесь отсчеты исходного сигнала выбраны с избыточной точностью. Если взять отсчеты через один, а воспроизвести их с той же частотой дискретизации, то получим результирующий сигнал с частотой в 2 раза меньше. То есть, по исходному набору отсчетов (точек) можно при помощи интерполяции получить сигнал любой частоты.
Вопрос. Сколько точек нужно для заполнения базового массива сигнала? Простой ответ - чем больше, тем лучше! Это не совсем верно! Дело в том, что увеличивая количество дискретов (точек по оси времени) мы улучшаем описание сигнала по оси времени. Но есть еще вертикальная ось! Напряжение. А здесь мы договорились о 256 уровнях (уровни квантования, по научному). Так вот, не имеет смысла бесконечно увеличивать количество точек по оси времени, поскольку точность по оси напряжения все равно не позволит сигналу стать намного лучше. Рассуждая эмпирически (по-инженерному, в общем), количество точек дискретизации должно быть примерно равным количеству уровней квантования.
Есть и еще одна проблема с описанием. Желательно, чтобы количество отсчетов, приходящихся на один период сигнала для базовой частоты было целым. Это позволит нам утверждать, что при воспроизведении базового сигнала с заданной частотой дискретизации мы получим некую целочисленную частоту. Это позволит нам не вносить погрешность в изначальное представление сигнала. Какую же частоту мы возьмем за базу.
Посчитаем. Итак, мы договорились о базовой частоте дискретизации. Это 187500 Гц. На какое число отсчетов его нужно разделить, чтобы получить целую частоту? Можно, например на 100. Делим: 187500 / 100 = 1875. Для представления частоты 100 Гц нам понадобится 1875 8-битных отсчетов. 8-битных потому, что 256 уровней квантования представляются при помощи 8 бит. Почти 2 килобайта! Вообще, никаких проблем! У нас же потрясающий CH32V003! А там 16 килобайт памяти для программ и данных! С лихвой! Но! Во-первых 1700 отсчетов явно избыточная точность, а во-вторых, призываю к дисциплине! Так никакой памяти не напасешься! Подбираем дальше. Бросается 500 на конце частоты дискретизации. Пробуем: 187500 / 500 = 375 отсчетов. Вот это то, что нам нужно!
Кстати. Не обязательно хранить в памяти отсчеты для целого периода синусоидального сигнала. Можно только для его четверти! Остальные части сигнала могут быть получены путем зеркального отражения! Только в этом случае частота дискретизации должна еще делится на 4.
Итак, останавливаемся на следующих параметрах. Тактовая частота контроллера максимальна - 48 МГц. ШИМ - 256 бит. Частота дискретизации - 187500 Гц. Базовая частота сигнала - 500 Гц. Количество отсчетов в массиве - 375 (пусть хранится целый период - если что, потом сократим).
Генерация ШИМ-сигнала.
Относительно этой задачи я повторяться не буду. Она легко решается при помощи любого из таймеров микроконтроллера. Способностью формирования ШИМ-сигнала обладает как продвинутый таймер TIM1, так и обычный (общего назначения) - TIM2. Ранее я уже неоднократно использовал эту функцию при воспроизведении звуковых сигналов.
Какой таймер буду использовать я станет понятно после того, как разберемся со схемотехникой устройства. Так-то выводы таймеров есть на всех ножках.
Фильтрация ШИМ-сигнала.
А вот этому вопросу я уделю самое пристальное внимание. Если уважаемые читатели помнят, то в статье про усилитель класса D на микроконтроллере, для решения этой задачи я использовал фильтр LC-фильтр Баттерворта 2-го порядка. Тогда мне не удалось отфильтровать частоту ШИМ-сигнала до конца. Причин несколько и главная - близость частоты дискретизации к верхней границе диапазона воспроизводимых частот. Затухание получилось на уровне 10 Дб при частоте дискретизации около 47 кГц. На осциллограммах эти колебания были явно видны. Утешало то, что для уха это было не так страшно! Оно все равно эти частоты не воспринимает.
С генератором - другое дело. Желательно предотвратить попадание в выходной сигнал различного рода помех, искажений и гармоник. Для этого сигнал следует хорошенько очистить и сгладить при помощи фильтра нижних частот.
В отличие от усилителей класса D мы не ограничены необходимостью передачи большой мощности через фильтр. Такой задачи не стоит, поэтому можно использовать более эффективные техники фильтрации. Также стоит учесть, что в нашем генераторе частота дискретизации (187 кГц) будет значительно отличаться от верхней частоты полосы пропускания (20 кГц). Почти на декаду. Разумеется, это делает любую фильтрацию более эффективной.
А еще в запасе у меня было одно решение! Решение это появилось с тех пор, как я обнаружил внутри копеечного микроконтроллера операционный усилитель! Лучше варианта для создания фильтра с характеристиками близкими к идеальным и придумать невозможно! Все верно! Речь идет о, так называемых, активных фильтрах. Операционный усилитель позволяет создавать эффективные фильтры без использования дросселей, допускает широкий диапазон сопротивлений нагрузки и обеспечивает характеристики близкие к теоретически возможным.
Попробую реализовать фильтр 3-го порядка. Типовая схемотехника такого фильтра имеет следующий вид.
В зависимости от соотношений резисторов и конденсаторов можно получать фильтры с разными частотными характеристиками:
- Бесселя с затуханием 5 дБ на октаву но максимально линейной фазо-частотной характеристикой (ФЧХ) и равномерной передаточной характеристикой (АЧХ) в полосе пропускания;
- Баттерворта с затуханием 6 дБ, линейной АЧХ и сносной ФЧХ;
- Чебышёва с затуханием 8 дБ, но неравномерной АЧХ.
Я остановился на любимом мною Баттерворте, отдав предпочтение линейности АЧХ и полагая, что теоретического затухания в 40 Дб (100 раз) будет вполне достаточно. Считал так. Теоретическое затухание фильтра 3-го порядка равно 18 дБ (3 x 6 дБ) на октаву (увеличение частоты в 2 раза). Если выбрать частоту среза 30 кГц, а я именно такую хотел - повыше диапазона звуковых колебаний для возможного анализа АЧХ фильтров и усилителей, то на 60 кГц получим 18 дБ затухания. Далее, на 120 кГц получим 36 дБ. На 187 килогерцах можно ожидать точно более 40 дБ даже с учетом разброса используемых деталей.
Тогда, подключив вход фильтра к выходу таймера, формирующего ШИМ-сигнал мы должны получить на его выходе чистейшую синусоиду!
Схемотехника и изготовление устройства
Схема устройства получилась на редкость лаконичной! Минимум деталей.
Номиналы деталей фильтра подбирал по тому, что у меня было в наличии с использованием одного из многочисленных онлайн-калькуляторов. Идеально все равно не подобрал. Идеальная емкость конденсатора C2 - 2600 пФ. Но, такой у меня не было.
Выходные цепи содержат защитный резистор R4 от короткого замыкания по выходу и емкость, опускающую средний уровень сигнала. Если средний уровень в половину напряжения питания является приемлемым, то емкость можно не ставить. В качестве выходного разъема H1 использован 3,5-миллиметровый аудио-разъем.
Кнопки BUTTON1 и BUTTON2 служат для управления генератором. По задумке - короткое нажатие приводит к увеличению/уменьшению частоты, а длинное к смене режима генерации. Никаких крутилок и аттенюаторов!
Для изготовления генератора я спроектировал одностороннюю печатную плату.
Как видно, удалось обойтись всего одной перемычкой! Печатная плата и все остальное, что необходимо для воспроизведения конструкции будут приложены к архиву в конце статьи.
Сама печатка, вырезанная на фрезере получилась такой.
В середине платы вырезано круглое отверстие. Сделано это для фиксации платы в корпусе. Я твердо решил сделать законченное изделие, поэтому корпус тоже будет!
Вот так выглядит печатная плата после монтажа. С одной стороны...
...и с другой.
Слева - аудиовыход, справа - выключатель питания (забыл его указать на принципиальной схеме. В середине 3-пиновая гребенка для подключения программатора. По сторонам отверстия контакты для подключения к аккумулятору 3,7 Вольта.
В качестве элементов питания для своих конструкций я в последнее время часто использую аккумуляторы от вейпов бывших в употреблении. Они довольно емкие и способны отдавать большой ток. В этот раз решил поступить также.
Емкость аккумулятора - 500 миллиампер. Нашему микроконтроллеру хватит просто со свистом!
В пару к аккумулятору я решил использовать стандартный модуль заряда с защитой от чрезмерного разряда. Всегда такие лежат у меня в запасе. Правда остались и старые, с микро-USB.
Для сборки конечного изделия я разработал модель корпуса в Компас 3D.
Только вот не спрашивайте у меня про внешний вид! Я художник - я так вижу! На самом деле всему виной аккумулятор и желание создать компактную конструкцию. Изначально я хотел сделать корпус цилиндрическим, но подумал, что тогда он точно укатится со стола!
Напечатать корпус я решил в моем любимом теплом желтом цвете! Пластик ABS. Мне с ним больше всего понравилось работать!
В длину корпус получился около 110 миллиметров. С одной стороны разместилось отверстие под выходной аудио-разъем, а с другой отверстие зарядки type-C.
Кстати, модель корпуса также будет в общем архиве, в конце статьи.
По задумке части корпуса фиксируются двумя винтами M2, под которые я установил резьбовые латунные закладные.
Можно сделать и на саморезах. Просто я подозреваю, что буду еще неоднократно разбирать его для перепрошивки микроконтроллера и расширения функционала. Кстати, обещаю держать моих дорогих читателей в курсе изменений функционала.
Перед сборкой все компоненты устройства соединяются между собой.
Видно, что провода, идущие к аккумуляторы умышленно сделаны избыточно длинными, чтобы по-месту подгонять их длину. С обратной стороны собранная конструкция выглядит так.
К зарядному модулю провода от батареи подпаяны, а вот к плате подключаются при помощи разъемных соединителей. У меня есть Dupont-овские контакты, которые я обжимаю кримпером, но можно и свить их, например из луженого провода, защитив термоусадочной трубкой, как у меня. Теперь плату можно легко отключить.
Окончательная сборка. Компоненты укладываются в корпус.
Видно, что в верхнюю часть корпуса установлены два красных толкателя кнопок. На плату модуля зарядки нужно сверху положить кусочек толстого поролона, для лучшей фиксации. После этого части корпуса соединяются вместе и скрепляются винтами M2x10 мм.
У нас в руках готовое изделие!
И с другой стороны.
А вот вид со стороны зарядного разъема.
Диаметр изделия равен 19 миллиметров. Согласитесь, похоже на электронную сигарету для детей!
Если подключить устройство к зарядке, то сквозь корпус отчетливо видно свечение красного светодиода.
По окончании зарядки цвет меняется на синий, но его видно чуть хуже.
Кстати, в этот момент времени я осознал, что забыл про индикатор включения. Надо будет припаять светодиод между шинами питания. Подумаю, как это лучше сделать, чтобы не забывать выключать прибор.
Софт и испытания
Говорить про программное обеспечение долго не буду. Все программное обеспечение будет в архиве. Практически каждая строчка кода снабжена достаточно подробным комментарием. Для интерполяции используется ступенчатый алгоритм. В будущем, скорее всего, заменю его на линейную интерполяцию. Хотя результат меня вполне устроил.
Традиционно для этого типа микроконтроллеров использую чистый Си. В качестве среды использую VS-Code с надстройкой PlatformIO. Уже совсем к ней привык, хотя во всех нюансах еще не разобрался.
Приступаем к испытаниям. Прежде всего, наблюдаем за частотой 1 кГц. С 500 Герцами все понятно, это база, а вот за интерполированными частотами понаблюдаем пристальнее.
Синусоида вполне приличная. Действующее значение примерно 1,5 вольта. Заметно, что размах колебания составляет примерно 4,2 вольта. То есть во всю шкалу источника питания. На низких частотах фильтр колебания не ослабляет. Его коэффициент передачи равен 1. Это и не мудрено, ведь операционный усилитель охвачен 100%-ной обратной связью. Увеличиваем частоту.
Синусоида по-прежнему на высоте. Действующее значение немного снизилось, до 1,48 Вольта. Пока АЧХ фильтра выглядит вполне равномерной.
На 10 килогерцах спад амплитуды незначителен.
А вот на 20 килогерцах уже пошло снижение, хотя оно пока тоже не превышает 10%. Напомню, что частоту среза я установил около 30 кГц, может чуть повыше. Форма синусоиды вполне приличная.
По расчетам уровень сигнала должен упасть до 0,707 (полоса пропускания) исходного около 31...32 кГц. Смотрим, что на 30: 1,5 * 0,707 = 1,061. Пока все в пределах предсказания. Сигнал все еще очень приличный, хотя, напомню, что сейчас на один период колебания приходится всего 6,25 отсчета! Движемся дальше.
Затухание растет. На фото не видно, но сигнал стал немного "живым". А что мы хотим? 4,7 отсчета на период.
Ну то, что меньше амплитуда, это понятно, но вы посмотрите на форму! 3,75 отсчета на период. Вот, что значит хороший фильтр!
Все, сигнал совсем ожил! Дальше его характеристики измерить не представляется возможным. Но, до этого уровня кривая АЧХ фильтра была очень неплохой. На всякий случай посмотрим на другом пределе...
Отчетливо видна нестабильность амплитуды. Это результат очень редкой частоты дискретизации. Ну и для интереса...
Дальше явно чувствуется, что осциллограф эту мешанину не улавливает! На этом и остановимся!
Что ж друзья, по-моему неплохой генератор у нас получился! Замечательный компаньон для универсального прибора, который я делал ранее.
А вот посмотрите, как они смотрятся вместе! Вот такая по-летнему теплая лаборатория у меня получается!
Конечно это еще не окончательная версия генератора. Точно планирую добавить генератор шума и пилу. Немного причешу и алгоритмы, хотя не думаю, что линейная интерполяция даст значительное улучшение. Подписывайтесь, кому интересно мое творчество и кого не обременяют длинные публикации!
Архив с материалами для повторения устройства можно скачать здесь.
С удовольствием восприму любые пожелания! Вы тоже спрашивайте! Постараюсь ответить в меру своих знаний и опыта!
Спасибо, что читаете-смотрите Terrabyte! Подписывайтесь, если вам интересна радиолюбительская тематика, микроконтроллеры, мини-ПК, необычные компьютерные решения и инновационные разработки! Спасибо всем, кто поддерживает нас с братом своими комментариями и лайками!
Наша группа ВК: https://vk.com/terrabyte
Наш канал на VK-Video: https://vk.com/video/@terrabyte/all
Наши разработки: