25.05.2010

LiquidCrystalRus для 0018

В развитие темы с LCD...

Выражаю сердечную благодарность пользователю a-monstr за внимание к моему решению по русификации, и специально для него и других желающих заставить дисплеи разговаривать по-русски - новая версия LiquidCrystalRus для ArduinoIDE 0018, сюда же вошли изменения для WH1601 из предыдущего поста.  Но в отличие от предыдущего варианта, этот не заменяет, а сосуществует с оригинальным LiquidCrystal. Для установки создайте каталог libraries в своем каталоге со скетчами и распакуйте туда содержимое архива.

Кстати - может быть сделать также и украинизированную версию?

23.05.2010

Однострочный LCD в двухстрочном режиме

С помощью библиотеки LiquidCrystal, входящей в стандартный комплект ArduinoIDE 0018, можно выводить алфавитно-цифровую информацию на LCD-дисплеи, совместимые с контроллером HD44780U. В Россию, например, импортируется большое количество дисплеев WinStar - они доступны по цене и неприхотливы в использовании.

Однако, существует пара досадных проблем, среди которых отсутствие поддержки русских букв (вроде как решена) и плохая работа с однострочными дисплеями - именно про нее пойдет повествование ;)

Дело в том, что у HD44780U максимальный размер алфавитно-цифрового дисплея составляет 80 знакомест. И хотя производители изготавливают дисплеи на любой вкус (2 строки по 20 символов, 1 строка по 16, 2 по 8 и т.д.), контроллер всегда оперирует памятью DDRAM размером в 80 байт. А вот по поводу  того, сколько на самом деле физический дисплей имеет строк и колонок, должен заботиться программист: например, записывать символы только в те ячейки памяти, за которыми закреплены физические знакоместа в ЖКИ.

Всё это было мне невдомек, пока однажды я не познакомился с дисплеем WH1601A-NGG-CT, 16 знакомест в один ряд:



У него есть небольшая особенность - логически он представляет собой двухстрочный дисплей по восемь символов в строке. На это красноречиво указывает документация, просто надо внимательно читать перед покупкой:



Таким образом, интуитивно правильно написанный скетч (вариация на тему примера "HelloWorld" к библиотеке LiquidCrystal):

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 1);
  // Print a message to the LCD.
  lcd.print("hello, world!");
}

void loop() {
}


Дает вот такой огорчающий результат:



Как я уже упоминал, дисплей логически работает в двухстрочном режиме, поэтому для отображения второй половины надо обязательно вызывать конструктор как lcd.begin(16, 2), в противном случае мы вообще никогда ничего во второй половине не увидим. Зато можно разбивать все строки попалам и перед отображением второй половины ставить lcd.setCursor(0, 1), хотя, на мой взгляд - это насилие над личностью программиста. Про что-то подобное возмущался пользователь abend в форуме arduino, его направили читать документацию - и, видимо, он до сих пор ее читает.

Так как же быть? Если бы это был логически однострочный дисплей, то вывод в ячейки памяти DDRAM с адресами 0x0 - 0x0f отобразился бы на экране. У нас же диапазон прерывается - сначала от 0x0 до 0x7, а далее - от 0x40 до 0x47. Всё, что потребуется - "подменить" обращения к адресам 0x08-0x0f на 0x40 - 0x47, но так, чтобы основная программа ни о чем не догадалась. Неприятный момент заключается в том, что адресный регистр работает в режиме автоинкремента и придется перед каждым выводом в память проверять, не попали ли мы в запрещенный диапазон 0x08-0x0f - иначе говоря, читать содержимое адресного регистра.

С точки зрения схемы, потребуется подключить пин RW к какому-то из свободных цифровых выходов Arduino, потому что именно он управляет направлением передачи данных по шине. Если раньше можно было спокойно вешать его на GND (всегда вывод в дисплей), теперь про такую экономию придется забыть.

Библиотечное API расширено функцией setDRAMModel(uint8_t), которой надо передать в качестве аргумента константу режима трансляции - в данном случае - LCD_DRAM_WH1601, после чего результат команды print будет уже гораздо более ожидаемым:

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 10, 11, 5, 4, 3, 2);

void setup() {
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  // set translation mode
  lcd.setDRAMModel(LCD_DRAM_WH1601);
  // Print a message to the LCD.
  lcd.print("hello, world! :)");
}

void loop() {
}




В остальном - функциональность в точности соответствует оригинальной библиотеке. Кстати, там есть чего оптимизировать и я этим воспользовался, чтобы прирост размера был не очень заметен.

