Как использовать диффиринциальный вход на ардуино?

Слышал, что на ардуино есть дифференциальный вход, который можен усиливать распознование сигнала в 100 раз. Но незнаю, как его использовать? Хочу его использовать для снятия показаний напряжения с ик-диода.

Давайте разберемся и с этим вопросом.

Если говорить про AVR серии Atmega, то вот список МК по группам, внутри которых есть операционный усилитель (ОУ) с программируемым коэффициентом усиления (PGA):  
линейка attinyx5 - коэффициент усиления 1х и 20х  
линейка attinyx4 - коэффициент усиления 1х и 20х  
линейка attinyx41 - коэффициент усиления 1х, 20х и 100х!  
линейка attinyx61 - 1x, 8x, 20x, 32x

x в названии МК может быть 2, 4 или 8 в зависимости от флеш памяти на борту. Вполне возможно, что это не полный список и где-то среди стареньких Atmega есть подобные.

Как видим, коэффициент усиления дискретен и предопределен заранее. Коэффициент усиления в 100 раз присутствует только у attiny441 и 841. Про них и буду рассказывать. Но сперва немного о том, что такое дифференциальные входы. Дифференциальных входов может быть несколько, но чаще, как и в нашем случае, их всего 2: неинвертирующий вход и инвертирующий вход. Дифференциальные входы усилителя характерны тем, что усиливается не абсолютная амплитуда сигнала, а разность амплитуд сигналов на этих входах. Например, на одном входе 3 вольта и на другом входе тоже 3 вольта. Тогда на выходе усилителя будет ноль вольт вне зависимости от его усиления. Что касается рассматриваемого нами МК, то на дифференциальные входы можно подавать любые напряжения, которые не превышают напряжения питания и ноль. Какие это даёт преимущества? К примеру, датчик Холла с аналоговым выходом - на сигнальном выводе в состоянии покоя присутствует ровно половина напряжения питания. Что делать в случае обычного (униполярного) входа АЦП? Либо ставить внешний ОУ и на нем смещать напряжение к нулю, но тогда мы потеряем отрицательную составляющую сигнала с датчика. Другой вариант - подать на вход АЦП сигнал как есть и программно высчитать напряжение покоя. А что делать, если амплитуда сигнала не так и велика, а опорное напряжение для АЦП мы вынуждены брать с шины питания? Правильно - теряем разрешение. И вот тут-то как раз нам и пригодится АЦП с дифференциальным входами. На инвертирующий вход через резистивный делитель подаем половину напряжения, а на неинвертирующий вход - сигнал с датчика Холла. В состоянии покоя напряжения вычтутся и на вход АЦП поступит ноль вольт. Как только датчик Холла окажется в магнитном поле, разница напряжений изменится и по её амплитуде мы сможем вычислить величину поля, а по знаку полярность. 

Если случай более распространенный и сигнал у нас униполярный, то инвертирующий вход достаточно посадить на ноль. Ну а дальше всё как обычно. 

АЦП в таких МК может работать в двух режимах - в униполярном и в биполярном. В униполярном всё как обычно: АЦП выдаёт значения от 0 и до 1023. В биполярном режиме АЦП выдаёт значения в дополнительном коде от -511 и до 512. Чем хорош биполярный режим? По значению с АЦП мы всегда знаем, на каком входе сигнал больше. Если сигналы равны, то АЦП выдаст ноль (в униполярном режиме 512).

Теперь я покажу, как программировать АЦП таких МК. МК возьму Attiny441/841.

Напишем функцию инициализации:

```
void ADC_Init(void)
{
	DIDR0 = (1<<ADC1D) | (1<<ADC2D);				// Отключаем триггер Шмитта от сигнальной ноги
	
	ADMUXB = (0<<REFS2) | (0<<REFS1) | (1<<REFS0) | (0<<GSEL1) | (1<<GSEL0);	// Внутренний ИОН 1.1в, усиление = 100

	ADCSRA = (1<<ADEN) | (1<<ADIE) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // Прескалер = 8
}
```
В первой строчке

```
DIDR0 = (1<<ADC1D) | (1<<ADC2D);
```
мы отключаем выбранные нами дифференциальные входы (о них чуть ниже) от внутренних входов триггеров Шмитта, чтобы они не вносили искажений в результат измерений. Триггер Шмитта - это тот триггер, который стоит между физическим выводом МК (который торчит наружу) и регистром PIN. Служит он для повышения помехоустойчивости цифровых сигналов. Но в нашем случае он только будет мешать, когда уровень входного сигнала будет достигать границы переключения этого триггера. В следующий строчке 

