29 июня 2012 г.

Превращаем аналоговые входы Arduino в цифровые

Кому-то может не хватить тринадцати цифровых входов Arduino, и для таких людей сегодня я расскажу, как превратить шесть аналоговых входов в цифровые. Нам опять потребуется немного низкоуровневой магии, но пусть вас это не пугает.

PORTC

Пока АЦП не работает, доступны другие функции ног процессора, занятых под преобразование. 


Смотрим на картинку и видим, что аналоговые входы 0...5 находятся на ногах 23...28. Однако основная функция этих ног - PC0...5. 

PC означает PORTC, или по-русски "Порт Цэ". Порт - это устройство ввода-вывода, где каждый бит может вводиться или выводиться отдельно, а может и в составе байта. У процессора ATmega328 три семибитных порта: B, C и D. Каждый пин порта управляется тремя битами в трех регистрах: DDxn, PORTxn, PINxn. Нам сейчас не интересны порты B и D, поэтому поглядим на регистры порта C. 

DDRC - регистр направления данных порта C
7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
DDRC-DDC6DDC5DDC4DDC3DDC2DDC1DDC0 
Каждый бит в этом регистре отвечает за направление данных на соответствующем пине:
0 - вход,
1 - выход. 
Значение по умолчанию - 0.

PORTC - регистр данных порта C
7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
PORTC-PORTC6PORTC5PORTC4PORTC3PORTC2PORTC1PORTC0 
Если пин сконфигурирован как вход, то при записи 1 в соответствующий бит этого регистра активируется подтягивающий резистор. При записи 0 подтягивающий резистор отключается. 
Если пин сконфигурирован как выход, то при записи 1 в соответствующий бит этого регистра пин подтягивается к питанию. При записи 0 пин притягивается к земле. 
Значение по умолчанию - 0.

PINC - Адреса входов порта C
7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
PINC-PINC6PINC5PINC4PINC3PINC2PINC1PINC0 
Каждый бит этого регистра содержит значение соответствующего пина. Прочитать значение можно независимо от значения DDxn.
Значение по умолчанию - не определено. 

Blink, 1 способ
Напишем всем известную программу Blink для аналогового входа (гы-гы). Подключим светодиодик к аналоговому входу 0:



Для начала назначим бывший аналоговый вход A0 как цифровой выход. Для этого запишем единицу в бит DDC0 регистра DDRC:

void setup(){
  DDRC = (1 << DDC0);
}
Чтобы включать и выключать светодиод, будем записывать то 1, то 0 в бит PORTC0 регистра PORTC:


void loop(){
  PORTC = PORTC | (1 << PORTC0);
  delay(1000);
  PORTC = PORTC ^ (1 << PORTC0);
  delay(1000);
}
Всё!




Blink, способ 2


Есть более читерский способ, который сокращает программу еще на 2 строчки. В гайде ATmega328 пишут, что запись единицы в PINxn переключает значение PORTxn, независимо от значения DDRxn. Ну то есть если там был 0, то становится 1, а если был 1, то становится 0. За сим накалякаем следующий код:


void setup(){
  DDRC = (1 << DDC0);
}


void loop(){
  PINC = PINC | (1 << PINC0);
  delay(1000);
}
Вот и весь код. Работает прямо так же, как на предыдущем видео. Можно было бы еще избавиться от строчки с DDRC=(1<<DDC), но в этом случае светодиод будет гореть очень слабо, поскольку пин не подтягивается к питанию (но все равно светодиод моргает).


Более продвинутая установка


Чтобы было весело, займем все 6 аналоговых входов. На 0-4 повесим светодиоды, а на 5 - кнопку:




Теперь назначим пины PC0...4 как выходы, а PC5 как вход. Помня, что выход - это 1 в DDCn, а вход - это 0, можно написать строчку, которая все сделает, так:


DDRC = B00011111;
Для того, чтобы сделать что-то по нажатию кнопки, надо сдетектировать это самое нажатие. Ждем единицу на PINC5, и по ней запускаем бегущие огни - записываем по очереди единицы в PORTCn, а потом записываем нули точно так же. Полный код:

void setup(){
  DDRC = B00011111;
}

void loop(){
  if (PINC & (1 << PINC5)){
    for (int i=0;i<5;i++){
      PORTC = PORTC | (1 << i);
      delay(100);
    }
    for (int i=0;i<5;i++){
      PORTC = PORTC ^ (1 << i);
      delay(100);
    }
  }
}
Программа в основном цикле содержит сколько же строк, сколько было бы при использовании стандартных цифровых выходов и конструкций digitalWrite(). Поэтому единственная сложность возникает лишь в ненаглядности функций.