01 августа 2014

Вольтметр из Arduino, или первые шаги.



Заказал я интернете Arduino Mega и дисплей 20x4 знакомест для великой цели измерения температуры с помощью пачки пирометров. Но великой цели я не достиг, потому что без преобразователей RS232-TTL ничего не получается. Преобразователи я заказал, но зуд в руках покоя не давал, поэтому решил сделать что-нибудь условно полезное.

Мигать диодом все умеют, поэтому я решил сделать подобие цифрового вольтметра с аналоговой шкалой. Но сначала надо было решить проблему подключения дисплея.
На плате самого дисплея была маркировка "QC2004A", подключен он был через микросхему с маркировкой "PCF8574T". Усиленный гуглинг вывел меня на одну статью, в которой автор довольно подробно расписал инициализацию дисплея в коде. Пришлось выкинут библиотеку "LiquidCrystal" из комплекта Arduino IDE и вместо нее поставить библиотеку со страницы https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads. В отличии от выкинутой эта умеет работать не только дисплеями, подключенными к портам на плате, но и с дисплеями на шине I2C, что и требуется.
Поиск адреса устройства осуществлялся с помощью скетча i2cscanner. А конфигурацию портов определил последовательным перебором параметров, приведенных в статье. Для моего дисплея подошел первый набор:
#define LCD_I2C_ADDR  0x27 // Define I2C Address where the PCF8574T is
#define BACKLIGHT     3
#define LCD_EN        2
#define LCD_RW        1
#define LCD_RS        0
#define LCD_D4        4
#define LCD_D5        5
#define LCD_D6        6
#define LCD_D7        7
И для подсветки:
lcd.setBacklightPin(BACKLIGHT,POSITIVE);
А дальше уже было проще, потому что практически всё есть в примерах. Сложнее было сделать свои символы для обозначения долей на шкале, но проблему решил с помощью конвертеров BIN2HEX. Ну и до кучи познал прелести переключателей (switch) в коде. Для полного лоска добавил еще вывод измерений в COM-порт и получилась вот такая штука:

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

// Init LCD
#define LCD_I2C_ADDR  0x27
#define BACKLIGHT     3
#define LCD_EN        2
#define LCD_RW        1
#define LCD_RS        0
#define LCD_D4        4
#define LCD_D5        5
#define LCD_D6        6
#define LCD_D7        7

// Объект для работы с дисплеем
LiquidCrystal_I2C lcd(LCD_I2C_ADDR,
LCD_EN,
LCD_RW,
LCD_RS,
LCD_D4,
LCD_D5,
LCD_D6,
LCD_D7);

// Массив символов для шкалы
const uint8_t charBitmap[][8] = {
 { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
 { 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 },
 { 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c },
 { 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e },
 { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f }
};

// Настройка
void setup() {
 // Создание символов из массива
 int charBitmapSize = (sizeof(charBitmap ) / sizeof (charBitmap[0]));
 // Последовательный порт
 Serial.begin(9600);
 // Аналоговый вход
 pinMode(A0,INPUT);
 // Запись созданных символов в дисплей
 lcd.begin(20,4);
 
 for ( int i = 0; i < charBitmapSize; i++ )
 {
  lcd.createChar ( i, (uint8_t *)charBitmap[i] );
 }
 
 // Настройка подсветки
 lcd.setBacklightPin(BACKLIGHT,POSITIVE);
 lcd.setBacklight(HIGH);
 // Подготовка надписей
 lcd.clear();
 lcd.setCursor(0,1);
 lcd.print("0   1   2   3   4  5");
 
}

// Основной код
void loop() {
 // Переменные
 char A0str[20]="" ;
 char str4[20]="" ;
 int A0val;
 float barl;
 // Считывание значения напряжения
 A0val=analogRead(A0);
 // Расчет напряжения и вывод на первую строку
 lcd.home();
 lcd.setCursor(0,0);
 sprintf(A0str, "U = %4d mV", int(A0val*4.883));
 lcd.print(A0str);
 // И в последовательный порт
 Serial.println(A0str);
 // Формирования полоски.
 lcd.setCursor(0,2);
 // Количество закрашенных мест
 barl = 20.0/1024.0*float(A0val);
 // Вывод
 for ( int i = 0; i < int(barl); i++ )
 {
  lcd.print(char(4));
 }
 // Определение и вывод
 // конца полоски
 int tmp = int(A0val*4.883) % 250;
 int partchar = tmp / 50;
 if (partchar > 0) {
  lcd.print(char(partchar));
  } else {
  lcd.print(" ");
 }
 // И добивание пробелами хвоста
 for ( int j = barl+2; j < 21; j++ )
 {
  lcd.print(" ");
 }
 // Задержка переде следующим замером
 delay(100);
}
А со стороны это выглядит так: