单片机学习笔记---LED点阵屏显示图形动画

目录

LED点阵屏显示图形

LED点阵屏显示动画

最后补充


上一节我们讲了点阵屏的工作原理,这节开始代码演示!

前面我们已经说了74HC595模块也提供了8个LED,当我们不使用点阵屏的时候也可以单独使用74HC595,这8个LED可以用来测试74HC595。

那接下来我们可以先不使用点阵屏,先用这8个LED来测试一下74HC595。

LED点阵屏显示图形

新创建一个工程:LED点阵屏显示图形。

写上框架

接下来先补充几个关键词的介绍:

sfr(special function register):特殊功能寄存器声明

例:sfr P0 = 0x80;

声明P0口寄存器,物理地址为0x80

sbit(special bit):特殊位声明

例:sbit P0_1 = 0x81;    或    sbit P0_1 = P0^1;

声明P0寄存器的第1位

可位寻址/不可位寻址:在单片机系统中,操作任意寄存器或者某一位的数据时,必须给出其物理地址,又因为一个寄存器里有8位,所以位的数量是寄存器数量的8倍,单片机无法对所有位进行编码,故每8个寄存器中,只有一个是可以位寻址的。对不可位寻址的寄存器,若要只操作其中一位而不影响其它位时,可用“&=”、“|=”、“^=”的方法进行位操作。

“&=”一般用于对某一位清零

“|=”一般用于对某一位置1

“^=”异或等于,一般用于对某一位取反,不一样就置1,一样就置0。异或这个用的比较少。

为什么要补充这几个关键词的介绍呢?是因为我们接下来要操作P35等三个引脚,但是为了方便操作,我们想要对这三个引脚重新命名,所以我们就要使用重命名的方法。

比如我们要重新命名P3的第五位,可以这样写

这样我们之后操作RCLK就等于操作P3的第5位

但是由于RLCK已经被头文件定义过了,所以我们不能重复定义,所以把这三个引脚都重新命名为:

不同的人对同一个引脚的命名也不一样,所以今后在芯片手册上要注意对应。

接下来我们写一个子函数,它的作用是将参数传过来的数据给写入那8个引脚,相当于给那8个引脚赋值。

由于推进来的第一位是要赋值给QH的,所以我们要先将参数的字节的最高位取出来

PS:SER是一位数据,如果将8位数据赋给它的话,非0即1

按照上一篇博客讲的工作原理,接下来要先给SERCLK初始化,

然后再给它一个高电平(上升沿),

SER过来的数据在移位寄存器中下降一位之后再将SERCLK清零,为下一次移位做准备。

到这里Byte的最高位已经操作完毕,接下来是移动次高位

同理,其他位也是这样操作,如此循环,我们可以用一个for循环语句

移完8位数据后,要输出就得先初始化RCLK锁存数据,

然后给它一个上升沿,

最后同时输出8位数据后,给它清零

这样操作完后,就能把8位数据送到IO口上了。

最后我们调用这个子函数测试一下

最后整个代码是这样的

编译无错误后看一下效果

的确这八个LED一半亮一半灭

测试74HC595完毕,接下来我们就开始实际操作点阵屏

我们会用到Delay函数的代码,所以要添加进本节的工程文件中。

由于点阵屏的操作和数码管的操作类似,所以我可以参照数码管的那一节代码。

这是我们之前写一个数码管的子函数,location表示它显示的位置,Number表示显示的段码

同理,我们把点阵屏的每一列看成location,每一行看成是段码

那么我们要添加一个子函数,column表示列,data表示行的数据

然后我们在这里先调用一下行的数据

然后要选择列的数据

这样写表示如果用户选的是第0列,也就是P07列,

那么就让P07=0,其他位等于1,所以就取反0x80=0111 1111

如果选P06=0,要如此循环写if语句,我们可以用另一种写法避免重复写if语句

然后调用一下这个函数

完整代码:

编译无错误测试一下

的确是第0列,行显示亮灭亮灭。

如果想要进行扫描的话,也需要进行消影,和数码管的操作类似(之前我们说数码管进行段选-位选-段选-位选-段选-位选的过程中,刚把下一位段选送入的时候,它会把这个段选送到上一位选,因为上一位的位选还没来得及改变,产生串位的现象,也就是残影的问题)

那么我们需要加一个过程,就是位选后,位选清零。

