65.9K
CodeProject 正在变化。 阅读更多。
Home

用于 Arduino 的 Print 派生串口 LCD 库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (8投票s)

2014 年 6 月 1 日

CPOL

6分钟阅读

viewsIcon

19504

downloadIcon

435

一个全新的、功能强大的、基于 'Print' 类的 Arduino 串口 LCD 库。

引言

LCD 显示屏主要用于基于 Arduino 的系统中,以显示应用程序的输出。它们价格便宜、随处可得、易于使用。因此,你可以在市场上找到各种各样的 LCD 显示屏。一个缺点是它会占用 Arduino 板上过多的引脚。作为一种解决方案,串口 LCD 显示屏应运而生,这得感谢善良的制造商们 :)

背景

Arduino IDE 自带一个用于基于 Hitachi-HD44780 的 LCD([4])的 LiquidCrystal 库([3])。你可以根据需要选择并写入寄存器来命令控制器。在 4 位使用模式下,你需要使用 Arduino 14 个数字引脚中的 6 个。真是浪费。

图 1:一个 2x16 LCD

基于 Hitachi 的控制器提供的一些功能

  • 定位光标
  • 设置光标类型(隐藏、下划线、闪烁)
  • 滚动显示(向左、向右)
  • 显示屏开/关
  • 清屏

LiquidCrystal 库通过向 Hitachi 控制器发送指令来暴露这些功能。专用引脚在构造函数中指定。

让我们看一个例子:

#include <Arduino.h>
#include <LiquidCrystal.h>

/*
Pin configuration:
LCD pin  <=>   Arduino Digital
-----------------------------
register select <=> 12
enable <=> 11
data4 <=> 2
data5 <=> 3
data6 <=> 4
data7 <=> 5
*/
LiquidCrystal lcd(12,11,2,3,4,5);

void setup() {
  lcd.begin(16,2); // specify columns & rows count

  lcd.print("that is a test");
  delay(5000);

  lcd.clear(); // clear screen
  
  lcd.setCursor(1,1); // second column of second row
}

void loop() {
  while(1);
}

在代码的开头,一个带有给定引脚配置的 LiquidCrystal 实例 (lcd) 被创建。该库使用此引脚配置向 LCD 发送命令和数据。

LiquidCrystal lcd(12,11,2,3,4,5); 

setup 函数中,LCD 会用其列数和行数进行初始化。当库设置光标位置时会使用这些信息。

lcd.begin(16,2); // specify columns & rows count

例如,如果你想将光标移动到第二行的第二列,目标光标位置是使用在 begin 函数中提供的值来决定的。下一次的 print 将从该位置开始。

lcd.setCursor(1,1); // second column of second row 

我希望这个示例足以说明如何使用 LiquidCrystal 库来操作 LCD。

串口 LCD

现在,是时候谈谈串口 LCD 了。当使用串口 LCD 时,那些底层的驱动问题被转移到了另一个微控制器上。你命令这个微控制器,然后它再命令 LCD(Hitachi 控制器)。为此,它为你提供了一个仅需一根线的串行接口。信息流是单向的,从 Arduino 到串口 LCD,因此它只需要你的 Arduino 的一个数字引脚。你只需使用 HardwareSerial[7])或 SoftwareSerial[8])的发送线与你的串口 LCD 通信。

图 2:一个串口 LCD(使用 SoftwareSerial 连接)

我在这个库中所做的是为我特定的串口 LCD([1])实现通信协议([2])。我的实现与 LiquidCrystal 有类似的功能,你可能也想到了,因为两者都基于 Hitachi 控制器。

发送给串口 LCD 的命令由带参数的单字节指令组成。要驱动串口 LCD,你需要通过串口发送这些命令。

命令:设置背光
命令字节:0x80
参数:0x00-0xFF (0-255),对应 0%-100% 的亮度

