Ранее мы научились использовать текстовые ЖК-индикаторы из Arduino . Это, бесспорно, очень классные устройства. Однако выводить с их помощью можно в основном только текст и какую-то простую псевдографику, например, прогресс бар. Для вывода же полноценной графики предназначены другие устройства — ЖК-матрицы. Сегодня при помощи такой ЖК-матрицы мы соберем термометр, который строит графики температуры.
Пара слов о датчике температуры TMP36
Датчик температуры TMP36 в этом блоге еще не упоминался, поэтому хотелось бы сказать о нем отдельные пару слов. На первый взгляд, это довольно простое устройство с тремя ножками, внешне похожее на транзистор. Если смотреть на него с плоской стороны головки, то левая ножка подключается к плюсу, правая — к минусу, а средняя — к аналоговому входу микроконтроллера. Считывая напряжение на соответствующем пине и используя незамысловатую формулу, микроконтроллер переводит напряжение в градусы Цельсия.
По крайне мере, так написано в любом учебнике по Arduino. Если вы попытаетесь использовать TMP36, как описано выше, вместо градусов Цельсия вы получите какую-то ерунду. Например, TMP36 покажет, что у вас дома 10 градусов, хотя по ощущениям скорее 20. За ответом, что же пошло не так, идем в даташит [PDF] .
Из него мы узнаем, что как можно ближе к датчику нужно поставить конденсатор на 100 нФ. Вжух, и показания сразу становятся похожими на правду! Но не надолго. Если на минуту положить устройство в холодильник, а потом достать, то он покажет сильно отрицательную температуру. Такая температура может быть в морозильной камере, но в обычной камере холодильника держится небольшая положительная температура. Что намного хуже, TMP36 будет показывать отрицательную температуру еще несколько часов, даже если попытаться отогреть его паяльным феном. В общем, опять получаем не показания, а какой-то мусор.
Чтобы датчик не измерял температуру окружающих его компонентов (например, большого ЖК-дисплея в металлическом корпусе), да и поменьше издеваться над этими самыми компонентами, к датчику нужно припаять около 50 см скрученных проводов и втыкать его в макетку через них. Я использовал провода от разрезанного Ethernet-кабеля (витой пары). Конденсатор в этом случае, похоже, не нужен. Но если припаять его к датчику, вряд ли станет хуже. Если при помощи датчика вы планируете измерять температуру на улице, бросив его за окно, советую сделать провода подлиннее. Так просто удобнее. Также не забудьте обеспечить нормальную изоляцию при помощи термоусадочных трубок.
Примите во внимание, что TMP36 вообще очень чувствителен к любым скачкам напряжения. Чтобы измерения получились точными, соединительные провода на макетке лишний раз лучше не теребить.
Кажется, это все, что касается тонкостей использования TMP36.
Вывода графика температуры при помощи ЖК-матрицы
Для экспериментов я использовал ЖК-матрицу MT-12864A-2YLG . Существуют аналогичные матрицы других моделей от других производителей, все они используются точно так же. Иногда в описании пишут, что они работают на базе контроллера KS0107 или его аналога. Главное, чтобы у матрицы было 20 пинов, если она с подсветкой, или 18, если подсветки нет. В теории пинов может быть и чуть меньше. Читайте далее, и сами поймете, почему.
Для работы с матрицей мы воспользуемся библиотекой GLCD . Мне пришлось ее слегка пропатчить, чтобы избавиться от ошибки при компиляции современном avr-gcc:
const in order to be put into read-only section by means of
‘__attribute__((progmem))’
static uint8_t System5x7[] PROGMEM = {
Просто допишите в соответствующей строке слово const, или используйте пропатченный GLCD из моего репозитория. Ссылку на него, как обычно, вы найдете в конце поста.
Библиотеку нужно положить к себе в репозиторий в каталог glcd. Также нужно подправить Makefile (что за Makefile рассказывалось в этом посте ):
CPPFLAGS := -I . / glcd -Wp,-w
Флаг -Wp,-w
нужен для того, чтобы заглушить ворнинги вроде:
#define PIN_A7 AVRIO_PIN(AVRIO_PORTA, 7)
^
Если вам не лень, можете еще немного поправить код GLCD. Мне было лень 🙂
Чтобы понять, какой пин ЖК-матрицы куда нужно подключать при использовании GLCD, открываем файл config/ks0108_Arduino.h:
#define glcdData0Pin 8 /* PB0 */
#define glcdData1Pin 9 /* PB1 */
#define glcdData2Pin 10 /* PB2 */
#define glcdData3Pin 11 /* PB3 */
#define glcdData4Pin 4 /* PD4 */
#define glcdData5Pin 5 /* PD5 */
#define glcdData6Pin 6 /* PD6 */
#define glcdData7Pin 7 /* PD7 */
#define glcdCSEL1 14 /* A0, PC0 */
#define glcdCSEL2 15 /* A1, PC1 */
#define glcdRW 16 /* A2, PC2 */
#define glcdDI 17 /* A3, PC3 */
#define glcdEN 18 /* A4, PC4 */
Само собой разумеется, номера пинов микроконтроллера в этом файле при желании можно переопределить.
Где у матрицы находятся пины с такими именами, можно узнать из таблицы 4 на странице 7 даташита [PDF] . Примите во внимание, что если у вас другая ЖК-матрица, она может и почти наверняка будет подключаться немного не так, как моя. Поэтому обязательно сверьтесь с даташитом!
Итак, используемая мной ЖК-матрица подключается таким образом:
- 1 — GND, земля;
- 2 — VCC, питание модуля;
- 3 — Uo, вход питания ЖК-панели. Подключается к VEE (см далее) через потенциометр на 10 кОм для регулирования контрастности. Подключать напрямую без потенциометра нельзя, увидите только черные квадраты;
- 4 — A0, выбор команды/данные, к glcdDI;
- 5 — R/W, выбор чтение/запись, к glcdRW;
- 6 — E, стробирование данных, к glcdEN;
- 7-14 — DB0-DB7, шина данных, к glcdData0Pin-glcdData7Pin;
- 15 — E1, выбор кристала один, к glcdCSEL1;
- 16 — E2, выбор кристала два, к glcdCSEL2;
- 17 — RES, сброс. Можно подключить к любому цифровому пину на ваш выбор, к reset-пину микроконтроллера или просто к плюсу;
- 18 — VEE, выход DC-DC преобразователя. Подключается через потенциометр к Uo как описано выше;
- 19 — A, анод подсветки. Подключаем к плюсу напрямую или через потенциометр, если хотим регулировать яркость;
- 20 — K, катод подсветки, к минусу;
В собранном виде цепь выглядит таким образом:
На фото вы видите устройство после того, как датчик температуры был помещен примерно на одну минут в морозилку, а затем извлечен из нее. Как можно видеть по графику, и до и после извлечения термометр показывает что-то похожее на правду.
Я предпочитаю собирать свою Arduino прямо на макетке , но вы с тем же успехом можете использовать обычную Arduino. У меня микроконтроллер использует встроенные часы и работает на частоте 1 МГц. Запрограммировано все это хозяйство самопальным AVR-программатором . Если вам больше по душе бутлоадер Arduino, вы с тем же успехом можете использовать и его.
Код прошивки довольно прост:
#include <glcd.h>
#include <fonts/SystemFont5x7.h>
#define ZERO_X_OFFSET 35
#define MAX_TEMPERATURE 28
#define MIN_TEMPERATURE (-28)
#define HIST_SIZE 128
float hist [ HIST_SIZE ] = { 0.0 } ;
int curr_hist_idx = 0 ;
void setup ( )
{
GLCD. Init ( NON_INVERTED ) ;
GLCD. SelectFont ( System5x7 ) ;
}
inline float sensor_to_celsius ( int sensor )
{
return ( ( ( ( float ) sensor * 5000.0 ) / 1024.0 ) — 500.0 ) / 10.0 ;
}
inline int limit_temperature ( int t )
{
if ( t > MAX_TEMPERATURE )
return MAX_TEMPERATURE ;
if ( t < MIN_TEMPERATURE )
return MIN_TEMPERATURE ;
return t ;
}
void loop ( )
{
int sensor = analogRead ( 5 ) ;
float celsius = sensor_to_celsius ( sensor ) ;
GLCD. ClearScreen ( ) ;
GLCD. CursorTo ( 0 , 0 ) ;
GLCD. Puts ( «Current: » + String ( celsius ) ) ;
hist [ curr_hist_idx ] = celsius ;
int stop_i = curr_hist_idx ;
curr_hist_idx ++ ;
if ( curr_hist_idx == HIST_SIZE )
curr_hist_idx = 0 ;
GLCD. DrawLine ( 0 , ZERO_X_OFFSET, HIST_SIZE — 1 , ZERO_X_OFFSET, BLACK ) ;
int prev_i ;
int i = curr_hist_idx ;
int x = 0 ;
do
{
if ( x >= 1 )
GLCD. DrawLine ( x — 1 , ZERO_X_OFFSET — limit_temperature ( hist [ prev_i ] ) ,
x, ZERO_X_OFFSET — limit_temperature ( hist [ i ] ) , BLACK ) ;
x ++ ;
prev_i = i ;
i ++ ;
if ( i == HIST_SIZE )
i = 0 ;
} while ( i ! = stop_i ) ;
delay ( 2000 ) ;
}
Список методов, поддерживаемых GLCD, легко grep’ается:
DrawLine ( uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2,
uint8_t color ) ;
DrawRect ( uint8_t x, uint8_t y, uint8_t width, uint8_t height,
uint8_t color ) ;
DrawRoundRect ( uint8_t x, uint8_t y, uint8_t width, uint8_t height,
uint8_t radius, uint8_t color ) ;
FillRect ( uint8_t x, uint8_t y, uint8_t width, uint8_t height,
uint8_t color ) ;
InvertRect ( uint8_t x, uint8_t y, uint8_t width, uint8_t height ) ;
SetDisplayMode ( uint8_t invert ) ;
DrawBitmap ( Image_t bitmap, uint8_t x, uint8_t y, uint8_t color ) ;
DrawVLine ( uint8_t x, uint8_t y, uint8_t height, uint8_t color ) ;
DrawHLine ( uint8_t x, uint8_t y, uint8_t width, uint8_t color ) ;
DrawCircle ( uint8_t xCenter, uint8_t yCenter, uint8_t radius,
uint8_t color ) ;
FillCircle ( uint8_t xCenter, uint8_t yCenter, uint8_t radius,
uint8_t color ) ;
GotoXY ( uint8_t x, uint8_t y ) ;
Все методы возвращают void. Они имеют говорящие имена и превосходно документированы прямо в коде библиотеки.
Заключение
Неоспоримое преимущество ЖК-матрицы перед текстовым ЖК-индикатором заключается в возможности выводить не только текст, но и произвольную (до тех пор, пока она черно-белая и умещается в 128 x 64 точек) графику. Однако ЖК-матрица больше по размеру. Кроме того, для ее использования требуется 13 пинов микроконтроллера, в то время, как текстовому ЖК-индикатору нужно только 6 пинов. Наконец, GLCD отъедает заметно больше flash-памяти микроконтроллера, чем LiquidCrystal. Точные цифры, увы, я забыл выписать. Можете добыть их и оставить в комментариях в качестве домашнего задания.
Если вам нужно просто выводить текст, используйте текстовый индикатор. Если вы решили сделать свой осциллограф , или свою убийцу GameBoy с тетрисами, змейками, марио и вот этим всем, используйте матрицу.
Полную версию исходников к этой заметке вы найдете в этом репозитории на GitHub . Вопросы и дополнения, как обычно, горячо приветствуются.
Дополнение: Использование ЖК-экранчика от Nokia 5110 в Arduino