01 |
Содержание:
|
|
02 |
Несмотря на кажущуюся схожесть с другой парой функций digitalRead() и digitalWrite(), которые работают по одному механизму, только в разных направлениях, функции analogRead() и analogWrite() имеют в корне разную природу, в первую очередь на аппаратном уровне. Поэтому и рассматривать их нужно в отрыве друг от друга — функционал analogRead() целиком и полностью реализован на «железном уровне» в аналого-цифровом преобразователе (АЦП), analogWrite() — в регистрах выходного сравнения OCR (ШИМ-генераторам), работающим на одном из встроенных таймеров. |
|
03 |
АЦП и analogRead() — немного теории В отличие от остальных функций, ускорение функции analogRead() достигается не модификацией её исходного кода, а настройкой режима работы АЦП. Но обо всем по порядку и начнем с описания работы АЦП микроконтроллера Atmega 328P. |
Datasheet на микроконтроллер Atmega 328P можно скачать на официальном сайте производителя Atmel — Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf (5,17 MB)
|
04 |
Микроконтроллер Atmega 328P, на котором построен Arduino Uno содержит встроенный 10-битный аналого-цифровой преобразователь (далее АЦП, англ. ADC — Analog to Digital Converter) последовательного приближения. Именно он, как видно из названия, отвечает за оцифровку входящего аналогового сигнала. |
|
05 |
Для осуществления корректного преобразования, АЦП необходимо эталонное значение напряжения, с которым будет сравниваться входящий аналоговый сигнал. Это эталонное значение называется источником опорного напряжения (ИОН). Микроконтроллер Atmega 328P позволяет в качестве ИОН использовать:
|
|
06 |
По умолчанию, у Arduino, в качестве ИОН выступает напряжение пиитания МК — 5 В. Для использования в качестве ИОН других источников референсного внешнего напряжения у микроконтроллера есть дополнительный вход AREF. Перед преобразованием аналогового сигнала, при использовании внешнего ИОН необходимо вызвать функцию analogReference(). |
|
07 |
Сигнал, поданный на вход АЦП должен быть в границах заданного диапазона 0 (GND)...ИОН. 10-битный означает, что заданный диапазон будет разбит на $2^{10}=1024$ значения, и входной сигнал будет оцифрован в соответствующее цифровое значение из диапазона 0...1023: |
|
09 |
Процесс оцифровки аналогового сигнала сводится к последовательному подбору наиболее близкого напряжения с известным (референсным) значением к входному напряжению. Принцип действия АЦП схематично показан на рисунке: |
|
10 |
Схема работы 10-битного АЦП последовательного приближения
|
|
11 |
Механизм преобразования прост и состоит из 10 шагов (отсюда 10 бит). На первом шаге АЦП формирует напряжение $U_{АЦП}$ по величине равное половине опорного напряжения — $\frac{ 1}{ 2} U_{ИОН}$ и сравнивает его с входящим аналоговым сигналом $U_{Вх}$. Далее, если $U_{АЦП}$ меньше $U_{Вх}$, то соответствующему биту присваивается значение 1, и $U_{АЦП}$ увеличивается уже на $\frac{ 1}{ 4} U_{ИОН}$, если $U_{АЦП}$ больше $U_{Вх}$, то соответствующему биту присваивается значение 0, и $U_{АЦП}$ уменьшается на $\frac{ 1}{ 4} U_{ИОН}$. На следующем шаге коррекция составляет $\frac{ 1}{ 8} U_{ИОН}$, потом $\frac{ 1}{ 16} U_{ИОН}$ и т.д. Математически процесс можно представить следующей системой уравнений: |
|
12 |
$$U_{АЦП_{n}}=\begin{cases}
\frac{ 1}{ 2} U_{ИОН} & \text{, при } n=1 \\
U_{АЦП_{n-1}}+\frac{ 1}{ 2^n} U_{ИОН} & \text{, при }n>1, U_{АЦП_{n-1}}<U_{Вх} \\
U_{АЦП_{n-1}}-\frac{ 1}{ 2^n} U_{ИОН} & \text{, при }n>1, U_{АЦП_{n-1}}>U_{Вх}
\end{cases}$$
|
|
13 |
Исходя из вышеизложенного и данных представленных на рисунке, нетрудно посчитать результат: |
|
14 |
В один момент времени АЦП может оцифровывать сигнал только с одного аналогового входа. Это связано с тем, что за выбор сигнала отвечает мультиплексор — устройство, которое по команде АЦП из нескольких аналоговых входов на выход подает только один выбранный. У Atmega 328P аналоговых входов 6 (в корпусе DIP) — все они принадлежат порту C (PORTC — помните, в первой части?). |
|
15 |
Запуск преобразования может осуществляться несколькими способами:
|
|
16 |
Для управления АЦП существует 5 основных восьмибитных регистров: |
|
17 |
|
|
18 |
Регистр ADCSRA (ADC Control and Status Register A) Назначение битов регистра ADCSRA (ADC Control and Status Register A, регистр управления и состояния):
|
|
19 |
Здесь стоит подробнее рассказать о том, как значения битов ADPS[2:0] влияют на скорость и точность АЦП. Частота работы МК Atmega 328P 16МГц. При настройках по умолчанию используется предделитель 128 (ADPS[2:0]=[111]), а это значит, что АЦП работает на частоте 16МГц/128=125КГц, что укладывается в данные даташита – 50-200КГц. Отсюда очень низкая скорость выполнения преобразования, но и самая высокая точность. Для того, чтобы ускорить работу АЦП необходимо уменьшить предделитель, но необходимо помнить, что чем выше частота, тем ниже точность преобразования. Экспериментально можно получить значение предделителя – 16 (ADPS[2:0]=[100]), при котором возможен компромисс 10-кратного прироста скорости, при сохранении точности. |
|
20 |
Напишем первый скетч, в котором используется родная функция analogRead(). Но отличие будет заключаться в том, что перед использованием, режим работы АЦП будет перенастроен: |
|
21 | Arduino (C++) |
1 2 3 4 5 6 7 8 9 10 11 12 13 int pinIn = A0; // Пин аналогового входа
void setup() {
pinMode(pinIn, INPUT);
ADCSRA |= (1 << ADPS2); //Биту ADPS2 присваиваем единицу - коэффициент деления 16
ADCSRA &= ~ ((1 << ADPS1) | (1 << ADPS0)); //Битам ADPS1 и ADPS0 присваиваем нули
}
void loop() {
Serial.println(analogRead(pinIn));
delay(1000);
} |
|
22 |
Замеры производительности показывают время выполнения функции — 16 мкс (вместо первоначальных 112 мкс): 1 2 analogRead(). Total time: 160052 microsec, AVG time: 16.00 microseconds. (iterations 10000)
Ускорение в 7 раз. |
|
23 |
А выбор предделителя с коэффициентом 8 (ADPS[2:0]=[011]) сократит время исполнения до 9 мкс... В общем здесь уже каждый в зависимости от целей и задач определяет необходимые параметры. |
|
24 |
Используя директивы препроцессора (о них написана отдельная статья), код можно модифицировать в более удобный и понятный: |
|
25 | Arduino (C++) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
int pinIn = A0; // Пин аналогового входа
void setup() {
pinMode(pinIn, INPUT);
sbi(ADCSRA, ADPS2); //Биту ADPS2 присваиваем единицу
cbi(ADCSRA, ADPS1); //Битам ADPS1 и ADPS0 присваиваем нули
cbi(ADCSRA, ADPS0);
}
void loop() {
Serial.println(analogRead(pinIn));
delay(1000);
} |
|
26 |
Регистр ADMUX (ADC Multiplexer Selection Register) Вспомним какие биты содержатся в регистре ADMUX:
|
|
27 |
Назначение битов регистра ADMUX:
|
|
28 |
Регистр ADCSRB (ADC Control and Status Register B) У регистра ADCSRB всего одна группа битов — ADTS[2:0]. О ней упоминалось в контексте описания бита ADATE. Биты ADTS[2:0] отвечают за выбор источника автоматического запуска АЦП. |
|
29 |
Здесь мы подошли ко второй важной части ускорения функции analogRead(). Если посмотреть на исходный код функции, можно увидеть строки вызова и ожидания результата: |
|
30 | Arduino (C++) |
1 2 3 4 5 6 7 // ...
// Запуск преобразования
sbi(ADCSRA, ADSC);
// Ожидание сброса бита ADSC, свидетельствующее об окончании преобразования
while (bit_is_set(ADCSRA, ADSC));
// ... |
wiring_analog.c
|
31 |
Теперь получается, что после вызова функции микроконтроллер останавливается и просто ждет результата (строка 6). В приложении моргания светодиодом это может быть совсем не важно, а вот в системе постоянного опроса аналоговых датчиков, да и ещё с какой-либо логикой, такой поведение уже критично. |
|
32 |
Для того, чтобы разгрузить микроконтроллер необходимо заставить АЦП самостоятельно заниматься оцифровкой, а по завершении преобразования АЦП сообщит об окончании прерыванием. |
|
33 |
АЦП и прерывания (Interrupts) Но полную мощь АЦП проявляет в связке с прерываниями — обособленный от ядра МК режим работы АЦП, при котором АЦП прерыванием информирует МК о завершенном преобразовании. Пока осуществляется преобразование, Arduino занимается своими делами — исполняет заданную программу. |
|
34 |
Для реализации описанного режима необходимо выполнить следующие настройки (изменить биты регистров АЦП): |
|
35 | Arduino (C++) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 uint8_t analog_ref = DEFAULT; // Эта переменная нужна будет для случаев перебора нескольких датчиков -
//она будет хранить начальное состояние регистра ADMUX
void setup()
{
//...
ADCSRA = 0; // Сбрасываем регистр ADCSRA
ADCSRB = 0; // Сбрасываем регистр ADCSRB
ADMUX |= (1 << REFS0); // Задаем ИОН
ADMUX |= (1 << ADLAR); // Меняем порядок записи бит, чтобы можно было читать только 8 бит регистра ADCH
// Таким образом отсекаются 2 последних "шумных" бита, результат 8-битный (0...255)
analog_ref = ADMUX; // Запоминаем состояние регистра - оно будет использоваться при смене пина входящего сигнала
ADMUX |= (0 & 0x07); // Выбираем пин A0 для преобразования
// Устанавливаем предделитель - 16 (ADPS[2:0]=100)
ADCSRA |= (1 << ADPS2); //Биту ADPS2 присваиваем единицу
ADCSRA &= ~ ((1 << ADPS1) | (1 << ADPS0)); //Битам ADPS1 и ADPS0 присваиваем нули
ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
ADCSRA |= (1 << ADIE); // Разрешаем прерывания по завершении преобразования
ADCSRA |= (1 << ADEN); // Включаем АЦП
ADCSRA |= (1 << ADSC); // Запускаем преобразование
} |
|
36 |
Для обслуживания прерывания, генерируемого АЦП существует процедура ISR(ADC_vect) {...} (англ. Interrupt service routine — процедура обслуживания прерывания), которая запускается каждый раз в момент завершения преобразования. В этой процедуре можно считать результат преобразования и обработать его: |
|
37 | Arduino (C++) |
1 2 3 4 5 6 7 8 9 10 11 ISR(ADC_vect){
//...
// Порядок записи результирующих битов был изменен из-за пренебрежения последними битами
// ADLAR=1
int result = ADCH; // Считываем только значимые 8 бит - значение из диапазона 0...255
// Если нужны все 10 бит (полная 10-битная точность), то если ADLAR=0:
int result = ADCL | (ADCH << 8);
// Если ADLAR=1:
int result = (ADCL>> 6) | (ADCH << 2);
}) |
|
38 |
В данном примере переменная result объявлена как int, а значит будет сразу преобразована к числу диапазона 0...255 или 0...1023. |
|
39 | На заметку: |
В случае использования в процедуре обработки прерывания переменной объявленной вне её, нельзя забывать использовать описатель volatile для корректной обработки переменной компилятором.
|
|
40 | На заметку: |
Если необходимо собирать данные с нескольких датчиков, то нужно после считывания преобразованного значения в процедуре ISR(ADC_vect) изменить входящий аналоговый пин на следующий:
1 2 3 4 5 6 7 8 9 volatile int analogPin = A0;
ISR(ADC_vect) {
int result = ADCL | (ADCH << 8);
analogPin += 1;
if (analogPin > A5) {
analogPin = A0;
};
ADMUX = analog_ref | (analogPin & 0x07);
})Но запустив код, будет ждать разочарование — результаты каждый раз будут некорректными. Это связано с тем, в момент считывания преобразованного значения и переключения регистра ADMUX на другой аналоговый вход, АЦП уже начал новый процесс преобразования. Поэтому получается, что результат состоит из начала предыдущего преобразования и конца следующего. Чтобы устранить такое поведение, нужно пропускать первое после переключения входов преобразование — следующий результат будет достоверным: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 volatile int analogPin = A0;
volatile bool trueValue = false;
ISR(ADC_vect) {
if (trueValue) {
int result = ADCL | (ADCH << 8);
analogPin += 1;
if (analogPin > A5) {
analogPin = A0;
};
ADMUX = analog_ref | (analogPin & 0x07);
// Устанавливаем флаг смены входного пина
trueValue = false;
}
else {
// Первый раз пропускаем считывание и устанавливаем флаг на чтение в следующий раз
trueValue = true;
}
}) |
|
41 |
Рассмотрим пример использования режима АЦП Free Runing Mode на следующем примере. Подключим к двум аналоговым входам потенциометр и датчик освещенности, и в зависимости от их значений будем регулировать яркость светодиодов. Схема: |
|
43 |
Предделитель пришлось увеличить до 32 (ADPS[2:0]=101) — при значении предделителя 16 преобразования влияли друг на друга. Скетч: |
|
44 | Arduino (C++) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 int pinLed1 = 6; // Потенциометр
int pinLed2 = 11; // Фоторезистор
uint8_t analog_ref = DEFAULT;
void setup()
{
pinMode(pinLed1, OUTPUT);
pinMode(pinLed2, OUTPUT);
ADC_init(); // Все настройки АЦП вынесены в отдельную процедуру
}
void ADC_init()
{
ADCSRA = 0; // Сбрасываем регистр ADCSRA
ADCSRB = 0; // Сбрасываем регистр ADCSRB
ADMUX |= (1 << REFS0); // Задаем ИОН
//ADMUX |= (1 << ADLAR); // Меняем порядок записи бит, чтобы можно было читать только 8 бит регистра ADCH
// В этом случае считывание данных с АЦП будет выглядеть так:
//int result = ADCH;
analog_ref = ADMUX; // Запоминаем состояние регистра - из него мы будем формировать маску для смены входного пина
ADMUX |= (0 & 0x07); // Выбираем пин A0 для преобразования
// Таким образом отсекаются 2 последних "шумных" бита, результат 8-битный (0...255)
// // Устанавливаем предделитель - 16 (ADPS[2:0]=100)
// ADCSRA |= (1 << ADPS2) ; //Биту ADPS2 присваиваем единицу
// ADCSRA &= ~ (1 << ADPS1) | (1 << ADPS0); //Битам ADPS1 и ADPS0 присваиваем нули
// Устанавливаем предделитель - 32 (ADPS[2:0]=101)
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //Биту ADPS2 присваиваем единицу
ADCSRA &= ~ (1 << ADPS1); //Битам ADPS1 и ADPS0 присваиваем нули
ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
ADCSRA |= (1 << ADIE); // Разрешаем прерывания по завершении преобразования
ADCSRA |= (1 << ADEN); // Включаем АЦП
ADCSRA |= (1 << ADSC); // Запускаем преобразование
}
volatile int currentSensorValue1 = 0;
volatile int currentSensorValue2 = 0;
long oldTime = 0;
void loop() {
if (millis() - 10 > oldTime) {
analogWrite(pinLed1, map(currentSensorValue1, 0, 1023, 0 , 255));
analogWrite(pinLed2, map(currentSensorValue2, 0, 1023, 0 , 255));
oldTime = millis();
}
}
volatile int analogPin = A0;
volatile bool trueValue = false;
ISR(ADC_vect) {
if (trueValue) {
int result = ADCL | (ADCH << 8); // Получаем 10-битный результат
if (analogPin == A0) { // Если актуальный входной пин A0, то присваиваем значение соответствующей переменной
currentSensorValue1 = result;
}
else {
currentSensorValue2 = result;
}
analogPin += 1; // Перебираем входные пины по кругу (А0...А1 - их может быть больше)
if (analogPin > A1) { // Все нужные перебрали...
analogPin = A0; // ...возвращаемся к первому
};
int pin = analogPin; // Приводим пин вида A0 (14) к удобному для регистра ADMUX - 0. A0=14, A1=15, ...
if (pin >= 14) pin -= 14;
ADMUX = analog_ref | (pin & 0x07);// Устанавливаем новый вход для преобразования
trueValue = false; // Устанавливаем флаг смены входного пина - следующее прерывание пропускаем
}
else {
trueValue = true; // Первый раз пропускаем считывание и устанавливаем флаг на чтение в следующий раз
}
} |
|
45 |
Для пренебрежения точностью, а с использованием в analogWrite() большая точность и не нужна, код можно модифицировать следующим образом: |
|
46 | Arduino (C++) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 int pinLed1 = 6; // Потенциометр
int pinLed2 = 11; // Фоторезистор
uint8_t analog_ref = DEFAULT;
void setup()
{
pinMode(pinLed1, OUTPUT);
pinMode(pinLed2, OUTPUT);
ADC_init(); // Все настройки АЦП вынесены в отдельную процедуру
}
void ADC_init()
{
ADCSRA = 0; // Сбрасываем регистр ADCSRA
ADCSRB = 0; // Сбрасываем регистр ADCSRB
ADMUX |= (1 << REFS0); // Задаем ИОН
ADMUX |= (1 << ADLAR); // Меняем порядок записи бит, чтобы можно было читать только 8 бит регистра ADCH
analog_ref = ADMUX; // Запоминаем состояние регистра - из него мы будем формировать маску для смены входного пина
ADMUX |= (0 & 0x07); // Выбираем пин A0 для преобразования
// Устанавливаем предделитель - 32 (ADPS[2:0]=101)
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //Биту ADPS2 присваиваем единицу
ADCSRA &= ~ (1 << ADPS1); //Битам ADPS1 и ADPS0 присваиваем нули
ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
ADCSRA |= (1 << ADIE); // Разрешаем прерывания по завершении преобразования
ADCSRA |= (1 << ADEN); // Включаем АЦП
ADCSRA |= (1 << ADSC); // Запускаем преобразование
}
volatile int currentSensorValue1 = 0;
volatile int currentSensorValue2 = 0;
long oldTime = 0;
void loop() {
if (millis() - 10 > oldTime) {
analogWrite(pinLed1, currentSensorValue1);
analogWrite(pinLed2, currentSensorValue2);
oldTime = millis();
}
}
volatile int analogPin = A0;
volatile bool trueValue = false;
ISR(ADC_vect) {
if (trueValue) {
int result = ADCH; // ADLAR=1, Получаем 8-битный результат, остальными битами пренебрегаем
if (analogPin == A0) { // Если актуальный входной пин A0, то присваиваем значение соответствующей переменной
currentSensorValue1 = result;
}
else {
currentSensorValue2 = result;
}
analogPin += 1; // Перебираем входные пины по кругу (А0...А1 - их может быть больше)
if (analogPin > A1) { // Все нужные перебрали...
analogPin = A0; // ...возвращаемся к первому
};
int pin = analogPin; // Приводим пин вида A0 (14) к удобному для регистра ADMUX - 0. A0=14, A1=15, ...
if (pin >= 14) pin -= 14;
ADMUX = analog_ref | (pin & 0x07);// Устанавливаем новый вход для преобразования
trueValue = false; // Устанавливаем флаг смены входного пина - следующее прерывание пропускаем
}
else {
trueValue = true; // Первый раз пропускаем считывание и устанавливаем флаг на чтение в следующий раз
}
} |
|
47 |
В описанном выше примере функция analogRead() фактически ни разу не вызывается, но в процедуре loop() можно делать все что захочется. При этом производительность от использования АЦП не проседает. |
|
48 |
Бонус В приведенном ниже коде показано, как можно сделать подсветку, плавно подстраивающуюся под изменение внешних условий: |
|
49 | Arduino (C++) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 int pinLed1 = 6; // Потенциометр
int pinLed2 = 11; // Фоторезистор
uint8_t analog_ref = DEFAULT;
void setup()
{
pinMode(pinLed1, OUTPUT);
pinMode(pinLed2, OUTPUT);
ADC_init(); // Все настройки АЦП вынесены в отдельную процедуру
}
void ADC_init()
{
ADCSRA = 0; // Сбрасываем регистр ADCSRA
ADCSRB = 0; // Сбрасываем регистр ADCSRB
ADMUX |= (1 << REFS0); // Задаем ИОН
//ADMUX |= (1 << ADLAR); // Меняем порядок записи бит, чтобы можно было читать только 8 бит регистра ADCH
analog_ref = ADMUX; // Запоминаем состояние регистра - из него мы будем формировать маску для смены входного пина
ADMUX |= (0 & 0x07); // Выбираем пин A0 для преобразования
// Устанавливаем предделитель - 32 (ADPS[2:0]=101)
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //Битам ADPS2 и ADPS0 присваиваем единицу
ADCSRA &= ~ (1 << ADPS1); //Биту ADPS1 присваиваем нули
ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
ADCSRA |= (1 << ADIE); // Разрешаем прерывания по завершении преобразования
ADCSRA |= (1 << ADEN); // Включаем АЦП
ADCSRA |= (1 << ADSC); // Запускаем преобразование
}
int currentLEDValue1 = 0;
int currentLEDValue2 = 0;
volatile int currentSensorValue1 = 0;
volatile int currentSensorValue2 = 0;
long oldTime = 0;
void loop() {
if (millis() - 5 > oldTime) {
if (currentLEDValue1 > currentSensorValue1) {
currentLEDValue1--;
analogWrite(pinLed1, map(currentLEDValue1, 0, 1023, 0 , 255));
}
else if (currentLEDValue1 < currentSensorValue1) {
currentLEDValue1++;
analogWrite(pinLed1, map(currentLEDValue1, 0, 1023, 0 , 255));
}
if (currentLEDValue2 > currentSensorValue2) {
currentLEDValue2--;
analogWrite(pinLed2, map(currentLEDValue2, 0, 1023, 0 , 255));
}
else if (currentLEDValue2 < currentSensorValue2) {
currentLEDValue2++;
analogWrite(pinLed2, map(currentLEDValue2, 0, 1023, 0 , 255));
}
oldTime = millis();
}
}
volatile int analogPin = A0;
volatile bool trueValue = false;
ISR(ADC_vect) {
if (trueValue) {
int result = ADCL | (ADCH << 8); // ADLAR=1, Получаем 8-битный результат, остальными битами пренебрегаем
if (analogPin == A0) { // Если актуальный входной пин A0, то присваиваем значение соответствующей переменной
currentSensorValue1 = result;
}
else {
currentSensorValue2 = result;
}
analogPin += 1; // Перебираем входные пины по кругу (А0...А1 - их может быть больше)
if (analogPin > A1) { // Все нужные перебрали...
analogPin = A0; // ...возвращаемся к первому
};
int pin = analogPin; // Приводим пин вида A0 (14) к удобному для регистра ADMUX - 0. A0=14, A1=15, ...
if (pin >= 14) pin -= 14;
ADMUX = analog_ref | (pin & 0x07);// Устанавливаем новый вход для преобразования
trueValue = false; // Устанавливаем флаг смены входного пина - следующее прерывание пропускаем
}
else {
trueValue = true; // Первый раз пропускаем считывание и устанавливаем флаг на чтение в следующий раз
}
} |
|
50 |
Демонстрация на видео: |
|
51 |
Подписывайтесь на канал
, чтобы быть в курсе обновлений! |
|
52 |
О том как ускорить работу функций digitalRead(), digitalWrite(), shiftOut() и т.д. написано в первой статье цикла, а об ускорении функции analogWrite() в третьей статье. |
|
53 |
Содержание:
|
|
55 |
Похожие запросы:
|
|