Сигма-дельта АЦП с разрядностью 16 бит в микроконтроллерах серии STM32F37х

Новая линейка микроконтроллеров STM32F3хx интересна наличием 16 битного сигма-дельта АЦП с частотой преобразования до 50 кГц.

К сожалению отладочная плата STM32F3-Discovery выполнена на микроконтроллере STM32F30x, который не содержат в своем составе данного АЦП.

Вообще получилась интересная ситуации либо мы имеет 16 битный АЦП + 12 битный АЦП с частотой преобразования до 1 МГц (STM32F37x), либо не имеем 16 битного АЦП, но имеем 12 битный АЦП с частотой преобразования до 5 МГц (STM32F30x). Будем надеется, что появиться микроконтроллер с 16 битным АЦП и скоростным 12 битным.

Сигма-дельта АЦП

Рассказывать о принципе работы сигма-дельта АЦП не буду, материала по данной теме в сети море, кратко опишу о его возможностях в данной реализации.

АЦП имеет 5 дифференциальных (симметричных) входов, которые могут использоваться как несимметричные (9 шт.)

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

Для большей наглядности, полностью не симметричный режим:

image

Дифференциальный режим:

image

и их комбинация (одна из возможных):

image

АЦП имеет входной усилитель с программируемым коэффициентом усиления от 0,5 до 32.

(Пока не забыл: для текущий ревизии в одном из несимметричных режимов усилитель работает не корректно при коэффициентах 16 и 32, см. листок ошибок STM32F37xxx STM32F383xx - Errata sheet)

Максимальная частота преобразований 50 кГц реализуется при работе АЦП на один вход, при обработки нескольких входов максимальная частота преобразований уже 16,6 кГц.

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

  • коэффициент усиления входного сигнала
  • дифференциальный или несимметричный режим
  • смещение

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

Можно выбрать различные источники опорного напряжения АЦП:

  • внутренний источник напряжением 1,8 В или 1,2 В
  • опорное напряжение равно аналоговому питанию АЦП
  • внешний источник опорного напряжения от 1,1 В до аналогового напряжения питания АЦП

Запуск преобразований могут осуществлять следующие источники:

  • программный запуск
  • внутренние таймеры
  • внешние события
  • при наличии более, чем одного модуля АЦП в микроконтроллере первый модуль может быть ведущим для остальных, т.е. все модули будут запускать преобразования синхронно с первым

Для автоматизации результаты преобразований можно “забирать” посредством модуля ПДП (DMA).

Ну и последнее, что стоит отметить это три энергосберегающих режима работы:

  • режим медленного преобразования (в этом режиме максимальные частоты преобразования 12 и 4 кГц)
  • режим ожидания
  • АЦП выключено

Функциональная схема модуля АЦП:image

Пример

В наличии у меня “своя” плата с микроконтроллером STM32F373R8.

Инициализировать все узлы микроконтроллера я уже привык при помощи библиотеки от производителя:

STM32F37x DSP and standard peripherals library (скачать)

В примерах от производителя идущих в комплекте со стандартной библиотекой на текущий момент нет примеров по использованию АЦП совместно с ПДП (DMA), поэтому интереснее рассмотреть именно такой режим.

Обозначим задачу:

  • последовательное преобразование 4-ёх несимметричных каналов
  • фиксированная частота преобразований (запуск от таймера) 400 Гц
  • результаты преобразования “забирать” посредством модуля ПДП (DMA)

Последовательность действий для настройки АЦП:

  • включить аналоговое питание
  • разрешить тактирование модуля и выбрать тактовую частоту
  • выбрать источник опорного напряжения
  • выбрать режим работы преобразований (быстрый - медленный)
  • включить АЦП
  • войти в режим инициализации
  • настроить необходимый режим работы
  • выйти из режима инициализации
Всё выше сказанное в коде:
// разрешаем тактирование сигма-дельта АЦП
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDADC1, ENABLE);
RCC_SDADCCLKConfig(RCC_SDADCCLK_SYSCLK_Div8);

// питание аналоговой части
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_SDADCAnalogCmd(PWR_SDADCAnalog_1, ENABLE);

// выбор опорного напряжения
SDADC_VREFSelect(SDADC_VREF_VDDA);

// пауза для стабилизации
delay_ms(10);

// включаем АЦП
SDADC_Cmd(SDADC1, ENABLE);

