PROGCONT.RU

Форма входа







Регистрация Вход/

STM32F DAC настройка и использование с помощью SPL а также пример генератора сигналов.

Ссылка на видео в YouTubeDAC если кто не знает это преобразователь цифрового значения в напряжение или противоположность ADC. В статье я буду использовать более простой вариант DAC который находится в контроллере STM32F051C8, есть более сложные например в контроллерах STM32F07x или STM32F09x которые могут самостоятельно генерировать некоторые сигналы.

Для создания простых сигналов таких как прямоугольный, синусный, треугольный, левая пила и правая пила я написал программу которую можете использовать для создания массива значений, она находится в разделе Программы называется «Генератор сигналов для DAC».


Схема DAC.

NO
VDDA и VSSA
положительное и отрицательное напряжение относительно которого будет формироваться выходной сигнал.
DAC_OUT
вывод DAC для аналогового сигнала, выводы DAC у контроллеров являются аналоговыми и перенаправляется не могут.
EXTI_9, SWTRIGx, TIM6_TRGO, TIM3_TRGO, TIM15_TRGO, TIM2_TRGO
входы триггера для старта передачи нового значения из регистра DHRx(регистр удержания) в DORx(регистр преобразования цифрового значения в аналоговое).
DAC control register
регистр управления DAC.
DHRx
регистр удерживания нового значения для преобразование, условное обозначение трёх регистров DAC_DHR12R1(двенадцати битное значение со смещение вправо), DAC_DHR12L1 (двенадцати битное значение со смещение влево) и DAC_DHR8R1(восьми битное значение со смещение влево).
DORx
регистр преобразования, из DHRx в него копируется результат самостоятельно или по событию триггера.
Control logic
блок управление передачей результата из DHRx в DORx.
Digital-to-analog-converterx
конвертер цифрового сигнала в цифровой, цепочка резисторов подключённая к DORx регистру.
BOFF
выходной буфер или операционный повторитель, предназначен для обеспечения необходимым током нагрузку.

Пример из кода ниже включения и выключения выходного буфера.

DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;

DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;


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

Особенности использования DAC.

Прежде чем начинать работать с DAC вам нужно ознакомится с этой вырезкой из datasheet.

NO

В таблице приведены минимальные и максимальные уровни напряжений при включенном и выключенном выходном буфере. Если буфер включен то уровень минимального выходного значения будет 0.2В и максимального Vdda-0.2В, то есть если нормально воспроизвести сигнал вы значение преобразования должны сместить на этот эквивалент напряжения.

Пример, если у нас Vssa=0В и Vdda=3.3В то значение минимальное будет при двенадцати битном значении(4095) DAC 0.2*4095/3.3=248 и максимальное 4095-248=3847, ниже и выше этих значений уровень выходных напряжений будет искажаться.

При выключенном выходном буфере минимально значение 0.0005В и максимальное Vdda-1LSB(значение младшего бита) или 3.3-3.3/4095=3.29В, здесь выходной сигнал лучше но если подключить нагрузку сопротивлением в 5.1кОм уровень напряжения упадёт с 1.65В до 0.6В.

Практика настройки и использования DAC или примеры.

Для примера будем использовать контроллер STM32F051C8 и его единственный вывод DAC_OUT1, не забываем подключить выводы Vdda(верхнее напряжение, не больше питания контроллера) и Vssa(общий контроллера или земля).

В примерах будет использоваться для преобразования двенадцати битовый результат DAC с правым смещением, который записывается в регистр DHRx с помощью функции.

DAC_SetChannel1Data( DAC_Align, Data)
возможны варианты для записи значения Data.
DAC_Align
варианты:
  1. DAC_Align_12b_R-двенадцати битовое значение с правым смещением;
  2. DAC_Align_12b_L-двенадцати битовое значение с левым смещением;
  3. DAC_Align_8b_R-восьми битное значение с правым смещением.


Первый пример простой генератор пяти разных сигналов(квадратурный, синусоидальный, треугольный, левая и правая пила), которые будут генерироваться по очереди, задержка перед записью нового значения в DHRx реализована в виде обычного цикла.

