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

Arduino 与 LCD 的接口

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (35投票s)

2009年7月16日

CPOL

7分钟阅读

viewsIcon

184757

downloadIcon

1932

Arduino 与字符 LCD 和图形 LCD 的接口。

文章 1:Arduino 硬件平台简介
文章 3:基于 Arduino 的 MIDI 表情踏板

引言

这是我正在撰写的关于 Arduino 硬件平台 三部分系列文章中的第二篇。本文重点介绍如何将 Arduino 连接到字符型 LCD 和图形型 LCD,并包含静态和动画演示,以展示每个显示器的功能。

字符型和图形型 LCD

在我最初发现 Arduino 平台后,我立即注意到可以连接到 Arduino 的各种组件——从便宜的 LED,到价格适中的 以太网扩展板,再到夸张且过度的 坦克扩展板(售价近 200 美元!)。在购买我的 Arduino 时,我注意到 LCD 相当便宜,所以我购买了一个 10 美元的 16x2 字符型 LCD,以及一个 18 美元的 128x64 图形型 LCD

字符型 LCD 和图形型 LCD 是完全不同的设备,需要不同的库和 API 来驱动它们。幸运的是,Arduino 社区支持这两种设备。对于我的字符型 LCD,我使用了 LiquidCrystal 库,对于我的图形型 LCD,我使用了 KS0108 图形 LCD 库

字符型 LCD

连接字符型 LCD 并对其进行编程非常简单,我没有遇到任何问题。我只是按照 Arduino 字符型 LCD 教程 中的说明和接线图进行操作,一切都按预期工作。运行 LCD_example 示例草图后,我编写了一个利用我的字符型 LCD 的草图,名为 HelloCodeProject

/*
    HelloCodeProject.cpp, based off of LCD_example from
    http://www.hacktronics.com/Tutorials/arduino-character-lcd-tutorial.html
*/

#include <LiquidCrystal.h>

const int BACK_LIGHT = 13;          // Pin 13 will control the backlight

// Connections:
// RS (LCD pin 4) to Arduino pin 12
// RW (LCD pin 5) to Arduino pin 11
// Enable (LCD pin 6) to Arduino pin 10
// LCD pin 15 to Arduino pin 13
// LCD pins d4, d5, d6, d7 to Arduino pins 5, 4, 3, 2
LiquidCrystal g_lcd(12, 11, 10, 5, 4, 3, 2);

void setup()
{
    pinMode(BACK_LIGHT, OUTPUT);
    digitalWrite(BACK_LIGHT, HIGH);     // Turn backlight on. 
                // Replace 'HIGH' with 'LOW' to turn it off.
    g_lcd.clear();                      // Start with a blank screen
    g_lcd.setCursor(0, 0);              // Set the cursor to the beginning
    g_lcd.print("Hello,");
    g_lcd.setCursor(0, 1);              // Set the cursor to the next row
    g_lcd.print("CodeProject");
}

void loop()
{
}

跑马灯

我编写的第二个草图是 TickerTape,它模拟消息在显示屏上滚动。由于 TickerTape 是我编写的第一个使用算法的草图(该算法使用单个缓冲区存储消息,一个 int 变量跟踪要显示的消息的起始点,并考虑消息“环绕”缓冲区末尾的情况),所以我决定首先在一个原生的 Win32 C/C++ 应用程序中编写该算法。由于 Arduino 的调试能力有限,我觉得先在一个支持行级调试和断点设置的环境中编写算法会比在 Arduino 中使用一堆 Serial.println() 语句进行调试更快。验证算法有效后,我编写了 TickerTape 草图。

/*
    TickerTape.cpp, based off of LCD_example from
    http://www.hacktronics.com/Tutorials/arduino-character-lcd-tutorial.html
*/

#include <LiquidCrystal.h>

