Сигнал произвольной формы с помощью ШИМ

Рассмотрим крайне интересное использование ШИМ (Широтно-Импульсной Модуляции) для создания аналогового сигнала произвольной формы (синусоида, пила, тангенс...), то есть сделаем своеобразный ЦАП (Цифро-Аналоговый Преобразователь).

Преобразование ШИМ-сигнала в синусоиду

Те, кто уже знакомы в ШИМ знают, что суть её, собственно, в изменении ширины импульса, приводящее к изменению среднего приложенного напряжения:если на нагрузку (здесь СИД) подать сигнал с достаточно большой частотой (чтобы глаз не замечал мерцание), то мы будем воспринимать его свечение как постоянное, хотя это не так. Теперь, не изменяя период сигнала T (время через которое он повторяется) изменяем время свечения и соответственно время простоя, видим, что интенсивность свечения меняется, так как изменяется среднее напряжение. На рисунках всё наглядно продемонстрировано:

Яркость свечения светодиодов при разной скважности ШИМ-сигнала

Попробуем подключить вольтметр и посмотреть изменение напряжения при изменении коэффициента заполнения сигнала, причём одна единица в регистре CCRn будет иметь вес:

ΔU = 3.3/255 = 0.012941 В = 12.94 мВ

Показания с вольтметра при разных значениях коэффициента заполнения

Судя по полученным результатам (ΔU = 42.5 – 29.6 = 12.9 мВ) работает достаточно точно. Но на осциллограмме это всё выглядит как прямоугольные импульсы, а нам нужно получить постоянное напряжение (прямая черта), пусть даже и с пульсациями. Как это сделать? На выход подключаем ФНЧ (Фильтр Низких Частот), простейшим его исполнением является интегрирующая RC-цепочка (суммирующая): резистор и конденсатор.

Фильтр Низких Частот

Работа ФНЧ на RC-компонентах

Разберём работу RC-фильтра низких частот, который одни частоты (значения зависят от номиналов R и C) пропускает, а другие задерживает, ну это в идеале. А сама суть роботы заключается в том, что у одного из компонентов изменяется ёмкостное сопротивление при изменении частоты, это конденсатор, оно вычисляется по выражению:

XC = 1/ω⸱C = 1/2⸱π⸱f⸱C

XC – ёмкостное сопротивление [Ом];
ω – циклическая частота [рад/с];
f – частота [Гц];
C – ёмкость конденсатора [Ф];

Итого, имеем делитель напряжения (считай, что два последовательно подключённых резистора), где одно сопротивление R постоянно, а сопротивление второго (ёмкостное) при постоянном сигнале (f = 0 Гц) равно бесконечности, а при увеличении частоты будет уменьшаться, тем самым уменьшаться будет и напряжение на выходе фильтра. Таким образом на низких частотах имеем большое сопротивление второго плеча (напряжение на выходе высокое), а на высоких частотах малое сопротивление второго плеча (напряжение на выходе низкое).Частоту среза fсреза следует выбрать не менее чем в 10 раз меньше частоты сигнала, она определяется по выражению:

fсреза = 1/2⸱π⸱R⸱C

fсреза – частота среза (где уровень сигнала составляет 0.71 от амплитуды) [Гц];
R – сопротивление резистора [Ом];
C – ёмкость конденсатора [Ф];

Создание программы (просто ШИМ-сигнал)

В общем начинаем. Создаём ШИМ-сигнал с частотой 1 кГц, коэффициентом заполнения 50%, амплитуда (наибольшее значение) равна напряжению питания МК (3.3 В).

1. После выбора микроконтроллера обязательно включаем отладку (SYS -> Debug: Serial Wire)

2. Включаем для Таймера 1 (TIM1) тактирование от внутреннего источника (Clock Source: Internal Clock) и генерированием ШИМ-сигнала на первом канале (Channel 1: PWM Generation CH1)

Выход ШИМ-сигнала на первый канал

3. На вкладке настроек (Configuration -> Control: TIM1) нужно занести значения в регистр предделителя (PSC — Prescelar), автоперезагрузки (ARR — Auto-Reload Register) и захвата-сравнения (CCRn Capture-Compare Register, n — номер канала). Для ШИМ-сигнала 1 кГц, D=50%, 8-битный:

Настройка Таймера 1 STM32

4. Собираем проект (Ctrl + Shift + G) с выбором соответствующих настроек для среды разработки и открываем проект5. Включаем генерацию ШИМ от Таймера 1, Канал 1:

HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

6. Собираем ( F7) и прошиваем (F8), не забываем при необходимости нажать на кнопку сброса на плате.

Проверка

Тыкаем щупом осциллографа на выход с микроконтроллера и на выход с ФНЧ (а если есть два канала, то и туда, и туда, так будет ещё нагляднее). Сигнал до и поел ФНЧ на экране осциллографа

