zepete: (Default)
[personal profile] zepete

Программы для arduino пишутся на обычном c++.
Для поддержки lcd там имеется специальный класс LiquidCrystal (LiquidCrystal Library), только он не доработан:
-нет метода аналогичного printf;
-не может читать позицию курсора и символ по конкретным координатам;
-на 4 строчном экране, после вывода на первую строку переходит писать на третью, а после третьей переходит на вторую:)

Для устранения этих недостатков придется редактировать этот класс, ибо производный от него не будет наследовать необходимые переменные (назначение выводов arduino) и функций.
Моя доработка касается только конфигурации с передачей байтов за два прохода по 4 бита (8 битная передача только занимает лишние 4 провода и мне она не нужна) и для четырех строчных экранов по 20 символов, вроде WinStar WEH002004ALPP5N00000.


Файл LCD.cpp

#include "LCD.h"

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "Arduino.h"
const int LCD::row_offsets[4] = {ROW0, ROW1, ROW2, ROW3};

// When the display powers up, it is configured as follows:
//
// 1. Display clear
// 2. Function set:
//    DL = 1; 8-bit interface data
//    N = 0; 1-line display
//    F = 0; 5x8 dot character font
// 3. Display on/off control:
//    D = 0; Display off
//    C = 0; Cursor off
//    B = 0; Blinking off
// 4. Entry mode set:
//    I/D = 1; Increment by 1
//    S = 0; No shift
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in that state when a sketch starts (and the
// LCD constructor is called).

LCD::LCD(uint8_t rs, uint8_t rw, uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
        uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LCD::LCD(uint8_t rs, uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
        uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, 255, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LCD::LCD(uint8_t rs, uint8_t rw, uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

LCD::LCD(uint8_t rs,  uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, 255, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

void LCD::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
    uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
    uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  _rs_pin = rs;
  _rw_pin = rw;
  _enable_pin = enable;
 
  _data_pins[0] = d0;
  _data_pins[1] = d1;
  _data_pins[2] = d2;
  _data_pins[3] = d3;
  _data_pins[4] = d4;
  _data_pins[5] = d5;
  _data_pins[6] = d6;
  _data_pins[7] = d7;

  pinMode(_rs_pin, OUTPUT);
  // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin#
  if (_rw_pin != 255) {
    pinMode(_rw_pin, OUTPUT);
  }
  pinMode(_enable_pin, OUTPUT);
 
  if (fourbitmode)
    _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
  else
    _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;
 
  begin(16, 1); 
}

void LCD::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
  if (lines > 1) {
    _displayfunction |= LCD_2LINE;
  }
  _numlines = lines;
  _currline = 0;

  // for some 1 line displays you can select a 10 pixel high font
  if ((dotsize != 0) && (lines == 1)) {
    _displayfunction |= LCD_5x10DOTS;
  }

  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
  // according to datasheet, we need at least 40ms after power rises above 2.7V
  // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
  delayMicroseconds(50000);
  // Now we pull both RS and R/W low to begin commands
  digitalWrite(_rs_pin, LOW);
  digitalWrite(_enable_pin, LOW);
  if (_rw_pin != 255) {
    digitalWrite(_rw_pin, LOW);
  }
 
  //put the LCD into 4 bit or 8 bit mode
  if (! (_displayfunction & LCD_8BITMODE)) {
    // this is according to the hitachi HD44780 datasheet
    // figure 24, pg 46

    // we start in 8bit mode, try to set 4 bit mode
    write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms

    // second try
    write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms
   
    // third go!
    write4bits(0x03);
    delayMicroseconds(150);

    // finally, set to 4-bit interface
    write4bits(0x02);
  } else {
    // this is according to the hitachi HD44780 datasheet
    // page 45 figure 23

    // Send function set command sequence
    command(LCD_FUNCTIONSET | _displayfunction);
    delayMicroseconds(4500);  // wait more than 4.1ms

    // second try
    command(LCD_FUNCTIONSET | _displayfunction);
    delayMicroseconds(150);

    // third go
    command(LCD_FUNCTIONSET | _displayfunction);
  }

  // finally, set # lines, font size, etc.
  command(LCD_FUNCTIONSET | _displayfunction); 

  // turn the display on with no cursor or blinking default
  _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; 
  display();

  // clear it off
  clear();

  // Initialize to default text direction (for romance languages)
  _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
  // set the entry mode
  command(LCD_ENTRYMODESET | _displaymode);

}

/********** high level commands, for the user! */

void LCD::clear()
{
  command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
}

void LCD::home()
{
  command(LCD_RETURNHOME);  // set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
}

void LCD::setCursor(uint8_t col, uint8_t row)
{
  if ( row >= _numlines ) {
    row = _numlines-1;    // we count rows starting w/0
  }
  command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}

// Turn the display on/off (quickly)
void LCD::noDisplay() {
  _displaycontrol &= ~LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LCD::display() {
  _displaycontrol |= LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turns the underline cursor on/off
void LCD::noCursor() {
  _displaycontrol &= ~LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LCD::cursor() {
  _displaycontrol |= LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turn on and off the blinking cursor
void LCD::noBlink() {
  _displaycontrol &= ~LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LCD::blink() {
  _displaycontrol |= LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// These commands scroll the display without changing the RAM
void LCD::scrollDisplayLeft(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void LCD::scrollDisplayRight(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

// This is for text that flows Left to Right
void LCD::leftToRight(void) {
  _displaymode |= LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This is for text that flows Right to Left
void LCD::rightToLeft(void) {
  _displaymode &= ~LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'right justify' text from the cursor
void LCD::autoscroll(void) {
  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'left justify' text from the cursor
void LCD::noAutoscroll(void) {
  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LCD::createChar(uint8_t location, uint8_t charmap[]) {
  location &= 0x7; // we only have 8 locations 0-7
  command(LCD_SETCGRAMADDR | (location << 3));
  for (int i=0; i<8; i++) {
    write(charmap[i]);
  }
}

/*********** mid level commands, for sending data/cmds */

inline void LCD::command(uint8_t value) {
  send(value, LOW);
}

inline size_t LCD::write(uint8_t value) {
  send(value, HIGH);
  return 1; // assume sucess
}

/************ low level data pushing commands **********/

// write either command or data, with automatic 4/8-bit selection
void LCD::send(uint8_t value, uint8_t mode) {
  digitalWrite(_rs_pin, mode);

  // if there is a RW pin indicated, set it low to Write
  if (_rw_pin != 255) {
    digitalWrite(_rw_pin, LOW);
  }
 
  if (_displayfunction & LCD_8BITMODE) {
    write8bits(value);
  } else {
    write4bits(value>>4);
    write4bits(value);
  }
}

void LCD::pulseEnable(void) {
  pulseON();
  digitalWrite(_enable_pin, LOW);
  delayMicroseconds(100);   // commands need > 37us to settle
}

void LCD::write4bits(uint8_t value) {
  for (int i = 0; i < 4; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }

  pulseEnable();
}

void LCD::write8bits(uint8_t value) {
  for (int i = 0; i < 8; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }
  pulseEnable();
}


uint8_t  LCD::GetBFlagAndCounter(void)
{
  uint8_t value;
  // read address counter and busy flag;
  digitalWrite(LCD::_rs_pin, LOW);
  digitalWrite(_rw_pin, HIGH);
//  if (_displayfunction & LCD_8BITMODE)
//    return read8bits(value);
  value = read4bits()*16;
  return value|(read4bits());
}

uint8_t  LCD::read4bits(void)
{
  uint8_t v=0;
  int8_t i=0;
  for (i = 0; i<4; i++) {
    pinMode(_data_pins[i],  INPUT);
  }
  pulseON();
  for (i = 3; i>=0; i--) {
    v=v*2+digitalRead(_data_pins[i]);
    pinMode(_data_pins[i], OUTPUT);
  }
  digitalWrite(_enable_pin, LOW);
  return v;
}

void LCD::CharOut(const char c)
{
  uint8_t p,l;
 
  // parse char
  switch(c)
  {
    case '\0':  // blank char
      return;
    case '\b':  // backspace
      p=GetBFlagAndCounter()&0x7f;
      switch(p)
      {
        case 0:
          break;
        case ROW1:
          p=row_offsets[0]+19;
          break;
        case ROW2:
          p=row_offsets[1]+19;
          break;
        case ROW3:
          p=row_offsets[2]+19;
          break;
        deafult:
          p-=1;
      }
      command(LCD_SETDDRAMADDR |p);
      return;
    case '\f': // clear screen
      command(LCD_CLEARDISPLAY);
      while(GetBFlagAndCounter()&0x80);
      return;
    case '\n': // new line
      getCursor(p,l);
      if(l<3)  // move cursor down
      {
        l+=1;
        setCursor(p,l);
        return;
      }
      // scroll screen up
      ScrollUp();
      return;
    case '\r':  // carrige return
      getCursor(p,l);
      setCursor(0,l);
      return;
    case '\t': // tab
      getCursor(p,l);
      p+=4;
      p=p/4;
      p*=4;
      if(p>19)
      {
        p=0;
        l+=1;
      }
      setCursor(p,l);
      return;
    case '\v': // line up
      getCursor(p,l);
      l-=1;
      if(l==0xff)  l=0;
      setCursor(p,l);
      return;
    default:
  //   output char to lcd
      write(c);
      break;
  }
  // get current address
  p=GetBFlagAndCounter()&0x7f;
  switch(p)
  {
    case ROW3:
      command(LCD_SETDDRAMADDR |row_offsets[2]);
      return;
    case ROW2:
      command(LCD_SETDDRAMADDR |row_offsets[1]);
      return;
    case 0:
      ScrollUp();
    case ROW1:
      command(LCD_SETDDRAMADDR |row_offsets[3]);
      return;
  }
}
void LCD::printf(const char * format, ... )
{
  char sz[81];
  va_list args;
  va_start (args, format);
  vsprintf (sz,format, args);
  // output buffer to LCD screen
  for(char* psz=sz;*psz!=0;psz++)
    CharOut(*psz);
}

void LCD::pulseON(void) {
  digitalWrite(_enable_pin, LOW);
  delayMicroseconds(1);   
  digitalWrite(_enable_pin, HIGH);
  delayMicroseconds(1);    // enable pulse must be >450ns
}
 
void LCD::getCursor(uint8_t& col, uint8_t& row)
{
  col=GetBFlagAndCounter()&0x7f;
  if(col>=ROW3)
  {
    col-=ROW3;
    row=3;
  }
  else if(col>=ROW1)
  {
    col-=ROW1;
    row=1;
  }
  else if(col>=ROW2)
  {
    col-=ROW2;
    row=2;
  }
  else
    row=0;
  return;
}
void  LCD::ScrollUp()
{
  uint8_t  uOldAddr=GetBFlagAndCounter()&0x7f;
  command(LCD_SETDDRAMADDR);
  for(int l=1;l<4;l++)
    for(int c=0;c<20;c++)
      CharOut(getChar(c+row_offsets[l]));
  command(LCD_SETDDRAMADDR | ROW3);
  for(int c=0;c<20;c++)
    write(' ');
  command(LCD_SETDDRAMADDR | uOldAddr);
}

char  LCD::getChar(uint8_t col, uint8_t row)
{
  char  value;
  uint8_t  uOldAddr=GetBFlagAndCounter()&0x7f;
  // set new adr
  setCursor(col,row);
  // read value
  digitalWrite(LCD::_rs_pin, HIGH);
  digitalWrite(_rw_pin, HIGH);
//  if (_displayfunction & LCD_8BITMODE)
//    return read8bits(value);
  value = read4bits()<<4;
  value|= read4bits();
  command(LCD_SETDDRAMADDR | uOldAddr);
  return value;
}

char  LCD::getChar(uint8_t addr)
{
  // store old addr
  char  value;
  uint8_t  uOldAddr=GetBFlagAndCounter()&0x7f;
  // set new adr
  command(LCD_SETDDRAMADDR | addr);
  // read value
  digitalWrite(LCD::_rs_pin, HIGH);
  digitalWrite(_rw_pin, HIGH);
//  if (_displayfunction & LCD_8BITMODE)
//    return read8bits(value);
  value = read4bits()<<4;
  value|= read4bits();
  command(LCD_SETDDRAMADDR | uOldAddr);
  return value;
}

void  LCD::setChar(uint8_t addr,char c)
{
  // store old addr
  uint8_t  uOldAddr=GetBFlagAndCounter()&0x7f;
  // set new addr
  command(LCD_SETDDRAMADDR | addr);
  // set value
  write(c);
  // restore addr
  command(LCD_SETDDRAMADDR | uOldAddr);
  return;
}

void LCD::CharOut_noscroll(const char c)
{
  uint8_t p,l;
  // parse char
  switch(c)
  {
    case '\0':  // blank char
      return;
    case '\b':  // backspace
      p=GetBFlagAndCounter()&0x7f;
      switch(p)
      {
        case 0:
          break;
        case ROW1:
          p=row_offsets[0]+19;
          break;
        case ROW2:
          p=row_offsets[1]+19;
          break;
        case ROW3:
          p=row_offsets[2]+19;
          break;
        deafult:
          p-=1;
      }
      command(LCD_SETDDRAMADDR |p);
      return;
    case '\f': // clear screen
      command(LCD_CLEARDISPLAY);
      while(GetBFlagAndCounter()&0x80);
      return;
    case '\n': // new line
      getCursor(p,l);
      if(l<3)  // move cursor down
      {
        l+=1;
        setCursor(p,l);
        return;
      }
      return;
    case '\r':  // carrige return
      getCursor(p,l);
      setCursor(0,l);
      return;
    case '\t': // tab
      getCursor(p,l);
      l=p;
      p+=4;
      p=p/4;
      p*=4;
      for(p-=l;p;p--)
        CharOut(' ');
      return;
    case '\v': // line up
      getCursor(p,l);
      l-=1;
      if(l==0xff)  l=0;
      setCursor(p,l);
      return;
    default:
  //   output char to lcd
      write(c);
      break;
  }
  // get current address
  p=GetBFlagAndCounter()&0x7f;
  switch(p)
  {
    case ROW3:
      command(LCD_SETDDRAMADDR |ROW2);
      return;
    case ROW2:
      command(LCD_SETDDRAMADDR |ROW1);
      return;
    case 0:
      command(LCD_SETDDRAMADDR |ROW3+19);
      return;   
    case ROW1:
      command(LCD_SETDDRAMADDR |ROW3);
      return;
  }
}

void LCD::printf_noscroll(const char * format, ... )
{
  char sz[81];
  va_list args;
  va_start (args, format);
  vsprintf (sz,format, args);
  // output buffer to LCD screen
  for(char* psz=sz;*psz!=0;psz++)
    CharOut_noscroll(*psz);
}




Файл LCD.h

#ifndef LiquidCrystal_h
#define LiquidCrystal_h

#include <inttypes.h>
#include "Print.h"

// адреса начала строк
#define ROW0 0
#define ROW1 0x40
#define ROW2 0x14
#define ROW3 0x54

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00

class LCD : public Print {
public:
  LCD(uint8_t rs, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
  uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LCD(uint8_t rs, uint8_t rw, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
  uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LCD(uint8_t rs, uint8_t rw, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);
  LCD(uint8_t rs, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);

  void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
     uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
   
  void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);

  void printf(const char * format, ... );
  void printf_noscroll(const char * format, ... );
  void CharOut(const char);
  void CharOut_noscroll(const char c);
  void clear();
  void home();

  void noDisplay();
  void display();
  void noBlink();
  void blink();
  void noCursor();
  void cursor();
  void scrollDisplayLeft();
  void scrollDisplayRight();
  void leftToRight();
  void rightToLeft();
  void autoscroll();
  void noAutoscroll();

  void createChar(uint8_t, uint8_t[]);
  void setCursor(uint8_t, uint8_t);
  void getCursor(uint8_t& col,uint8_t& row);
  void setChar(uint8_t,char);
  char getChar(uint8_t);
  char getChar(uint8_t col, uint8_t row);
 
  void ScrollUp();
  virtual size_t write(uint8_t);
  void command(uint8_t);
  uint8_t  GetBFlagAndCounter(void);
 
  using Print::write;
private:
  static const int row_offsets[4];
  uint8_t  read4bits(void);
  void send(uint8_t, uint8_t);
  void write4bits(uint8_t);
  void write8bits(uint8_t);
  void pulseEnable();
  void pulseON();

  uint8_t _rs_pin; // LOW: command.  HIGH: character.
  uint8_t _rw_pin; // LOW: write to LCD.  HIGH: read from LCD.
  uint8_t _enable_pin; // activated by a HIGH pulse.
  uint8_t _data_pins[8];

  uint8_t _displayfunction;
  uint8_t _displaycontrol;
  uint8_t _displaymode;

  uint8_t _initialized;

  uint8_t _numlines,_currline;
};
#endif



В моем классе LCD дополнительно доступны методы.
1. void printf(const char * format, ... ) - обычный printf.
2. void printf_noscroll(const char * format, ... ) - printf без прокрутки экрана вврех.
3. void CharOut(const char) - вывод символа в текущую позицию курсора.
4. void CharOut_noscroll(const char c) - вывод символа в текущую позицию курсора без прокрутки изображения, если кончиться экран.
5. void getCursor(uint8_t& col,uint8_t& row) - получение позиции курсора.
6. void setChar(uint8_t addr,char c) - запись символа "c" в адрес видеобуфера "addr".
7. char getChar(uint8_t addr) - получение содержимого ячейки видеобуфера addr.
8. char  LCD::getChar(uint8_t col, uint8_t row) - получение символа в колонке col и строке row.
9. void ScrollUp() - прокрутка экрана вверх.

Profile

zepete: (Default)
zepete

January 2026

S M T W T F S
    1 23
4 56 78910
11121314151617
18192021222324
25262728293031

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 14th, 2026 02:08 pm
Powered by Dreamwidth Studios