Файл-архив LiquidCrystal-WH1601-0018.zip надо распаковать в arduino-0018/libraries/, желательно предварительно сохранив старый каталог LiquidCrystal - на всякий случай.

19.05.2010

Удвоение пинов Arduino (3)

Часть 3. Использование PCF8574

Уважаемые читатели смогли меня переубедить, что тема расширения пинов Arduino далеко не исчерпана. Поэтому - возвращаемся. Начало см. в Части 2 и Части 1.

Итак - практически все стандартные и недорогие логические микросхемы мы перебрали, но ведь есть и специализированные. Это значит - такие, которые функционально служат для увеличения количества выводов DIO - Digital Input/Output.

Что такое PCF8574?



В двух словах - это 8 дополнительных линий DIO по I2C - шина довольно распространенная и  поддерживаемая многими современными контроллерами. В ATmega поддержка I2C именуется Two Wire Interface или TWI - в пределах простых задач её можно считать аналогом I2C. В Arduino существует библиотека Wire, которая позволяет ему работать по I2C как в режиме Master, так и Slave.

Расплатившись двумя пинами analog4 и analog5 (именно на них у ATmega8/168/328 живет аппаратная поддержка TWI), мы получаем возможность подключить к ним от одной до шестнадцати микросхем PCF8574 параллельно, что означает не более 128 цифровых DIO дополнительно.

Схема пина наглядно представлена в документации:


Сигнал "data from shift register" поступает на привычный уже триггер-защелку (latch), который хранит выведенное однажды значение. Его выход через инвертор поступает на пару полевых транзисторов, которые стоят "на распутье" у внешнего входа пина "P0-P7". В результате вход оказывается либо притянут к Vcc, либо замкнут на GND (логическая единица и ноль соответственно).

Теперь надо обратить внимание на второй триггер - его D-вход соединен с ответвлением, служащим для получения входного сигнала (находится между все теми же полевыми транзисторами). Если приглядеться внимательнее, станет очевидно, что получить на входе единицу или ноль можно только если пин притянут к единице, в противном случае входной сигнал победно уходит на землю.

Из всего этого следует простое правило: для работы в режиме входа надо записать логическую единицу в соответствующий разряд (для сравнения - в Arduino надо использовать оператор pinMode, а в avrgcc - записать нужное значение в соответствующий DDR-регистр).

Внутренне микросхема устроена так:


В кубике "IO Port" располагается восемь одинаковых схем, рассмотренных выше.

Shift Register накапливает данные по мере поступления по последовательной шине I2C (в чем-то это напоминает микросхему 595), а сигнал прерывания по изменению состояния входа INT собирается воедино в одну линию со всех разрядов.

Шина I2C представлена в виде линий SDA и SCL, а также адресных входов A0, A1 и A2.

Раньше микросхемы снабжались специальным входом CS (Crystal Select) - низкий уровень на нем соответствовал "выбору кристалла", позволяя таким образом главному устройству (как правило, микроконтроллеру или микропроцессору) определять, с какой именно микросхемой будет происходить обмен в данный конкретный момент времени. Часто это называлось "адресация", а для формирования CS обычно служили дешифраторы.

На шине I2C дешифратор должен находиться внутри чипа, потому для адресации устройства используются передаваемые по этой же шине данные - первым в посылке идет байт адреса (точнее, 7-битный адрес плюс бит операции - чтение или запись). Вот так он должен выглядеть для PCF8574:


Если одной микросхемы на шине мало, можно подключить несколько, но каждой должен быть заранее присвоен свой адрес через входы A0-A2, как правило - путем банального притягивания к питанию и земле (единице и нулю). Тогда, приняв первый байт адреса, микросхема сравнивает его с текущим состоянием A0-A2 и легко определяет, с ним ли будет сейчас разговаривать master шины I2C. Поскольку внешних линий адреса - всего три, это дает  2 ^ 3 = 8 разных адресов. Если же требуется больше восьми микросхем на одной шине, надо закупаться микросхемой с буквой A (справа), они идентичны во всем, кроме начала адреса - 0111 вместо 0100.

Попробуем подключить одну микросхему к Arduino:


Традиционных подтягивающих резисторов для кнопок в данном случае не требуется - уже и так подтянуто (см. схему пина выше).

На макетке это выглядит так (резисторы я упразднил, поскольку и без них все работает замечательно):


Теперь напишем скетч средней сложности, который будет считывать состояние двух кнопок и управлять светодиодами - пусть один светодиод будет гаснуть и зажигаться синхронно с нажатием и отпусканием одной кнопки, а другой - мерцать при нажатии на вторую:

#include <Wire.h>

