Как стать автором
Обновить

Комментарии 9

На месте автора, я бы делал подобный DDS генератор на PWM режиме таймера, т.к. при его использовании можно добиться полной гальванической развязки выходных цепей, иначе зачем тогда применять гальванически развязанный DC/DC преобразователь. Кроме того, используя комбинация 2-х PWM генераторов можно поднять разрешение задаваемой частоты больше, чем в 12 разрядов, получаемых от ЦАП.
Огромное спасибо за статью!
Проделал все в железе. Правда, контроллер у меня попроще, L0, супротив вашего F7.
Однако же DDS генерирует. Все предельно ясно и доступно.
А вот пример с DMA запустить пока не удалось, но очень хочется. Возникли вопросы.
1. Для чего нужен канал DMA в DAC это понятно, и направление стоит логичное: Память -> Периферия. Но для чего отдельный канал DMA таймеру? Разве его задача не управлять? Какие он данные передает? Направление стоит Периферия -> Память. Я почему обратил на это внимание, для моего контроллера (STM32L053) добавить это канал не выходит, пишет, что нет ресурса.
2. Когда мы вызываем функцию HAL_DAC_Start_DMA, то через приведение типа указателя, говорим ей, что она должна интерпретировать массив синусов, как 32-ух битный. Но мы то его объявляли как 16-ти битный. То-есть, получается, что функция скушает данных в два раза больше (это если мерить в байтах), чем массив. Или массив в ОЗУ изначально уже размазан по 4-х байтовым ячейкам, и в реальности занимает не 2*N*4, а 4*N*4 байт? Тогда зачем его объявлять, как uint16?

P.S.: С архитектурой STM32 только начинаю знакомится.
Видимо, нужно завязывать с ночным программингом.
В Кубе, в настройках DAC, нужно было на первой вкладке «Parameter Setting» настроить триггер. Выбрать «Timer 6 Trigger Out event». В статье это момент опущен.
Все работает.
Спасибо за комментарий!
Да, массив 32-битный.
Я сейчас пишу новую статью, и заодно тщательно перепроверю весь материал этой. По результатам внесу исправления и дополнения. Ещё раз спасибо.
Я сейчас всё это решил перепроверить.
Массив 16-битный, конечно. Просто функция HAL_DAC_Start_DMA требует тип int32_t*, поэтому мы приводим массив к этому типу. А как его интерпретировать, мы указали в настройках DMA, когда выбрали DataWidth = Half Word
Да. Именно так. Длина должна быть указана в отчетах, размер которых мы указали в конфигурации DMA. Если проследить цепочку вызовов функций, то можно увидеть, что это значение записывается в регистр CNDTR — число транзакций DMA, которое должно быть выполнено. Каждую транзакцию счетчик декрементируется.

Меня жутко смутило, что в комментариях к аналогичной функции, но для ADC:
 HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)

указано, что:
@param  Length: Length of data to be transferred from ADC peripheral to memory (in bytes)

in bytes
А оказалось в отсчетах.

P.S.: Когда ждать очередную статью?

Я не могу обещать каких-то сроков, времени нет катастрофически.
Но постараюсь в течении недели.

Перепроверил всё, кое-где поправил. В последнем исходнике, в частности нужно (int16_t) вместо (uint16_t).
Но для чего отдельный канал DMA таймеру?

Он не нужен, спасибо, что обратили внимание.
Недостатком метода с DMA является то, что здесь невозможны трюки с записью в массив четверти периода и вычисление накопления фазы.

Очень даже возможны. В STM есть прерывание по обработке половины буфера в DMA. Пока выводится одна половина буфера DMA, никто не мешает спокойно заполнять данными другую его половину в том числе и копируя синусоиду из таблицы. По любому это удобнее и требует меньше ресурсов, чем выводить через прерывание каждое значение в DAC.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.