当前位置: 首页 > news >正文

【51单片机】8. 矩阵LED显示自定义图案、动画

1. 显示原理

  • LED点阵屏与数码管类似,只是将数码管每一列的像素以8字型排列

  • 与数码管一样,有共阴和共阳两种接法,不同接法对应电路结构不同

  • LED点阵需要逐行或逐列扫描,才能使所有LED同时显示

在这里插入图片描述

2. 74HC595电路图原理

在这里插入图片描述

看OE,是低电平有效,所以要弄一下J24这个接线帽

  • RCLK:Register Clock

  • SRCLR:Serial 串行清零端

  • SRCLK:串行时钟

  • SER:串行数据

74HC595是串行输入并行输出的移位寄存器,用3根线输入串行数据,8根线输出并行数据,多片级联可以输出16、24、32位等,常用于IO口扩展

在这里插入图片描述

数据通过SER一位一位地进来,上升沿移位SERCLK脉冲来一次就SER写一次数据,最后经上升沿锁存RCLK,将数据从左侧挪到右侧。

(弹匣装弹,霰弹枪出来的原理)

扩展多位的原理如下:

在这里插入图片描述

第一片板子SER里面的数据满了,就经过线送到下一片板子里,SERCLK和RCLK接在同一个位置。这样可以形成多片级联,形成更多位的输出。

3. 单片机

单片机输出端口是弱上拉的,给1的时候电流小,给0的时候VCC流回负极电流更大,如果想要将点阵直接接在单片机的端口上是不可行的,需要加一个三极管,三极管作为放大电路使用。

4. 74HC595的使用

江科大使用的单片机上74HC595模块是有接LED灯,以观察输出情况的,我买的普中板子上没有,所以只是按照看视频的理解写了相应的函数。