const int BACK_LIGHT = 13;          // Pin 13 will control the backlight
const char* MESSAGE = "Example 2: Hello, CodeProject. ";
const int MESSAGE_LENGTH = 31;
const int DISPLAY_WIDTH = 16;

// Connections:
// RS (LCD pin 4) to Arduino pin 12
// RW (LCD pin 5) to Arduino pin 11
// Enable (LCD pin 6) to Arduino pin 10
// LCD pin 15 to Arduino pin 13
// LCD pins d4, d5, d6, d7 to Arduino pins 5, 4, 3, 2
LiquidCrystal g_lcd(12, 11, 10, 5, 4, 3, 2);

void setup()
{
    pinMode(BACK_LIGHT, OUTPUT);
    digitalWrite(BACK_LIGHT, HIGH);     // Turn backlight on. 
                        // Replace 'HIGH' with 'LOW' to turn it off.
    g_lcd.clear();                      // Start with a blank screen
    Serial.begin(9600);
}

void loop()
{
    static int s_nPosition = 0;
    int i;
    if(s_nPosition < (MESSAGE_LENGTH - DISPLAY_WIDTH))
    {
        for(i=0; i<DISPLAY_WIDTH; i++)
        {
            g_lcd.setCursor(i, 0);
            g_lcd.print(MESSAGE[s_nPosition + i]);
        }
    }
    else
    {
        int nChars = MESSAGE_LENGTH - s_nPosition;
        for(i=0; i<nChars; i++)
        {
            g_lcd.setCursor(i, 0);
            g_lcd.print(MESSAGE[s_nPosition + i]);
        }

        for(i=0; i<(DISPLAY_WIDTH - nChars); i++)
        {
            g_lcd.setCursor(nChars + i, 0);
            g_lcd.print(MESSAGE[i]);
        }
    }

    s_nPosition++;
    if(s_nPosition >= MESSAGE_LENGTH)
    {
        s_nPosition = 0;
    }

    delay(500);
}

图形液晶显示器

我的图形液晶显示器启动和运行并不像字符液晶显示器那么顺利。连接好所有 20 根电线后,KS0108 库附带的示例 ExampleGLCD 草图无法工作。经过一番折腾,我发现已发布的 KS0108 库版本无法与我的液晶显示器配合使用,我需要一个测试版库(该版本后来已升级为库的官方 Release 2)。一旦这个问题解决,一切都正常工作了。

CodeProjectLogo

运行 ExampleGLCD 示例草图后,我开始编写我的第一个图形 LCD 草图,CodeProjectLogo。我做的第一件事是编写一个 .NET 控制台应用程序,将单色 1 位图像转换为代码中使用的数组。第一个版本创建了一个“1”和“0”的数组。不幸的是,这行不通,因为布尔数组太大,导致生成的草图太大而无法放入 Arduino 中。

我的第二次尝试通过游程编码压缩图像。由于图像的许多像素重复,我认为这可能是减少图像所需数据的有效方法。每行的格式是

    [1,0,-42], run1, run2, ..., runN, -1

第一个 int 是标记。1 表示该行的第一个游程是黑色,0 表示该行的第一个游程是白色,而 -42 表示数组已结束。第二个 int 表示游程长度,并在黑色和白色之间来回切换,直到找到 -1,表示该行的结束。例如,如果出现

    1, 64, 32, 32, -1

... 将绘制 64 个黑色像素,然后是 32 个白色像素,然后是 32 个黑色像素。

这效果更好,因为草图大小现在足够小,可以上传到 Arduino,但是数组对于 Arduino 的堆栈来说太大了。当我包含整个数组时,Arduino 崩溃并反复重新启动,我发现如果只包含总数组的 1/4,它就可以正常工作,所以 RLE 方法也没有奏效。