命令:设置波特率(重置后),默认为 9600
命令字节:0x81
参数:0x00-0x0A (0-10),对应的值为 300, 1200, 2400, 2400, 9600, 14400, 19200, 28800, 38400, 57600, 115200

以下命令控制 LCD 的行为,并且都以 0xFE 命令字节为前缀。

命令:清屏,并将光标位置设置为 0
命令字节:0x01(无参数)

命令:光标右移
命令字节:0x14(无参数)

命令:光标左移
命令字节:0x10(无参数)

命令:显示向右滚动
命令字节:0x1C(无参数)

命令:关闭显示(背光不变)
命令字节:0x08(无参数)

命令:打开显示
命令字节:0x0C(无参数)

命令:下划线光标
命令字节:0x0E(无参数)

命令:闪烁光标
命令字节:0x0D(无参数)

命令:关闭光标(如果显示屏是开启的)
命令字节:0x0C(无参数)

命令:设置光标位置
命令字节:0x80
参数:1 字节的位置索引,从 0 开始(左上角)

命令:切换启动画面
命令字节:0x1E(无参数)

(我曾为串口 LCD 开发过另一个库([9]),但在设计和功能方面不够好。我更倾向于将其视为一次实验性研究。)

使用代码

这是 SerialLcd2 的类声明

class SerialLcd2: public Print {
protected:
    // underlying stream connected to serial LCD.
    // can be HardwareSerial or SoftwareSerial
    Stream* _Stream;
public:
    // constructor
    SerialLcd2(Stream* stream): _Stream(stream) { };
    
    // override for Print::write(uint8_t)
    size_t write(uint8_t b) { return this->_Stream->write(b); }; 

    // line feed, same as print('\n');
    void printLine() { this->write(SL_CHR_LF); };
    // back space
    void del() { this->write(SL_CHR_BCK); };

    // direction: SL_CMD_CURSOR_MV_R|SL_CMD_CURSOR_MV_L
    void moveCursor(uint8_t cmd); 
    // cmd = SL_CMD_CURSOR_OFF|SL_CMD_CURSOR_UNDERLINE|SL_CMD_CURSOR_BLINK
    void showCursor(uint8_t cmd);
    // pos: 0..(max chars-1)
    void setCursorPos(uint8_t pos);
    
    // clear screen
    void clr(); 
    // backlight: 0-255
    void setBacklight(uint8_t b); 
    // direction: SL_CMD_SCROLL_R|SL_CMD_SCROLL_L
    void scrollScreen(uint8_t cmd);
    // on if true
    void displayOn(bool);

    // toggle splash screen display. 
    void toggleSplash();
    // set baud rate
    void setBaudRate(uint8_t);
};

我在这里做的是实现了串行通信协议和一些其他方便的函数。

让我们看一个例子。

SerialLcd2* lcd; // pointer for SerialLcd2
Stream* s; // stream to be connected

要通过硬件串口 (Serial) 连接,我们首先需要初始化 Arduino 的串口。我没有更改我的 LCD 的波特率,因此连接的波特率将是 9600 bps。

    Serial.begin(9600);
    s = &Serial; // connect to tx(pin 1)

如果我们想用 SoftwareSerial 连接,也需要类似的初始化,只是我们必须为 SoftwareSerial 预留引脚。

    SoftwareSerial ss(2,3); // rx=2, tx=3(connection)
    ss.begin(9600);
    s = &ss;

然后用它的串行 Stream ([6]) 创建一个 SerialLcd2 实例。

lcd = new SerialLcd2(s); 

注意!我们应该将 Stream 的发送线连接到 LCD 的接收线,这是它唯一的通信线路。

现在,我们准备好使用它了。

首先,我们可以像 Print 类([5])一样打印任何东西,无需任何进一步处理。这是通过重写 Print 类的虚函数 write 来实现的,因为 Print 类在所有打印版本中都使用它。

  lcd->print(1); // integer
  lcd->print('A'); // char
  lcd->print(0.1); // float
  lcd->print("string"); // const char*
  lcd->print(F("flash string")); // flashmem string

