#1 2017-04-03 10:20:01

renoshnik
Участник
Зарегистрирован: 2017-04-03
Сообщений: 145

analogRead() — прощай

Не знаю как кого, но меня больше всего раздражала функция 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");
}

  =========    Проведем небольшой тест ...   =========   


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


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

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

Редактировался renoshnik (2017-09-19 08:17:11)

#2 2017-04-03 12:28:49

NoName
Customer
Из Київ
Зарегистрирован: 2014-07-08
Сообщений: 1,269

Re: analogRead() — прощай

+,
у Вас интересный блог )

Редактировался NoName (2017-04-03 12:31:06)

#3 2017-04-03 15:21:34

renoshnik
Участник
Зарегистрирован: 2017-04-03
Сообщений: 145

Re: analogRead() — прощай

NoName пишет:

+,
у Вас интересный блог )

Спасибо ...

#4 2017-05-13 14:03:01

Olehomeole
Гость

Re: analogRead() — прощай

Статья очень актуальна , особенно если надо более лаконично написать программу нежели предлагает ардуино.
Спасибо!

#5 2017-07-31 19:16:17

vosara
Гость

Re: analogRead() — прощай

Очень полезно!!!
Спасибо!

#6 2017-08-28 22:22:35

Roman1984
Участник
Зарегистрирован: 2017-02-17
Сообщений: 33

Re: analogRead() — прощай

Обычно, измерение сигнала с помощью АЦП производят от 8 до 10 раз, а затем выводят средний результат. Если этого не сделать, цифры в рельной (не виртуальной) системе будут прыгать. Функция analogRead так и поступает. Если выполнить измерение 10 раз прямым способом, то время таким и будет, как у analogRead, так что менять шило на мыло нет необходимости. Библиотеки ведь тоже пишут не дураки.

#7 2017-08-29 06:01:46

renoshnik
Участник
Зарегистрирован: 2017-04-03
Сообщений: 145

Re: analogRead() — прощай

Roman1984 пишет:

Функция analogRead так и поступает.

Вы в этом уверены ?

#8 2017-09-18 13:50:10

Nefreemen
Участник
Из Киев
Зарегистрирован: 2015-12-19
Сообщений: 598

Re: analogRead() — прощай

Навеяло smile . Вот как раз когда писал ответ сюда http://forum.arduino.ua/viewtopic.php?id=1248. Возникла мысль о "медлительности" стандартной библиотеки, я конечно библиотеку не смотрел, но возник вопрос к ТС  а не кажется ли Вам что причина ее медлительности в использовании режима ADC Noise Canceler, на который Вы извиняюсь болт положили  smile, отсюда и  Ваша скорость smile .

Редактировался Nefreemen (2017-09-18 13:51:06)

#9 2017-09-18 18:07:15

renoshnik
Участник
Зарегистрирован: 2017-04-03
Сообщений: 145

Re: analogRead() — прощай

Nefreemen пишет:

Навеяло smile . Вот как раз когда писал ответ сюда http://forum.arduino.ua/viewtopic.php?id=1248. Возникла мысль о "медлительности" стандартной библиотеки, я конечно библиотеку не смотрел, но возник вопрос к ТС  а не кажется ли Вам что причина ее медлительности в использовании режима ADC Noise Canceler, на который Вы извиняюсь болт положили  smile, отсюда и  Ваша скорость smile .

А разве analogRead() использует встроенное подавление шумов ?
Мне слабо верится, что analogRead() использует спячку...

Редактировался renoshnik (2017-09-18 18:13:02)

#10 2017-09-18 21:25:00

Nefreemen
Участник
Из Киев
Зарегистрирован: 2015-12-19
Сообщений: 598

Re: analogRead() — прощай