```
ADMUXB = (0<<REFS2) | (0<<REFS1) | (1<<REFS0) | (0<<GSEL1) | (1<<GSEL0);
```
мы первыми тремя битами определяем источник опорного напряжения (ИОН): в нашем случае это внутренний ИОН напряжением 1.1 вольт. Но можно выбрать и другие - это ИОН 2.2 вольта, 4.096 вольт, напряжение питания (Vcc), либо же можем к пину AREF подключить внешний ИОН с любым необходимым напряжением. Следующими двумя битами мы задаем коэффициент усиления - 100 раз.

В последней строчке

```
ADCSRA = (1<<ADEN) | (1<<ADIE) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
```
первым битом разрешаем работу АЦП, затем разрешаем прерывание по готовности результата измерения и последние три бита определяют коэффициент делителя тактовой частоты для АЦП. Я предполагаю, что наш МК тактируется осциллятором на 8МГц и делитель здесь выбран 8. Поэтому на АЦП поступает тактовая частота 1 МГц. Это предельная частота для АЦП. Выше крайне не рекомендуется (хотя АЦП работоспособен и на 2 Мгц, но результат будет сильно искажен), для более точных измерений даташит не рекомендует подниматься выше 200 кГц.

Как запустить преобразование? Ведь мы только проинициализировали АЦП, но от этого АЦП не запустится. Прежде всего, необходимо выбрать режим запуска. Это может быть ручной режим, а можно заставить наш АЦП запускаться по таймеру. Тогда мы получим результаты преобразования через равные промежутки времени. В первом случае мы ничего писать не будем - ручной запуск стоит по умолчанию. Для запуска АЦП по сигналу от таймера в функцию инициализации необходимо дописать инициализацию такого регистра:

```
ADCSRB = (1<<ADTS1) | (1<<ADTS0);
```
и переписать этот регистр:

```
ADCSRA = (1<<ADEN) | (1<<ADIE) | (1<<ADATE) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
```
бит ADATE разрешает автоматический запуск АЦП от выбранного сигнала. В нашем случае это компаратор 0А таймера0.

Какой бы мы режим запуска не выбрали, первый запуск АЦП должен быть ручным. Запишем функцию запуска:

```
void ADC_Start_Conversion (uint8_t Analog_input)
{
	ADMUXA = Analog_input;

	ADCSRA |= Bit(ADSC);
}
```
В регистр ADMUXA записываем код выбора дифференциальных входов, бит ADSC запускает преобразование. По окончании преобразования этот бит автоматически падает в ноль. Если мы выбрали ручной запуск, то этот бит надо снова взвести (записать 1), ежели запуск от таймера, то ничего не надо делать - далее система работает в автоматическом режиме. Нам же надо отследить готовность результата. Для этого мы разрешили прерывания от АЦП. Обработчик прерывания выглядит так:

```
ISR(ADC_vect)
{
	ADC_RES  = ADC_Get_Data();
}
```
где ADC\_RES - заранее определённая двухбайтная переменная с квалификатором volatile:

А функция взятия результата выглядит так:

```
uint16_t ADC_Get_Data (void)
{
	return ADC;
}
```
Теперь где-то в главной функции мы запишем:

```
	ADC_Init();	
	
	ADC_Start_Conversion(ADC_diff);
```
ADC\_diff - это определение дифференциальной пары входов.Я выбрал входы ADC1 и ADC2. Они расположены на ногах A1 и A2 соответственно (A - это порт A). Открываем даташит и смотрим, что нам надо записать вот такое число, чтобы внутренний мультиплексор аналоговых входов выбрал наши ADC1 и ADC2:

```
#define ADC_diff			0b010010
```
Теперь о недостатках такого решения.

1. Даташит уверяет, что дифференциальные входы имеют частотный диапазон до 4 кГц. Но я оцифровывал и 8 кГц - заметного провала на АЧХ я не заметил

2. Смещение нуля у ОУ. Оно очень велико, что сказывается на точности измерений. 

3. Усиление в 100 раз - это очень много! Здесь я не показал, потому как и так написано много, но чтобы производить максимально точные замеры, на момент работы АЦП МК необходимо останавливать. Также лучше не обольщаться тем, что АЦП 10-ти битный. Лучше его перевести в 8-ми битный режим и выше усиления 20 (32) не подниматься. Вот результат оцифровки с датчика Холла с усилением 100 раз - всё изрядно шумит:

![](http://forum.amperka.ru/attachments/hall-png.15010/)

На этом я закончу - и так получилось много. Хотя про АЦП можно много чего ещё рассказать)

Добавлено:

Дифференциальные входы также есть у ATMega32 (16). У этих МК, а также у Attiny441(841) в биполярный режим АЦП переходит автоматически, как только мы выбрали дифференциальную пару входов. У Attiny25 (45, 85) и у Attiny24 (44, 84) есть бит BIN в регистре ADCSRA, установив который в единицу, мы включим биполярный режим АЦП.