经过一番调查,我发现大量数据需要使用 PROGMEM 关键字存储在闪存中。使用 PROGMEM,原始数组声明为

    static prog_uchar bits[] PROGMEM = {

然后通过 pgm_read_byte_near() 从闪存中读取数据。

    char val = pgm_read_byte_near(bits + (i + (128 * j)));

...然后通过 GLCD.SetDot() 逐个像素地绘制。

除了发现需要将数据存储在闪存中之外,我还了解到 KS0108 库的测试版还包含了一个新函数 DrawBitmap()CodeProjectLogo 的第二个版本(只需注释掉 #define USE_SET_DOT 语句)将数组声明为

    static uint8_t newBitmap[] PROGMEM = {

...并通过 GLCD.DrawBitmap() 绘制位图

    GLCD.DrawBitmap(newBitmap, 0, 0, BLACK);

我已包含 BMP_Dump 项目的源代码(一个 C# Visual Studio 2008 解决方案)。它不是很健壮(它没有做任何错误检查),但它确实完成了任务,尽管它仅用于开发用途,并且应该小心使用。我保留了所有三种格式(原始布尔数组、压缩 RLE int 数组和打包分页 int 数组),以防有人想尝试生成的不同格式并在其图形 LCD 中显示不同的图像(生成后只需复制粘贴数组即可)。

滑雪

我的下一个图形 LCD 草图是一个名为 Ski 的游戏,它让人想起 80 年代计算机杂志中发布的 8 位游戏,例如 Compute!A.N.A.L.O.G.Antic。这是一个非常简单的游戏,玩家试图保持在随机生成的滑雪道范围内,并使用电位器控制玩家的方向。我应该指出,我不是游戏程序员,Ski 不是一个非常精致的游戏,只是一个概念验证,但它确实展示了可以实现的可能性。对于此草图,除了连接到 Arduino 的 128x64 LCD 外,您还需要一个连接到数字引脚 2 的按钮和一个连接到模拟引脚 5 的 10K 欧姆电位器。

注意:这张照片拍得不太好(但这是我这张照片中最好的一张)。我把图片调亮了一点,但有些电线仍然有点难以分辨。从面包板出来的地线连接到 Arduino 的地引脚,电位器的地线连接到面包板的地。由于拍摄角度(和对比度),在图片中,电位器的地线看起来也连接到 Arduino 的地引脚。如果放大图片,接线会更清晰一些。

玩家首先会看到开始屏幕。一旦他/她按下按钮,游戏就开始了,并生成一条随机的赛道。赛道和玩家都可以有 -2、-1、0、1 或 2 的方向(坡度)。

赛道保存在一个循环内存缓冲区 (g_course) 中,当前位置由 g_nCircularBuferPosition 跟踪。每进行 16 次移动后,都会计算一个新的坡度。

(《滑雪》的源代码有点长,因此请参阅压缩的项目文件以获取完整的源代码。)

滑雪2

在第一个版本的 Ski 中,我绘制了每一行的每一个像素。因此,游戏速度太慢,所以我修改了代码,只绘制有可能改变的像素,并重点绘制当前边缘 +/- 3 像素。

    nEdge = g_course[i] - 3;
    if(nEdge < 0)
        nEdge = 0;
    GLCD.DrawLine(
        nEdge,
        nYPos,
        g_course[i],
        nYPos,
        BLACK);
    .
    .
    .

...我发现这使得游戏更具可玩性(速度提高了很多,以至于我需要添加一个 delay() 调用)。

结论

在本文中,我演示了如何将各种输入和输出硬件与 Arduino 连接,并展示了可以实现的可能性。我认为这些组件是基本的构建块,人们可以从中混合搭配并制作出新颖独特的设备。潜力巨大,仅受限于一个人的想象力。有关使用 Arduino 可以做什么的灵感和示例,请务必查看 飞艇套件虚拟魔镜电子鼓接口个人飞行记录仪 项目。

历史

  • 2009 年 7 月 16 日:初始发布
  • 2009 年 7 月 16 日:文章更新
© . All rights reserved.