Ну верить или не верить дело Ваше. Ни я ни Вы библиотеку не смотрели. Вопрос в другом, я очень сомневаюсь в том что те кто писал библиотеку не умеют использовать регистры или по крайней мере знают их хуже Вас тем более вряд ли умудрились их использовать "криво" (я кстати не знаю как написать функцию чтения АЦП без использования регистров, а Вы smile ?). Вот открыл свою настольную "библию" -А.В. Евстифеев " Микроконтроллеры  AVR семейств Tiny и Mega фирмы ATMEL" так вот в рекомендациях по повышению точности измерения при использовании АЦП, кроме схемотехнических решений (разводки проводников, увеличения пощади земли аналоговой (если она есть отдельная) под кристаллом использования LC фильтра по питанию  АЦП особо подчеркивается эффективность  именно  использования  ADC Noise Canceler. Я полностью согласен с тем что разработчики плат Arduino тоже положили болт на эти схемотехнические решения. Но если Вы предлагаете другой способ (отказаться от библиотеки) то надо поступать до конца "честно" не взирая на халтуру инженеров Arduino  и следовать рекомендациям той же скажем Atmel и использовать именно  этот режим. Мне если честно лень дописывать Ваш пример, и смотреть  как все изменится  smile . А Вам как автору рацухи как говорится сам Бог велел это сделать и привести результаты  smile .
P.S. Можно кстати провести  лабораторку и исследовать точность измерений АЦП big_smile тем самим "расставить все точки над и". Собрать простой параметрический стабилизатор например на 3.3В (стабилитронов таких множество) заклеить его в пенопласт и мерить АЦП разными способами и если есть возможность то точным вольтметром, было бы интересно roll .

Редактировался Nefreemen (2017-09-18 21:37:51)

#11 2017-09-18 21:25:11

Green
Участник
Зарегистрирован: 2015-11-08
Сообщений: 310

Re: analogRead() — прощай

Не, не, не. У analogRead() проблема только в ADPS. Из за предделитель и низкая скорость преобразования. Заряжайте выше и скорость будет соответствующая. Нужно только не переборщить - учитывать погрешность в зависимости от частоты преобразования.

#12 2017-09-18 21:42:30

Nefreemen
Участник
Из Киев
Зарегистрирован: 2015-12-19
Сообщений: 598

Re: analogRead() — прощай

Green пишет:

Не, не, не. У analogRead() проблема только в ADPS. Из за предделитель и низкая скорость преобразования. Заряжайте выше и скорость будет соответствующая. Нужно только не переборщить - учитывать погрешность в зависимости от частоты преобразования.

А "откудо дровишки"( Ваше категорическое "не") или тоже умозаключения smile . Просто интересно, Вы смотрели внутрь и где это смотреть roll .

#13 2017-09-18 21:51:37

Green
Участник
Зарегистрирован: 2015-11-08
Сообщений: 310

Re: analogRead() — прощай

Там нет никакого ни oversampling-a, ни подавления шумов. Это слишком жирно и слишком было бы всё НА БЛЮДЕЧКЕ для ардуинщиков. Пущая сами изгаляются и напрягаются! Вот здесь как раз и есть поле для творчества.))

#14 2017-09-18 21:54:17

Green
Участник
Зарегистрирован: 2015-11-08
Сообщений: 310

Re: analogRead() — прощай

Nefreemen пишет:

А "откудо дровишки"( Ваше категорическое "не") или тоже умозаключения smile . Просто интересно, Вы смотрели внутрь и где это смотреть roll .

А в чём проблема посмотреть? wiring.c настройка, wiring_analog.c - сами функции.

#15 2017-09-19 08:16:43

renoshnik
Участник
Зарегистрирован: 2017-04-03
Сообщений: 145

Re: analogRead() — прощай

/*
  wiring_analog.c - analog input and output
  Part of Arduino - http://www.arduino.cc/

  Copyright (c) 2005-2006 David A. Mellis

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA

  Modified 28 September 2010 by Mark Sproul
*/

#include "wiring_private.h"
#include "pins_arduino.h"

uint8_t analog_reference = DEFAULT;

void analogReference(uint8_t mode)
{
	// can't actually set the register here because the default setting
	// will connect AVCC and the AREF pin, which would cause a short if
	// there's something connected to AREF.
	analog_reference = mode;
}

int analogRead(uint8_t pin)
{
	uint8_t low, high;

#if defined(analogPinToChannel)
#if defined(__AVR_ATmega32U4__)
	if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#endif
	pin = analogPinToChannel(pin);
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
	if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__)
	if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__)
	if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#else
	if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif

#if defined(ADCSRB) && defined(MUX5)
	// the MUX5 bit of ADCSRB selects whether we're reading from channels
	// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
	ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif
  
	// set the analog reference (high two bits of ADMUX) and select the
	// channel (low 4 bits).  this also sets ADLAR (left-adjust result)
	// to 0 (the default).