#define PCF8574  0x20
#define PCF8574A 0x38
#define A0  0
#define A1  0
#define A2  0
#define DIOADDR  (PCF8574A|A0|(A1<<1)|(A2<<2))

#define LED1 B11011111
#define LED2 B01111111
#define BTN1 B00000010
#define BTN2 B00000100

boolean L1 = true;
boolean L2 = true;

void setup() {
  Wire.begin();
  Wire.beginTransmission(DIOADDR);
  Wire.send(0xff & LED1 & LED2);
  Wire.endTransmission();
}

void loop() {
  Wire.requestFrom(DIOADDR,1);
  while (!Wire.available()) delay(30);
  
  byte dio_in = Wire.receive();
  byte dio_out = 0xff;

  L1 = dio_in & BTN1;
  
  if (L1) {
    dio_out |= ~LED1;
  } else {
    dio_out &= LED1;
  }
  
  if (!(dio_in & BTN2)) L2 = !L2;
   else L2 = false;
  
  if (L2) {
    dio_out &= LED2;
  } else {
    dio_out |= ~LED2;
  }
   
  dio_out |= (BTN1|BTN2);
  
  Wire.beginTransmission(DIOADDR);
  Wire.send(dio_out);
  Wire.endTransmission();
  
  delay(30);  
}




Адресные пины A0-A2 брошены на GND, что соответствует адресу 0x38 на шине I2C в случае применения микросхемы с буквой 'A' или же - 0x20, если микросхема без буквы. Вы можете всё поменять, исправив соответствующие #define в начале скетча.

LED1, LED2 - это битовые маски светодиодов. Обратите внимание - на схеме они притянуты к +5В, поэтому, чтобы они загорелись, надо создать разность потенциалов - сформировать на выходе логический 0. Переменные L1 и L2 хранят текущее состояние светодиодов, BTN1, BTN2 - битовые маски кнопок.

Важно понимать одну деталь: записываемый байт запоминается в защелке PCF8547 и управляет режимом вывода, в то время как байт, который мы читаем - отражает текущее состояние по входам, и это два разных значения. Нельзя просто поправить что-то в прочитанном по I2C байтике и вернуть его обратно: если на каком-то входе был 0, то мы тут же переведем его таким образом в режим выхода и он будет стабильно и независимо от состояния по входу возвращать 0 (см. пояснения к схеме пина выше) - пока мы снова не запишем туда единицу. Именно поэтому для считываемого и записываемого значений заведены две отдельные  переменные dio_in и dio_out.

Подведем краткий итог:
  • микросхема PCF8574(A) все-таки стоит дороже, чем стандартная логика, но зато позволяет произвольно гибко комбинировать входы и выходы, экономя на количестве корпусов;
  • возможно каскадирование с независимым доступом к каждому входу, в отличие от 595-ой микросхемы, которая в закаскадированном режиме требует для модификации сдвиг всего каскада целиком;
  • возможна работа по прерыванию.

12.05.2010

Вот такая Mega

Случилось так, что мне пришлось внимательно рассматривать Arduino Mega и попутно я выяснил одну забавную деталь, которая может быть интересна владельцам.



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

Но при разработке Arduino Mega итальянцы вдруг решили заменить электролиты на чип-тантал (фото оригинальной платы):



106 - так обозначается 10 мкФ, а магическая комбинация 1C кодирует предельное напряжение - 16 Вольт. Смотрим в схему:



Слева - разъем подачи внешнего питания, C6 и C7 - "те самые" электролиты. Самое время заглянуть в спецификации, которые оговаривают напряжение питания на этом разъеме. Рекомендованное заявлено как 7-12 Вольт, а предельное - 6-20 Вольт. Получается, что это не соответствует установленным элементам: предельное напряжение для такой платы все-таки 16 Вольт.

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

03.05.2010

Фотоячейка

Неплохой альтернативой нашему фоторезистору ФР-764 может служить стандартный фото-свитч, он же - фотоячейка обыкновенная:



Собственно, принцип действия здесь аналогичный - сопротивление изменяется в зависимости от освещенности. Чем ярче свет, падающий на герметичную колбу, тем меньше сопротивление.

Соберем схему и вспомним скетч из предыдущего эксперимента.



В темноте получаем около 760..790, рассеянный дневной свет 260..290, а прямо под 20Вт-ной галогеновой лампой - в зависимости от расстояния - 30..125. Таким образом, чувствительность - выше, чем ФР-764.

Характеристики этой ячейки на поверку оказались такими: темновое сопротивление не менее 2 МОм, предельное напряжение 150 VDC, пик чувствительности - 540 нм. Но самое главное - скорость реакции - 25 мс. Именно поэтому иногда их называют "фотопереключатель".