江科大51单片机笔记【8】LED点阵屏显示图形及动画(下)
一.LED点阵屏显示图形
1.硬件知识
C51的sfr、sbit
- 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个寄存器中,只有一个是可以位寻址的。对不可位寻址的寄存器,若要只操作其中一位而不影响其他位时,可用“&=”、“|=”、“^="的方式进行位操作
2.调试74HC595
普中的新板子需要 在 main 函数中加一句 P0_0 = 0; 然后确保 LEd 点阵屏左侧的 J24 跳线帽 OE 和 GND 连接在一起
#include <REGX52.H>
//为了使接口更易懂以及避免重复定义
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
//不能以数字为开头
//目的:把参数写入到D0~D7的引脚,相当于赋值
//由原理图可知,第一位进来的是高位
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
//循环一次即将8位数据都移进去
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i); //1000 0000 如果最高位为1则为1(非0即1).如果最高位为0则为0
SCK=1; //给一个上升沿
SCK=0;
}
RCK=1; //把8位数据输出
RCK=0;
}
// //把最高位移进去
// SER=Byte&0x80; //1000 0000 如果最高位为1则为1(非0即1).如果最高位为0则为0
// SCK=1; //给一个上升沿
// SCK=0;//给完马上置0
//
// //把次高位移进去
// SER=Byte&0x40; //0100 0000 如果最高位为1则为1(非0即1).如果最高位为0则为0
// SCK=1; //给一个上升沿
// SCK=0;//给完马上置0
//
// //....
void main()
{
//一开始把这两个都置0
SCK=0;
RCK=0;
_74HC595_WriteByte(0xF0);
P0_0=0;
while(1)
{
}
}
现象
3.开始写代码
这里我们可以参考数码管的代码,因为点阵屏和数码管操作方式差不多
验证一下显示函数
//节选,该部分为上文的代码改动而来
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
_74HC595_WriteByte(Data);
P0=~(0x80>>Column);
}
void main()
{
//一开始把这两个都置0
SCK=0;
RCK=0;
MatrixLED_ShowColumn(7,0xAA);
while(1)
{
}
}
这里设计一个跟数码管一样的知识
残影问题:一般数码管运行都是 段选 位选 段选 位选 段选 位选。但会出现段选串位到上一个的位选的问题 ,就需要在位选后,延时,并位清零,即段选 位选 延时 位清零 段选 位选 延时 位清零 段选 位选 延时 位清零。
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
_74HC595_WriteByte(Data);
P0=~(0x80>>Column);
Delay(1);
P0=0xFF;
}
利用Excel表格编写笑脸的代码
按列扫描转换为二进制即为
0x3C 0x42 0xA9 0x85 0x85 0xA9 0x42 0x3C
下面为main完整代码
#include <REGX52.H>
#include " Delay.h"
//为了使接口更易懂以及避免重复定义
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
//不能以数字为开头
//目的:把参数写入到D0~D7的引脚,相当于赋值
//由原理图可知,第一位进来的是高位
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
//循环一次即将8位数据都移进去
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i); //1000 0000 如果最高位为1则为1(非0即1).如果最高位为0则为0
SCK=1; //给一个上升沿
SCK=0;
}
RCK=1; //把8位数据输出
RCK=0;
}
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
_74HC595_WriteByte(Data);
P0=~(0x80>>Column);
Delay(1);
P0=0xFF;
}
void main()
{
//一开始把这两个都置0
SCK=0;
RCK=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);
}
}
现象
4.优化代码
把P0口给定义
给函数注释
#include <REGX52.H>
#include " Delay.h"
//为了使接口更易懂以及避免重复定义
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
#define MATRIX_LED_PORT P0
/**
* @brief 74HC595写入一个字节
* @param 要写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
//循环一次即将8位数据都移进去
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i); //1000 0000 如果最高位为1则为1(非0即1).如果最高位为0则为0
SCK=1; //给一个上升沿
SCK=0;
}
RCK=1; //把8位数据输出
RCK=0;
}
/**
* @brief LED点阵屏显示一列数据
* @param Column 要选择的列,范围:0~7,0在最左边
* @param Data 选择的列显示的数据,高位在上,1为亮,0为灭
* @retval 无
*/
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
_74HC595_WriteByte(Data);
MATRIX_LED_PORT=~(0x80>>Column);
Delay(1);
MATRIX_LED_PORT=0xFF;
}
void main()
{
//一开始把这两个都置0
SCK=0;
RCK=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);
}
}
二、LED点阵屏显示动画
1.把上面的函数模块化
//MatrixLED.h
#ifndef __MATRIXLED_H_
#define __MATRIXLED_H_
void MatrixLED_ShowColumn(unsigned char Column,Data);
void MatrixLED_Init();
#endif
//MatrixLED.c
#include <REGX52.H>
#include " Delay.h"
//为了使接口更易懂以及避免重复定义
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
#define MATRIX_LED_PORT P0
/**
* @brief 74HC595写入一个字节
* @param 要写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
//循环一次即将8位数据都移进去
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i); //1000 0000 如果最高位为1则为1(非0即1).如果最高位为0则为0
SCK=1; //给一个上升沿
SCK=0;
}
RCK=1; //把8位数据输出
RCK=0;
}
/**
* @brief 点阵屏初始化
* @param 无
* @retval 无
*/
void MatrixLED_Init()
{
SCK=0;
RCK=0;
}
/**
* @brief LED点阵屏显示一列数据
* @param Column 要选择的列,范围:0~7,0在最左边
* @param Data 选择的列显示的数据,高位在上,1为亮,0为灭
* @retval 无
*/
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
_74HC595_WriteByte(Data);
MATRIX_LED_PORT=~(0x80>>Column);
Delay(1);
MATRIX_LED_PORT=0xFF;
}
//main.c
#include <REGX52.H>
#include " Delay.h"
#include " MatrixLED.h"
void main()
{
void MatrixLED_Init();
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);
}
}
验证发现没问题
2.开始写代码
思路:做一个长条的数组存储,从左往右移动就是数组
这里用到一个工具,取字模软件,在下面这里有
https://jiangxiekeji.com/download.html
0xFF,0x08,0x08,0x08,0xFF,0x00,0x0E,0x15,0x15,0x15,0x08,0x00,0x7E,0x01,0x02,0x00,
0x7E,0x01,0x02,0x00,0x06,0x09,0x09,0x06,0x00,0x7D,0x00,0x00,0x00,0x00,0x00,0x00,
把前8项全部显示在点阵屏上
for(i=0;i<8;i++)
{
MatrixLED_ShowColumn(i,Animation[i]);
}
当选择数据时,带上后面7个数据,这样在递增的过程(从第一个往右选择数据),就会一直带上后面7个数据,这样就会形成整体8个数据一起移动的效果。
#include <REGX52.H>
#include " Delay.h"
#include " MatrixLED.h"
unsigned char Animation[]={
// 前后加上八个00,这样确保最后一个字符在移动完后再开始下一次显示
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0x08,0x08,0x08,0xFF,0x00,0x0E,0x15,
0x15,0x15,0x08,0x00,0x7E,0x01,0x02,0x00,
0x7E,0x01,0x02,0x00,0x06,0x09,0x09,0x06,
0x00,0x7D,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
void main()
{
unsigned char i,Offset,Count=0;
MatrixLED_Init();
while(1)
{
for(i=0;i<8;i++)
{
MatrixLED_ShowColumn(i,Animation[i+Offset]);
}
Count++; //这里不能直接用延时函数延时,因为在一直扫描
if(Count>10) //这里相当于每隔1帧扫描10次
{
Count=0;
Offset++;
if(Offset>40) //防止Offset溢出,数组48个-选择8个=40个,让“!”移除点阵屏再开始下一次循环
{
Offset=0;
}
}
}
}
到这里已经完成了流动字幕的动画
如果想实现逐帧的动画,下面还有
3.变式
#include <REGX52.H>
#include " Delay.h"
#include " MatrixLED.h"
unsigned char code Animation[]={
// 加上八个00循环一次后再回来
0x3C,0x42,0xA9,0x85,0x85,0xA9,0x42,0x3C,
0x3C,0x42,0xA1,0x85,0x85,0xA1,0x42,0x3C,
0x3C,0x42,0xA5,0x89,0x89,0xA5,0x42,0x3C,
};
void main()
{
unsigned char i,Offset=0,Count=0;
MatrixLED_Init();
while(1)
{
for(i=0;i<8;i++)
{
MatrixLED_ShowColumn(i,Animation[i+Offset]);
}
Count++; //这里不能直接用延时函数延时,因为在一直扫描
if(Count>15) //这里相当于每隔1帧扫描10次
{
Count=0;
Offset+=8; //这里直接加8,每一次显示完一帧后直接跳到下一帧
if(Offset>16) //防止Offset溢出,数组48个-选择8个=40个
{
Offset=0;
}
}
}
}
这里写的是这三帧在一直跳动形成动画
如果想要加更多的帧就要在Animation[]数组里加
这里设计一个小知识
我们单片机有两个暂存器
一种是程序运行时的暂存器叫RAM
另一种是放在Flash里面的程序存储器,这里的空间会很大,但是不可以再改变数组的内容,不过我们动画一般都不会更改,所以我们一直会放在Flash里面
即在上图中数组前加上“code”关键字