[i] Пример и особенности работы с АЦП
В данной статье рассмотрен простой пример работы с блоком аналого-цифрового преобразователя микроконтроллера К1986ВЕ92QI в составе отладочной платы. Блок АЦП идентичен по своей структуре в следующих МК: К1986ВЕ1QI, К1901ВЦ1QI , К1986ВЕ92QI , К1986ВК214 и К1986ВК234. Однако в каждом процессоре есть свои особенности. МК К1901ВЦ1QI содержат два независимых АЦП разрядностью 12 бит, до 16 каналов каждый, в то время как в МК К1986ВЕ1QI, К1986ВК214 и К1986ВК234 по одному АЦП такой же разрядности, до 8 каналов. Пример проекта, для К1986ВЕ92QI, рассмотренного в статье, доступен для скачивания в конце статьи.
Введение
Блок аналого-цифрового преобразователя представляет собой устройство, на вход которого подается аналоговый сигнал, а на выходе выдается цифровой код, пропорциональный поданному напряжению. АЦП является основой многих измерительных приборов, например, цифровых мультиметров, электронных весов, приборов для измерения температуры, давления и многих других…
АЦП может быть как отдельной микросхемой, так и быть в составе микроконтроллера. К1986ВЕ1QI, К1901ВЦ1QI, К1986ВЕ92QI, К1986ВК214 и К1986ВК234 содержат в себе интегрированный блок АЦП. Основные характеристики АЦП — это разрядность и время преобразования. Разрядность 12 бит, следовательно, АЦП может различать 212 = 4096 различных уровней подаваемого на вход напряжения. А время преобразования, в свою очередь, зависит от частоты, подаваемой на АЦП. По спецификации для осуществления преобразования требуется не менее 28 тактов синхронизации CLK, в качестве которой можно использовать как частоту процессора CPU_CLK, так и частоту ADC_CLK, формируемую в блоке «Сигналов тактовой частоты».
АЦП имеет следующие основные режимы работы:
-режим одиночного преобразования по одному каналу (с возможностью опроса бита окончания преобразования или с прерыванием по окончанию преобразования);
-режим многократного преобразования (по одному каналу/с автоматическим переключением нескольких каналов и возможностью использования прямого доступа к памяти).
Преобразование с контролем границ
Микроконтроллеры К1901ВЦ1QI и К1986ВЕ92QI имеют в своём арсенале 2 независимых АЦП – ADC1, ADC2, которые входят в состав блока АЦП. Общая схема приведена на рисунке 1. Только у К1986ВЕ92QI АЦП имеют по 8 каналов, у К1901ВЦ1QI по 16.
Рисунок 1 - Структурная схема АЦП К1901ВЦ1QI
Основной идеей данного примера является использование контроля уровня входного сигнала. В начале примера зададим следующие переменные:
H_Level = 0x900;
L_Level = 0x800;
которые являются границами и за которыми МК будет следить. Изменять на входе напряжение будем с помощью подстроечного резистора, который согласно описанию платы, подключен к 7 каналу АЦП. Для этого необходимо установить перемычку на разъём XP6 в положение “TRIM” (рисунок 2).
Рисунок 2 - Фрагмент платы К1986ВЕ92QI с подстроечным резистором и перемычкой XP6
Таким образом, в случае нарушения данных границ, то есть выхода за границы интервала от 0x800 до 0x900, МК возведёт флаг Flg REG AWOIFEN в регистре ADCx_STATUS.
Настройка АЦП
Как было отмечено ранее, в микроконтроллерах серии К1986ВЕ92QI и К1901ВЦ1QI есть два независимых АЦП. Поэтому в спецификации есть два регистра настроек ADC1_CFG и ADC2_CFG.
Настройка АЦП в К1986ВЕ92QI происходит с использованием двух структур: ADC_StructInit и ADCx_StructInit. Это обусловлено тем, что первая структура ADC_StructInit содержит «общие настройки», которые применимы для самого контроллера блока ADC. А уже структура ADCx_StructInit содержит в себе настройку конкретного АЦП1 или АЦП2. Рассмотрим каждую структуру по отдельности.
Первую структуру ADC_StructInit оставим без изменений:
ADC_InitStruct->ADC_SynchronousMode = ADC_SyncMode_Independent;
/* параметр отвечает за синхронный/независимый запуск двух АЦП (только для К1986ВЕ92QI и К1901ВЦ1QI )*/
#endif
ADC_InitStruct->ADC_StartDelay = 0;
/* позволяет задать задержку между запусками АЦП1 и АЦП2 */
ADC_InitStruct->ADC_TempSensor = ADC_TEMP_SENSOR_Disable;
/* запрет работы температурного датчика и источника опорного напряжения (бит TS_EN) */
ADC_InitStruct->ADC_TempSensorAmplifier = ADC_TEMP_SENSOR_AMPLIFIER_Disable;
/* запрет работы выходного усилителя (бит TS_BUF) */
ADC_InitStruct->ADC_TempSensorConversion = ADC_TEMP_SENSOR_CONVERSION_Disable;
/* запрет выбора датчика температуры (бит SEL_TS) */
ADC_InitStruct->ADC_IntVRefConversion = ADC_VREF_CONVERSION_Disable;
/*запрет выбора источника опорного напряжения для оцифровки
ADC_InitStruct->ADC_IntVRefTrimming = 0;
/*задавая значения от 0 до 7, можно в небольших пределах подстроить */
Здесь стоит отметить следующий момент: параметр ADC_IntVRefConversion разрешает и запрещает выбор источника опорного напряжения бит SEL_VREF регистра ADC1_CFG. Затем с помощью параметра ADC_IntVRefTrimming можно выполнить подстройку значений от 0 до 7 - биты 24..21 TR того же регистра ADC1_CFG.
В микроконтроллерах К1986ВЕ1QI, К1986ВК214 и К1986ВК234 разрешение выбора данного датчика, а также подстройка значений, выполняется в отдельном регистре ADC1_TRIM.
Рассмотрим конфигурацию АЦП1.
sADCx.ADC_ClockSource = ADC_CLOCK_SOURCE_CPU;
/* выбор источника тактирования, частота ядра */
sADCx.ADC_SamplingMode = ADC_SAMPLING_MODE_CICLIC_CONV;
/* режим многократного преобразования */
sADCx.ADC_ChannelSwitching = ADC_CH_SWITCHING_Disable;
/* автоматическое переключение каналов */
sADCx.ADC_ChannelNumber = ADC_CH_ADC7;
/* выбор номера канала */
sADCx.ADC_Channels = 0;
/* количество используемых каналов, если включен перебор */
ADC_CH_SWITCHING_Enable sADCx.ADC_LevelControl = ADC_LEVEL_CONTROL_Enable;
/* контроль уровня входного сигнала */
sADCx.ADC_LowLevel = L_Level;
/* нижний уровень контроля */
sADCx.ADC_HighLevel = H_Level; // верхний уровень
sADCx.ADC_VRefSource = ADC_VREF_SOURCE_INTERNAL;
/* выбор внутреннего источника опорного напряжения */
sADCx.ADC_IntVRefSource = ADC_INT_VREF_SOURCE_INEXACT;
/* вид источника для датчика опорного напряжения */
sADCx.ADC_Prescaler = ADC_CLK_div_32768; //выбор делителя тактовой частоты
sADCx.ADC_DelayGo = 0xF;
/* значение задержки перед началом следующего преобразования */
Прерывания по завершению преобразования
После инициализации АЦП с помощью функции ADC1_ITConfig разрешим прерывания по завершению преобразования. Затем функцией ADC1_Cmd (ENABLE) разрешаем работу АЦП. Как только АЦП выполнит преобразование, произойдет переход в функцию обработчика прерываний, в котором сначала будет выполнена проверка установки флага выхода за границы. Если значение на входе АЦП не попадает в обозначенные границы, загорается диод VD3. После этого считывается значение регистра ADC1_RESULT и накладывается маска, поскольку биты данного регистра содержат также номер канала. Сравнивается результат. Если верхнее значение границы было превышено, то вместе с VD3 горит диод VD4. При попадании значения на входе АЦП в нужный интервал все диоды погаснут. В конце обработчика выполняется очистка флагов.
Функция обработчика прерывания:
void ADC_IRQHandler(void)
{
if(ADC1_GetFlagStatus(ADCx_FLAG_OUT_OF_RANGE) == SET)
{
/* Turns LED1 On */
PORT_SetBits(MDR_PORTC, PORT_Pin_0);
}
else
{
/* Turns LED1 Off */
PORT_ResetBits(MDR_PORTC, PORT_Pin_0);
}
tmp = MDR_ADC->ADC1_RESULT & 0x0FFF;
if(tmp > H_Level)
{
/* Turns LED2 On */
PORT_SetBits(MDR_PORTC, PORT_Pin_1);
}
else
{
/* Turns LED2 Off */
PORT_ResetBits(MDR_PORTC, PORT_Pin_1);
}
/* Clear ADC1 OUT_OF_RANGE interrupt bit */
MDR_ADC->ADC1_STATUS = (ADCx_IT_END_OF_CONVERSION | ADCx_IT_OUT_OF_RANGE)<<2;
}
В режиме отладки можно посмотреть, как происходят изменения значения в регистре ADC1_RESULT в зависимости от изменения сопротивления подстроечного резистора, чтобы отловить момент выхода за объявленные границы (рисунок 3).
Рисунок 3 - Регистры АЦП в режиме "отладки"
Расчет и измерение времени преобразования АЦП описано в статье "Расчет времени преобразования АЦП и время заряда внутренней емкости"
Последовательное преобразование нескольких каналов
В данном режиме работы есть определенное условие, которое надо обязательно соблюдать. Необходимо обеспечить выставление бит каналов, в регистре ADC1_CHSEL, участвующих в преобразовании до конфигурирования самого блока АЦП, то есть до записи в регистр ADCx_CFG.
Пример работы:
#define Amount_Conv 10
uint32_t status[Amount_Conv], data[Amount_Conv], counter = 0;
volatile uint32_t a=0;
int main (void)
{
MDR_RST_CLK->PER_CLOCK=0xFFFFFFFF;
MDR_PORTD->ANALOG=0xFFFF;
MDR_PORTD->PWR=0xFFFFFFFF;
MDR_PORTD->OE=0xFFFF;
MDR_ADC->ADC1_CHSEL=(1<<2) | (1 <<3);
MDR_ADC->ADC1_CFG |= (1<<3) | (1<<9) | (3<<12) | (7<<25);
MDR_ADC->ADC1_CFG |= 0x1;
while( counter < Amount_Conv )
{
while( ( MDR_ADC->ADC1_STATUS & (1<<2) ) == 0){}
status[counter] = MDR_ADC->ADC1_STATUS;
data[counter] = MDR_ADC->ADC1_RESULT;
counter++;
}
while(1);
}
Сохранить статью в PDF