DAC в STM32, аппаратный генератор

ЦАП stm32 может аппаратно генерировать сигналы. Это может быть либо треугольный сигнал, либо шум.
Генерация возможно только при запуске преобразования ЦАП от события. При возникновении события данные суммируются с DHRx и переносятся в выходной регистр DORx, затем вычисляется следующее значение на протяжении 3 тактов. Можно выбирать амплитуду и смещение сигнала относительно 0. Смещение определяется значением в DHRx, амплитуда битами MAMPx в регистре DAC->CR.
Генератор белого шума
Генератор сделан по такой схеме:

В сдвиговом регистре по умолчанию загружено 0xAAA, сдвигается после каждого события запуска ЦАП за 3 тактовых импульса. Если регистр станет равным нулю, в него автоматически загрузиться 1. Включается путем установки битов WAVEx[1:0] в “01”. Также можно изменять амплитуду (или разрядность) путем установки битов MAMPx[3:0]

По событию ЦАП данные из LFSR (linear feedback shift register) регистра суммируется с DHRx и результат загружаются в DORx.
Инициализации выглядит так:
void DAC_init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_DACEN; // вкл. тактирование ЦАП
DAC->CR |= DAC_CR_MAMP1; // амплитуда на максимум
DAC->CR |= DAC_CR_WAVE1_0; // вкл. генератор шума
DAC->CR |= DAC_CR_TEN1; // включение запуска от события, по умолчанию таймер 6
DAC->CR |= DAC_CR_EN1; // вкл. ЦАП 1
}
На выходе ЦАП получаем такой сигнал

Интересная штука, если не включать ЦАП
//DAC->CR |= DAC_CR_EN1;
то генератор все-равно будет работать, только выход будет отключен. Поскольку чтение с выходного регистра дозволено то мы получаем аппаратный генератор псевдослучайных чисел. #define STM32F10X_LD_VL
#include "stm32f10x.h"
void SystemInit (void)
{
return;
}
void DAC_init(void);
void TIM6_init(void);
void UART_init(void);
int main (void)
{
int i;
DAC_init();
TIM6_init();
UART_init();
for (;;)
{
USART1->DR = DAC->DOR1;
for (i=200000; i>0; i--)
;
}
}
void DAC_init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_DACEN; // вкл. тактирование ЦАП
DAC->CR |= DAC_CR_TEN1; // включение запуска от события, по умолчанию таймер 6
DAC->CR |= DAC_CR_MAMP1; // амплитуда на максимум
DAC->CR |= DAC_CR_WAVE1_0; // вкл. генератор шума
//DAC->CR |= DAC_CR_EN1; // вкл. ЦАП 1
}
void TIM6_init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // тактирование таймера
TIM6->PSC = 0x000F; // предделитель
TIM6->ARR = 0xFF;
TIM6->CR2 |= TIM_CR2_MMS_1; // ЦАП будет запускаться по переполнению
TIM6->CR1 |= TIM_CR1_CEN; // запуск счета
}
void UART_init(void)
{
// включаю тактирование ЮАРТ1 и порта на которам он висит
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
RCC->APB2ENR|= RCC_APB2ENR_AFIOEN;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// настраиваю соответсв. порты на вход и выход от расшир. функций
GPIOA->CRH &= ~GPIO_CRH_MODE9;
GPIOA->CRH &= ~GPIO_CRH_CNF9;
GPIOA->CRH &= ~GPIO_CRH_MODE10;
GPIOA->CRH &= ~GPIO_CRH_CNF10;
GPIOA->CRH |= GPIO_CRH_MODE9_1; // выход на 2 МГц
GPIOA->CRH |= GPIO_CRH_CNF9_1;
GPIOA->CRH |= GPIO_CRH_CNF10_0;
// 8 МГц/16*115200 = 4.34
USART1->BRR = (4 << 4) + 5;
// включаю ЮАРТ
USART1->CR1 |= USART_CR1_UE;
USART1->CR1 |= USART_CR1_TE; // включил передатчик
//USART1->CR1 |= USART_CR1_RE; // включил приемник
}
Младшие 8 бит отправляются по ЮАРТу, после ресета видим такие данные
E8 D8 CB 3A 98 5C 2F 35 BF B0 98 86 0A EA 31 D7 31 0A ED C8 CA 98 3D B3 6F 7F 76 DF 5C 49 50 F8 D9 69 9F 77
К сожалению после следующего ресета приходит то же самое.
Генератор треугольного сигнала
Там все просто, стоит счетчик который считает до значения определенного битами MAMPx[3:0] (те же что и для генератора шума), и вниз до нуля, и так по кругу.

