材料准备 和 MAX7219 点阵屏

MAX7219 驱动的点阵屏在网上各种 Arduino 教程中无处不在,不过在本页面中,将结合使用 MD_ParolaMD_MAX72XX 这两个 Arduino 库,使用这两个库将会使在点阵屏上显示滚动文本和其他动画变得非常容易。 本文示例的最终效果如下图所示:

MAX7219 点阵屏 实物图
MAX7219 点阵屏 实物图

MAX7219 LED 驱动器可用于控制 64 个单独的LED,该驱动器通过 SPI 与 Arduino 进行通信。由于 MAX7219 最多可以控制 64 个LED,它可以驱动的最大尺寸的点阵显示器是 8×8 像素。然而,将多个驱动器和点阵屏串联起来,可以轻松控制更大的显示屏,如 8×32、8×64,甚至更大。

在本例中将使用到以下一些电子模块或者材料配件:

电子模块或者材料配件 数量
8×32 MAX7219 LED点阵显示屏1
D1 mini 控制板1
连接线若干
Micro USB 数据线1

下面是一个典型的 MAX7219 8×32 LED 点阵屏的规格:

电压5 V
驱动MAX7219 x 4
亮度等级16
尺寸32 x 128 x 15 mm
像素点8×32, ⌀ 3 mm
MAX7219 点阵屏 渲染图 (Solidworks)
MAX7219 点阵屏 渲染图 (使用 Solidworks 渲染)

硬件接线方式

MAX7219 LED 点阵屏驱动器通过 SPI(Serial Peripheral Interface,串行外设接口)与 Arduino 进行通信。通过 SPI 接口,一个主设备(Arduino)控制外围设备(也称为从属设备)。通过 Arduino 的硬件 SPI 接口 或 三个任意的数字引脚(软件 SPI)来控制点阵屏。硬件 SPI 引脚(MOSI、MISO 和 SCK)在每种 Arduino 板上有其特定的位置。这比起使用软件 SPI 要快,但需要使用以下固定的输出引脚。各个常用的 Arduino 硬件 SPI 针脚位置如下表所示:

控制板MOSIMISOSCK电压
Arduino Uno11 or ICSP-412 or ICSP-113 or ICSP-35 V
Arduino Mega51 or ICSP-450 or ICSP-152 or ICSP-35 V
Arduino LeonardoICSP-4ICSP-1ICSP-35 V
各个常用的 Arduino 硬件 SPI 针脚位置

而在本例中将使用 D1 mini 进行 MAX7219 点阵屏的控制,连接方式如下表所示。如果将第一个点阵屏的 DOUT 连接到下一个点阵屏的 DIN 上,可以使所有点阵屏之间共享 VCC、GND、CLK 和 CS。

MAX7219 LED 点阵屏D1 mini
CLKD2
DATA 或 DIND4
CSD3
GNDGND
VCC5V

一般来说,这样大小的点阵屏(4块 8*8 串联)组合使用控制板的供电就没问题,但是如果想控制一个大的点阵屏组合,建议使用外部电源。

库文件安装

如何安装库文件可以查看这篇文章:Arduino 制造光污染 | 全彩灯带 WS2812 中“找一个合适的库”这一节。

MD_Parola 库可用于创建许多不同的文本动画,如滚动文本效果。这个库依赖 MD_MAX72XX 库,它实现了 LED 点阵屏的硬件功能。在本例中软件部分将使用 Arduino IDE,此外还将使用到以下一些库文件:

库名称作用下载地址
MD_Parola 创建许多不同的文本动画,如滚动文本效果。 Github
MD_MAX72XX 实现了 LED 点阵屏的硬件功能。 Github
必要的库文件列表

为了配合本页中的示例代码,还需要以下库文件以及 ESP8266 的 Arduino 官方库:

库名称作用下载地址
NTPClient提供与 NTP 服务器主机的连线,并回传日期时间。Github
其他必要的库文件列表

示例代码

