Меню Закрыть

ADC

АЦП — аналого-цифровой преобразователь.

АЦП(Analog-to-Digital Converter) это модуль внутри микроконтроллера, который преобразует аналоговый сигнал (например, напряжение от датчика) в цифровое значение, которое уже может обрабатываться микроконтроллером. Простой пример: у нас есть датчик температуры, который выдаёт напряжение от 0 до 3.3 В. Микроконтроллер не понимает аналоговое напряжение, но с помощью оно может быть преобразовано в число понятное микроконтроллеру. 

Основные характеристики АЦП.

  1. Разрядность (битность).

Показывает, на сколько уровней делится диапазон входного сигнала.

8 бит -256 уровней (0–255)
10 бит — 1024 уровня (0–1023)
12 бит — 4096 уровней (0–4095)
Чем выше разрядность, тем точнее измерение.

     2. Диапазон входного напряжения.

Обычно АЦП работает с напряжением от 0 до Vref (например, 3.3 В или 5 В). Некоторые микроконтроллеры позволяют использовать внутренний или внешний эталон напряжения.

    3.Скорость преобразования.

Измеряется в выборках в секунду (SPS, samples per second). Чем выше скорость, тем быстрее микроконтроллер может считывать данные. Но высокие скорости иногда снижают точность.

Шум в АЦП. 

Шум в АЦП это случайные колебания измеряемого значения даже при стабильном входном напряжении; он возникает из-за нестабильного питания (Vcc и Vref), помех от цифровой части микроконтроллера, внешних наводок (длинные провода, моторы, ШИМ), собственного теплового шума внутренних компонентов АЦП и эффекта квантования. Полностью избавиться от шума нельзя, но его можно уменьшить: программно  с помощью усреднения, медианных фильтров и oversampling, и аппаратно  применяя RC-фильтры на входе, качественную развязку питания (конденсаторы, фильтры), правильную разводку земли (разделение analog/digital GND), а также увеличивая время выборки и при необходимости используя буферные усилители.

Типы АЦП. 

Существует несколько типов АЦП, которые отличаются принципом работы, скоростью и точностью. 

  1. SAR (Successive Approximation) — самый распространённый, средняя скорость и точность, подходит для большинства микроконтроллеров и датчиков. 
  2. Sigma-Delta — медленный, но очень точный, используется там, где важны мелкие изменения сигнала, например в датчиках веса или звука.
  3. Flash (параллельный) — сверхбыстрый, но дорогой и шумный, используют для очень высокочастотных сигналов, например в радиотехнике.

Выбор АЦП зависит от задачи, если нужен обычный датчик температуры или напряжения, берём SAR, если нужен супер точный измеритель, Sigma-Delta, если нужно измерять быстрые сигналы в реальном времени, Flash.

Принцип работы 

Мы уже знаем что разрядность это число на которое делится Входное измеряемое напряжение. Допустим у нас 10 разрядный АЦП. Это значит что он делит сигнал на 1024 отрезка. Это значение двойки в степени. Например 2 в 10 ой степени = 1024. 

Далее разберемся с понятием опорного напряжения. 

Опорным напряжением называют максимальное напряжение которое может измерить наш АЦП. Оно может быть как внутренним, например 3.3В так и внешним, заданным извне микроконтроллера.Чтобы измерить большее значение напряжения необходим резисторный делитель, чтобы приводить значение напряжения к уровню микроконтроллера. Соответственно необходимо пересчитывать в коде значение которое приходит на вход АЦП с учетом делителя.

Следующее понятие это шаг квантования. Оно получается путем деления опорного напряжения на разрядность. Например 3.3/1024=0.0032в или 3.2мв

Например. Нам на вход АЦП пришло некое напряжение X, не превышающее опорное. Для простоты мы будем заведомо знать что это значение 2.5В. Наше опорное напряжение 3.3В значит, как мы уже установили шаг квантования будет 0.0032В. Далее 2.5/0.0032=776 Такое число получает наш АЦП после измерения. Чтобы получить значение в вольтах, надо 776 разделить на 1023 и умножить на 3.3. Получиться 2.5в.

Измерение напряжения пальчиковой батарейки с помощью АЦП.

Теперь попробуем на практике измерить напряжение с помощью АЦП. Имеем плату Discovery с микроконтроллером STM32F407VGT6. С его помощью будем измерять значение напряжения на обычных пальчиковых батарейках. Сразу скажу, сделаем сначала без DMA чтобы разобраться с основами, а затем используя DMA, т.к. на практике это сильно ускоряет работу и разгружает ядро.

Создадим новый проект в CubeIDE. Для нашего примера будем использовать ADC1. Он находится на APB2. В нашем случае установлена частота 16МГц, чего более чем достаточно для измерения.Выбираем канал IN0 нашего ADC.

Всего таких каналов 15, и ещё есть дополнительные для измерения температуры и калибровки, их мы пока касаться не будем. Разберёмся с настройками.
Mode — independent, настройка по умолчанию. 
Clock Prescaler — делим частоту на 2 для уменьшения нагрузки на АЦП. 
Resolution — разрядность. Чем выше разрядность тем точнее, но медленнее. Оставим 12.
Остальные настройки оставим по умолчанию.

