Adc описание. Описание протокола ADC

22.12.2023 Мониторы

В микроконтроллерах STM32 есть мощный модуль АЦП, который имеет действительно хорошие характеристики и интересные особенности:

  • 18 каналов ввода (16 внешних и 2 внутренних)
  • разрешение 12 бит
  • всевозможные режимы преобразования:
    1. однократное
    2. непрерывное
    3. по триггеру
    4. по таймеру
  • удобное выравнивание битов результата
  • конечно же, генерирование всевозможных прерываний и сигналов для DMA
  • скорость оцифровки - до 0.9 MSPS с программируемым временем захвата и преобразования
  • автокалибровка
  • режим сканирования входов по списку
  • аналоговый вотчдог

Необходимость в этом модуле возникает часто: просто потому, что природа вокруг нас не дискретна, а непрерывна, и всевозможные датчики обычно выдают именно аналоговый сигнал. Особенно это касается звука, но точно так же можно сделать и, к примеру, осциллограф: популярный китайский USB-осциллограф DSO Nano сделан именно на STM32F103.

Внутреннее строение

Измерение и опорные напряжения

Принцип оцифровки очень прост: входное напряжение сравнивается с опорными напряжениями V_REF- и V_REF+:

  • V_REF- нужно подключить к земле
  • V_REF+ по желанию: либо к питанию процессора (оно плавающее и шумное, поэтому этот вариант годится только для неточных измерений), либо к внешнему источнику опорного напряжения (ИОН)

Впрочем, есть возможность программно настроить эти ноги на прямое соединение с землёй и питанием.

Входное напряжение V_In будет измерено относительно V_REF- и V_REF+, и результат преобразования сложен в выходной регистр в такой пропорции:

К примеру, 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)

Практика: включаем АЦП

Самый простой случай использования АЦП: без прерываний, без всяких сложных режимов - просто берём и измеряем в цикле.

Инициализация

  1. Включаем тактирование модуля АЦП
  2. Настраиваем параметры модуля
  3. Включаем модуль АЦП
  4. Настраиваем вход (номер канала АЦП)
  5. Проводим калибровку

Я исхожу из того, что ножки кристалла не настроены, то есть находятся в дефолтном состоянии «аналоговый вход». Именно этот режим нам и нужен.

Только некоторые ноги 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 настроен, откалиброван и готов к измерениям на восьмом канале.

Измерение

Измерение производится просто:

  1. Запускаем преобразование
  2. Ожидаем окончания оцифровки (проверяем флаг EOC = End Of Conversion)
  3. Читаем результат из регистра 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(); }

Можно просто запустить программу, остановить её брейкпойнтом и прочитать в отладчике измеренное значение.

Post Views: 309

ADC - это текстовый протокол для клиент-серверных сетей, наподобие Neo-Modus" Direct Connect (NMDC) . Целью является создание простого протокола, который не нагружает ни клиент, ни сервер, и который можно расширять. В нём исправлены некоторые неудачные решения протокола NMDC, но не все.

Рассматриваются те-же самые взаимодействия: клиент-клиент и клиент-сервер. Данная документация разделена на две части; первая часть описывает структуру протокола, вторая - специфичность системы протокола для использования этой структуры. Advanced Direct Connect - это первая версия, которая будет постепенно улучшаться.