于是就变成段选-位选-位清零-段选-位选--位清零-段选-位选

顺便加一段延时过程

段选-位选-延时-位清零-段选-位选-延时-位清零-段选-位选

这样即使它发生串位也是串到了位清零的时刻,但是位清零什么都没有显示,这样就能达到消影的效果。

但是这样的结果是它执行一次就会马上灭掉,所以我们把调用放在while循环里面让它循环执行。

这样我们还可以显示多位了,比如让它显示一条对角的斜线

如果我们想要想让显示一个笑脸的话,可以先把效果脸的图画在一个表格中

上图画1的位置点亮,其他不亮

我们要一列一列的进行扫描,把每一列的数据转换成16进制

每一列的数据转换好之后写进去

效果

成功显示了一个笑脸

然后我们对这个代码进行一下优化

重定义一下P0

然后把我们程序中的所有P0口改成MATRIX_LED_PORT

增加这行代码的意义在于当我们用不同开发板(有可能不同引脚)运行程序时,我们只需要修改#define MATRIX_LED_PORT P0后面的P0就行,而无需逐个修改整个程序中的P0。

优化后的代码加上注释:

PS: void MatrixLED_ShowColumn(unsigned char Column,Data)这个子函数这里给1为亮,0为灭是因为:

根据点阵屏的原理图,列是把每列LED的阴极连在一起,行是把每行LED的阳极两在一起,当阴极(列)给0,阳极(行)给1的时候,对应的LED才会亮)

因此段选==选择行给了低电平之后,位选==选择列就要给高电平1才能让对应的LED亮。

第一个代码演示完毕,源码放在评论区了,自取!

LED点阵屏显示动画

接下来我们演示第二个示例代码:显示动画

把第一个代码演示的工程文件复制一份,打开后改造并把程序模块化一下

main.c

MatrixLED.c

MatrixLED.h

然后接下来我们来写显示动画

比如我们想要显示一串字符“Hello!”然后让它向左流动。

那么我们先要存一个数组,这个数组存的是一长条的整幅动画,就是8*32或者更多。

接下来我们介绍一个辅助工具:文字取模软件

这个软件可以自动帮我们数据给提出来

首先要新建图像

填上宽度和高度32*8,点击确定后创建好一个区域

放大格点

参数设置,其他选项

纵向取模,确定

然后在这里我们可以手动地画一下我们要显示的东西

字幕画好了之后

然后我们先控制前八个显示,依次往后移动,然后字幕就能在屏幕上流动

取模方式,C51格式,下面就显示出来了我们的数据

直接把这个数据复制过来,放进我们定义好的数组里面

这个数组内每一个元素分别表示每一列的数据

然后我们需要定义数组元素的下标

然后用一个for循环让它循环流动显示每一列的数据

然后我们需要定义一个变量offset表示偏移量。

我们一开始一个for循环显示完8个元素(0~7号元素),也就是动画第一帧结束。

i+offset就让它从第二个元素开始显示起(1~8号元素),也就是动画的第二帧。每次+offset就从下一位循环起,如此反复每一次都把起始位置移一下,就形成了动画的很多帧。

然后我们还需要定义一个Count,每扫描一次后就+1,扫描10次的话相当于延时了(因为每一帧让它循环显示10遍,这样做的目的是让前一帧动画显示的时间长一点,为了避免下一帧动画显示时把前一帧动画给覆盖掉,显示10遍的话即使下一帧动画来覆盖也不可能完全覆盖掉前一帧动画)

Offset一直在加,当它加到头的时候溢出数组了,所以后面就产生乱码

为了防止数组溢出,再加一个if语句

但是这样写的话当它执行完所有帧的时候它会直接跳回到第一帧,这样显得比较突兀,我们可以在数组第一个元素前再加8个元素0x00(刚好等于一帧动画),然后最后一个元素后面也加8个元素0x00,这样就相当于第一帧动画前加一帧空白帧,最后一帧动画后面也加一帧空白帧,让最后一帧动画和第一帧动画能对接上,这样字幕播放就是流畅的,不会出现最后一帧动画播放完后立马又闪现出第一帧的动画的现象!

然后这里改成32

但是这样写还有一个问题就是播放最后一帧动画显示感叹号的时候,感觉突然消失了

我们可以再改成40,也就是让屏幕显示完40个数据(前五帧)后再对offset清零,这样就能让最后一个感叹号显示完之后显示一个空白帧然后再对offset清零,进入第二轮字幕播放。

