Adc описание. Описание протокола ADC
В микроконтроллерах 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+, и результат преобразования сложен в выходной регистр в такой пропорции:
К примеру, 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
Самое простое использование этих функций:
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 - переводить их в цифровой сигнал. На практике же, для перевода в цифровой сигнал обычно используется величина напряжения.
Из многочисленных характеристик АЦП следует выделить три основных:
- Разрядность - это наименьшая единица аналогового сигнала, изменение которой может зафиксировать ADC, обычно измеряется в битах.
- Частота преобразования - количество измерений в секунду, измеряется в SPS (samples per second)
- Рабочий диапазон - диапазон величин, в котором работает данный преобразователь.
Так как АЦП уже не такой простой прибор, как 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 как на скриншоте.
- Gain - коэффициент усиления.
- Input - вход прибора.
- Reference - опорное напряжение.
Настройки ADC выставляем так:
- TMR Clock - тактовая частота таймера.
- Input - вход прибора (подключен к PGA).
- 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()).