Большинство идей для протокола пришли из проекта DCTNG (Jan Vidar Krey"s). Основные участники разработки: Dustin Brody, Walter Doekes, Timmo Stange, Fredrik Ullner, Fredrik Stenberg и другие. Jon Hess посодействовал как создатель протокола NMDC.

Преимущества протокола

  • SID, а также названия команд протокола, состоят из четырёх латинских символов в верхнем регистре. Протокол достаточно хорошо подходит для реализации на языке СИ. По стандарту языка СИ sizeof(char) == 1, то есть 4 символа будут занимать 4 байта, или могут быть представлены в виде четырёхбайтового целого числа. Преобразование строки в число и обратно значительно упрощает и оптимизирует работу с командами и даёт возможность хранить команду в виде объединения.

Недостатки протокола

  • Разделителями протокола являются очень распространённые символы (пробел и перенос), которые нужно заменять на \s и \n. Так как эти символы очень распространены в сообщениях чата, то число замен всегда будет большим, чего не было в протоколе NMDC.
  • По сравнению с протоколом NMDC, в протоколе ADC есть возможность в UserCommand удалить определённое меню. Однако, по-прежнему отсутствует возможность удаления определённого меню в определённом контексте .

История версий

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

Версия 1.0.1, 2008-05-02

  • Расширения выделены в отдельный документ.
  • Спецификации выделены в отдельный проект на SourceForge.

Версия 1.0, 2007-12-01

  • Первый релиз

Структура протокола

  • Все команды протокола начинаются с четырёх букв. Первая буква определяет то, как команда должна быть послана, следующие три показывают что должно быть выполнено.
  • Параметры разделяются пробелами, а каждая команда заканчивается символом переноса строки (код 0x0a). Экранируются элементы: "\s" - пробел, "\n" - новая строка и "\\" - обратный слеш. Данная версия протокола резервирует все остальные экранированные символы для возможного дальнейшего использования; любые команды, содержащие неизвестные экранированные символы, должны игнорироваться.
  • Все отсылаемые команды должны отправляться в кодировке UTF-8 - закодированный Юникод в С нормализации.
  • Клиенты должны игнорировать неизвестные/неправильные команды. Хабы должны игнорировать неправильные команды, и должны отсылать неизвестные команды, согласно их типу (префиксу).
  • Адреса клиентов должны быть определены в виде десятичных чисел, разделённых точками ("x.x.x.x") для IPv4 или в формате RFC (1884) для IPv6. Адреса хабов должны быть определены ссылкой с добавкой "adc" спереди, которая показывает специфику данного протокола ("adc://server:port/").
  • Числа отсылаются как строки в соответствии со стандартом плавающей точки, в качестве разделителя между целой дробной частью используется точка "." . Целыми являются числа без дробной части и без экспоненциальной добавки. Приложения должны иметь возможность оперировать с 64-битными положительными числами и с 64-битными числами с плавающей точкой. Префиксом отрицания является знак "-".

D (Direct message) Хаб должен отправить эту команду для пользователя с указанным SID .

E (Echo message) Хаб должен отправить команду для пользователей с sid и my_sid.

F (Feature broadcast) Хаб должен отослать эту команду всем клиентам, которые поддерживают(+)/не поддерживают(-) данную характеристику. Поддержка клиентом той или иной характеристики определяется из поля SU, которое содержится в команде INF , отсылаемой клиентом.

H (Hub message) Клиенты должны использовать данный тип для отсылки команды, которая предназначена только хабу.

I (Info message) Хабы должны использовать данный тип для отсылки команды, которая не была отослана другим клиентом.

U (UDP message) Клиенты должны использовать эту команду только для прямого соединения по протоколу UDP .

Хеш-функции

Некоторые команды используются для определяния хеш-функций. При установке каждого нового соединения по команде SUP происходит обмен хеш-функциями. При коннекте клиент передаёт серверу несколько хеш-функций посредствам параметров команды SUP . Сервер вбирает одну из них и передаёт её клиенту, поставив её до любой другой хеш-функции в команде SUP , которая отправится с сервера. Клиент и хаб должны иметь по крайней мере одну одинаковую хеш-функцию, которая будет использоваться в протоколе и в файловой идентификации.

Идентификация клиента

Каждый клиент идентифицируется по трём различным идентификаторам: идентификатор сессии - Session ID (SID), личный идентификатор - Private ID (PID) и идентификатор клиента - Client ID (CID).

Файлы

Имена файлов отсчитываются от относительного (вымышленного) корня в шаре пользователя. "/" - разделитель директорий; каждый файл или имя директории должно быть уникальным в регистро-независимом контексте. Все печатные символы, включая пробел, допустимы в имени файла, символы "/" и "\" экранируются символом "\". Клиенты должны использовать фильтры для имён под свои файловые системы, имена файлов, полученные от других клиентов, должны также подчиняться этим правилам. Специальные имена "." и ".." не могут содержаться в директории или имени файла; любой полученный файл-лист, содержащий эти имена должен быть проигнорирован. Все имена директорий должны оканчиваться на "/".

Расшаренные файлы идентифицируются относительно безымянного корня "/" ("/dir/subdir/filename.ext"), тогда как дополнения могут добавить имя корня. Например, "TTH/…" для TIGR дополнения используют имя корня "TTH" для идентификации файлов по их "Tiger Tree Hash". Это недопустимо для имён из безымянного корня, которые попали в шару с идентификатором по контрольной сумме.

Без корневое имя файла "files.xml" определяет полный файл-лист, в формате XML в кодировке UTF-8 . Клиентам рекомендуется использовать дополнения чтобы сжимать данный файл-лист.

Дополнения могут добавлять к имени файла свои расширения, обычно это делается для того, чтобы избежать повторения имён.

Специальный тип "list" используется для просмотра списков файлов. Частичный файловый список имеет ту же структуру, что и нормальный список, но директории могут быть теговыми с атрибутом Incomplete="1", который показывает на частичность. Только директории без корневых файлов могут начинаться с символа "/". Содержимое такой директории в последствии будет послано просящему клиенту на глубину, выбранную им (это нужно для отправки только того уровня, который требуется пользователю). Атрибут "Base" для поля "FileListing" определяет к какой конкретной директории принадлежит данный файл.

Отличительные особенности:

  • Малое время преобразования: 1.4 мкс, макс
  • Встроенная функция выборки/фиксации
  • Нет потери кода
  • Не требует регулировки пользователем
  • Однополярное питание +5 В
  • Не требует внешнего тактового сигнала
  • Простое согласование с микропроцессорами

Области применения:

  • Цифровая обработка сигналов
  • Быстродействующие системы сбора данных
  • Телекоммуникации
  • Быстродействующие цепи серво - управления
  • Аудио системы

Функциональная схема:

Расположение выводов:

Описание:

ИС ADC0820 является быстродействующим, совместимым с микропроцессорами, 8-ми разрядным аналого- цифровым преобразователем (АЦП), использующим «half- flash» - технологию для получения времени преобразования 1.4 мкс. Конвертор имеет диапазон входных аналоговых сигналов от 0 В до +5 В и однополярное питание +5 В.

Встроенная система выборки/фиксации со скоростью нарастания выходного напряжения до 100 мВ/мкс исключает необходимость использования внешней системы выборки /фиксации.

Данный АЦП легко согласуется с микропроцессорами, благодаря возможности обращения к нему, как к ячейке памяти, или к порту I/O, без необходимости использования внешней согласующей логики. Выходы данных имеют буферные каскады с тремя логическими состояниями и фиксацией уровней, что позволяет подключать ИС, непосредственно к шине данных микропроцессора, или к системному порту I/O данных. Выходной сигнал переполнения АЦП обеспечивает возможность каскадирования ИС для достижения более высокого уровня разрешения.

ИС ADC0820 производства Maxim является, совместимой по выводам, с ИС ADC0820 производства National Semiconductor и имеет более высокие эксплуатационные характеристики. ИС выпускается в корпусах типов: 20-pin SO, DIP, CERDIP.

Нужно сначала разобраться с компонентами, которые использовались нами для создания прошивки. А использовался нами только один компонент - PWM8. PWM расшифровывается как Pulse Width Modulator, что значит Широтно-импульсный модулятор. Суть прибора в том, что он позволяет изменять широту импульсов, генерируемых микроконтроллером. Тем самым, меняя выходное напряжения для устройств, не чувствительных к частоте.
Например: рабочая частота микроконтроллера 1Гц (то есть, период генерации импульсов 1с), ширина импульса 0.5с, напряжение импульса 5В.

Тогда среднее выходное напряжение за секунду равно 2.5 вольта. А получается эта величина просто: сложением подимпульсных площадей и делением их на общий промежуток времени. На всякий случай распишу подробней. Допустим, мы взяли промежуток времени в 1 секунду, из рисунка видно первый импульс длился 0.5. Умножаем 0.5с*5В (напряжение импульса) и все это делим на интервал времени. 0.5с*5В/1с = 2.5В. Если нам понадобится выходное напряжение в 3.33В, мы должны будем увеличить подимпульсную площадь до 75%. В литературе ещё часто фигурирует термин скважность. Так вот, скважность и есть отношений длительности импульса к длительности нулевого потенциала, например, для первого случая, она была 50%, для второго - 75%.

Я думаю, что теории уже достаточно и с установкой PWM на схему проблем не случилось. А вот настройки самого компонента стоит рассмотреть подробней. Для этого приведу скриншот с выделенными изменёнными параметрами.

Clock - это рабочая частота ШИМа, любой цифровой или аналогово-цифровой блок должен работать на определённой тактовой частоте. Для этого и придуманы делители частоты и поле Clock. SysClk - это системная частота, выставленная в закладке Global Resources. Интересно заметить то, что в выпадающем списке Clock присутствуют такие поля, как Row_0_Input_0. Это значит, что тактовый генератор модуля может быть снаружи чипа и синхронизироваться они будут через шину Row_0_Input_0.

Enable - уровень логической единицы данного блока. Обычно используются два стандарта: High = 5В и Low = 3В. Сам микроконтроллер, кстати, можно так же перевести на один из режимов High или Low.

CompareOut - выход широтно-импульсного модулятора.

Может возникнуть вопрос: зачем была дана выше приведенная теория, если на практике мы это нигде не использовали? А ответ в том, что мы использовали дефолтные значения для длинны импульса и времени периода (поля Period и PulseWidth) 0 0. При таких значениях на выходе PWM будет сплошной сигнал, равный по величине логической единице. Менять время периода и значения ширины импульса можно так же программно в режиме работы микроконтроллера, функциями PWM8_1_WritePeriod() и PWM8_1_WritePulseWidth().

ADC или АЦП

АЦП - аналого-цифровой преобразователь (или ADC Analog-to-digital converter) - это устройство, позволяющие преобразовать аналоговый сигнал в цифровой. Любые физические величины (давление, скорость, угол поворота, напряжение, ток, сила света) являются аналоговыми, и задача ADC - переводить их в цифровой сигнал. На практике же, для перевода в цифровой сигнал обычно используется величина напряжения.
Из многочисленных характеристик АЦП следует выделить три основных:

  1. Разрядность - это наименьшая единица аналогового сигнала, изменение которой может зафиксировать ADC, обычно измеряется в битах.
  2. Частота преобразования - количество измерений в секунду, измеряется в SPS (samples per second)
  3. Рабочий диапазон - диапазон величин, в котором работает данный преобразователь.

Так как АЦП уже не такой простой прибор, как PWM. Придется разобрать ещё некоторые теоретические аспекты микроконтроллера и некоторые фичи самого АЦП.

Общие характеристики микроконтроллера

Перечень некоторых понятий и обозначений о которых нужно знать при работе c чипами PSoC(кстати и не только всё ниже сказанное будет верно и для микроконтроллеров AVR)

В описаниях и на принципиальных схемах в даташитах часто фигурируют такие обозначения как Vcc, Vdd, Vss, AGND. И разница между ними порой не самая очевидная. Vcc - это напряжение питания микроконтроллера (сс - collector-to-collector), то же самое что и Vdd, так уж исторически сложилось что одна и та же величина имеет 2 обозначения. Vss - минимальный потенциал на микроконтроллере, очень часто бывает, что эта величина эквивалентна AGND. Буква "A" в аббревиатуре AGND указывает на что, это artificial граунд или искусственная земля. Стоит упомянуть про такое напряжение на схеме, которое обычно называется как BandGap. BandGrap - это опорное напряжения. Опорное напряжение означает то что оно остаётся постоянным не зависимо от напряжения питания МК, температуры и других внешних показателей. Vref - опорное напряжения отдельно рассматриваемого модуля. Очень долго я не мог врубиться, что такое Rail-to-Rail. А попадалась мне эта фраза в контекстах типа: "Данный модуль может работать в режиме Rail-to-Rail". Так вот Rail-to-Rail означает то, что элемент может работать на всем размахе напряжений от Vcc до AGND.

Пример 2. Voltage Measuring

Задача: Измерить напряжение на потенциометре, зашитом в отладочную плату и вывести значение на экран.

Вот тут уже будет по интересней. Как обычно запускаем дизайнер, создаем проект. Идем в User Modules -> Misc Data -> LCD и левой кнопкой перетягиваем его на микроконтроллер. LCD очень полезный и простой модуль, да к тому же ещё и не занимающий место на цифровых блока. Увидеть его можно в закладке Workspace Explorer. Из настоек ему нужно выбрать только LCDPort = Port_2. Теперь заходим в файл main.c, напомню, что он лежит в Workspace Explorer -> [Имя проекта] -> Source Files -> main.c. И добавляем в функцию main() следующий код.

LCD_Start(); LCD_Position(0,0); LCD_PrCString("Measured Voltage");

Компилируем код, прошиваем микроконтроллер. Если всё было правильно сделано, получим на экране строку которая была прописана выше. Более простого управления экраном, и придумать не могли. И это радует. Теперь дело осталось за ADC. Выбираем User Modules -> Legacy -> ADCINC12 и выкидываем его на контроллер. Может возникнуть вопрос "Почему мы не выбрали просто ADCINC, а ADCINC12 да и ещё в придачу из папки Legacy? Причина кроется в сложности инициализации модулей. ADCINC более сложный и гибкий модуль, который требует более тщательной и доскональной настройки. В папке Legacy, что значит унаследованные, уже храниться модуль который менее гибкий, зато более простой в реализации. Далее не думая заходим в папку, amplifers и выкидываем на схему PGA(Programmable Gain Amplifier). Программируемый операционный усилитель. Он нужен не столько для усиления, сколько для повышения входного сопротивления. Что бы величина текущего тока в цепи не влияла на точность измерения.
Выставляем настройки PWM как на скриншоте.

  1. Gain - коэффициент усиления.
  2. Input - вход прибора.
  3. Reference - опорное напряжение.

Настройки ADC выставляем так:

  1. TMR Clock - тактовая частота таймера.
  2. Input - вход прибора (подключен к PGA).
  3. CNT Clock - частота счетчика.

Последнее, что понадобиться нам для нормальной работы, это глобальный ресурс называемый Ref Mux. Ref Mux - это рабочий диапазон напряжений аналоговых блоков. Если мы выставим значение (Vdd/2)+/-(Vdd/2) то получим полную разбежку напряжений от 0 до 5В. Но в этом есть один определённый минус. Так как за опорное напряжение принимается напряжение питания. Если будет плавать напряжение Vdd, это повлияет на правдивость результатов. В таком случае нам и пригодился бы BandGap. Но пока не будем париться, и выставляем (Vdd/2)+/-(Vdd/2).
Для перепроверки скину скрин получившейся коммутации аналоговых блоков.

Переходим в main.c и добавляем в функцию main() следующий код:

PGA_Start(PGA_HIGHPOWER); //запуск PGA ADCINC12_Start(ADCINC12_HIGHPOWER); //запуск АЦП ADCINC12_GetSamples(0); //установка АЦП на песперерывную работу M8C_EnableGInt ; // Uncomment this line to enable Global Interrupts while(1) // главный цикл прошивки { if (ADCINC12_fIsDataAvailable() != 0) //проверка на данных в ADC { result = ADCINC12_iGetData() + 2048; ADCINC12_ClearFlag(); LCD_Position(1,0); //установка позиции для вывода LCD_PrHexInt(result); //вывод результата в хексе } }

К данным функции iGetData() прибавляется 2048 для того что перевести данные в беззнаковый эквиваленте (если интересно смотреть google "c++ знаковые и беззнаковые переменные").

Соединяем потенциометр с платой следующим образом.

Прошиваем микроконтроллер и оцениваем результат работы.

Осталось только перевести хексы в напряжение и проверить корректность данных тестером. Добиваться этой цели будем чисто опытным путём. Облегчим себе ситуацию, и примем нулевой потенциал за 0x0000 значение на выходе ADC. Затем запускаем нашу уже написанную программу, выкручиваем потенциометр на максимум и смотрим получившиеся значения. У меня, например, получилось 0х0FEC. Затем берём тестер и измеряем реальное напряжение на потенциометре. У меня получилось 4.78В. Теперь делим 4.78/0х0FEC (у кого проблемы с системами исчисления, советую подкачаться) и получаем шаг квантования, то есть единицу напряжения, которой соответствует одно значение выхода АЦП. У меня получилась 0.0011727183513248 вот такое вот число. Теперь просто результат с АЦП перемножаем на эту величину и выводим на экран. Для этого добавим переменные в глобальное поле видимости (это всё то что вне функции main()).