最后主程序的代码如下:

效果请看视频:

用点阵屏做的流动字幕

最后补充

如果想要用点阵屏做一个逐帧动画也是可以的,可以自己琢磨在这个程序的基础上改。

还要说明一点:如果想要设置更长的动画的话,就在数组里面去更多的数据,就可以显示不同的动画。但是这个数组元素是否能无限加呢?不能!如果数据过多的时候,单片机的RAM就不够,因为我们这个数组的数据其实是存放在RAM里面的。

单片机有两种存储,一种是程序运行时的暂存器叫RAM,另一种是放在Flash里面的程序存储器(持续空间会大一些)。

而且这个数组里面的数据一般不需要改变其中的值,所以放在RAM里面会很浪费内存,所以我们一般把它放在flash里面。

怎么放在flash里面呢?需要在这里加一个关键字code,加上这个关键字后,数组里面的数据就会放在flash里面。

但是如果将这个数组放在code里面有个不好的地方就是这个数组不能更改了。如果尝试将数组中的某个元素重新赋值,程序就会报错。

以上就是本节的全部内容,源码已经放在评论区,如有问题可在评论区留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2775522.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

了解海外云手机的多种功能

随着社会的高度发展,海外云手机成为商家不可或缺的工具,为企业出海提供了便利的解决方案。然而,谈及海外云手机,很多人仍不了解其强大功能。究竟海外云手机有哪些功能,可以为我们做些什么呢? 由于国内电商竞…

ChatGPT 变懒最新解释!或和系统Prompt太长有关

大家好我是二狗。 ChatGPT变懒这件事又有了最新解释了。 这两天,推特用户Dylan Patel发文表示: 你想知道为什么 ChatGPT 和 6 个月前相比会如此糟糕吗? 那是因为ChatGPT系统Prompt是竟然包含1700 tokens,看看这个prompt里面有多…

波奇学Linux: 文件描述符

文件和操作系统的关系 操作系统控制进程,文件的打开是在进程中进行。意味着用来控制进程的PCB必然有文件的信息,操作系统通过控制PCB的信息来控制文件的读写。 Q1:如何证明文件打开是在进程中进行? 编写c文件调用fopen来操作文件…

032-安全开发-JavaEE应用Servlet路由技术JDBCMybatis数据库生命周期

032-安全开发-JavaEE应用&Servlet路由技术&JDBC&Mybatis数据库&生命周期 #知识点: 1、JavaEE-HTTP-Servlet技术 2、JavaEE-数据库-JDBC&Mybatis 演示案例: ➢JavaEE-HTTP-Servlet&路由&周期 ➢JavaEE-数据库-JDBC&Mybat…

2月3日作业

1.编程实现单向循环链表的头插&#xff0c;头删、尾插、尾删 尾插/头插&#xff0c;头删&#xff0c;尾删&#xff1a; 头文件&#xff1a; #ifndef __HEAD_H_ #define __HEAD_H_#include<stdio.h> #include<string.h> #include<stdlib.h>enum {FALSE-1,SU…

大模型是如何实现Function Call函数调用的?

▼最近直播超级多&#xff0c;预约保你有收获 近期直播&#xff1a;《Agent 企业级应用案例实战》 —1— 大模型如何实现函数调用&#xff1f; 大模型要实现精确的函数调用&#xff08;Function Call&#xff09;需要理解能力和逻辑能力&#xff0c;理解能力就是对用户的 Prom…

Ps:窗口排列

Ps菜单&#xff1a;窗口/排列 Window/Arrange Photoshop 的“窗口/排列” Arrange子菜单中提供了多种方式来组织和查看打开的文档窗口&#xff0c;这在处理多个文档或比较图像时非常有用。 ◆ ◆ ◆ 常用操作方法与技巧 1、同文档双窗口处理法 将同一个图像显示在两个窗口中&…

基于java+springboot+vue实现的仓库管理系统(文末源码+Lw)23-115

1 绪论 现在大家正处于互联网加的时代&#xff0c;对于物资信息的管理来说&#xff0c;传统的通过纸质文档记录信息的方式已经落后了&#xff0c;依靠手工管理物资信息&#xff0c;不仅花费较长的工作时间&#xff0c;在对记录各种信息的文档进行信息查询以及信息核对操作时&a…