#if defined(ADMUX)
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
	ADMUX = (analog_reference << 4) | (pin & 0x07);
#else
	ADMUX = (analog_reference << 6) | (pin & 0x07);
#endif
#endif

	// without a delay, we seem to read from the wrong channel
	//delay(1);

#if defined(ADCSRA) && defined(ADCL)
	// start the conversion
	sbi(ADCSRA, ADSC);

	// ADSC is cleared when the conversion finishes
	while (bit_is_set(ADCSRA, ADSC));

	// we have to read ADCL first; doing so locks both ADCL
	// and ADCH until ADCH is read.  reading ADCL second would
	// cause the results of each conversion to be discarded,
	// as ADCL and ADCH would be locked when it completed.
	low  = ADCL;
	high = ADCH;
#else
	// we dont have an ADC, return 0
	low  = 0;
	high = 0;
#endif

	// combine the two bytes
	return (high << 8) | low;
}

// Right now, PWM output only works on the pins with
// hardware support.  These are defined in the appropriate
// pins_*.c file.  For the rest of the pins, we default
// to digital output.
void analogWrite(uint8_t pin, int val)
{
	// We need to make sure the PWM output is enabled for those pins
	// that support it, as we turn it off when digitally reading or
	// writing with them.  Also, make sure the pin is in output mode
	// for consistenty with Wiring, which doesn't require a pinMode
	// call for the analog output pins.
	pinMode(pin, OUTPUT);
	if (val == 0)
	{
		digitalWrite(pin, LOW);
	}
	else if (val == 255)
	{
		digitalWrite(pin, HIGH);
	}
	else
	{
		switch(digitalPinToTimer(pin))
		{
			// XXX fix needed for atmega8
			#if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__)
			case TIMER0A:
				// connect pwm to pin on timer 0
				sbi(TCCR0, COM00);
				OCR0 = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR0A) && defined(COM0A1)
			case TIMER0A:
				// connect pwm to pin on timer 0, channel A
				sbi(TCCR0A, COM0A1);
				OCR0A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR0A) && defined(COM0B1)
			case TIMER0B:
				// connect pwm to pin on timer 0, channel B
				sbi(TCCR0A, COM0B1);
				OCR0B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR1A) && defined(COM1A1)
			case TIMER1A:
				// connect pwm to pin on timer 1, channel A
				sbi(TCCR1A, COM1A1);
				OCR1A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR1A) && defined(COM1B1)
			case TIMER1B:
				// connect pwm to pin on timer 1, channel B
				sbi(TCCR1A, COM1B1);
				OCR1B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR1A) && defined(COM1C1)
			case TIMER1C:
				// connect pwm to pin on timer 1, channel B
				sbi(TCCR1A, COM1C1);
				OCR1C = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR2) && defined(COM21)
			case TIMER2:
				// connect pwm to pin on timer 2
				sbi(TCCR2, COM21);
				OCR2 = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR2A) && defined(COM2A1)
			case TIMER2A:
				// connect pwm to pin on timer 2, channel A
				sbi(TCCR2A, COM2A1);
				OCR2A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR2A) && defined(COM2B1)
			case TIMER2B:
				// connect pwm to pin on timer 2, channel B
				sbi(TCCR2A, COM2B1);
				OCR2B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR3A) && defined(COM3A1)
			case TIMER3A:
				// connect pwm to pin on timer 3, channel A
				sbi(TCCR3A, COM3A1);
				OCR3A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR3A) && defined(COM3B1)
			case TIMER3B:
				// connect pwm to pin on timer 3, channel B
				sbi(TCCR3A, COM3B1);
				OCR3B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR3A) && defined(COM3C1)
			case TIMER3C:
				// connect pwm to pin on timer 3, channel C
				sbi(TCCR3A, COM3C1);
				OCR3C = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR4A)
			case TIMER4A:
				//connect pwm to pin on timer 4, channel A
				sbi(TCCR4A, COM4A1);
				#if defined(COM4A0)		// only used on 32U4
				cbi(TCCR4A, COM4A0);
				#endif
				OCR4A = val;	// set pwm duty
				break;
			#endif
			
			#if defined(TCCR4A) && defined(COM4B1)
			case TIMER4B:
				// connect pwm to pin on timer 4, channel B
				sbi(TCCR4A, COM4B1);
				OCR4B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR4A) && defined(COM4C1)
			case TIMER4C:
				// connect pwm to pin on timer 4, channel C
				sbi(TCCR4A, COM4C1);
				OCR4C = val; // set pwm duty
				break;
			#endif
				
			#if defined(TCCR4C) && defined(COM4D1)
			case TIMER4D:				
				// connect pwm to pin on timer 4, channel D
				sbi(TCCR4C, COM4D1);
				#if defined(COM4D0)		// only used on 32U4
				cbi(TCCR4C, COM4D0);
				#endif
				OCR4D = val;	// set pwm duty
				break;
			#endif

							
			#if defined(TCCR5A) && defined(COM5A1)
			case TIMER5A:
				// connect pwm to pin on timer 5, channel A
				sbi(TCCR5A, COM5A1);
				OCR5A = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR5A) && defined(COM5B1)
			case TIMER5B:
				// connect pwm to pin on timer 5, channel B
				sbi(TCCR5A, COM5B1);
				OCR5B = val; // set pwm duty
				break;
			#endif

			#if defined(TCCR5A) && defined(COM5C1)
			case TIMER5C:
				// connect pwm to pin on timer 5, channel C
				sbi(TCCR5A, COM5C1);
				OCR5C = val; // set pwm duty
				break;
			#endif

			case NOT_ON_TIMER:
			default:
				if (val < 128) {
					digitalWrite(pin, LOW);
				} else {
					digitalWrite(pin, HIGH);
				}
		}
	}
}