#include <REGX52.H>// 重新命名,会更清晰一些,要不然容易不清楚每个口的具体含义
// P3^X:这不是异或的意思,这样的形式表示选中P3的第5位
sbit RClK = P3^5;  // 上升沿锁存,置1时将SER的内容全部写入输出口
sbit SRCLK = P3^6;  // 上升沿移位,置1时写入SER的内容
sbit SER = P3^4;void _74HC595_WriteByte(unsigned char byteData)
{unsigned char i = 0;for (i = 0; i < 8; i++){SER = byteData & (0x80 >> i); // 通过这种方式取出第i位// byteData & 0x80相当于选出最高位的数据,结果要么是1000 0000,要么是0000 0000// 因为SER的类型是sbit,所以要么为1要么为0// 如果结果1000 0000,SER就会被置为1// 如果结果0000 0000,SER就会被置为0// SRCLK原本为0,置1给一个电平写入,然后置0SRCLK = 1; SRCLK = 0;}// RClK原本为0,置1给一个电平统一转移数字,然后置0RClK = 1;RClK = 0;
}void main()
{SRCLK = 0; // 初始置0,后面才更容易给电平RClK = 0; // 初始置0,后面才更容易给电平while(1){}
}

5. 点阵屏的显示

在4中函数的基础上,选中列后再对行进行选中,就可以实现点阵屏显示

#include <REGX52.H>
#include "Delay.h"// 重新命名,会更清晰一些,要不然容易不清楚每个口的具体含义
// P3^X:这不是异或的意思,这样的形式表示选中P3的第5位
sbit RClK = P3^5;  // 上升沿锁存,置1时将SER的内容全部写入输出口
sbit SRCLK = P3^6;  // 上升沿移位,置1时写入SER的内容
sbit SER = P3^4;void _74HC595_WriteByte(unsigned char byteData)
{unsigned char i = 0;for (i = 0; i < 8; i++){SER = byteData & (0x80 >> i); // 通过这种方式取出第i位// byteData & 0x80相当于选出最高位的数据,结果要么是1000 0000,要么是0000 0000// 因为SER的类型是sbit,所以要么为1要么为0// 如果结果1000 0000,SER就会被置为1// 如果结果0000 0000,SER就会被置为0// SRCLK原本为0,置1给一个电平写入,然后置0SRCLK = 1; SRCLK = 0;}// RClK原本为0,置1给一个电平统一转移数字,然后置0RClK = 1;RClK = 0;
}void MatrixLED_ShowColumn(unsigned char column, byteData)
{_74HC595_WriteByte(byteData); // 选中列P0 = ~(0x80) >> column; // 选中指定行,给低电平点灯,column表示点亮第几行,做一个右移// 避免显示下一个的时候出现残影,Delay后置灭Delay(1);P0 = 0xFF;
}void main()
{SRCLK = 0; // 初始置0,后面才更容易给电平RClK = 0; // 初始置0,后面才更容易给电平MatrixLED_ShowColumn(7,0xAA);while(1){	}
}

对于为什么要Delay和置灭,是因为这通常是一个:

段选→位选→段选→位选→…的过程

这会导致一个问题,在执行第三个段选的时候,上一次的位选还没改变,从而导致上一次的位选和下一次的段选结合,呈现错误的结果/残影,所以正确的是:

段选→位选→延时→位清零→段选→位选→…的过程

6. 矩阵LED屏显示自定义图案

有了上面的基础,显示笑脸就很简单了,用EXCEL画出需要亮灯的部分:

在这里插入图片描述

第一列:0011 1100 → 3C

第二列:0100 0010 → 42

第三列:1010 1001 → A9

第四列:1000 0101 → 85

第五列:1000 0101 → 85

第六列:1010 1001 → A9

第七列:0100 0010 → 42

第八列:0011 1100 → 3C

主函数修改为:

void main()
{SRCLK = 0; // 初始置0,后面才更容易给电平RClK = 0; // 初始置0,后面才更容易给电平while(1){MatrixLED_ShowColumn(0, 0x3C);MatrixLED_ShowColumn(1, 0x42);MatrixLED_ShowColumn(2, 0xA9);MatrixLED_ShowColumn(3, 0x85);MatrixLED_ShowColumn(4, 0x85);MatrixLED_ShowColumn(5, 0xA9);MatrixLED_ShowColumn(6, 0x42);MatrixLED_ShowColumn(7, 0x3C);}
}

效果如下(长曝光):

在这里插入图片描述

类似的如果是一个爱心:

在这里插入图片描述

第一列:0001 1000 → 18

第二列:0010 0100 → 24

第三列:0100 0010 → 42

第四列:0010 0001 → 21

第五列:0010 0001 → 21

第六列:0100 0010 → 42

第七列:0010 0100 → 24

第八列:0001 1000 → 18

主函数修改为:

void main()
{SRCLK = 0; // 初始置0,后面才更容易给电平RClK = 0; // 初始置0,后面才更容易给电平while(1){MatrixLED_ShowColumn(0, 0x18);MatrixLED_ShowColumn(1, 0x24);MatrixLED_ShowColumn(2, 0x42);MatrixLED_ShowColumn(3, 0x21);MatrixLED_ShowColumn(4, 0x21);MatrixLED_ShowColumn(5, 0x42);MatrixLED_ShowColumn(6, 0x24);MatrixLED_ShowColumn(7, 0x18);}
}

效果如下(长曝光):

在这里插入图片描述

7. 矩阵LED屏显示动画

我们的LED一共是8列,显示动画的原理实际上是将每列要显示内容组成一个数组,然后每次显示其中8列,例如第一次显示数组的0-7,第二次显示数组的1-8,以此类推。

具体显示的字符动画可以利用字模提取软件获得对应的结果,使用步骤如下:

第一步:新建图像,高度为8(开发板上的高度,宽度选择对应字符长度)

在这里插入图片描述

第二步:模拟动画里放大格点

在这里插入图片描述

第三步:绘制格点:

在这里插入图片描述

第四步:生成点阵

在这里插入图片描述

接下来就可以写代码,实际上就是用循环来遍历上面的这些内容:

#include <REGX52.H>
#include "MatrixLED.h"
#include "Delay.h"unsigned char Animation[] = {0x00,0x00,0x00,0x00,0x00,0xFF,0x81,0x42,0x3C,0x00,0x0C,0x12,0x12,0x0E,0x01,0x00,0xFF,0x81,0x42,0x3C,0x00,0x0C,0x12,0x12,0x0E,0x01,0x00,0xFD,0x00,0x00,0x00,0x00, 
}; // 字符提取软件生成void main()
{unsigned int i = 0, offset = 0, count = 0; Init_Matrix_LED();while(1){for (i = 0; i < 8; i++) // for循环,8位循环显示{MatrixLED_ShowColumn(i, Animation[i+offset]); // offset表示数组中的便宜}// count这里实际上起到一个控制速度的作用,大于号后面的数字越大则动的越慢count++;if (count > 10){count = 0;offset++; // 增加偏移量if (offset > 24) offset = 0; // 偏移量到临界时需要重置,避免乱码}}
}

这里控制速度不能用Delay,用Delay的话点阵屏上的结果会消失,这应该是我们的点亮函数会置0导致的。

效果如下:

在这里插入图片描述

附上MatrixLED的内容:

MatrixLED.c:

#include <REGX52.H>
#include "Delay.h"// 重新命名,会更清晰一些,要不然容易不清楚每个口的具体含义
// P3^X:这不是异或的意思,这样的形式表示选中P3的第5位
sbit RClK = P3^5;  // 上升沿锁存,置1时将SER的内容全部写入输出口
sbit SRCLK = P3^6;  // 上升沿移位,置1时写入SER的内容
sbit SER = P3^4;#define MATRIX_LED_PORT P0/*** @brief 矩阵LED初始化* @param 无* @retval 无*/
void Init_Matrix_LED()
{SRCLK = 0; // 初始置0,后面才更容易给电平RClK = 0; // 初始置0,后面才更容易给电平
}/*** @brief 74HC595 写入一个字节* @param 要写入的字节* @retval 无*/
void _74HC595_WriteByte(unsigned char byteData)
{unsigned char i = 0;for (i = 0; i < 8; i++){SER = byteData & (0x80 >> i); // 通过这种方式取出第i位// byteData & 0x80相当于选出最高位的数据,结果要么是1000 0000,要么是0000 0000// 因为SER的类型是sbit,所以要么为1要么为0// 如果结果1000 0000,SER就会被置为1// 如果结果0000 0000,SER就会被置为0// SRCLK原本为0,置1给一个电平写入,然后置0SRCLK = 1; SRCLK = 0;}// RClK原本为0,置1给一个电平统一转移数字,然后置0RClK = 1;RClK = 0;
}/*** @brief LED点阵屏显示一列数据* @param * @retval*/
void MatrixLED_ShowColumn(unsigned char column, byteData)
{_74HC595_WriteByte(byteData); // 选中列MATRIX_LED_PORT = ~(0x80) >> column; // 选中指定行,给低电平点灯,column表示点亮第几行,做一个右移// 避免显示下一个的时候出现残影,Delay后置灭Delay(1);MATRIX_LED_PORT = 0xFF;
}

MatrixLED.h:

#ifndef __MATRIXLED_H__
#define __MATRIXLED_H__void Init_Matrix_LED();
void _74HC595_WriteByte(unsigned char byteData);
void MatrixLED_ShowColumn(unsigned char column, byteData);#endif

Delay.c:

#include <INTRINS.H>void Delay(unsigned int ms)		//@11.0592MHz
{unsigned char i, j;while (ms){_nop_();i = 2;j = 199;do{while (--j);} while (--i);ms--;}
}

Delay.h:

#ifndef __DELAY_H__
#define __DELAY_H__void Delay(unsigned int ms);#endif
http://www.lryc.cn/news/571874.html

相关文章:

  • Mac m1 通过docker镜像安装kafka
  • 【GateWay】和权限验证
  • RKNN开发环境搭建3-RKNN Model Zoo 板载部署以Whisper为例
  • 【AI作画】用comfy ui生成漫画风图画
  • spring-webmvc @InitBinder 典型用法
  • 架构优化——submodule转为subtree
  • ES 索引加载 vs BulkLoad
  • ArcGIS中利用泰森多边形法分析站点与流域占比
  • docker拉取Elasticsearch和Kibana
  • python3:线程管理进程
  • C++ 进阶:深入理解虚函数、继承与多态
  • 管件接头的无序抓取
  • C++11中alignof和alignas的入门到精通指南
  • 大语言模型指令集全解析
  • ATX电源
  • Java 淘宝商品详情接口实战解析
  • 小白成长之路-Rsync+sersync实现数据实时同步
  • 基于集体智能长尾识别的超声乳腺病变亚型分类|文献速递-深度学习医疗AI最新文献
  • 从零接入高德路径规划2.0:实现精准物流距离计算实战
  • FPGA基础 -- Verilog行为级建模之initial语句
  • C++11 移动语义详解
  • 基于大模型的胆囊结石全周期诊疗方案研究报告
  • vue3 javascript 多字段求和技巧
  • BitsAndBytes(简称 BnB)是一个用于“压缩”大语言模型的工具包
  • OpenStack入门
  • Karate UI 基本概念之一
  • python校园服务交流系统
  • 自动打电话软件设计与实现
  • cloudera manager 页面启动nodemanager失败,后端没有启动 8040
  • Python装饰器decorators和pytest夹具fixture详解和使用