// вход в реим инициализации
SDADC_InitModeCmd(SDADC1, ENABLE);
while (SDADC_GetFlagStatus(SDADC1, SDADC_FLAG_INITRDY) == RESET)
{
}

// выбор режима работы (конфигурационная ячейка 0)
SDADC_AINStructure.SDADC_InputMode = SDADC_InputMode_SEZeroReference;
SDADC_AINStructure.SDADC_Gain = SDADC_Gain_1;
SDADC_AINStructure.SDADC_CommonMode = SDADC_CommonMode_VSSA;
SDADC_AINStructure.SDADC_Offset = 0;
SDADC_AINInit(SDADC1, SDADC_Conf_0, &SDADC_AINStructure);

// выбор каналов и задания режима работы
SDADC_ChannelConfig(SDADC1, SDADC_Channel_4, SDADC_Conf_0);
SDADC_ChannelConfig(SDADC1, SDADC_Channel_5, SDADC_Conf_0);
SDADC_ChannelConfig(SDADC1, SDADC_Channel_6, SDADC_Conf_0);
SDADC_ChannelConfig(SDADC1, SDADC_Channel_8, SDADC_Conf_0);

SDADC_InjectedChannelSelect(SDADC1, SDADC_Channel_4 | SDADC_Channel_5 | SDADC_Channel_6 | SDADC_Channel_8);

// внешний триггер
SDADC_ExternalTrigInjectedConvConfig(SDADC1, SDADC_ExternalTrigInjecConv_T13_CC1);
SDADC_ExternalTrigInjectedConvEdgeConfig(SDADC1, SDADC_ExternalTrigInjecConvEdge_Rising);

// выход из режима настройки
SDADC_InitModeCmd(SDADC1, DISABLE);

// самокалибровка
SDADC_CalibrationSequenceConfig(SDADC1, SDADC_CalibrationSequence_1);
SDADC_StartCalibration(SDADC1);
while (SDADC_GetFlagStatus(SDADC1, SDADC_FLAG_EOCAL) == RESET)
{
}

// разрешение работы с ПДП
SDADC_DMAConfig(SDADC1, SDADC_DMATransfer_Injected, ENABLE);

Примечание: ожидания нужно сделать с таймаутами.

Далее настраиваем контроллер ПДП:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);

// ПДП2 канал 3 - сигма-дельта АЦП
DMA_DeInit(DMA2_Channel3);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &SDADC1->JDATAR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &adc_400hz_buffer[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = ARRAY_LENGHT(adc_400hz_buffer);
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Channel3, &DMA_InitStructure);

DMA_ITConfig(DMA2_Channel3, DMA_IT_HT, ENABLE);
DMA_ITConfig(DMA2_Channel3, DMA_IT_TC, ENABLE);

DMA_Cmd(DMA2_Channel3, ENABLE);

И таймер, для запуска преобразований:

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13, ENABLE);

// 400 Гц
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 625 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = MCU_SYSCLK_MHZ  * 2 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM13, &TIM_TimeBaseStructure);

// запуск преобразований АЦП
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 1;
TIM_OC1Init(TIM13, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM13, TIM_OCPreload_Disable);

// разрешение работы таймера
TIM_Cmd(TIM13, ENABLE);

В обработчике прерываний по заполнению половины и полного буфера обрабатываем данные:

void DMA2_Channel3_IRQHandler(void)
{
	PIN_OFF(PIN_LED_RED);

	// НЧ канал АЦП нечетный блок
	if (DMA_GetITStatus(DMA2_IT_HT3))
	{
		mcu_adc_400hz_refresh(&adc_400hz_buffer[0]);

		DMA_ClearITPendingBit(DMA2_IT_HT3);
	}

	// НЧ канал АЦП четный блок
	if (DMA_GetITStatus(DMA2_IT_TC3))
	{
		mcu_adc_400hz_refresh(&adc_400hz_buffer[ADC_400HZ_CHANNEL_QAUNTITY]);

		DMA_ClearITPendingBit(DMA2_IT_TC3);
	}
}

Не забываем переводить линии ввода-вывода в аналоговый режим.

Проект целиком не привожу, ещё не все реализовал как хотел.


Документация

RM0313: STM32F37xx and STM32F38xx advanced ARM-based 32-bit MCUs



comments powered by Disqus