Редактировался renoshnik (2017-09-19 08:17:26)

#16 2017-09-19 10:53:25

Вячеслав Азаров
Участник
Из Запорожье
Зарегистрирован: 2017-05-25
Сообщений: 565

Re: analogRead() — прощай

Ардуино, Атмел, Мега - просто альфа и омега
Я так больше не могу, наверно в Бостон убегу!

Редактировался Вячеслав Азаров (2017-09-19 10:54:08)

#17 2017-09-20 23:11:05

Nefreemen
Участник
Из Киев
Зарегистрирован: 2015-12-19
Сообщений: 598

Re: analogRead() — прощай

renoshnik, спасибо, убедил. Наверное и я теперь буду говорить "прощай" lol . Разве еще допишу ADC Noise и как то немножко по другому назову функцию smile .

#18 2017-09-21 07:10:39

Вячеслав Азаров
Участник
Из Запорожье
Зарегистрирован: 2017-05-25
Сообщений: 565

Re: analogRead() — прощай

Nefreemen пишет:

renoshnik, спасибо, убедил. Наверное и я теперь буду говорить "прощай" lol . Разве еще допишу ADC Noise и как то немножко по другому назову функцию smile .

Позвольте встрять. На точность измерений влияют не только внутренние шумы цифровых схем контрооллера но и котструктивное исполнение срединений, выходное сопротивление источника, спектральный состав сигнала, температура и непредсказуемые влияния. Что бы получить абсолютную погрешность на уровне дифференциальной нелинейности АЦП (около 0.25 LSB, это 268 мкВ) нужно очень постараться да и детали аналоговой и цифровой части должны быть высококачественными (блокировочные конденсаторы, конденсаторы входного фильтра, резисторы делителей). Просто "усыпляя" процессор на время измерения можно не получить желаемый результат, хотя он будет, конечно, лучше. Вы можете изучить эту проблему руководствуясь рекомендациями бесплатно предоставленными STMicroelectronucs. Хотя речь идет о контроллерах STM32, это касается всех аналогово - цифровых SoC, включая Атмеловские AVR.
http://www.st.com/content/ccc/resource/ … 211314.pdf

Желаю успехов.

Редактировался Вячеслав Азаров (2017-09-21 07:19:41)

#19 2017-09-21 18:01:22

Nefreemen
Участник
Из Киев
Зарегистрирован: 2015-12-19
Сообщений: 598

Re: analogRead() — прощай