Включается установкой битов WAVEx[1:0] в “10”, амплитуду надо выбрать до включения ЦАП.
void DAC_init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_DACEN; // вкл. тактирование ЦАП
DAC->CR |= DAC_CR_TEN1; // включение запуска от события, по умолчанию таймер 6
DAC->CR |= DAC_CR_MAMP1;// амплитуда на максимум
DAC->CR |= DAC_CR_WAVE1_1; // вкл. генератор треуг. сигн.
DAC->CR |= DAC_CR_EN1; // вкл. ЦАП 1
}

Выходной буфер
Я упоминал о выходном буфере, который по умолчанию включен и нужен чтобы к ЦАП можно было подключать хоть какую-то нагрузку без внешнего повторителя, также я говорил что он ухудшает динамические характеристики.
На низких частотах сигнал ровный и красивый с включенным буфером, но если поднять частоту повыше, видим такую штуку:

Вместо такого (то же, но с выключенным буфером)

Выключается установкой бита DAC_CR_BOFFx в регистре DAC->CR
DAC->CR |= DAC_CR_BOFF1;
P.S.
Зачем там понадобились эти генераторы трудно сказать, пишут что для создания электронной музыки и тестирования усилителей.

- +1
- 04 октября 2011, 20:46
- hellraiser
Забыл замерять, по расчетам то что на фотке 3.9 кГц, завтра проведу нормальные измерения.
- hellraiser
- 04 октября 2011, 21:53
- ↑
- ↓
У ST есть AN где улучшается разрешение АЦП путем подмешивания шума или треугольного сигнала с DAC на вход ADC. Думаю, что это основное применение встроенного генератора. Если бы для тестирования усилителей — то вшили бы встроенный синус. А так — шум и треугольник — как раз те сигналы, что нужны для увеличения разрешения АЦП.
Это было написано в аппноуте к ЦАП, про улучшение расширения АЦП тоже упоминается, но решил не писать поскольку не знаю как это работает.
- hellraiser
- 05 октября 2011, 16:11
- ↑
- ↓
На семинаре по STM32L кто то ВНЕЗАПНО, когда докладчик рассказывал про ЦАП, выдал такую гипотезу:
Слабо верится, что он допер до этого самостоятельно прямо во время семинара, а не прочитал заранее, скорее всего здесь))Интересная штука, если не включать ЦАП //DAC->CR |= DAC_CR_EN1; то генератор все-равно будет работать, только выход будет отключен. Поскольку чтение с выходного регистра дозволено то мы получаем аппаратный генератор псевдослучайных чисел.
Cудя по рисункам могу предположить, что скорость буфера к искажениям сигнала имеет слабое отношение. Скорее всего это буфер просто не может дотянуть выходной сигнал до минуса. В моих экспериментах по моему 0,2 вольта не дотягивал с буфером.
да, действительно до нуля не дотягивает, только у меня меньше напряжение было
- hellraiser
- 08 ноября 2011, 22:38
- ↑
- ↓
а как на основе stm32 (конкретно f4 дискавери) сделать генератор тональных сигналов? про это в интернете вообще почти нет конкретных листингов, да и в целом задача не слишком ясна, особенно если только начала с этим работать. может быть, есть какие-то примеры? если знаете, конечно :)
- tinuviel65
- 17 апреля 2012, 14:54
- ↓
Поищите информацию по DDS генераторам.
Если просто, то у вас есть таблица (или функция) синуса. Есть какая-то переменная, которая содержит фазу. Есть таймер и прерывание по таймеру. В прерывании рассчитываете значение синуса основываясь на значении фазы, выдаете значение на ЦАП. Тут же увеличиваете значение фазы. В зависимости от приращения фазы вы получаете различные частоты.
Вместо синуса можете взять косинус :)
Если просто, то у вас есть таблица (или функция) синуса. Есть какая-то переменная, которая содержит фазу. Есть таймер и прерывание по таймеру. В прерывании рассчитываете значение синуса основываясь на значении фазы, выдаете значение на ЦАП. Тут же увеличиваете значение фазы. В зависимости от приращения фазы вы получаете различные частоты.
Вместо синуса можете взять косинус :)
- hellraiser
- 19 апреля 2012, 20:20
- ↑
- ↓
Комментарии (15)
RSS свернуть / развернуть