#include <FastLED.h>
#define LED_PIN 5
#define MATRIX_WIDTH 35
#define MATRIX_HEIGHT 8
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
CRGB leds[NUM_LEDS];
// 起点在右上角,蛇形排列:偶数行从右向左,奇数行从左向右
int XY(int x, int y) {
if (x < 0 || y < 0 || x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT) return -1;
int base = y * MATRIX_WIDTH;
if (y % 2 == 0) {
// 偶数行:从右往左 → x=0 是最右边
return base + (MATRIX_WIDTH - 1 - x);
} else {
// 奇数行:从左往右 → x=0 是最左边
return base + x;
}
}
// 5x7 字模数据(ASCII 32 到 126),每字节表示一列的7行位图(低位在上)
const uint8_t font5x7[][5] PROGMEM = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // ' ' (32)
{0x00, 0x00, 0x5F, 0x00, 0x00}, // '!' (33)
{0x00, 0x07, 0x00, 0x07, 0x00}, // '"' (34)
{0x14, 0x7F, 0x14, 0x7F, 0x14}, // '#' (35)
{0x24, 0x2A, 0x7F, 0x2A, 0x12}, // '$' (36)
{0x43, 0x33, 0x08, 0x66, 0x61}, // '%' (37)
{0x36, 0x49, 0x55, 0x22, 0x50}, // '&' (38)
{0x00, 0x05, 0x03, 0x00, 0x00}, // ''' (39)
{0x00, 0x1C, 0x22, 0x41, 0x00}, // '(' (40)
{0x00, 0x41, 0x22, 0x1C, 0x00}, // ')' (41)
{0x14, 0x08, 0x3E, 0x08, 0x14}, // '*' (42)
{0x08, 0x08, 0x3E, 0x08, 0x08}, // '+' (43)
{0x00, 0x50, 0x30, 0x00, 0x00}, // ',' (44)
{0x08, 0x08, 0x08, 0x08, 0x08}, // '-' (45)
{0x00, 0x60, 0x60, 0x00, 0x00}, // '.' (46)
{0x20, 0x10, 0x08, 0x04, 0x02}, // '/' (47)
{0x3E, 0x51, 0x49, 0x45, 0x3E}, // '0' (48)
{0x00, 0x42, 0x7F, 0x40, 0x00}, // '1' (49)
{0x42, 0x61, 0x51, 0x49, 0x46}, // '2' (50)
{0x21, 0x41, 0x45, 0x4B, 0x31}, // '3' (51)
{0x18, 0x14, 0x12, 0x7F, 0x10}, // '4' (52)
{0x27, 0x45, 0x45, 0x45, 0x39}, // '5' (53)
{0x3C, 0x4A, 0x49, 0x49, 0x30}, // '6' (54)
{0x01, 0x71, 0x09, 0x05, 0x03}, // '7' (55)
{0x36, 0x49, 0x49, 0x49, 0x36}, // '8' (56)
{0x06, 0x49, 0x49, 0x29, 0x1E}, // '9' (57)
{0x00, 0x36, 0x36, 0x00, 0x00}, // ':' (58)
{0x00, 0x56, 0x36, 0x00, 0x00}, // ';' (59)
{0x08, 0x14, 0x22, 0x41, 0x00}, // '<' (60)
{0x14, 0x14, 0x14, 0x14, 0x14}, // '=' (61)
{0x00, 0x41, 0x22, 0x14, 0x08}, // '>' (62)
{0x02, 0x01, 0x51, 0x09, 0x06}, // '?' (63)
{0x3E, 0x41, 0x5D, 0x55, 0x1E}, // '@' (64)
{0x7C, 0x12, 0x11, 0x12, 0x7C}, // 'A' (65)
{0x7F, 0x49, 0x49, 0x49, 0x36}, // 'B' (66)
{0x3E, 0x41, 0x41, 0x41, 0x22}, // 'C' (67)
{0x7F, 0x41, 0x41, 0x22, 0x1C}, // 'D' (68)
{0x7F, 0x49, 0x49, 0x49, 0x41}, // 'E' (69)
{0x7F, 0x09, 0x09, 0x09, 0x01}, // 'F' (70)
{0x3E, 0x41, 0x49, 0x49, 0x7A}, // 'G' (71)
{0x7F, 0x08, 0x08, 0x08, 0x7F}, // 'H' (72)
{0x00, 0x41, 0x7F, 0x41, 0x00}, // 'I' (73)
{0x20, 0x40, 0x41, 0x3F, 0x01}, // 'J' (74)
{0x7F, 0x08, 0x14, 0x22, 0x41}, // 'K' (75)
{0x7F, 0x40, 0x40, 0x40, 0x40}, // 'L' (76)
{0x7F, 0x02, 0x04, 0x02, 0x7F}, // 'M' (77)
{0x7F, 0x02, 0x04, 0x08, 0x7F}, // 'N' (78)
{0x3E, 0x41, 0x41, 0x41, 0x3E}, // 'O' (79)
{0x7F, 0x09, 0x09, 0x09, 0x06}, // 'P' (80)
{0x3E, 0x41, 0x51, 0x21, 0x5E}, // 'Q' (81)
{0x7F, 0x09, 0x19, 0x29, 0x46}, // 'R' (82)
{0x46, 0x49, 0x49, 0x49, 0x31}, // 'S' (83)
{0x01, 0x01, 0x7F, 0x01, 0x01}, // 'T' (84)
{0x3F, 0x40, 0x40, 0x40, 0x3F}, // 'U' (85)
{0x1F, 0x20, 0x40, 0x20, 0x1F}, // 'V' (86)
{0x3F, 0x40, 0x38, 0x40, 0x3F}, // 'W' (87)
{0x63, 0x14, 0x08, 0x14, 0x63}, // 'X' (88)
{0x03, 0x04, 0x78, 0x04, 0x03}, // 'Y' (89)
{0x61, 0x51, 0x49, 0x45, 0x43}, // 'Z' (90)
{0x00, 0x7F, 0x41, 0x41, 0x00}, // '[' (91)
{0x02, 0x04, 0x08, 0x10, 0x20}, // '\' (92)
{0x00, 0x41, 0x41, 0x7F, 0x00}, // ']' (93)
{0x04, 0x02, 0x01, 0x02, 0x04}, // '^' (94)
{0x80, 0x80, 0x80, 0x80, 0x80}, // '_' (95)
{0x00, 0x03, 0x07, 0x08, 0x00}, // '`' (96)
{0x20, 0x54, 0x54, 0x54, 0x78}, // 'a' (97)
{0x7F, 0x44, 0x44, 0x44, 0x38}, // 'b' (98)
{0x38, 0x44, 0x44, 0x44, 0x20}, // 'c' (99)
{0x38, 0x44, 0x44, 0x44, 0x7F}, // 'd' (100)
{0x38, 0x54, 0x54, 0x54, 0x18}, // 'e' (101)
{0x08, 0x7E, 0x09, 0x01, 0x02}, // 'f' (102)
{0x08, 0x54, 0x54, 0x54, 0x3C}, // 'g' (103)
{0x7F, 0x08, 0x04, 0x04, 0x78}, // 'h' (104)
{0x00, 0x44, 0x7D, 0x40, 0x00}, // 'i' (105)
{0x20, 0x40, 0x44, 0x3D, 0x00}, // 'j' (106)
{0x7F, 0x10, 0x28, 0x44, 0x00}, // 'k' (107)
{0x00, 0x41, 0x7F, 0x40, 0x00}, // 'l' (108)
{0x7C, 0x04, 0x18, 0x04, 0x78}, // 'm' (109)
{0x7C, 0x04, 0x04, 0x04, 0x78}, // 'n' (110)
{0x38, 0x44, 0x44, 0x44, 0x38}, // 'o' (111)
{0x7C, 0x14, 0x14, 0x14, 0x08}, // 'p' (112)
{0x08, 0x14, 0x14, 0x14, 0x7C}, // 'q' (113)
{0x7C, 0x08, 0x04, 0x04, 0x08}, // 'r' (114)
{0x48, 0x54, 0x54, 0x54, 0x20}, // 's' (115)
{0x04, 0x3F, 0x44, 0x40, 0x20}, // 't' (116)
{0x3C, 0x40, 0x40, 0x20, 0x7C}, // 'u' (117)
{0x1C, 0x20, 0x40, 0x20, 0x1C}, // 'v' (118)
{0x3C, 0x40, 0x30, 0x40, 0x3C}, // 'w' (119)
{0x44, 0x28, 0x10, 0x28, 0x44}, // 'x' (120)
{0x0C, 0x50, 0x50, 0x50, 0x3C}, // 'y' (121)
{0x44, 0x64, 0x54, 0x4C, 0x44}, // 'z' (122)
{0x00, 0x08, 0x36, 0x41, 0x00}, // '{' (123)
{0x00, 0x00, 0x77, 0x00, 0x00}, // '|' (124)
{0x00, 0x41, 0x36, 0x08, 0x00}, // '}' (125)
{0x02, 0x01, 0x02, 0x04, 0x02} // '~' (126)
};
// 16x16 汉字字模数据(横向取模,高位在前)
/*const uint8_t hanzi_zhong[] PROGMEM = {
0x00,0x00,0x3C,0x3C,0x24,0x24,0x24,0x24,0x24,0x24,0xFF,0xFF,0x24,0x24,0x24,0x24,
0x24,0x24,0x24,0x24,0x3C,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,
0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00
};
const uint8_t hanzi_wen[] PROGMEM = {
0x10,0x10,0x10,0x10,0x10,0x10,0xD0,0xD0,0x50,0x50,0x90,0x90,0x10,0x10,0x10,0x10,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x14,0x14,0x14,0x14,0x14,0x14,0x08,0x08,
0x08,0x08,0x08,0x08,0x14,0x14,0x14,0x14,0x14,0x14,0x10,0x10,0x10,0x10,0x10,0x10
};*/
// 8x8 简化汉字点阵(每字 8 字节,横向取模,高位在上)
const uint8_t hanzi_zhong_8x8[] PROGMEM = {
0x3C, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x3C // "中"
};
const uint8_t hanzi_wen_8x8[] PROGMEM = {
0x10, 0x10, 0x90, 0x50, 0x30, 0x50, 0x90, 0x10 // "文"
};
/*struct HanziEntry {
uint16_t unicode;
const uint8_t* data;
};
// 支持的汉字列表(可扩展)
const HanziEntry hanzi_table[] PROGMEM = {
{0x4E2D, hanzi_zhong}, // "中"
{0x6587, hanzi_wen} // "文"
};
#define HANZI_COUNT (sizeof(hanzi_table) / sizeof(HanziEntry))*/
struct HanziEntry {
uint16_t unicode;
const uint8_t* data;
};
const HanziEntry hanzi_table_8x8[] PROGMEM = {
{0x4E2D, hanzi_zhong_8x8}, // "中"
{0x6587, hanzi_wen_8x8} // "文"
};
#define HANZI_COUNT (sizeof(hanzi_table_8x8) / sizeof(HanziEntry))
/*void drawHanzi(int x, int y, uint16_t code, CRGB color) {
// 查找对应汉字
const uint8_t* data = nullptr;
for (int i = 0; i < HANZI_COUNT; i++) {
if (pgm_read_word(&hanzi_table[i].unicode) == code) {
data = (const uint8_t*)pgm_read_dword(&hanzi_table[i].data);
break;
}
}
if (!data) return; // 未找到该汉字
// 绘制 16x16 点阵
for (int row = 0; row < 16; row++) {
uint8_t hi = pgm_read_byte(&data[row * 2 + 0]);
uint8_t lo = pgm_read_byte(&data[row * 2 + 1]);
for (int col = 0; col < 16; col++) {
bool on = false;
if (col < 8) {
on = lo & (1 << (7 - col));
} else {
on = hi & (1 << (15 - col));
}
if (on) {
int px = x + col;
int py = y + row;
if (px >= 0 && px < MATRIX_WIDTH && py >= 0 && py < MATRIX_HEIGHT) {
int idx = XY(px, py);
if (idx != -1 && idx < NUM_LEDS) {
leds[idx] = color;
}
}
}
}
}
}*/
void drawHanzi(int x, int y, uint16_t code, CRGB color) {
const uint8_t* data = nullptr;
for (int i = 0; i < HANZI_COUNT; i++) {
if (pgm_read_word(&hanzi_table_8x8[i].unicode) == code) {
data = (const uint8_t*)pgm_read_dword(&hanzi_table_8x8[i].data);
break;
}
}
if (!data) return;
// 绘制 8x8 点阵
for (int row = 0; row < 8; row++) {
uint8_t byte = pgm_read_byte(&data[row]);
for (int col = 0; col < 8; col++) {
if (byte & (1 << (7 - col))) { // 高位在前
int px = x + col;
int py = y + row;
if (px >= 0 && px < MATRIX_WIDTH && py >= 0 && py < MATRIX_HEIGHT) {
int idx = XY(px, py);
if (idx != -1 && idx < NUM_LEDS) {
leds[idx] = color;
}
}
}
}
}
}
bool drawChar(int x, int y, const char* utf8_char, CRGB color) {
if (*utf8_char == 0) return false;
// 判断是否为 UTF-8 编码的汉字
if ((uint8_t)utf8_char[0] >= 0xC0) {
if ((uint8_t)utf8_char[0] >= 0xE0) {
// 3 字节 UTF-8 汉字
uint16_t code = ((utf8_char[0] & 0x0F) << 12) |
((utf8_char[1] & 0x3F) << 6) |
(utf8_char[2] & 0x3F);
drawHanzi(x, y, code, color);
return true;
}
else if ((uint8_t)utf8_char[0] >= 0xC0) {
// 2 字节 UTF-8(不常见于汉字)
return false; // 可扩展支持
}
} else {
// 英文字符
char c = *utf8_char;
if (c < 32 || c > 126) return false;
const uint8_t* cd = &font5x7[c - 32][0];
for (int col = 0; col < 5; col++) {
uint8_t bits = pgm_read_byte(&cd[col]);
for (int row = 0; row < 7; row++) {
if (bits & (1 << row)) {
int px = x + col;
int py = y + row;
int idx = XY(px, py);
if (idx != -1 && idx < NUM_LEDS) {
leds[idx] = color;
}
}
}
}
return true;
}
return false;
}
/// 新增:在串口显示字符的点阵图案
void printCharPattern(char c) {
if (c < 32 || c > 126) {
Serial.print("字符 ");
Serial.print((int)c);
Serial.println(" 不支持");
return;
}
Serial.print("字符: '");
Serial.print(c);
Serial.print("' (ASCII ");
Serial.print((int)c);
Serial.println(")");
const uint8_t* cd = &font5x7[c - 32][0];
Serial.println("点阵图案 (5列 × 7行):");
Serial.println("行数: 0 (顶部) 到 6 (底部)");
Serial.println("列数: 0 (左) 到 4 (右)");
Serial.println();
// 方法1:逐行打印(更容易理解)
Serial.println("逐行显示(从左到右阅读):");
for (int row = 0; row < 7; row++) {
Serial.print("第 ");
Serial.print(row);
Serial.print(" 行: ");
for (int col = 0; col < 5; col++) {
uint8_t bits = pgm_read_byte(&cd[col]);
if (bits & (1 << row)) {
Serial.print("█"); // 点亮像素
} else {
Serial.print("·"); // 未点亮像素
}
}
Serial.println();
}
// 方法2:逐字节打印十六进制和二进制
Serial.println("\n十六进制数据(每列一个字节,低位在上):");
for (int col = 0; col < 5; col++) {
uint8_t bits = pgm_read_byte(&cd[col]);
Serial.print("列 ");
Serial.print(col);
Serial.print(": 0x");
if (bits < 0x10) Serial.print("0");
Serial.print(bits, HEX);
Serial.print(" (二进制: ");
for (int bit = 6; bit >= 0; bit--) {
Serial.print((bits >> bit) & 1 ? "1" : "0");
}
Serial.println(")");
}
// 方法3:矩阵形式(更直观)
Serial.println("\n矩阵形式(旋转90度,实际显示方向):");
Serial.println(" 列0 列1 列2 列3 列4");
for (int row = 0; row < 7; row++) {
Serial.print("行");
Serial.print(row);
Serial.print(": ");
for (int col = 0; col < 5; col++) {
uint8_t bits = pgm_read_byte(&cd[col]);
if (bits & (1 << row)) {
Serial.print("██ ");
} else {
Serial.print(".. ");
}
}
Serial.println();
}
Serial.println("\n" + String(32, '-')); // 分隔线
}
// 新增:显示字符串中所有字符的点阵图案
void printTextPattern(const char* text) {
int len = strlen(text);
Serial.println("\n========== 开始显示点阵图案 ==========");
Serial.println("原始文本: \"" + String(text) + "\"");
Serial.println("总字节数: " + String(len));
Serial.println("=====================================\n");
int charIndex = 0;
for (int i = 0; i < len;) {
charIndex++;
uint8_t byte1 = text[i];
if (byte1 >= 0xE0 && byte1 <= 0xEF && i + 2 < len) {
uint8_t byte2 = text[i + 1];
uint8_t byte3 = text[i + 2];
uint16_t code = ((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F);
Serial.print("字符 #"); Serial.print(charIndex);
Serial.print(" [UTF-8 三字节]: ");
Serial.print(byte1, HEX); Serial.print(" ");
Serial.print(byte2, HEX); Serial.print(" ");
Serial.print(byte3, HEX);
Serial.print(" → Unicode U+");
Serial.println(code, HEX);
if (code == 0x4E2D) {
Serial.println("→ 汉字: '中'");
} else if (code == 0x6587) {
Serial.println("→ 汉字: '文'");
} else {
Serial.println("→ 汉字: 未知 (U+" + String(code, HEX) + ")");
}
Serial.println(); // ← 关键:添加空行分隔
i += 3;
}
else if (byte1 >= 0xC0 && byte1 <= 0xDF && i + 1 < len) {
Serial.print("字符 #"); Serial.print(charIndex);
Serial.print(" [UTF-8 双字节]: 0x");
Serial.print(byte1, HEX);
Serial.print(" 0x");
Serial.print(text[i + 1], HEX);
Serial.println();
Serial.println("→ 类型: 扩展字符(暂不支持)");
Serial.println();
i += 2;
}
else if (byte1 >= 32 && byte1 <= 126) {
char c = (char)byte1;
Serial.print("字符 #"); Serial.print(charIndex);
Serial.print(" [ASCII]: '");
Serial.print(c);
Serial.print("' (0x");
Serial.print(byte1, HEX);
Serial.print(", ");
Serial.print(byte1);
Serial.println(")");
printCharPattern(c); // 显示点阵图
Serial.println(); // ← 分隔符
i += 1;
}
else {
Serial.print("字符 #"); Serial.print(charIndex);
Serial.print(" [无效字节]: 0x");
Serial.println(byte1, HEX);
Serial.println();
i += 1;
}
delay(600); // 给串口足够时间发送数据
}
Serial.println("========== 点阵图案显示完成 ==========");
}
/*void showTextWithChinese(const char* text, CRGB color, int speed = 100) {
int len = strlen(text);
int totalWidth = 0;
// 计算总宽度(英文5像素,汉字16像素)
for (int i = 0; i < len;) {
if ((uint8_t)text[i] >= 0xE0) {
totalWidth += 16; // 汉字占16列
i += 3; // UTF-8 三字节
} else if ((uint8_t)text[i] >= 0xC0) {
totalWidth += 16;
i += 2;
} else {
totalWidth += 6; // 英文+空隙
i += 1;
}
}
for (int offset = MATRIX_WIDTH; offset > -totalWidth; offset--) {
fill_solid(leds, NUM_LEDS, CRGB::Black);
int x = offset;
int i = 0;
while (i < len) {
if ((uint8_t)text[i] >= 0xE0) {
char buf[4] = { text[i], text[i+1], text[i+2], 0 };
drawChar(x, 0, buf, color);
x += 16;
i += 3;
} else if ((uint8_t)text[i] >= 0xC0) {
char buf[3] = { text[i], text[i+1], 0 };
drawChar(x, 0, buf, color);
x += 16;
i += 2;
} else {
drawChar(x, 0, &text[i], color);
x += 6;
i += 1;
}
}
FastLED.show();
delay(speed);
}
}*/
void showTextWithChinese(const char* text, CRGB color, int speed = 100) {
int len = strlen(text);
int totalWidth = 0;
// 计算总宽度:英文宽6像素,汉字宽8像素
for (int i = 0; i < len;) {
if ((uint8_t)text[i] >= 0xE0) {
totalWidth += 8; // 汉字占8列
i += 3; // UTF-8 三字节
} else if ((uint8_t)text[i] >= 0xC0) {
totalWidth += 8;
i += 2;
} else {
totalWidth += 6; // 英文+空隙
i += 1;
}
}
for (int offset = MATRIX_WIDTH; offset > -totalWidth; offset--) {
fill_solid(leds, NUM_LEDS, CRGB::Black);
int x = offset;
int i = 0;
while (i < len) {
if ((uint8_t)text[i] >= 0xE0) {
char buf[4] = { text[i], text[i+1], text[i+2], 0 };
drawChar(x, 0, buf, color); // y=0 开始绘制
x += 8;
i += 3;
} else if ((uint8_t)text[i] >= 0xC0) {
char buf[3] = { text[i], text[i+1], 0 };
drawChar(x, 0, buf, color);
x += 8;
i += 2;
} else {
drawChar(x, 0, &text[i], color);
x += 6;
i += 1;
}
}
FastLED.show();
delay(speed);
}
}
void setup() {
// 初始化串口通信
Serial.begin(115200);
delay(1000); // 等待串口初始化完成
Serial.println("\nLED点阵显示器初始化...");
Serial.println("作者: ESP32 LED Matrix");
Serial.println("版本: 1.0");
Serial.println("=====================================\n");
// 初始化LED
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(64); // 推荐亮度 32~100,避免过流
// 清空LED显示
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
}
void loop() {
// 示例:每循环几次后显示一次点阵图案
static int loopCount = 0;
loopCount++;
if (loopCount % 2 == 0) {
// 显示特定字符的点阵图案到串口
Serial.println("\n=== 显示当前文本的点阵图案 ===");
printTextPattern("中文!");
delay(1000);
}
//showTextWithChinese("Helloabc123, ESP32!", CRGB::Blue);
showTextWithChinese("中文!", CRGB::Yellow, 80);
} 字符 #1 [UTF-8 三字节]: E4 B8 AD → Unicode U+4E2D
→ 汉字: ‘中’
字符 #2 [UTF-8 三字节]: E6 96 87 → Unicode U+6587
→ 汉字: ‘文’
字符 #3 [ASCII]: ‘!’ (0x21, 33)
字符: ‘!’ (ASCII 33)
点阵图案 (5列 × 7行):
行数: 0 (顶部) 到 6 (底部)
列数: 0 (左) 到 4 (右)
在LED屏上显示的还不是中文两个字
最新发布