Вячеслав, я тоже упоминал о схемотехнических рекомендациях от той же Atmel. Но дело в том что речь идет о Arduino а не о самостоятельно изготовленном устройстве. Конечно если делать самому платку то ее можно "заточить" под тот же АЦП. А так имеем что имеем, а "инженеры" плюнули  на все, в некоторых платках даже емкости нет в цепи AVCC не говоря о дросселе. Ну и как тут показал renoshnik  еще и стандартная библиотека г..но  smile .

#20 2017-09-21 18:04:07

renoshnik
Участник
Зарегистрирован: 2017-04-03
Сообщений: 145

Re: analogRead() — прощай

Nefreemen пишет:

renoshnik, спасибо, убедил. Наверное и я теперь буду говорить "прощай" lol . Разве еще допишу ADC Noise и как то немножко по другому назову функцию smile .

smile  я как-то оверсемплингом ограничился, вполне достаточно "для дома, для семьи"...

#21 2017-12-06 17:14:45

Vlad17
Гость

Re: analogRead() — прощай

А ни кого не смущает, что ТС в модифицированном скетче мягко говоря не время преобразования замерил?

#22 2017-12-06 19:37:19

Green
Участник
Зарегистрирован: 2015-11-08
Сообщений: 310

Re: analogRead() — прощай

Ох и дурят нашего брата!)) Хоть один бдительный гражданин попался.))
Значит не особо то и надо, коль никто исходники не смотрит.((

#23 2017-12-06 23:06:44

renoshnik
Участник
Зарегистрирован: 2017-04-03
Сообщений: 145

Re: analogRead() — прощай

Vlad17 пишет:

А ни кого не смущает, что ТС в модифицированном скетче мягко говоря не время преобразования замерил?

а, что замерял ?

#24 2017-12-07 10:08:53

Vlad17
Гость

Re: analogRead() — прощай

Я так понимаю, что замеряете время на операцию по расчету переменной V, ну и может иногда вычитка из регистра в data попадает в тайминг (кстати, переменная data должна быть объявлена с квалификатором volatile).

На самом деле, на сколько помню, analogRead использует самый большой предделитель 16Mhz/128 => 1/0.125=8мкс на такт ADC, на один замер тратится 13 тактов минимум (минимум - т.к. на первый замер, например, тратится 25 тактов, и еще какие-то настройки там есть). Итого 104мкс минимум на замер + вычитать результат из регистров + преобразовать, итого и получаем те самые 124-128мкс

У вас с предделителем на 32 будет 2мкс на такт и минимум 26мкс на замер + накладные расходы (то, что вы замерили). Только вот точность уже будет не 10bit. Это если одинаковую методику замера скорости делать.

Другой вопрос, что пока проводится преобразование, контроллер может своими делами заниматься, а в analodRead он тупо ждет выставление флага. Вот и вся магия ускорения analogRead

#25 2017-12-07 11:54:43

Вячеслав Азаров
Участник
Из Запорожье
Зарегистрирован: 2017-05-25
Сообщений: 565

Re: analogRead() — прощай

Vlad17 пишет:

Я так понимаю, что замеряете время на операцию по расчету переменной V, ну и может иногда вычитка из регистра в data попадает в тайминг (кстати, переменная data должна быть объявлена с квалификатором volatile).

На самом деле, на сколько помню, analogRead использует самый большой предделитель 16Mhz/128 => 1/0.125=8мкс на такт ADC, на один замер тратится 13 тактов минимум (минимум - т.к. на первый замер, например, тратится 25 тактов, и еще какие-то настройки там есть). Итого 104мкс минимум на замер + вычитать результат из регистров + преобразовать, итого и получаем те самые 124-128мкс

У вас с предделителем на 32 будет 2мкс на такт и минимум 26мкс на замер + накладные расходы (то, что вы замерили). Только вот точность уже будет не 10bit. Это если одинаковую методику замера скорости делать.

Другой вопрос, что пока проводится преобразование, контроллер может своими делами заниматься, а в analodRead он тупо ждет выставление флага. Вот и вся магия ускорения analogRead

Простите, но програмпмное обеспечение Ардуино везде и всегда тупо ждет и на это транится большая часть вычислительного ресурса. И изменить эту ситуацию невозможно без полной переработки "стандартного" программного обеспечения.

Быстрое сообщение

Введите сообщение и нажмите Отправить

Подвал раздела