Среднее приложенное напряжение получилось равным примерно половине от наибольшего значения, имеются пульсации (ломаная треугольная кривая).Попробуем изменять сопротивление резистора фильтра и посмотрим, как при этом изменяется сигнал, видим наглядно превращение прямоугольного сигнала в почти пологий (здесь треугольный):

Превращение прямоугольного сигнала в треугольный

При изменении ширины импульса через изменение значения в регистре захвата-сравнения CCRn (Capture-Compare Register) чётко видно изменение постоянного напряжения (полоса перемещается).

Изменение напряжения путем загрузки нового значения в CCRn

Создание программы (ШИМ-синусоида)

А теперь погнали создавать какую-то кривую в прямоугольной системе координат, пусть это будет синусоида (или косинусоида). Обычно сразу забиваются значения для синусоиды в массив, но зачем, если это можно сделать автоматически, причём потом будет возможность по-быстрому изменить её разрядность и амплитуду. Не забываем подключить стандартную библиотеку математики (для использования функции синус):

#include "math.h"
uint8_t SinArray[256];
for(uint16_t i = 0; i < 256; i++){
SinArray[i] = 128 + 128*sin(2*3.14*i/255);
}

Значения тригонометрической функции sin, как и cos находятся в диапазоне [1 ν -1], отрицательное напряжение МК не выдаст, для получения необрезанной кривой смещаем её повыше, то есть чтобы наибольшему отрицательному значению sin = -1 соответствовало 0 В, а наибольшему положительному значению sin = 1 напряжение питание контроллера 3.3 В.Делим количество отсчётов на два, при расчёте каждого из них одна часть будет множиться на синус, а вторая прибавляться. Для примера ниже приведены значения для синуса разрядностью 8 бит (28 = 256 = отсчётов включая нуль), результатами расчётов должны быть, конечно, числа нецелые, но так как массив синусов имеет тип данных целочисленный, то они округляются в меньшую сторону.На рисунке чётко видно, что наибольшему значению sin = 1 соответствует наибольшее доступное значение 255 в регистре CCRn.

Значения синуса

Для простоты осуществим загрузку в главном цикле (хотя это лучше делать в прерывании от таймера):

1) Создадим переменную типа uint8_t (unsigned – беззнаковая; integer – целочисленная; допустимые значения 0-255) с произвольным именем:

uint8_t i = 0;

2) В главном цикле запишем код, который будет брать значения из массива SinArray и запихивать в регистр CCRn. Сначала в регистр CCR1 (Таймер 1, Канал 1) поступит первое значение из массива ( i = 0, а счёт начинается с нуля!), далее происходит увеличение значения переменной i на единицу (i++;), небольшая задержка (определяет частоту выходного сигнала). При следующей загрузке в CCRn залетит уже второе значение из SinArray (т.к уже i = 1), ну и так далее до 255 (наибольшее возможное значение для uint8_t), а 256 уже не будет, а будет 0).

TIM1->CCR1 = SinArray[i];
i++;
HAL_Delay(10);

Собираем ( F7) и прошиваем (F8), проверяем осциллографом форму сигнала на выходе с МК до и после ФНЧ, получаем искомую кривую:

Синусоида, созданная при помощи ШИМ и ФНЧ

Чем определяется частота создаваемой синусоиды? Как её можно изменять?

Синусоидальный сигнал

Временем, через которое загружается новое значение из массива SinArray в регистр CCRn. В предложенном коде это делается каждые 10 мс. Для управления частотой можно загружать новые значения в прерывании от таймера и соответственно для изменения частоты выходного сигнала менять частоту вызова прерывания через загрузку новых значений в его регистр предделителя (PSC) и регистр перезагрузки (ARR).

Чем определяется амплитуда создаваемой синусоиды? Как её можно изменять?

Наибольшим значением, загруженным в регистр CCRn относительно регистра ARR таймера, который генерирует данный ШИМ-сигнал. Если в регистре перезагрузки ARR загружено значение 255, то наибольшему значению (питание МК) на выходе и будет соответствовать 255 в регистре CCRn.На этапе создания постоянных значений для массива синусоиды следует сделать вот так:

for(uint16_t i = 0; i < n; i++){
SinArray[i] = (A*n/2*3.3) + (A*n/2*3.3)*sin((2*3.14*i)/n); /* n – количество отсчётов, A – амплитуда сигнала */
}

Таким образом при указании амплитуды A равной 3.3В числа просто сократятся и ничего не изменяться, при A = 2 В половинки от количества отсчётов n/2 умножиться на 2/3=0.606 и получим уже для 8-битной синусоиды наибольшее значением в регистре захвата-сравнения CCRn 154, при наибольшем возможном 255. То бишь для изменения амплитуды сигнала пересчитываем все значения, опять же сделать это можно в прерывании от таймера.

RSS
Комментарий удален
Загрузка...