Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Продолжая цикл публикаций по микроконтроллерам на ядре Cortex-M0 компании Megawin (см. предыдущие статьи 1, 2 и 3), сегодня рассмотрим АЦП и аналоговый компаратор, а также проведем эксперименты с более "продвинутым" МК MG32F02A064.
Модуль АЦП
Функциональные возможности
МК серии MG32F02 оснащены модулем АЦП последовательного приближения ADC0, имеющим следующие особенности:
конфигурируемая разрядность (разрешение) 8, 10 или 12 бит;
до 16 внешних измерительных каналов (зависит от типа и корпуса МК);
до 8 внутренних измерительных каналов;
внешний или встроенный источник опорного напряжения IVR24 (2.40 В);
конфигурируемое время измерения;
запуск измерения в автоматическом режиме, по внешнему импульсу, по внутреннему событию, включая таймеры;
аппаратное выравнивание данных;
устройство выборки-хранения (УВХ);
возможность предусиления с программируемым коэффициентом 1-4 (PGA);
встроенный источник напряжения VBUF номиналом 1.4 В;
прерывание по завершению одного или серии измерений;
"оконный" режим срабатывания с верхним и нижним порогами (функция "watchdog");
три регистра-аккумулятора для суммирования и усреднения измерений;
режимы работы: одиночное измерение, серия измерений, одиночное и циклическое сканирование каналов по маске;
аппаратная калибровка;
пересылка данных с помощью DMA;
режим автоматического отключения (если нет преобразования);
измерение напряжения встроенного термометра.
Отметим, что в рассматриваемых актуальных типах МК не реализована возможность подачи на вход АЦП дифференциального сигнала.
Особенности реализации АЦП в зависимости от типа МК приведены в следующей таблице.
Тип МК | Макс. скорость преобразования | Аппаратная калибровка | Режим автоотключения |
---|---|---|---|
MG32F02A032 | 800 кс/с | нет | нет |
MG32F02A064MG32F02U064MG32F02A128MG32F02U128 | 1500 кс/с | есть | есть |
Функциональная схема модуля АЦП приведена на следующем рисунке.
Модуль АЦП включает следующие узлы:
схему тактирования на основе сигналов CK_ADCx_PR и CK_PLL;
схему управления питанием модуля;
блок управления сканированием каналов;
мультиплексор выбора каналов AMUX;
блок управления началом измерений;
АЦП последовательного приближения SAR ADC;
источник опорного напряжения (ИОН) IVR24 (кроме MG32F02A032);
блок обработки результата измерений, включая калибровку и контроль "окна";
регистры-аккумуляторы для суммирования результатов измерений;
регистр хранения результата измерений DAT0.
Питание и тактирование
Модуль АЦП работает только в двух режимах питания ON и SLEEP, в режиме STOP модуль работать не может.
Источник основного тактового сигнала модуля CK_ADC0_PR определяется битом CSC_CKS0.CSC_ADC0_CKS
из сигналов CK_APB или CK_AHB. Здесь и далее для краткости регистр и поле (бит) будем называть "поле" ("бит") и совмещать в одном идентификаторе их названия через точку. Источник тактового сигнала блока АЦП CK_ADC0_INT определяется отдельным мультиплексором на основании значения поля ADC0_CLK.ADC0_CK_SEL2
:
0 — сигнал CK_ADC_PR через делитель частоты с коэффициентом 1, 2, 4 или 16 (определяется полем
ADC0_CLK.ADCx_CK_DIV
);1 — системный сигнал CK_PLL через делитель частоты с коэффициентом 2, 4, 5 или 6 (определяется полем
ADC0_CLK.ADCx_CK_DIV2
);2 — выход таймера TM00_TRGO через делитель частоты на 2;
3 — выход таймера TM01_TRGO через делитель частоты на 2.
По-умолчанию выбирается сигнал CK_ADC_PR (0) без деления частоты. В User Guide рекомендуется выбирать частоту Fs сигнала CK_ADC0_INT по крайней мере в 2 раза ниже частоты CK_ADC_PR. Максимальная скорость преобразования Fconv (без учета времени предварительного сэмплирования) определяется как Fs / Nconv, где Nconv — минимальное время преобразования в тактах Fs, которое определяется битом ADC0_ANA.ADC0_CONV_TIME
из значений 24 или 30. Fconv не должна превышать предельное значение 800 или 1500 кГц в зависимости от типа МК, что также накладывает ограничение на Fs.
Для включения модуля ADC0 необходимо:
включить тактирование модуля установкой бита
CSC_APB0.CSC_ADC0_EN
(предварительно нужно разблокировать возможность записи через регистрCSC_KEY
),включить питание модуля установкой бита
ADC0_CR0.ADC0_EN
.
Согласно даташиту, во включенном состоянии модуль ADC0 потребляет 1.2 мА при VDD=5 В. Время готовности модуля после включения — 5 мкс.
Источник опорного напряжения
Возможность использовать тот или иной ИОН зависит от типа и корпуса МК. В следующей таблице приведены возможные варианты ИОН.
Тип МК | Тип корпуса | Внешний VREF+ | Встроенный IVR24 (2.40 В) | Количество внешних каналов |
---|---|---|---|---|
MG32F02A032 | TSSOP20 | соединен с VDD | нет | 2 |
MG32F02A032 | QFN32 | соединен с VDD | нет | 8 |
MG32F02A032 | LQFP48 | есть | нет | 12 |
MG32F02A064 MG32F02U064 | LQFP48 | есть | есть | 12 |
MG32F02A064 MG32F02U064 MG32F02A128 MG32F02U128 | LQFP64 | есть | есть | 16 |
MG32F02A128 MG32F02U128 | LQFP80 | есть | есть | 16 |
Отметим, что для МК MG32F02A032 в корпусе TSSOP20 единственным возможным ИОН является источник питания VDD (соединен внутри корпуса).
Выбор ИОН (кроме MG32F02A032) определяется битом ADC0_ANA.ADC0_IVREF_SEL
:
0 — внешний ИОН, подключенный к выводу VREF+;
1 — встроенный ИОН IVR24 номиналом 2.4 В.
Допустимый диапазон напряжения внешнего ИОН: от 2.4 В (2.7 В для MG32F02A032) до VDDA (VDD).
В случае выбора встроенного источника, его необходимо дополнительно включить установкой бита ADC0_ANA.ADC0_IVR_EN
.
Измерительные каналы и внутренние источники напряжения
Группа внешних измерительных каналов (выводы ADC0-ADC15) подключается по-умолчанию (бит ADC0_START.ADC0_CH_SEL
сброшен). Выбор конкретного канала определяется полем ADC0_START.ADC0_CH_MUX
(4 бита). Доступность каждого из каналов зависит от типа и корпуса МК. Общее число доступных внешних измерительных каналов было показано в таблице выше. Используемый вывод должен быть сконфигурирован как аналоговый в соответствующем регистре PA_CRx
установкой в 0 поля PA_IOMx
. Поскольку по-умолчанию все выводы именно так и сконфигурированы, специально настраивать поле PA_IOMx
необязательно.
Группа внутренних источников становится доступной при установке бита ADC0_START.ADC0_CH_SEL
. Выбор конкретного источника определяется полем ADC0_START.ADC0_CH_MUX
. В следующей таблице приведены доступные для подключения ко входу АЦП внутренние источники с указанием номера канала ADC0_CH_MUX
.
Канал | Источник | MG32F02A032 | MG32F02A064 MG32F02A128 | MG32F02U064 MG32F02U128 |
---|---|---|---|---|
0 | VSSA (аналоговая земля) | + | + | + |
1 | IVREF (напряжение ИОН) | + | - | - |
2 | DAC_P0 (выход ЦАП) | - | + | + |
3 | VBUF (доп. ИОН 1.4 В) | + | + | + |
4-7 | не определен | |||
8 | LDO VR0 | + | + | + |
9 | TSO (термометр) | - | + | + |
10 | 1/2 VDD | - | + | + |
11 | VPG | - | + | + |
12 | LDO V33 (для USB) | - | - | + |
13-15 | не определен |
Во всех МК имеется встроенный дополнительный ИОН для АЦП и аналогового компаратора VBUF номиналом 1.40 В. Для его включения необходимо установить бит PW_CR0.PW_IVR_EN
. Внутренний источник напряжения VPG может использоваться для калибровки PGA.
Встроенный термометр TSO, согласно даташиту, способен работать в диапазоне температур от -40 до +125 °C с разрешением 2 °C. Перед использованием термометр нужно включить установкой бита ADC0_ANA.ADC0_TS_EN
и выдержать интервал 100 мкс, после которого сенсор будет готов к работе. Производителем предполагается применение источника TSO совместно со встроенным ИОН IVR24 (очевидно, для выбора оптимального динамического диапазона). В МК предусмотрено автоматическое включение данного ИОН при выборе TSO в поле ADC0_START.ADC0_CH_MUX
и возвращение обратно к VREF при выборе другого источника. Для включения данной функции нужно установить бит ADC0_ANA.ADC0_TS_AUTO
.
События и прерывания
Модуль ADC0 может генерировать событие сброса RST_ADC и прерывание INT_ADC на основе внутренних событий согласно следующей схеме (общее описание подсистемы сброса МК и контроллера прерываний см. в первой статье):
Все флаги событий представлены в регистре статуса ADC0_STA
. Все биты разрешения прерывания по соответствующим внутренним событиям собраны в регистре управления прерываниями ADC0_INT
и совпадают по номеру с соответствующими флагами регистра статуса. Перечень внутренних событий приведен в следующей таблице.
Номер бита | Флаг события | Название | Описание |
---|---|---|---|
2 | ESMPF | End of Input Sampling | Завершено сэмплирования входного напряжения |
3 | E1CNVF | End of One-shot Conversion | Завершено преобразования, результат доступен в регистре |
5 | ESCNVF | End of Sequence Сonversion | Завершено преобразования в режиме сканирования нескольких каналов |
7 | OVRF | Conversion Overrun | Завершено преобразование, но результат предыдущего преобразования не был востребован |
8 | WDLF | Voltage Window Detect Outside Low | Напряжение ниже пределов окна |
9 | WDIF | Voltage Window Detect Inside | Напряжение в пределах окна |
10 | WDHF | Voltage Window Detect Outside High | Напряжение выше пределов окна |
13 | SUMOF | Accumulation Sum Overflow | Переполнение накопленной суммы измерений |
14 | SUMCF | Accumulation Sum Complete | Завершено накопление суммы измерений |
15 | SUMOVRF | Accumulation Sum Overrun | Завершено накопление суммы измерений, но предыдущий результат не был востребован |
Для включения прерывания модуля ADC0 необходимо:
Выбрать событие (или события) в регистре
ADC0_INT
. Например, для разрешения прерывания по завершению одиночного преобразования нужно установить битADC0_E1CNV_IE
.Разрешить прерывание самого модуля установкой бита
ADC0_INT.ADC0_IEA
.Разрешить прерывание IRQ от модуля ADC0 (IRQ#10) в контроллере прерываний NVIC установкой бита 10 в регистре
CPU_ISER
.
Настройка и процесс измерения
Перед запуском того или иного режима измерений необходимо настроить параметры АЦП:
включить АЦП и настроить тактирование;
выбрать источник опорного напряжения (внешний или внутренний);
подключить требуемый измерительный канал или внутренний источник;
выбрать разрядность преобразования 8, 10 или 12 бит в поле
ADC0_CR0.ADC0_RES_SEL
(по-умолчанию 12);выбрать время преобразования 24 или 30 тактов Fs в поле
ADC0_ANA.ADC0_CONV_TIME
(по-умолчанию 24, в МК MG32F02A032 всегда 30);при необходимости увеличить время сэмплирования на значение в регистре
ADC0_CR0.ADC0_SMP_SEL
(по-умолчанию 0);при необходимости выбрать коэффициент предусилителя PGA.
На следующем рисунке показана общая временная диаграмма работы АЦП.
Процесс измерения (или измерений) начинается с запуска внешним сигналом (выбирается, обозначен как TRG_START) или программно в зависимости от значения в поле ADC0_START.ADC0_START_SEL
:
0 — программный запуск установкой бита
ADC0_START.ADC0_START
,1 — сигналом с таймера TM00_TRGO,
2 — внешним сигналом запуска АЦП ADC0_TRG,
3 — сигналом с компаратора CMP0_OUT,
4 — сигналом с компаратора CMP1_OUT,
5 — сигналом с таймера TM01_TRGO,
6 — сигналом с таймера TM20_TRGO,
7 — сигналом с таймера TM36_TRGO.
При выборе варианта 1-7 фронт запуска выбирается в поле ADC0_START.ADC0_TRG_SEL
.
На первом этапе происходит сэмплирование (выборка и сохранение аналогового значения напряжения), которое длится (4 + ADC0_SMP_SEL
) тактов Fs, т.е. при необходимости его можно настраивать. Этап завершается установкой флага ESMPF. Затем выполняется аналого-цифровое преобразование, завершающееся активацией флага E1CNVF. После этого результат преобразования готов к считыванию в регистре ADC0_DAT0
. Измеренное значение напряжения U определяется стандартно (для 12-битного преобразования):
U = Uref · D / 4096,
где Uref — величина опорного напряжения, D — результат преобразования (биты 0-15 регистра ADC0_DAT0
). В регистре ADC0_DAT0
также имеются флаги ADC0_DAT0_WDLF
, ADC0_DAT0_WDIF
, ADC0_DAT0_WDHF
, ADC0_DAT0_CF
(преобразование завершено) и ADC0_DAT0_OVRF
, связанные с соответствующими событиями, описанными выше.
Модуль АЦП включает в себя предварительный усилитель с настраиваемым коэффициентом PGA (Programmable Gain Amplifier). Узел включается установкой бита ADC0_ANA.ADCx_PGA_EN
, а коэффициент усиления Kpga выбирается в поле ADC0_GAIN.ADC0_GAIN_PGA
(6 бит) в диапазоне 1.0 - 4.0 по формуле:
Kpga = ADC0_GAIN_PGA·3 / (63 + (63 - ADC0_GAIN_PGA)·3) + 1
Зависимость Kpga от значения ADC0_GAIN_PGA
нелинейная, для удобства подбора в User Guide в п. 24.8.5 приводится таблица 24-5. В модуле предусмотрена корректировка смещения напряжения при использовании PGA, которая включается установкой бита ADC0_CAL.ADC0_CAL_POFFT
. Величина смещения определяется в поле ADC0_GAIN.ADC0_OFFT_PGA
(6 бит). Более детально процесс калибровки АЦП описан в п. 24.8.5, 24.8.6 User Guide.
Режим работы модуля ADC0 в целом задается с помощью полей ADC0_CONV_MDS
и ADC0_TRG_CONT
регистра ADC0_START
. Возможные режимы работы будут рассмотрены далее.
Измерение одного канала
Режим задается установкой поля ADC0_START.ADC0_CONV_MDS
в значение 0 ("One shot"). Выбор канала определяется полем ADC0_START.ADC0_CH_MUX
. Если бит ADC0_START.ADC0_TRG_CONT
сброшен, после старта выполняется одно преобразование и АЦП переходит в ожидание следующего старта. Если бит установлен, после завершения одного преобразования автоматически начинается следующее, и так до тех пор, пока бит ADC0_TRG_CONT
не будет сброшен (или пока не будет отключен АЦП).
При завершении каждого преобразования устанавливается флаг E1CNVF, что может быть использовано для генерации прерывания. Если используется прерывание по этому флагу, он должен сбрасываться программно в обработчике прерывания путем установки бита ADC0_STA.ADC0_E1CNVF
, в противном случае возникнет условие бесконечных прерываний.
Сканирование нескольких каналов
В этом режиме АЦП автоматически выполняет измерения поочередно с нескольких заданных каналов (по маске). Режим задается установкой поля ADC0_START.ADC0_CONV_MDS
в значение 1 ("Scan"). Маска требуемых каналов задается в битах 0-15 регистра ADC0_MSK
. Установить в 1 нужно биты, соответствующие выбранным для измерения каналам. Сбросить в 0 нужно биты, соответствующие отключенным каналам (в User Guide на этот счет путаница).
Если бит ADC0_START.ADC0_TRG_CONT
сброшен, после старта выполняется измерение с первого выбранного в маске канала, устанавливается флаг E1CNVF, после чего АЦП переходит в ожидание следующего старта. При следующем старте выполняется измерение со следующего выбранного канала и так далее.
Если бит ADC0_START.ADC0_TRG_CONT
установлен, после завершения измерения с одного выбранного канала автоматически выполняется измерение со следующего канала и так до тех пор, пока не будут измерены все выбранные в маске каналы. Другими словами, в этом варианте для измерения всех каналов достаточно одного старта. Условия прекращения измерений аналогичны режиму измерения одного канала.
Если в поле ADC0_START.ADC0_CONV_MDS
установить значение 2 ("Loop"), то после одной серии измерений автоматически будет начинаться следующая. Состояние бита ADC0_START.ADC0_TRG_CONT
в этом режиме значения не имеет. Для прекращения измерений можно записать 0 в разряд ADC0_START.ADC0_START
.
В любом из трех вариантов после измерения последнего выбранного канала (завершения одной серии измерений) устанавливается флаг ESCNVF, что может быть использовано для генерации прерывания.
После завершения каждого преобразования устанавливается флаг E1CNVF, результат записывается в поле ADC0_DAT0
регистра ADC0_DAT0
(разряды 0-15), а в поле ADC0_DAT0_CH
этого же регистра записывается номер канала, который был измерен.
При использовании непрерывного сканирования и высокой скорости работы АЦП надо учитывать, что время обработки прерывания может значительно превышать интервалы между преобразованиями (событиями E1CNVF), что приведет к потере данных (они не будут успевать обрабатываться).
Преобразование с суммированием
Регистры-аккумуляторы
Модуль ADC0 имеет возможность аппаратного суммирования результатов нескольких измерений для дальнейшего усреднения. Для этого в модуле предусмотрены три одинаковых по формату регистра-аккумулятора, программно доступных как ADC0_SUM0
, ADC0_SUM1
и ADC0_SUM2
. Поле ADC0_SUMx_DAT
(биты 0-15) регистров предназначено для накопления суммы и должно обнуляться программно перед началом суммирования. Биты ADC0_SUMx_OF
, ADC0_SUMx_CF
и ADC0_SUMx_OVRF
этих регистров являются флагами-источниками событий SUMOF, SUMCF и SUMOVRF. Также имеется флаг ADC0_SUMx_UF
(Accumulation Sum Underflow), но он применяется только в дифференциальном режиме в моделях МК, снятых с производства.
Число измерений, результат которых нужно суммировать, определяется для всех регистров-аккумуляторов в одном поле ADC0_CR1.ADC0_SUM_NUM
из диапазона 1-64. Значение 0 (по-умолчанию) отключает функцию суммирования. Номера каналов, измерения которых будут суммироваться, определяются в полях ADC0_SUM0_MUX
, ADC0_SUM1_MUX
и ADC0_SUM2_MUX
регистра ADC0_MSK
для каждого из регистров-аккумуляторов соответственно.
Суммирование может работать в одном из двух режимов, определяемом значением бита ADC0_CR1.ADC0_SUM_MDS
: 0 — режим "Single" (по-умолчанию), 1 — режим "All".
Режим "Single"
В общем режиме измерения одного канала (при любом значение бита ADC0_START.ADC0_TRG_CONT
) суммирование выполняется только в регистре ADC0_SUM0
, номер канала задается в поле ADC0_MSK.ADC0_SUM0_MUX
. По завершению каждого преобразования результат добавляется к сумме. Если при этом возникает переполнение регистра ADC0_SUM0_DAT
, активируются флаг "Overflow" ADC0_SUM0_OF
и общий флаг SUMOF. Внутренний счетчик суммирований увеличивается на 1 после каждого преобразования АЦП и при достижении заданного в поле ADC0_CR1.ADC0_SUM_NUM
значения активируются флаг "Complete" ADC0_SUM0_CF
и общий флаг SUMCF. Если при этом предыдущий результат не был считан в программе, активируются флаг "Overrun" ADC0_SUM0_OVRF
и общий флаг SUMOVRF. При старте значение поля ADC0_START.ADC0_CH_MUX
также должно содержать номер выбранного в поле ADC0_MSK.ADC0_SUM0_MUX
канала.
В режиме суммирования крайне рекомендуется устанавливать бит ADC0_CR1.ADC0_SOVR_MDS
для сохранения накопленной суммы до ее востребования. Если суммирование одного канала выполнялось с установленным битом ADC0_START.ADC0_TRG_CONT
, то после завершения одного суммирования и началом следующего необходимо выполнить действия:
обнулить поле
ADC0_CR1.ADC0_SUM_NUM
,сбросить бит
ADC0_START.ADC0_TRG_CONT
,сбросить все флаги в регистре
ADC0_STA
.
В общем режиме сканирования нескольких каналов суммирование выполняется во всех трех регистрах-аккумуляторах на каналах, номера которых указаны в соответствующих полях ADC0_SUMx_MUX
регистра ADC0_MSK
. Флаги работаю аналогично предыдущему случаю согласно выше приведенной схеме формирования сброса и прерывания. Однако, имеются отличия в работе счетчика суммирований.
В варианте "Scan" при сброшенном бите ADC0_TRG_CONT
внутренний счетчик суммирований увеличивается на 1 после каждого преобразования АЦП. При установленном бите ADC0_TRG_CONT
, а также в варианте "Loop" счетчик увеличивается на 1 после каждой серии измерений всех выбранных каналов. В любом случае суммирование завершается при достижение числа, указанного в поле ADC0_CR1.ADC0_SUM_NUM
.
Режим "All"
В этом режиме суммируются результаты всех измерений со всех выбранных каналов в одном регистре-аккумуляторе ADC0_SUM0_DAT
. В варианте "One", а также в варианте "Scan" при сброшенном бите ADC0_TRG_CONT
внутренний счетчик суммирований увеличивается на 1 после каждого преобразования АЦП. В варианте "Scan" при установленном бите ADC0_TRG_CONT
, а также в варианте "Loop" счетчик увеличивается на 1 после каждой серии измерений всех выбранных каналов. В любом случае суммирование завершается при достижение числа, указанного в поле ADC0_CR1.ADC0_SUM_NUM
.
Оконный контроль напряжения
Оконный (пороговый) контроль напряжения включается установкой бита ADC0_CR1.ADCx_WIND_EN
. Режим измерений, в котором будет выполняться контроль, определяется битом ADC0_CR1.ADC0_WIND_MDS
. Если бит сброшен (режим "Single"), контроль выполняется при измерении одного канала, заданного в поле ADC0_START.ADC0_CH_MUX
. Если бит ADC0_CR1.ADC0_WIND_MDS
установлен (режим "All"), контроль выполняется во всех режимах работы модуля ADC0.
Пороговые значения задаются в 12-битовых полях ADC0_WIND_LT
и ADC0_WIND_HT
регистра ADC0_WINDTH
. Если результат измерений меньше или равен значению ADC0_WIND_LT
, активируется флаг WDLF. Если результат измерений больше или равен значению ADC0_WIND_HT
, активируется флаг WDHF. Иначе, когда результат "попадает" в окно, активируется флаг WDIF. Любой из флагов может генерировать прерывание.
Модуль ADC0 может формировать внешний сигнал на выводе ADCx_OUT. Источник сигнала определяется полем ADC0_CR1.ADC0_OUT_SEL
. Одними из источников могут быть флаги оконного контроля напряжения. Таким образом, программно настроенный модуль ADC0 может выполнять функцию сложного аппаратного компаратора.
Аппаратная часть
На этот раз мы продолжим экспериментировать с серией MG32F02 на примере МК MG32F02A064AD48 в корпусе LQFP48, поскольку первый рассматриваемый МК MG32F02A032 не содержит некоторые нужные нам узлы АЦП, например, вход VREF+ и встроенный ИОН на 2.40 В. Схема эксперимента в целом остается аналогичной предыдущей. На новой схеме светодиоды и кнопки подключены на функционально менее нагруженные выводы: кнопки на порт PC, светодиоды — на порт PB. Подключение UART также изменилось в соответствии с таблицей ASF (порт PB). Пин PC0/ICKO теперь стал свободным и может быть использован для вывода контрольного тактового сигнала (см. функцию setup_icko()
в прилагаемом файле init.c
). Ко входу VREF+ МК подключен внешний ИОН U2 типа ADR4530 номиналом 3.00 В для тестирования АЦП. Для подключения аналоговых сигналов будут использоваться выводы порта PA.
Из приборов для измерения напряжения будем использовать 5-разрядный лабораторный мультиметр GWInstek GDM-8246. В качестве тестового источника будем использовать поверенный 4-разрядный калибратор напряжения и тока Fluke 715.
ИОН на ADR4530 показал отличный результат:
Программная часть
Для проверки функционирования модуля ADC0 в различных режимах работы согласно описанной ранее методике отладки кода в ОЗУ доработаем некоторые файлы проекта. В базовой части (Supervisor) нужно добавить первичный обработчик прерывания ADC_IRQHandler()
(ISR#10) в файле svr.c
:
__attribute__ ((interrupt))
void ADC_IRQHandler() {
if (hdlr[10]) hdlr[10]();
}
Весь остальной код уже можно включать в отлаживаемую часть (Application) для запуска в ОЗУ. В начале создадим функцию инициализации модуля ADC0 и выделим ее в отдельный библиотечный файл adc.c
:
void adc_init() {
*(volatile uint16_t*)CSC_KEY_h0 = 0xA217; // unlock access to CSC regs
*(volatile uint8_t*)CSC_APB0_b0 |= 1; // CSC_ADC0_EN = 1
*(volatile uint16_t*)CSC_KEY_h0 = 0x1111; // lock access to CSC regs
*(volatile uint8_t*)ADC0_CR0_b0 = 1;// ADC0_RES_SEL = 0 (12bit), ADC0_EN = 1
//*(volatile uint8_t*)ADC0_CLK_b0 = (2 << 4); // ADC0_CK_DIV = 2 (DIV4);
//*(volatile uint8_t*)ADC0_CLK_b0 = (1 << 4); // ADC0_CK_DIV = 1 (DIV2);
}
Здесь включается общее тактирование модуля (CSC_ADC0_EN = 1
), затем включается сам модуль (ADC0_EN = 1
) с настройками по-умолчанию, в том числе с разрядностью 12 бит. Пока закомментируем строки по настройке делителя частоты. Тактирование блока АЦП будет происходить от сигнала CK_ADC_PR без деления частоты, т.е. в итоге от сигнала CK_APB с частотой 12 МГц, поскольку значение CSC_CKS0.CSC_ADC0_CKS
осталось по-умолчанию (0).
Рассмотрим функцию app()
основного файла app.c
:
void app() {
// Настройка выводов для LED D1, D2:
*(volatile uint16_t*)PB_CR13_h0 = 0x0002; // PB13 -> push-pull output
*(volatile uint16_t*)PB_CR14_h0 = 0x0002; // PB14 -> push-pull output
// Выключаем светодиоды:
*(volatile uint16_t*)PB_SC_h1 = (1 << 13) | (1 << 14);
setup_icko();
//setup_ihrco();
if (setup_xosc()) led1_flash(); else led2_flash();
// Настройка выводов URT0:
*(volatile uint16_t*)PB_CR8_h0 = (3 << 12) | 2; // PB8 -> URT0_TX, push pull output
*(volatile uint16_t*)PB_CR9_h0 = (3 << 12) | (1 << 5) | 3; // PB9 -> URT0_RX, pull-up resister enable, digital input
uart_init(PORT);
uart_puts(PORT,"Hello",UART_NEWLINE_CRLF);
adc_test_one();
}
В начале настраиваем выводы светодиодов, затем выводим тактовый сигнал для контроля на вывод PC0/ICKO, включаем кварцевый генератор XOSC (хотя это и необязательно) и настраиваем модуль URT0, который нам потребуется для вывода информации. В завершении app()
будем запускать ту или иную тестовую функцию АЦП.
Начнем с простейшего режима единичного измерения без прерываний, с опросом флага готовности, реализованного в функции adc_test_one()
:
/// Режим единичного измерения
void adc_test_one() {
uint16_t d;
char s[8];
adc_init();
while (1) {
led1_flash();
adc_start_one(8);
d=adc_read();
strUint16(s,5,d);
uart_puts(PORT,s,UART_NEWLINE_CRLF);
delay_ms(500);
}
}
Мигание светодиода в начале цикла будет говорить о том, что ничего не зависло. Далее используем пару функций из файла adc.c
:
void adc_start_one(uint8_t chn) {
// ADC0_CH_MUX = chn & ADC0_START = 1
*(volatile uint16_t*)ADC0_START_h0 = (chn << 8) | 1;
}
uint16_t adc_read() {
// waiting ADC0_E1CNVF==1
while ( ! (*(volatile uint8_t*)ADC0_STA_b0 & 0x08));
return *(volatile uint16_t*)ADC0_DAT0_h0; // ADC0_DAT0
}
Функция adc_start_one(uint8_t chn)
запускает измерение с заданного канала chn
, при этом можно переключиться и на группу внутренних источников, если в chn
установить бит 4 (таким образом будет установлен бит ADC0_START.ADC0_CH_SEL
, поскольку его позиция 12). Функция adc_read()
ожидает готовность преобразования по флагу E1CNVF и возвращает результат.
В этом примере будем использовать внешний ИОН на ADR4530 с номиналом 3.00 В, подключенный согласно приведенной схеме. На вывод PA8/ADC8 подадим напряжение 1.000 от калибратора. В терминале получаем следующий вывод:
01367
01366
01365
01367
01367
01365
01366
01365
01366
01365
Пересчитываем в напряжение среднее значение 1366 из этой выборки и получаем
Ux = 3.000 В · 1366 / 4096 ≈ 1.000 В.
Такой хороший результат на самом деле может и не быть во всем диапазоне значений. Необходимо снять характеристику преобразования (ХП) и более полно оценить возможности АЦП в статике.
Исследование АЦП и встроенного ИОН
В основной части вызов функций adc_start_one()
и adc_read()
заменим на функцию преобразования с аппаратным суммированием adc_measure_sum(uint8_t chn)
(код функции можно посмотреть в прилагаемом архиве).
С помощью данной функции проведено снятие ХП АЦП без предварительной калибровки со следующими параметрами:
разрядность АЦП: 12,
ИОН номиналом 3.00 В,
основная тактовая частота сигнала CK_ADC: 12 МГц,
время сэмплирования: 24 такта CK_ADC,
аккумулирование суммы из 16 измерений,
температура окружающей среды: +26 °C.
Полученная с помощью калибратора характеристика преобразования показана на следующем рисунке.
Видно, что в целом характеристика линейная и практически совпадает с идеальной. Для детального анализа отдельно построим зависимость абсолютной погрешности (разности результата измерения и расчетного значения на выходе АЦП) от напряжения:
Видно, что минимальная абсолютная погрешность наблюдается в середине динамического диапазона и укладывается в заявленную в даташите интегральную нелинейность 6 LSB, правда, приведенную для VDD=5 В и тактовой частоты 24 МГц. А вот погрешность в конце диапазона вышла за предел "Full scale error" 10 LSB, приведенную для тех же условий.
Посмотрим, какими характеристиками будет обладать встроенный ИОН на 2.40 В. Переключим АЦП на встроенный ИОН IVR24, а на вход ADC8 подадим эталонное напряжение вдвое меньше (1200 мВ). Ожидаемый результат — 2047-2048. Проведем измерение с помощью той же функции adc_measure_sum()
при тех же условиях усреднения из 16 измерений. Начнем с комнатной температуры (+26 °C). Получаем результат преобразования 2044: ошибка в 3-4 LSB, что в целом неплохо. Рассчитаем напряжение ИОН:
Uref = Ux · 4096 / D = 1200 мВ · 4096 / 2044 ≈ 2405 мВ
В даташите заявлен рабочий температурный диапазон МК от -40 до +105 °C, погрешность или какие-либо допуски по номиналу ИОН не указаны. Прогреваем корпус МК феном до 85 °C, температуру на поверхности корпуса контролируем термопарой, выдерживаем около 3 мин (выше 85 °C прогревать МК на отладочной плате с кучей проводов в ПВХ изоляции рискованно). Результат: показания "просели" всего на 3 единицы (до 2041), что соответствует напряжению ИОН 2408 мВ. Оценим погрешность ИОН на всем диапазоне. В линейном предположении температурной зависимости получаем коэффициент Kt = (2408-2405)/(85-26) ≈ 0.05 мВ/°C. Для всего рабочего интервала температур Δt = 145 °C получаем изменение напряжения ΔUref = Kt · Δt = 7.25 мВ, т.е. относительную погрешность ΔUref / Uref = 0.3 %, а для более реального диапазона применения от -30 до +40 °C — 0.15 %.
Выводы:
встроенный ИОН показал хорошую точность и температурную стабильность и вполне может заменить отдельную микросхему ИОН в задачах, где в принципе применим 12-битный АЦП последовательного приближения;
микроконтроллер действительно работоспособен при высоких температурах (по крайней мере, в индустриальном диапазоне).
Тест сканирования нескольких каналов
Завершим тестирование модуля ADC0 проверкой режима сканирования нескольких каналов ("Scan"). Для этого в файле app.c
создадим функцию adc_test_scan()
:
/// Режим сканирования нескольких каналов
void adc_test_scan() {
uint32_t i;
adc_init();
SVC2(SVC_HANDLER_SET,10,adc_hdl_scan); // устанавливаем обработчик прерываний
// включаем прерывания в модуле ADC:
*(volatile uint16_t*)ADC0_INT_h0 = (1 << 3) | 1; // ADC0_E1CNV_IE | ADC0_IEA
// включаем прерывание в модуле NVIC:
*(volatile uint32_t*)CPU_ISER_w = (1 << 10); // SETENA 10
// Увеличиваем время сэмплирования:
//*(volatile uint8_t*)ADC0_CR0_b2 = 20; // ADC0_SMP_SEL
while (1) {
af=0; for (i=0; i<16; i++) ad[i]=0; // init
led1_flash();
adc_start_mask(0x0f00); // выбираем каналы 8-11
delay_ms(200);
uart_puts(PORT,"------------",UART_NEWLINE_CRLF);
if (*(volatile uint8_t*)ADC0_STA_b0 & (1 << 5)) { // ADC0_ESCNVF==1
uart_puts(PORT,"ESCNVF",UART_NEWLINE_CRLF);
}
debug('M',af);
debug('8',ad[8]);
debug('9',ad[9]);
debug('A',ad[10]);
debug('B',ad[11]);
}
}
Поскольку режим работы АЦП автоматический, для контроля происходящего удобно использовать прерывание. После инициализации модуля в функции устанавливается конечный обработчик прерывания adc_hdl_scan()
, а далее разрешается прерывание по срабатыванию флага E1CNV (завершено преобразование). Для отображения результатов в файле app.c
создадим глобальные переменные, доступные обеим функциям:
uint32_t af; // adc flags
uint16_t ad[16]; // adc results
Первая переменная флаговая: при завершении каждого преобразования в ней устанавливается бит, соответствующий номеру канала измерения. Вторая переменная — массив результатов измерений по каждому возможному каналу. Обе переменные в начале каждого цикла обнуляются, после чего запускается серия измерений функцией adc_start_mask()
с предварительной установкой маски требуемых каналов:
void adc_start_mask(uint16_t mask) {
*(volatile uint16_t*)ADC0_MSK_h0 = mask;
// ADC0_CONV_MDS = 1 (Scan), ADC0_TRG_CONT = 1, ADC0_START = 1
*(volatile uint32_t*)ADC0_START_w = (1 << 24) | (1 << 19) | 1;
}
Обработка прерывания выполняется в функции adc_hdl_scan()
(файл app.c
):
/// Обработчик прерывания ADC0 для adc_test_scan()
void adc_hdl_scan() {
*(volatile uint8_t*)ADC0_STA_b0 = 0x08; // clear ADC0_E1CNVF flag
uint32_t c,d;
d = *(volatile uint32_t*)ADC0_DAT0_w;
c = d >> 28; // ADC0_DAT0_CH
af |= (1 << c);
ad[c]=d; // ADC0_DAT0
}
В начале сбрасывается флаг E1CNVF, чтобы при выходе из обработчика это прерывание не сработало снова. Далее в переменную d
копируется весь регистр ADC0_DAT0
, содержащий номер обработанного канала в битах 28-31, флаги (здесь не используются) и собственно результат (биты 0-15), который помещается в массив ad
. В переменной af
устанавливается флаг обработки данного канала. Функция обработки сделана минимальной, чтобы быстро обработать текущее прерывание и быть готовым к следующему.
На выполнение одной серии измерений в главном цикле отводится 200 мс (взято с большим запасом), после чего результат работы модуля ADC0 выводится в терминал с помощью функции debug()
(файл app.c
, первый аргумент — условная метка, второй — число uint16_t
, которое выводится в hex и dec форматах). В данном примере сначала выводится строка ESCNVF
, если сработал флаг "сканирование завершено", затем переменная af
и значения каналов. В маске отмечаем каналы 8-11 (0x0f00).
К выводу ADC8 подключим калибратор с напряжением 1.500 В, ИОН оставим внешним на 3.000 В. В терминале получаем следующий результат:
------------
ESCNVF
M 0A00 02560
8 0000 00000
9 0854 02132
A 0000 00000
B 08B3 02227
По флагу ESCNVF видно, что аппаратно серия измерений была завершена, но в маске обработанных каналов нет 8-го и 10-го. Предположительно, время выполнения обработчика превысило интервал между преобразованиями и два прерывания были просто пропущены. Смотрим листинг дизассемблера с помощью описанного ранее скрипта lst_app
:
200000a8 <adc_hdl_scan>:
; *(volatile uint8_t*)ADC0_STA_b0 = 0x08; // clear ADC0_E1CNVF flag
200000a8: 23b6 movs r3, #182 ; 0xb6
200000aa: 2208 movs r2, #8
...
200000ca: bd10 pop {r4, pc}
Функция adc_hdl_scan()
занимает 18 слов, обработчик ADC_IRQHandler()
— еще 8, т.е. в сумме минимум 26 тактов, это еще не считая времени автоматического сохранения и восстановления фрейма стеке при наступлении прерывания. Время преобразования АЦП было выбрано 24 такта той же частоты 12 МГц. Таким образом, нужно включить по крайней мере делитель частоты на 2 для сигнала CK_ADC_INT. Раскомментируем в функции adc_init()
строку
*(volatile uint8_t*)ADC0_CLK_b0 = (1 << 4); // ADC0_CK_DIV = 1 (DIV2);
В результате получаем маску зафиксированных каналов 0x0E00 (нет канала 8), т.е. нужно включить делитель на 4:
*(volatile uint8_t*)ADC0_CLK_b0 = (2 << 4); // ADC0_CK_DIV = 2 (DIV4);
Посмотрим результат работы:
------------
ESCNVF
M 0F00 03840
8 0804 02052
9 0836 02102
A 086A 02154
B 0854 02132
Теперь все в порядке, все обработанные каналы мы успели зафиксировать и получили для подключенного 8-го канала результат 2052 с ошибкой всего 4 LSB, что нормально с учетом измерения без суммирования. Добавим, что время измерения можно было увеличить и за счет сэмплирования (в примере увеличение на 20 тактов Fs):
*(volatile uint8_t*)ADC0_CR0_b2 = 20; // ADC0_SMP_SEL
Аналоговый компаратор
Функциональные возможности
МК серии MG32F02 оснащены двумя функционально одинаковыми компараторами CMP0 и CMP1, имеющими следующие особенности:
полный рабочий диапазон напряжений входного сигнала (rail-to-rail);
6 коммутируемых источников к каждому из входов "+" и "-";
4 внешних входа (2 для каждого компаратора + 2 общих);
подключаемый ко входам встроенный 6-разрядный ЦАП (IVREF) с ИОН VDD или LDO_VR0 (для каждого компаратора свой);
выбираемый гистерезис 0 или 10 мВ;
выбираемое время срабатывания 200 нс или 10 мс;
возможность совмещенной работы в качестве оконного трехвходового компаратора;
выбор полярности выходного сигнала;
возможность использования выхода как источника событий для других модулей.
Особенности реализации компараторов в зависимости от типа МК приведены в следующей таблице.
Функция | MG32F02A032 | MG32F02A064 MG32F02U064 MG32F02A128 MG32F02U128 |
---|---|---|
ИОН для встроенного ЦАП (IVREF) | VDD | VDD, LDO_VR0 |
LDO_VRO в качестве входа | + | - |
Выход модуля DAC в качестве входа | - | + |
Возможность подключения выходавстроенного ЦАП (IVREF) к выводу МК | - | + |
Функциональная схема модулей CMP0 и CMP1 приведена на следующем рисунке (обозначения для CMP0).
Модули CMP0/1 включают следующие узлы:
мультиплексор выбора источника для положительного входа,
мультиплексор выбора источника для отрицательного входа,
6-разрядный ЦАП (IVREF),
компаратор со схемой выбора гистерезиса и схемой управления скоростью срабатывания,
блок синхронизации выхода с выбором сигнала тактирования,
мультиплексор выбора источника выходного сигнала,
схему формирования событий модуля.
Питание и тактирование
Модули CMP0/1 работают только в двух режимах питания ON и SLEEP, в режиме STOP модуль имеет возможность пробуждения. Модули CMP0/1, в отличие, например, от модулей URTx, имеют общий набор регистров статуса и управления, поэтому далее будем указывать биты и поля для модулей CMP0/1 через знак дроби. Для включения модуля CMP0/CMP1 нужно установить бит CMP_CR0.CMP_AC0_EN
/ CMP_CR1.CMP_AC1_EN
.
Для включения тактирования обоих модулей нужно установить бит CSC_APB0.CSC_CMP_EN
(предварительно нужно разблокировать возможность записи через регистр CSC_KEY
). Модули тактируются от общего сигнала CK_CMP_PR, который в зависимости от бита CSC_CKS0.CSC_CMP_CKS
выбирается из сигналов CK_APB или CK_AHB.
Настройка входов
Источник для положительного входа компаратора CMP0/1 настраивается в поле CMP_CR0.CMP_AC0_PMUX
/ CMP_CR1.CMP_AC1_PMUX
, для отрицательного входа — в поле CMP_CR0.CMP_AC0_NMUX
/ CMP_CR1.CMP_AC1_NMUX
.
Возможные источники, которые можно подключить ко входам компараторов, приведены в следующей таблице:
Значение поля CMP_ACx_(P/N)MUX | Модуль CMP0 | Модуль CMP1 |
---|---|---|
0 | IVREF | IVREF2 |
1 | вход CMP0_I0 | вход CMP1_I0 |
2 | вход CMP0_I1 | вход CMP1_I1 |
3 | общий вход CMP_C0 | общий вход CMP_C0 |
4 | общий вход CMP_C1 | общий вход CMP_C1 |
5 | выход DAC_P0 | выход DAC_P0 |
Источники опорного напряжения IVREF и IVREF2 представляют собой 6-разрядный ЦАП на основе R-цепочки и имеют один общий регистр конфигурации CMP_ANA
, причем за IVREF отвечает байт 0, за IVREF2 — такой же по формату байт 1. Далее рассмотрим этот блок на примере IVREF. Блок включается установкой бита CMP_IVREF_EN
. Верхнее значение напряжения ИОН Utop определяется битом CMP_IVREF_SEL
: 0 — VDD(A), 1 — LDO_VR0 (1.65 В). В МК MG32F02A032 выбора нет, верхнее напряжение — только VDD. Значение напряжения Uref, подаваемого на входы компаратора, определяется в поле CMP_IVREF_RS
согласно формуле:
Uref = CMP_IVREF_RS · Utop / 63.
Стоит обратить внимание, что шаг дискрета составляет Utop / 63, а не Utop / 64, так что можно выставить ровно значение Uref. Интересно, что Uref можно вывести на внешний вывод МК CMPx_I0 и использовать блок как отдельный ЦАП. Для этого необходимо:
включить соединение выхода IVREF со входом CMPx_I0, установив бит
CMP_CRx.CMP_ACx_IVROE
,соединить вход CMPx_I0 с одним из входов компаратора, чтобы вывод в принципе соединился с модулем CMPx.
Выводы МК, используемые как входы компаратора, должны быть сконфигурированы в аналоговый режим в соответствующем регистре PA_CRx
установкой в 0 поля PA_IOMx
(режим по-умолчанию).
Настройка выхода
В зависимости от значения поля CMP_CRx.CMP_ACx_FSEL
сигнал с выхода компаратора может быть инвертирован (при CMP_CRx.CMP_ACx_INV
=1), затем либо обработан блоком "3-clock Filter" (к сожалению, В User Guide не описано, как именно обрабатывается сигнал), либо сразу передан на схему формирования событий (флагов) как внутренний сигнал CMP0_OUT. Сигнал CMP0_OUT также можно вывести на внешний вывод МК CMPx_P0, сконфигурировав его как цифровой выход. При этом возможно включить дополнительную инверсию (при CMP_CRx.CMP_ACx_PINV
=1).
Настройки компаратора
В компараторе можно включить гистерезис 10 мВ установкой бита CMP_CRx.CMP_ACx_HYS
(по-умолчанию отключен). Также имеется возможность настроить время срабатывания 200 нс или 10 мс с помощью бита CMP_CRx.CMP_ACx_RES
(по умолчанию 200 нс (0)). Уменьшение времени срабатывания может несколько снизить потребление компаратора (примерно на 200 мкА).
События и прерывания
Модули CMP0/1 могут генерировать следующие события:
сигнал RST_CMPx для подсистемы сброса МК,
прерывание INT_CMP,
событие пробуждения МК WUP_CMPx.
Каждый компаратор CMPx может формировать два внутренних события:
rising-edge — срабатывание компаратора по переднему фронту (флаг ACx_RF),
falling-edge — срабатывание компаратора по заднему фронту (флаг ACx_FF).
В регистре статуса CMP_STA
представлены флаги событий обоих компараторов, а также биты CMP_ACx_S
, соответствующие состоянию выходов компараторов (учитывается включение инверсии в CMP_CRx.CMP_ACx_INV
). Все биты разрешения прерывания по соответствующим внутренним событиям собраны в регистре управления прерываниями CMP_INT
.
Для включения прерывания модуля CMPx необходимо:
Выбрать событие (или события) в регистре
CMP_INT
.Разрешить прерывание самого модуля установкой бита
CMP_INT.CMP_IEA
.Разрешить прерывание IRQ от компаратора (IRQ#7) в контроллере прерываний NVIC установкой бита 7 в регистре
CPU_ISER
.
Тестирование компаратора
Для проверки функционирования компаратора (на примере CMP0) создадим функцию инициализации модуля (включение тактирования):
void cmp_init() {
*(volatile uint16_t*)CSC_KEY_h0 = 0xA217; // unlock access to CSC regs
*(volatile uint8_t*)CSC_APB0_b0 |= 4; // CSC_CMP_EN = 1
*(volatile uint16_t*)CSC_KEY_h0 = 0x1111; // lock access to CSC regs
}
и собственно функцию тестирования, вызов которой поместим в app()
:
void cmp_test() {
cmp_init(0);
// Входы: PA8 -> CMP0_I0 (pos), PA9 -> CMP0_I1 (neg)
// Выход (PC9):
*(volatile uint16_t*)PC_CR9_h0 = (1 << 12) | 2; // AFS: CMP0_P0, PC9 -> push-pull output
*(volatile uint32_t*)CMP_CR0_w =
(0 << 18) | //CMP_AC0_FSEL: bypass
(0 << 12) | // CMP_AC0_NMUX: IVREF
(1 << 8) | // CMP_AC0_PMUX: CMP0_I0
//(1 << 4) | // CMP_AC0_HYS = 1, 10 mV
1; // CMP_AC0_EN = 1
*(volatile uint8_t*)CMP_ANA_b0 =
(63 << 2) | // CMP_IVREF_RS = 63
3; // CMP_IVREF_SEL =1: VR0 (1.650 V), CMP_IVREF_EN = 1
}
Аналоговые входы CMP0_I0 (PA8) и CMP0_I1 (PA9) настраивать нет необходимости. Выход компаратора пробросим на пин CMP0_P0 (PC9), к которому подключим светодиод D1. Оставляем режим соединения с выходом bypass (без фильтра) и оставляем отключенными все инверсии (оставляем в коде пока бесполезные нули для наглядности). На положительный вход подадим внешний сигнал со входа COM0_I0, на отрицательный вход подадим опорное напряжение от IVREF. Пока не будем включать гистерезис. Далее включаем внутренний ЦАП, устанавливаем выход LDO_VR0 номиналом 1.65 В в качестве его ИОН и выставляем максимальное напряжение (63).
На выводе VR0 МК (выход встроенного LDO) измеренное тестером напряжение составило 1.652 В. Теперь осталось подключить к PA8 калибратор и "покрутить" напряжение в районе этого значения. В результате изменения напряжения шагами по 1 мВ от 1.600 до 1.700 В светодиод начал мерцать на отметке 1.650 В и окончательно включился после подачи 1.656 В. Компаратор работает. Теперь попробуем включить гистерезис, раскомментировав строку
(1 << 4) | // CMP_AC0_HYS = 1, 10 mV
Результат получился следующий: компаратор уверенно включается при 1.655 В и отключается при 1.651 В — гистерезис тоже заработал вполне предсказуемо.
В данном компараторе представляет интерес встроенный ЦАП, который, к тому же, можно использовать самостоятельно. Попробуем его протестировать отдельно. Можно соединить его выход с внешним пином CMP0_I0 (PA8) и непосредственно измерять напряжение. Будем использовать следующую функцию:
void cmp_test_ivref() {
cmp_init(0);
// Входы: PA8 -> CMP0_I0 (pos), PA9 -> CMP0_I1 (neg)
*(volatile uint32_t*)CMP_CR0_w =
(1 << 31) | // CMP_AC0_IVROE = 1
(2 << 12) | // CMP_AC0_NMUX: CMP0_I1
(1 << 8) | // CMP_AC0_PMUX: CMP0_I0
1; // CMP_AC0_EN = 1
*(volatile uint8_t*)CMP_ANA_b0 =
(63 << 2) | // CMP_IVREF_RS = 63
1;// 3; // CMP_IVREF_SEL = 0: VDD (3.3 V), CMP_IVREF_EN = 1
}
Здесь выход компаратора можно вообще не использовать. Вход CMP0_I0 соединяем с положительным входом, вход CMP0_I1 — с отрицательным. Соединяем выход IVREF со входом PA8 ( CMP_AC0_IVROE
=1) и выставляем пока максимальное напряжение ЦАП с напряжением питания МК VDD в качестве ИОН (для большего динамического диапазона). Результат получился следующий: измеренное значение VDD 3.275 В, напряжение на PA8 — 3.267 В, т.е. имеется небольшое отклонение на 8 мВ. Выставив половину диапазона (31), получаем результат 1.609 В при ожидаемом напряжении 1,634 В (половина от выхода IVREF) с ошибкой в 1.5 %. При установленном 0 получаем результат 2 мВ. Наверно это не так плохо для "вспомогательного" ЦАП. В заключении всех наших экспериментов сгенерируем "пилу" на этом ЦАПе и посмотрим его ХП:
void cmp_test_ivref_gen() {
uint8_t i;
cmp_init(0);
// Входы: PA8 -> CMP0_I0 (pos), PA9 -> CMP0_I1 (neg)
*(volatile uint32_t*)CMP_CR0_w =
(1 << 31) | // CMP_AC0_IVROE = 1
(2 << 12) | // CMP_AC0_NMUX: CMP0_I1
(1 << 8) | // CMP_AC0_PMUX: CMP0_I0
1; // CMP_AC0_EN = 1
while (1) {
for (i=0; i<=63; i++) *(volatile uint8_t*)CMP_ANA_b0 = (i << 2) | 1;
}
}
Получаем следующий результат:
В целом характеристика линейная, но есть отклонения. Вывод: в прецизионных задачах вместо IVREF лучше использовать встроенный 12-битный модуль DAC (источник DAC_P0).
Все файлы, рассматриваемые в статье, собраны в прилагаемом архиве. На этом мы завершаем четвертую статью цикла. В следующий раз рассмотрим SysTick Timer, RTC, сторожевые таймеры IWDT и WWDT.