Собираем наш проект. CubeIDE создал функцию инициализации ADC  MX_ADC1_Init(); с установленными нами настройками. Теперь добавим код для работы с нашим АЦП. 

HAL_ADC_Start(&hadc1);//запуск АЦП
HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);//ждём завершения измерения
uint32_t adc_value=HAL_ADC_GetValue(&hadc1);//ложим результат в переменную
float voltage=(adc_value/4095.0f)*3.3f;//переводим в вольты
char buffer[50];//буфер для строки
int volts=(int)voltage;//целая часть
int cents=(int)((voltage-volts)*100);//дробная часть (2 знака)
int len=sprintf(buffer,"Voltage: %d.%02d V\r\n",volts,cents);//формируем строку
HAL_UART_Transmit(&huart3,(uint8_t*)buffer,len,HAL_MAX_DELAY);//передаём по UART
HAL_Delay(1000);//ждем 1 секунду

В общем виде это выглядит так. Запустили измерение АЦП, дождались результата. Положили число в переменную. Это число далее переводим в вольты. Для наглядности я передаю его по UART на компьютер и вывожу в терминале. 

В реальном примере конечно необходимо делать усреднение чтобы убрать шум и получить более точное значение, но мы пока этого делать не будем. Теперь попробуем сделать тоже самое но с помощью DMA что будет гораздо более правильным.

Измерение напряжения с помощью АЦП и DMA.

После того как мы разобрались с базовым измерением напряжения с помощью АЦП, становится очевидно, что такой подход требует постоянного участия процессора: запуск преобразования, ожидание результата, чтение данных. Это работает, но неэффективно, особенно если нужно получать измерения регулярно или с высокой скоростью. В таких случаях на помощь приходит DMA (Direct Memory Access)  механизм, позволяющий передавать данные от АЦП напрямую в память без участия CPU. В этой части статьи мы рассмотрим, как организовать измерение напряжения с помощью АЦП и DMA на STM32F407VGT6, чтобы сделать систему более производительной, гибкой и приближенной к реальным embedded-приложениям. Для этого мы используем связку DMA — ADC — Tim

Общая идея такая. АЦП выполняет измерение  а результаты сразу записываются в память без участия процессора. Далее по событию прерывания таймера мы просто берём накопленные данные, обрабатываем их, усредняем и переводим в напряжение. Затем отправляем в наш терминал по UART.Таким образом, процессор не тратит время на каждое измерение, а занимается только готовыми данными, что делает систему более эффективной и удобной для масштабирования.

Выставим необходимые настройки в CubeIDE.

Зайдем во вкладку ADC1.И добавим поток DMA.

Mode — Normal. Данные записываем из периферии в память. 

Важный момент! Обязательно необходимо включить Continuous Conversion Mode и DMA Continuous Requests.

Параметр Continuous Conversion Mode включает непрерывную работу АЦП, при которой преобразования выполняются автоматически одно за другим без участия процессора, а DMA Continuous Requests позволяет после каждого такого преобразования сразу передавать результат в память через DMA. В связке эти настройки обеспечивают постоянный поток данных: АЦП непрерывно измеряет сигнал, а DMA автоматически записывает значения в буфер, полностью разгружая CPU.

Создадим буфер для хранения измеренных значений.

 

uint16_t adc_buffer[10];//массив для DMA

Далее запускаем DMA.

HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adc_buffer,10);

Чтобы придать системе управляемость, добавим таймер, который будет срабатывать, например, раз в секунду и запускать обработку накопленных данных. В отличие от постоянного опроса в основном цикле, таймер позволяет четко задать период обновления: АЦП с DMA непрерывно собирают значения, а по прерыванию таймера мы просто берём готовый буфер, обрабатываем его, усредняем и отправляем результат по UART. 

Выставим следующие настройки для таймера Tim2. 

В таком виде мы будем вызывать таймер раз в секунду, т.к. входная частота 16МГц.

HAL_TIM_Base_Start_IT(&htim2);//запуск с прерыванием

Добавим callback нашего таймера. 

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance==TIM2)
    {
        uint32_t sum=0;
        for(int i=0;i<10;i++)
        {
            sum+=adc_buffer[i];
        }
        uint32_t adc_avg=sum/10;
        float voltage=(adc_avg/4095.0f)*3.3f;
        char buffer[50];
        int volts=(int)voltage;
        int cents=(int)((voltage-volts)*100);
        int len=sprintf(buffer,"Voltage: %d.%02d V\r\n",volts,cents);
        HAL_UART_Transmit(&huart3,(uint8_t*)buffer,len,HAL_MAX_DELAY);
    }
}

При срабатывании таймера он проверяет, что источник  нужный таймер,в нашем случае второй, затем суммирует значения из буфера adc_buffer, вычисляет среднее значение, переводит его в напряжение, разбивает результат на целую и дробную часть и формирует строку. После этого полученное значение напряжения отправляется по UART, обеспечивая периодический вывод измеренных данных без участия основного цикла программы.

В итоге в терминале мы видим следующее

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *