В микроконтроллерах STM32 есть мощный модуль АЦП, который имеет действительно хорошие характеристики и интересные особенности:
- 18 каналов ввода (16 внешних и 2 внутренних)
- разрешение 12 бит
- всевозможные режимы преобразования:
- однократное
- непрерывное
- по триггеру
- по таймеру
- удобное выравнивание битов результата
- конечно же, генерирование всевозможных прерываний и сигналов для DMA
- скорость оцифровки — до 0.9 MSPS с программируемым временем захвата и преобразования
- автокалибровка
- режим сканирования входов по списку
- аналоговый вотчдог
Необходимость в этом модуле возникает часто: просто потому, что природа вокруг нас не дискретна, а непрерывна, и всевозможные датчики обычно выдают именно аналоговый сигнал. Особенно это касается звука, но точно так же можно сделать и, к примеру, осциллограф: популярный китайский USB-осциллограф DSO Nano сделан именно на STM32F103.
Содержание:
Внутреннее строение
Измерение и опорные напряжения
Принцип оцифровки очень прост: входное напряжение сравнивается с опорными напряжениями V_REF- и V_REF+:
- V_REF- нужно подключить к земле
- V_REF+ по желанию: либо к питанию процессора (оно плавающее и шумное, поэтому этот вариант годится только для неточных измерений), либо к внешнему источнику опорного напряжения (ИОН)
Впрочем, есть возможность программно настроить эти ноги на прямое соединение с землёй и питанием.
Входное напряжение V_In будет измерено относительно V_REF- и V_REF+, и результат преобразования сложен в выходной регистр в такой пропорции:
Напряжение | Результат |
V_Ref- | |
V_In | V_In / (V_Ref+ — V_Ref-) * 4096 |
V_Ref+ | 4096 |
К примеру, 1.2 В при питании АЦП от 3.3 В преобразуются в 1490.
Регистры АЦП в STM32
SR — регистр статуса
0 бит: флаг AWD (Analog WatchDog). Входной сигнал пересёк значения регистров LTR или HTR.
1 бит: флаг EOC (End Of Conversion). После окончания преобразования переключается в 1. Сбрасывается вручную или при чтении регистра DR.
4 бит: флаг STRT (Start). Сигнализирует о начале преобразования.
CR1 — первый регистр настроек
0..4 биты: значение AWDCH (Analog WatchDog Channel). Задаёт номер канала для слежения вотчдогом.
5 бит: EOCIE (End Of Conversion Interrupt Enable). Включает прерывание по окончанию преобразования.
6 бит: AWDIE (Analog WatchDog Interrupt Enable). Включает прерывание по срабатыванию аналогового вотчдога.
7 бит: JEOCIE.
8 бит: SCAN. Включает режим сканирования каналов по списку в регистрах SQR1, SQR2, SQR3.
9 бит: AWDSGL (Analog WatchDog Single). Задаёт тип срабатывания вотчдога в режиме SCAN: на один канал (1) или на все (0).
10 бит: JAUTO.
11 бит: DISCEN (Discontinious mode Enabled). Включает «рваный» режим работы — АЦП включается по внешнему триггеру.
12 бит: JDISCEN.
13..15 биты: DISCNUM (Discontinious mode Number of channels). Количество каналов для преобразования в «рваном» режиме.
16..19 биты: DUALMOD (Dual Mode selection). Задаёт режим совместной работы двух АЦП.
22 бит: JAWDEN.
23 бит: AWDEN (Analog WatchDog Enabled). Включает аналоговый вотчдог.
CR2 — второй регистр настроек
0 бит: ADON (Analog/Digital converter On/off). Включает АЦП.
1 бит: CONT (Continious coversion). Включает режим однократного (0) или зацикленных измерений (1).
2 бит: CAL (Calibration). Установка в 1 включает калибровку; после окончания калибровки сбрасывается в 0. Сначала нужно сбросить регистры.
3 бит: RSTCAL (Reset Calibration). Сброс регистров калибровки, точно так же устанавливаем в 1 и ждём сброса.
8 бит: DMA. Включает DMA.
11 бит: ALIGN. Выравнивает данные по правому (0) или левому (1) краю регистра.
12..14 бит: JEXTSEL.
15 бит: JEXTTRIG.
17..19 бит: EXTSEL (External event Select). Назначает номер события для запуска (TIM1 CC1, TIM1 CC2, TIM1 CC3, TIM1 CC4, TIM3 TRGO, TIM4 CC4, EXTI_11, SWSTART).
20 бит: EXTTRIG (External Trigger). Включает запуск преобразования по внешнему триггеру.
21 бит: JSWSTART.
22 бит: SWSTART (Start conversion). Запускает преобразование. После окончания сбрасывается.
23 бит: TSVREFE (Temp sensor and V_REF Enabled). Включает температурный сенсор и внутренний ИОН.
DR — регистр результата измерения
SMPR1, SMPR2 — время преобразования
Регистр настройки времени преобразования для каждого канала.
HTR и LTR — пределы вотчдога
Верхний и нижний пределы для аналогового вотчдога, аналогичны регистру DR.
SQR1, SQR2, SQR3 — список каналов для сканирования
Режим SCAN (бит SCAN в регистре CR1)
Практика: включаем АЦП
Самый простой случай использования АЦП: без прерываний, без всяких сложных режимов — просто берём и измеряем в цикле.
Инициализация
- Включаем тактирование модуля АЦП
- Настраиваем параметры модуля
- Включаем модуль АЦП
- Настраиваем вход (номер канала АЦП)
- Проводим калибровку
Я исхожу из того, что ножки кристалла не настроены, то есть находятся в дефолтном состоянии «аналоговый вход». Именно этот режим нам и нужен.
Только некоторые ноги STM32 могут работать в качестве входа АЦП, они обозначены символом ANx (x = 0..15, эта цифра — номер канала). Это удобно прикидывать в программе STM32Cube.
void adc_init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// настройки ADC
ADC_InitTypeDef ADC_InitStructure;
ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // режим работы - одиночный, независимый
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // не сканировать каналы, просто измерить один канал
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // однократное измерение
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // без внешнего триггера
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //выравнивание битов результат - прижать вправо
ADC_InitStructure.ADC_NbrOfChannel = 1; //количество каналов - одна штука
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
// настройка канала
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_55Cycles5);
// калибровка АЦП
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
}
После выполнения этой функции АЦП1 настроен, откалиброван и готов к измерениям на восьмом канале.
Измерение
Измерение производится просто:
- Запускаем преобразование
- Ожидаем окончания оцифровки (проверяем флаг EOC = End Of Conversion)
- Читаем результат из регистра DR
uint16_t get_adc_value()
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
Самое простое использование этих функций:
void main()
{
adc_init();
uint16_t value = 0;
while(1)
value = get_adc_value();
}
Можно просто запустить программу, остановить её брейкпойнтом и прочитать в отладчике измеренное значение.