【doghead】uv_loop_t的创建及线程执行

worker测试程序,类似mediasoup对uv的使用,是one loop per thread 。创建一个UVLoop 就可以创建一个uv_loop_t Transport 创建一个: 试验配置创建一个: UvLoop 封装了libuv的uv_loop_t ,作为共享指针提供 对uv_loop_t 创建并初始化

vue3 之 商城项目—二级分类

二级分类功能描述 配置二级路由 准备组件模版 <script setup></script><template><div class"container "><!-- 面包屑 --><div class"bread-container"><el-breadcrumb separator">"><el-bre…

初识NodeJS

本文主要基于极客时间《Nodejs开发实战》课程。 本篇&#xff08;一&#xff09;为课程的第二篇——技术预研篇。 什么是Nodejs? 来源官网&#xff1a; Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型&#x…

计算机网络相关题目及答案(第八章)

第八章 习题&#xff1a; P19. 考虑下面对于某 SSL会话的一部分的Wireshark输出。 a. Wireshark分组112是由客户还是由服务器发送的? b.服务器的IP地址和端口号是什么? c.假定没有丢包和重传&#xff0c;由客户发送的下一个TCP报文段的序号将是什么? d. Wireshark分组…

(十七)springboot实战——spring securtity的授权流程源码解析

前言 本节内容是关于spring security安全框架授权流程的源码分析&#xff0c;spring security的授权流程主要是在FilterSecurityInterceptor过滤器中实现的。我们会通过源码层级的分析&#xff0c;了解清楚spring security的底层是如何实现用户授权的。 正文 1.配置一个请求…

【LeetCode每日一题】525连续数组 303区域和检索(前缀和的基本概念和3个简单案例)

前缀和 // 构造prefix let prefix [0] arr.forEach(num > {prefix.push(prefix.at(-1) num); })如果想要计算某个区间 i 到 j 这个子数组的和时&#xff0c;可以根据 prefix[j1] - prefix[i] 获得。 例题1&#xff1a;303.区域和检索 - 数组不可变 给定一个整数数组 num…

【stomp实战】websocket原理解析与简单使用

一、WebSocket 原理 WebSocket是HTML5提供的一种浏览器与服务器进行全双工通讯的网络技术&#xff0c;属于应用层协议。它基于TCP传输协议&#xff0c;并复用HTTP的握手通道。浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直接可以创建持久性的连接&#xff0c; 并…

有道ai写作,突破免费限制,无限制使用

预览效果 文末提供源码包及apk下载地址 有道ai写作python版 import hashlib import time import json import ssl import base64 import uuidfrom urllib.parse import quote import requests from requests_toolbelt.multipart.encoder import MultipartEncoder from Crypto…

Redis核心技术与实战【学习笔记】 - 27.限制Redis Cluster规模的因素(通信开销)

简述 Redis Cluster 能保存的数据量以及支撑的吞吐量&#xff0c;跟集群实例规模相关。 Redis 官方给出了 Redis Cluster 的规模上线&#xff0c;就是一个集群运行 1000 个实例。 其实&#xff0c;限定 Redis Cluster 集群规模的一个关键因素就是&#xff0c;实例间的通信开销…

TCP 传输控制协议

1 TCP 1.1 TCP 最主要的特点 1.TCP 是面向连接的运输层协议。 2.每一条 TCP 连接只能有两个端点 (endpoint)&#xff0c;每一条 TCP 连接只能是点对点的&#xff08;一对一&#xff09;。 3.TCP 提供可靠交付的服务。 4.TCP 提供全双工通信。 5.面向字节流 TCP 中的“流…

力扣刷题之旅:进阶篇(二)

力扣&#xff08;LeetCode&#xff09;是一个在线编程平台&#xff0c;主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目&#xff0c;以及它们的解题代码。 继续我的力扣刷题之旅&#xff0c;在上一篇文章中&#xff0c;我深入探索了图算法和动态…

Unity3d Shader篇(六)— BlinnPhong高光反射着色器

文章目录 前言一、BlinnPhong高光反射着色器是什么&#xff1f;1. BlinnPhong高光反射着色器的工作原理2. BlinnPhong高光反射着色器的优缺点优点缺点 3. 公式 二、使用步骤1. Shader 属性定义2. SubShader 设置3. 渲染 Pass4. 定义结构体和顶点着色器函数5. 片元着色器函数 三…