#include "stm32f0xx.h" /*Квадратурный сигнал.*/ const uint16_t arr_square[]={3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 3847, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248}; /*Синусоидальный сигнал.*/ const uint16_t arr_sin[]={2048, 2236, 2422, 2604, 2779, 2947, 3105, 3252, 3385, 3503, 3606, 3691, 3759, 3808, 3837, 3847, 3837, 3808, 3759, 3691, 3606, 3503, 3385, 3252, 3105, 2947, 2779, 2604, 2422, 2236, 2048, 1859, 1673, 1491, 1316, 1148, 990, 843, 710, 592, 489, 404, 336, 287, 258, 248, 258, 287, 336, 404, 489, 592, 710, 843, 990, 1148, 1316, 1491, 1673, 1859}; /*Треугольный сигнал.*/ const uint16_t arr_triangle[]={248, 368, 488, 608, 728, 848, 968, 1088, 1208, 1328, 1448, 1568, 1688, 1808, 1928, 2048, 2167, 2287, 2407, 2527, 2647, 2767, 2887, 3007, 3127, 3247, 3367, 3487, 3607, 3727, 3847, 3727, 3607, 3487, 3367, 3247, 3127, 3007, 2887, 2767, 2647, 2527, 2407, 2287, 2167, 2048, 1928, 1808, 1688, 1568, 1448, 1328, 1208, 1088, 968, 848, 728, 608, 488, 368}; /*Левая пила сигнал.*/ const uint16_t arr_left_saw[]={248, 309, 370, 431, 492, 553, 614, 675, 736, 797, 858, 919, 980, 1041, 1102, 1163, 1224, 1285, 1346, 1407, 1468, 1529, 1590, 1651, 1712, 1773, 1834, 1895, 1956, 2017, 2078, 2139, 2200, 2261, 2322, 2383, 2444, 2505, 2566, 2627, 2688, 2749, 2810, 2871, 2932, 2993, 3054, 3115, 3176, 3237, 3298, 3359, 3420, 3481, 3542, 3603, 3664, 3725, 3786, 3847}; /*Правая пила сигнал.*/ const uint16_t arr_right_saw[]={3847, 3786, 3725, 3664, 3603, 3542, 3481, 3420, 3359, 3298, 3237, 3176, 3115, 3054, 2993, 2932, 2871, 2810, 2749, 2688, 2627, 2566, 2505, 2444, 2383, 2322, 2261, 2200, 2139, 2078, 2017, 1956, 1895, 1834, 1773, 1712, 1651, 1590, 1529, 1468, 1407, 1346, 1285, 1224, 1163, 1102, 1041, 980, 919, 858, 797, 736, 675, 614, 553, 492, 431, 370, 309, 248}; int main(){ RCC_DeInit( ); /*Настройка вывода DAC_OUT1.*/ RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitTypeDef gpio_dac; gpio_dac.GPIO_Mode = GPIO_Mode_AN; gpio_dac.GPIO_Pin = GPIO_Pin_4; gpio_dac.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init( GPIOA, &gpio_dac); /*Настройка DAC для самостоятельной перезаписи нового значения.*/ RCC_APB1PeriphClockCmd( RCC_APB1Periph_DAC, ENABLE); DAC_InitTypeDef DAC_InitStructure; DAC_StructInit( &DAC_InitStructure); DAC_InitStructure.DAC_Trigger = DAC_Trigger_None; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; DAC_Init( DAC_Channel_1, &DAC_InitStructure); DAC_Cmd( DAC_Channel_1, ENABLE); while(1){ /*Генерация прямоугольного сигнала.*/ for( uint16_t count=0; count<1000; count++){ for( uint32_t i=0; i<60; i++){ DAC_SetChannel1Data( DAC_Align_12b_R, arr_square[i]); for( uint32_t w=0; w<10; w++);//Задержка перед следующим преобразованием. } } /*Генерация синусоидального сигнала.*/ for( uint16_t count=0; count<1000; count++){ for( uint32_t i=0; i<60; i++){ DAC_SetChannel1Data( DAC_Align_12b_R, arr_sin[i]); for( uint32_t w=0; w<10; w++);//Задержка перед следующим преобразованием. } } /*Генерация треугольного сигнала.*/ for( uint16_t count=0; count<1000; count++){ for( uint32_t i=0; i<60; i++){ DAC_SetChannel1Data( DAC_Align_12b_R, arr_triangle[i]); for( uint32_t w=0; w<10; w++);//Задержка перед следующим преобразованием. } } /*Генерация левая-пила сигнал.*/ for( uint16_t count=0; count<1000; count++){ for( uint32_t i=0; i<60; i++){ DAC_SetChannel1Data( DAC_Align_12b_R, arr_left_saw[i]); for( uint32_t w=0; w<10; w++);//Задержка перед следующим преобразованием. } } /*Генерация правая-пила сигнал.*/ for( uint16_t count=0; count<1000; count++){ for( uint32_t i=0; i<60; i++){ DAC_SetChannel1Data( DAC_Align_12b_R, arr_right_saw[i]); for( uint32_t w=0; w<10; w++);//Задержка перед следующим преобразованием. } } } return 0; }


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

После отсчёта периода TIM2 значение будет передано из DHRx в DORx но новое значение запишется в DHRx в прерывании от переполнении TIM2. Прерывание требует определённое количество тактов для выполнения и поэтому значение периода таймера не должно быть меньше 114(получено опытным путём), увеличивая значение периода получится плавное уменьшение частоты генерируемого сигнала.