在 Arduino 代码中设置点阵屏时,需要将 HARDWARE_TYPE 设置为 FC16_HW,并指定所连接的设备数量。一个 8×8 的点阵屏算作一个设备,所以如果想控制一个 8×32 的模块,那需要将 MAX_DEVICES 设置为 4(一个8×32的点阵屏包含 4 个 MAX7219),设置针脚和参数如下图所示:

MD_MAX72XX 和 MD_Parola 代码设置
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

String formattedDate;
String dayStamp;
String timeStamp;

#define DEMO_DURATION 5000
typedef void (*Demo)(void);
int modeNumber = 0;

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers are for ESO8266 hardware SPI and will probably not
// work with your hardware and may need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CLK_PIN   D2 // or SCK
#define DATA_PIN  D4 // or MOSI
#define CS_PIN    D3 // or SS
#define buttonIO  D1
// SOFTWARE SPI
MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
// Scrolling parameters
uint8_t scrollSpeed = 25;    // default frame delay value
textEffect_t scrollEffect = PA_SCROLL_LEFT;
textPosition_t scrollAlign = PA_LEFT;
uint16_t scrollPause = 2000; // in milliseconds
// Global message buffers shared by Wifi and Scrolling functions
#define BUF_SIZE  512
char curMessage[BUF_SIZE] = { "" };
char newMessage[BUF_SIZE] = { "Hello! Enter new message?" };
bool newMessageAvailable = true;
int state = 0;
int buttonState = 0;
int timeCount = 0;

const char* ssid     = "ce";
const char* password = "ce";

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

void setup() {
  Serial.begin(57600);
  P.begin();

  WiFi.begin(ssid, password);
  Serial.print("\n\r \n\rWorking to connect");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  timeClient.begin();

  // 时区 *60分 * 60秒,例如:
  // GMT +1 = 3600
  // GMT +8 = 28800
  // GMT 0 = 0
  timeClient.setTimeOffset(28800);
  pinMode(buttonIO, INPUT);
}

void loop() {
  while (!timeClient.update()) {
    timeClient.forceUpdate();
  }

  buttonState = digitalRead(buttonIO);
  if (buttonState == LOW) {
    state = 2;
  }

  if (P.displayAnimate()) {
    // formattedDate 取得的日期格式为:2018-05-28T16:00:13Z
    formattedDate = timeClient.getFormattedDate();
    Serial.println(formattedDate);

    // 获取日期
    int splitT = formattedDate.indexOf("T");
    dayStamp = formattedDate.substring(0, splitT);
    Serial.print("DATE: ");
    Serial.println(dayStamp);

    // 获取时间
    timeStamp = formattedDate.substring(splitT + 1, formattedDate.length() - 1);
    Serial.print("TIME:");
    Serial.println(timeStamp);

    switch (state) {
      case 0: {
          timeStamp.toCharArray(newMessage, timeStamp.length() + 1);
          strcpy(curMessage, newMessage);
          newMessageAvailable = true;
          state = 1;
          P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
          break;
        }
      case 1: {
          dayStamp.toCharArray(newMessage, dayStamp.length() + 1);
          strcpy(curMessage, newMessage);
          newMessageAvailable = true;
          state = 0;
          P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
          break;
        }
      case 2: {
          scrollPause = random(50, 800);
          scrollSpeed = random(5, 25);
          timeCount = timeCount + scrollPause + scrollSpeed * 32;
          Serial.println(timeCount);
          String newMessageTemp = "666";
          newMessageTemp.toCharArray(newMessage, newMessageTemp.length() + 1);
          strcpy(curMessage, newMessage);
          newMessageAvailable = true;
          P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
          state = 3;
          break;
        }
      case 3: {
          scrollPause = 3000;
          scrollSpeed = 50;
          String newMessageTemp = String(timeCount)+" ms";
          newMessageTemp.toCharArray(newMessage, newMessageTemp.length() + 1);
          strcpy(curMessage, newMessage);
          newMessageAvailable = true;
          state = 0;
          P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
          timeCount = 0;
          break;
        }
    }
    P.displayReset();
  }
}

类似文章

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注