analogRead() — прощай

Опубліковано: 16 березня 2017р. 19:24
Я їжджу на Mitsubishi Colt VI
Павлоград, Україна

Не знаю как кого, но меня больше всего раздражала функция analogRead()… своей медлительностью и не функциональностью. Если манипулирования портами через регистры (речь о цифровых) у большинства не вызывает особых затруднений, то на аналоговых портах многие "ардуинщики" буксуют…

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

Настройка АЦП сводится к следующим действиям:

1. Необходимо настроить регистр ADMUX (Регистр настройки мультиплексора АЦП).
2. Настройки регистра ADCSRA (Регистр статуса и контроля А)
3. При необходимости, настройки DCSRB (Регистр статуса и контроля B)
3. Чтение результата преобразования

В данном примере мы настроим два основных регистра ADMUX и ADCSRA, даже не регистра, а биты из которых состоят эти регистры.
*****************************************
1. Настраиваем регистр ADCSRA:
*****************************************
Для включения АЦП необходимо установить бит ADEN в уровень логической единицы (1 << ADEN)
Необходимо выбрать предделитель частоты преобразования битами ADPS0 ADPS1 ADPS2.
Например, микроконтроллер прошит на использование тактовой частоты (8 MHz), с предделителем частоты на 8.
Таким, образом тактовая частота микроконтроллера равна 1 MHz. Для корректного и быстрого преобразования,
достаточно частоты 125 kHz (преобразование будет происходить за 8 мкс).
Таким, образом необходимо выбрать предделитель 8. ((1 << ADPS0) | (1 << ADPS1))

** отмечу, что для достижения полной 10-битной точности рекомендуют использовать частоты ниже 200кГц.
Частоты выше 200кГц можно использовать если точность не важна, а важна скорость.
Например, при частоте 1МГц получаем 8-битное разрешение, а при 2МГц – 6-битное.
ADPS2:ADPS2:ADPS0
001 СК/2
010 СК/4
011 СК/8
100 СК/16
101 СК/32
110 СК/64
111 СК/128

*****************************************
2. Настраиваем регистр ADMUX
*****************************************
Битами REFS1 и REFS0 выставляем тип опорного напряжения.
REFS1:REFS0
00 AREF
01 AVcc, с внешним конденсатором на AREF
10 Резерв
11 Внутренний 2.56В источник, с внешним конденсатором на AREF

Битами MUX0 … MUX3 выбираем номер пина на порту С (в atmega328p порт С отведён под аналоговый вход).
MUX3:MUX2:MUX1:MUX0
0000 ADC0
0001 ADC1
0010 ADC2
0011 ADC3
0100 ADC4
0101 ADC5
0110 ADC6
0111 ADC7
*****************************************
*****************************************
А теперь приступим к самому интересному:
И начинаем запуск преобразования. В регистре ADCSRA, битом ADSC необходимо запустить преобразование и выполнять его,
пока не будет выставлен бит о свидетельствующий о том, что преобразование завершено (бит ADIF).
Данной строчкой (ADCSRA & (1 << ADIF) == 0 происходит проверка и одновременное сбрасывание флага об окончании преобразования.
Далее считываем полученное значение, и преобразуем в Вольты. Вот и вся программа, как видно использовать АЦП не так уж и сложно!

Теперь собственно сам "скетч" … arduino Pro Mini 328 (5 Volt, 16 MHz)

unsigned int data;
float V;

void setup() {
Serial.begin(57600);
DDRC = B000000; // назначает выводы с 14 по 19 входными
PORTC = B000000; // устанавливает HIGH на выводах с 14 по 19 (на 19 LOW)

ADCSRA |= (1 << ADEN) // Включаем АЦП
|(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0); // устанавливаем предделитель преобразователя на 128

ADMUX |= (0 << REFS1)|(1 << REFS0) // выставляем опорное напряжение Vcc
|(1 << MUX0)|(1 << MUX1)|(0 << MUX2)|(0 << MUX3); // снимать сигнал будем с входа AC3
}

void loop() {
do{ ADCSRA |= (1 << ADSC); } // Начинаем преобразование
while ((ADCSRA & (1 << ADIF)) == 0); // пока не будет выставлен флаг об окончании преобразования
data = (ADCL|ADCH << 8); // Считываем полученное значение
V = (float) data * 0.0047031; // Переводим в вольты
/* 0.0047031 = Vcc / 1024 */
Serial.print("V = "); Serial.print(V, 3); Serial.println(" V");
}

++++++++++++++++++++++++++
добавлено 16.03.2017 21:12
++++++++++++++++++++++++++

Для тех кто не понимает зачем это, приведу небольшой пример…

Это стандартный скетч

unsigned int data;
float V;
unsigned long time;

void setup() {
analogReference(DEFAULT); // DEFAULT INTERNAL использовать Vcc как AREF
Serial.begin(57600);
}

void loop() {
time = micros();
data = analogRead(A3); // Считываем полученное значение
V = (float) data * 0.0047031; // Переводим в вольты
// 0.0047031 = Vcc / 1024
time = (micros() — time);
Serial.print("V = "); Serial.print(V, 3); Serial.print(" V ");
Serial.print("T = "); Serial.print(time); Serial.println(" mks");
delay (500);
}

Соответственно результат этого стандартного скетча

V = 4.811 V T = 220 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 124 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks
V = 4.811 V T = 128 mks

Теперь МОДИФИЦИРУЕМ "скетч"

unsigned int data;
float V;
unsigned long time;

void setup() {
Serial.begin(57600);
DDRC = B000000; // назначает выводы с 14 по 19 входными
PORTC = B000000; // устанавливает HIGH на выводах с 14 по 19 (на 19 LOW)

ADCSRA |= (1 << ADEN); // Включаем АЦП
ADCSRA |= (1 << ADPS2)|(0 << ADPS1)|(1 << ADPS0); // устанавливаем предделитель преобразователя на 32
ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
ADCSRA |= (1 << ADIE); // Разрешаем прерывания по завершении преобразования
ADCSRA |= (1 << ADSC); // Запускаем преобразование

ADMUX |= (0 << REFS1)|(1 << REFS0) // выставляем опорное напряжение Vcc
|(1 << MUX0)|(1 << MUX1)|(0 << MUX2)|(0 << MUX3); // снимать сигнал будем с входа AC3
}

void loop() {
time = micros();
V = (float) data * 0.0047031; // Переводим в вольты
// 0.0047031 = Vcc / 1024
time = (micros() — time);
Serial.print("V = "); Serial.print(V, 3); Serial.print(" V ");
Serial.print("T = "); Serial.print(time); Serial.println(" mks");
delay (500);
}

ISR(ADC_vect) { data = (ADCL|ADCH << 8); }

Получаем результат

V = 0.000 V T = 12 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 20 mks
V = 4.811 V T = 20 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 20 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 20 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 20 mks
V = 4.811 V T = 16 mks
V = 4.811 V T = 20 mks
V = 4.811 V T = 20 mks
V = 4.811 V T = 20 mks
V = 4.811 V T = 20 mks
V = 4.811 V T = 16 mks

Думаю, что все понятно без объяснений…

0 0

Коментарі

Щоб залишати коментарі, потрібно авторизуватись.