Сигма-дельта АЦП с разрядностью 16 бит в микроконтроллерах серии STM32F37х
Новая линейка микроконтроллеров STM32F3хx интересна наличием 16 битного сигма-дельта АЦП с частотой преобразования до 50 кГц.
К сожалению отладочная плата STM32F3-Discovery выполнена на микроконтроллере STM32F30x, который не содержат в своем составе данного АЦП.
Вообще получилась интересная ситуации либо мы имеет 16 битный АЦП + 12 битный АЦП с частотой преобразования до 1 МГц (STM32F37x), либо не имеем 16 битного АЦП, но имеем 12 битный АЦП с частотой преобразования до 5 МГц (STM32F30x). Будем надеется, что появиться микроконтроллер с 16 битным АЦП и скоростным 12 битным.
Сигма-дельта АЦП
Рассказывать о принципе работы сигма-дельта АЦП не буду, материала по данной теме в сети море, кратко опишу о его возможностях в данной реализации.
АЦП имеет 5 дифференциальных (симметричных) входов, которые могут использоваться как несимметричные (9 шт.)
Настройка входных каналов независимая, поэтому можно использовать их различные комбинации (симметричные + несимметричные).
Для большей наглядности, полностью не симметричный режим:
Дифференциальный режим:
и их комбинация (одна из возможных):
АЦП имеет входной усилитель с программируемым коэффициентом усиления от 0,5 до 32.
(Пока не забыл: для текущий ревизии в одном из несимметричных режимов усилитель работает не корректно при коэффициентах 16 и 32, см. листок ошибок STM32F37xxx STM32F383xx - Errata sheet)
Максимальная частота преобразований 50 кГц реализуется при работе АЦП на один вход, при обработки нескольких входов максимальная частота преобразований уже 16,6 кГц.
В связи с разнообразием настроек (режимы работы входов, усиление) были реализованы три конфигурационных ячейки, в каждую из них можно записать одну из возможных комбинаций, следующих параметров АЦП:
- коэффициент усиления входного сигнала
- дифференциальный или несимметричный режим
- смещение
Каждому входному каналу АЦП можно (нужно) сопоставить одну из конфигурационных ячеек, чтобы переключение между различными режимами работы происходило автоматически.
Можно выбрать различные источники опорного напряжения АЦП:
- внутренний источник напряжением 1,8 В или 1,2 В
- опорное напряжение равно аналоговому питанию АЦП
- внешний источник опорного напряжения от 1,1 В до аналогового напряжения питания АЦП
Запуск преобразований могут осуществлять следующие источники:
- программный запуск
- внутренние таймеры
- внешние события
- при наличии более, чем одного модуля АЦП в микроконтроллере первый модуль может быть ведущим для остальных, т.е. все модули будут запускать преобразования синхронно с первым
Для автоматизации результаты преобразований можно “забирать” посредством модуля ПДП (DMA).
Ну и последнее, что стоит отметить это три энергосберегающих режима работы:
- режим медленного преобразования (в этом режиме максимальные частоты преобразования 12 и 4 кГц)
- режим ожидания
- АЦП выключено
Функциональная схема модуля АЦП:
Пример
В наличии у меня “своя” плата с микроконтроллером 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