#include "stm32f0xx.h" /*Синусоидальный сигнал, 60 значений.*/ const uint16_t arr_sin[]={2048, 2236, 2422, 2604, 2779, 2947, 3105, 3252, 3385, 3503, 3606, 3691, 3759, 3808, 3837, 3847, 3837, 3808, 3759, 3691, 3606, 3503, 3385, 3252, 3105, 2947, 2779, 2604, 2422, 2236, 2048, 1859, 1673, 1491, 1316, 1148, 990, 843, 710, 592, 489, 404, 336, 287, 258, 248, 258, 287, 336, 404, 489, 592, 710, 843, 990, 1148, 1316, 1491, 1673, 1859}; /*Счётчик значений.*/ uint16_t count=0; int main(){ RCC_DeInit( ); /*Настройка TIM2 для генерации сигнала.*/ RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseInitTypeDef TIM2_InitStructure; TIM2_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM2_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM2_InitStructure.TIM_Period = 114; TIM2_InitStructure.TIM_Prescaler = 0; TIM2_InitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit( TIM2, &TIM2_InitStructure); /*После переполнения будет срабатывать выходной триггер.*/ TIM_SelectOutputTrigger( TIM2, TIM_TRGOSource_Update); TIM_ClearFlag( TIM2, TIM_FLAG_Update); TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE); NVIC_EnableIRQ( TIM2_IRQn); TIM_Cmd( TIM2, ENABLE); /*Настройка вывода DAC.*/ RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitTypeDef gpio_dac; gpio_dac.GPIO_Mode = GPIO_Mode_AN; gpio_dac.GPIO_Pin = GPIO_Pin_4; gpio_dac.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init( GPIOA, &gpio_dac); /*Настройка DAC для перезаписи нового значения от входного триггера.*/ RCC_APB1PeriphClockCmd( RCC_APB1Periph_DAC, ENABLE); DAC_InitTypeDef DAC_InitStructure; DAC_StructInit(&DAC_InitStructure); /*Подключаем входной триггер.*/ DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; DAC_Init(DAC_Channel_1, &DAC_InitStructure); DAC_Cmd(DAC_Channel_1, ENABLE); while(1){} return 0; } void TIM2_IRQHandler(void){ TIM_ClearFlag( TIM2, TIM_FLAG_Update); DAC_SetChannel1Data( DAC_Align_12b_R, arr_sin[count]); count++; if( count==60 ) count=0; }


И последний пример с использование DMA, самый быстрый способ генерации сигнала и сложный.

#include "stm32f0xx.h" /*Адрес в памяти нужного DHRx, высчитывается самостоятельно.*/ #define DAC_DHR12R_Address 0x40007408 //Синусоидальный сигнал, 60 значений. const uint16_t arr_sin[]={2048, 2236, 2422, 2604, 2779, 2947, 3105, 3252, 3385, 3503, 3606, 3691, 3759, 3808, 3837, 3847, 3837, 3808, 3759, 3691, 3606, 3503, 3385, 3252, 3105, 2947, 2779, 2604, 2422, 2236, 2048, 1859, 1673, 1491, 1316, 1148, 990, 843, 710, 592, 489, 404, 336, 287, 258, 248, 258, 287, 336, 404, 489, 592, 710, 843, 990, 1148, 1316, 1491, 1673, 1859}; /*Счётчик данных массива сигнала для DMA.*/ uint16_t count=60; /*Задержка для перезаписи нового значения сигнала в DHRx, должна быть больше нуля.*/ uint16_t signal_speed=50; int main(){ RCC_DeInit( ); RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitTypeDef gpio_dac; gpio_dac.GPIO_Mode = GPIO_Mode_AN; gpio_dac.GPIO_Pin = GPIO_Pin_4; gpio_dac.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init( GPIOA, &gpio_dac); RCC_APB1PeriphClockCmd( RCC_APB1Periph_DAC, ENABLE); DAC_InitTypeDef DAC_InitStructure; DAC_StructInit(&DAC_InitStructure); DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; DAC_Init(DAC_Channel_1, &DAC_InitStructure); DAC_Cmd(DAC_Channel_1, ENABLE); DAC_DMACmd(DAC_Channel_1, ENABLE); RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel3); DMA_InitTypeDef DMA_Settings_DMA_DAC; DMA_Settings_DMA_DAC.DMA_PeripheralBaseAddr = DAC_DHR12R_Address; DMA_Settings_DMA_DAC.DMA_MemoryBaseAddr = (uint32_t)&arr_sin; DMA_Settings_DMA_DAC.DMA_DIR = DMA_DIR_PeripheralDST; DMA_Settings_DMA_DAC.DMA_BufferSize = count; DMA_Settings_DMA_DAC.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_Settings_DMA_DAC.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_Settings_DMA_DAC.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_Settings_DMA_DAC.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_Settings_DMA_DAC.DMA_Mode = DMA_Mode_Circular; DMA_Settings_DMA_DAC.DMA_Priority = DMA_Priority_VeryHigh; DMA_Settings_DMA_DAC.DMA_M2M = DMA_M2M_Disable; DMA_Init( DMA1_Channel3, &DMA_Settings_DMA_DAC); DMA_Cmd( DMA1_Channel3, ENABLE); RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseInitTypeDef TIM2_InitStructure; TIM2_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM2_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM2_InitStructure.TIM_Period = signal_speed; TIM2_InitStructure.TIM_Prescaler = 0; TIM2_InitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit( TIM2, &TIM2_InitStructure); TIM_SelectOutputTrigger( TIM2, TIM_TRGOSource_Update); TIM_Cmd( TIM2, ENABLE); while(1){} return 0; }

Комментариев нет  Только зарегистрированные пользователи могут оставлять комментарии!