不要使用 println,因为它不会像你预期的那样将光标移动到下一行的开头。相反,直接发送 '\n' 或使用 printLine() 函数。然后,光标会移动到下一行,或者如果它在 LCD 的最后一行,则会移动到第一行。

  lcd->print('\n'); // or
  lcd->printLine();

一个好处是,你可以删除最后打印的字符,就像按下退格键一样。

  lcd->del();

其余的就和 Arduino 的 LiquidCrystal 库一样,是关于控制 LCD 的。

  /* screen control */
  lcd->clr(); // clear screen
  
  lcd->setBacklight(0); // backlight off
  lcd->setBacklight(0xFF); // backlight max
  
  lcd->scrollScreen(SL_CMD_SCROLL_R); // scroll right
  lcd->scrollScreen(SL_CMD_SCROLL_L); // scroll left
  
  lcd->displayOn(false); // hides printed text and makes backlight off
  lcd->displayOn(true); // shows printed text and makes backlight max
  
  /* cursor control */
  lcd->showCursor(SL_CMD_CURSOR_OFF); // cursor off
  lcd->showCursor(SL_CMD_CURSOR_UNDERLINE); // underline cursor
  lcd->showCursor(SL_CMD_CURSOR_BLINK); // blink cursor
  
  // moves cursor to the first column of the first row
  lcd->setCursorPos(0); 
  // moves cursor to the first column of the second row
  // if the total number of columns is 16
  lcd->setCursorPos(16); 
  
  lcd->moveCursor(SL_CMD_CURSOR_MV_R); // increments cursor position
  lcd->moveCursor(SL_CMD_CURSOR_MV_L); // decrements cursor position

  /* misc */
  lcd->setBaudRate(4); // sets to 9600bps after reset
  lcd->toggleSplash(); // toggles splash screen for my specific serial LCD

需要提及的一些要点

  • 在 LCD 的串口命令中,打开/关闭显示对背光没有任何影响。我在我的实现中改变了这种行为。如果你调用 displayOn(false),背光也会被关闭,反之亦然。
  • 设置光标位置与 LiquidCrystal 库不同。你需要将 [列, 行] 值传递给 LiquidCrystal::setCursor 函数,而将 [距左上角的偏移量] 传递给 SerialLcd2::setCursorPos
  • LiquidCrystal 中不存在波特率和启动画面的切换功能。

你可以在下载区找到一个关于该库用法的示例应用程序。

关注点

这个库的好处是它派生自 Print 类。因此它拥有 Print 类的强大功能。你可以像使用 Print 一样打印任何东西,比如格式化的数字或来自 Arduino 闪存的字符串。

另一件事是,它使用 HardwareSerialSoftwareSerial 没有任何区别,因为它将两者都视为 Stream。因此,你可以将串口 LCD 的接收线连接到任何配置为串口发送的引脚上。

在幕后,串口 LCD 套件在其固件([10])中使用了 LiquidCrystal 库。这是通过与 Arduino 板上使用的相同技术实现的。串口套件有一个 ATmega328 MCU,并且上面上传了 Arduino 引导加载程序。因此,可以通过其串行编程接口上传任何代码。该固件只是另一个 Arduino 代码,可以被看作是你的 Arduino 和 LCD 之间的代理。你甚至可以更改串口 LCD 的通信协议,并创建你自己的协议。尽情享受吧!

如果你对这个实现有任何想法/建议,我将很乐意倾听。

参考

[1] 来自 Cooking-Hacks.com 的串口 LCD 套件
[2] 与串口 LCD 的串行通信
[3] Arduino LiquidCrystal 库
[4] Hitachi LCD 控制器
[5] Arduino Print 类
[6] Arduino Stream 类
[7] Arduino Serial
[8] Arduino SoftwareSerial
[9] 我为 SerialLcd 实现的另一个版本
[10] 串口 LCD 套件的固件
 

© . All rights reserved.