【51单片机】程序实验7&8.IO扩展-LED点阵

发布于:2024-11-28 ⋅ 阅读:(13) ⋅ 点赞:(0)

主要参考学习资料:B站【普中官方】51单片机手把手教学视频

前置知识:C语言

单片机套装:普中STC51单片机开发板A4标准版套餐7

码字不易,求点赞收藏加关注(´•ω•̥`)

有问题欢迎评论区讨论~

IO扩展-74HC595

外围设备占用的IO口较多,而51单片机提供的IO口非常有限,如果想要连接更多的外围设备,就需要通过扩展IO来实现。这里使用的是74HC595的串转并芯片。

74HC595芯片介绍

74HC95是一个8位串行输入、并行输出的位移缓存器,其中并行输出位三态输出(高电平、低电平和高阻抗)。

Q0~Q7为并行数据输出,Q7’为串行数据输出,DS为串行数据输入。通过串行数据输出连接串行移位输入可以级联多个595芯片,从而实现IO扩展功能。

MR为低电平复位引脚,名称上面一杠说明低电平有效。

SH_CP为移位寄存器的时钟输入,控制移位寄存器一位一位地移动存储数据;ST_CP为存储寄存器的时钟输入,控制移位寄存器中的八位数据一次性全部存储到存储寄存器当中。存储寄存器的输出连接到并行输出口Q0~Q7。

OE为使能引脚,如果该信号为低电平则并行输出口可以输出。

硬件介绍

在51单片机中,595芯片被用于LED点阵。LED点阵的原理与矩阵按键类似,对行共阳、列共阴二极管来说,列线路低电平、行线路高电平时相应的二极管被点亮。

595芯片的两个时钟输入分别连接P35-36引脚,串行输入连接P34引脚,用于接受并行输出的数据。并行输出引脚与LED点阵行线路引脚一一对应。

实验7-1 IO扩展

实现功能:通过72HC595模块控制LED点阵以行为单位循环滚动显示。

#include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

//定义两个时钟输入和串行输入端口
sbit SRCLK = P3^6;
sbit rCLK = P3^5;
sbit SER = P3^4;

//定义LED点阵端口
#define LED_COL_PORT P0

//存储点亮LED点阵每一行的串行输入数据
u8 ghc595_buf[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

//延时函数
void delay_10us(u16 time)
{
	while(time --);
}

//写入串行输入数据函数
void hc595_write_data(u8 dat)
{
	u8 i = 0;
	for(i = 0;i < 8;i++)
	{
        //移位寄存器从高位到低位依次存储数据
		SER = dat >> 7; //存储最高位
		dat <<= 1; //将数据的次高位移到最高位
        
        //移位寄存器时钟产生上升沿(低电平转高电平瞬间)控制SER写入寄存器
		SRCLK = 0;
		delay_10us(1); //寄存器对传输延时有要求(详见芯片手册),该芯片寄存器延时为纳秒级
		SRCLK = 1;
	}
    //移位寄存器存储完8位数据,存储寄存器时钟产生上升沿控制数据转入存储寄存器
	rCLK = 0;
	delay_10us(1);
	rCLK = 1;
}

void main()
{
	u8 i = 0;
    //LED点阵列线路有下拉电阻默认高电平,需全部设为低电平
	LED_COL_PORT = 0x00;
	while(1)
	{
		for(i = 0;i < 8;i++)
		{
            //将使LED点阵每一行点亮的串行输入数据依次写入芯片
			hc595_write_data(ghc595_buf[i]);
			delay_10us(50000);
		}
	}
}

使用LED点阵需要用黄色跳线帽插在GND和OE上将OE接地,使并行输出口有效。不使用时则插在OE和VCC上。

实验7-2 IO扩展(595级联)

当多个595芯片级联时,数据会以字节为单位从前一个芯片推向后一个芯片。以上图为例,当四个字节的数据存入移位寄存器后,在转入存储寄存器时,最后一个字节在该芯片并行输出,而前三个字节溢出到串行输出口传输到第二个芯片。以此类推,第二个芯片并行输出倒数第二个字节,前两个字节溢出到第三个芯片;第三个芯片并行输出第二个字节,第一个字节溢出到最后一个芯片并行输出。其中前两个芯片分别控制前八行、后八行,后两个芯片分别控制前八列、后八列。

实现功能:通过72HC595模块控制LED点阵以行为单位循环滚动显示。

#include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

sbit SRCLK = P3^6;
sbit rCLK = P3^5;
sbit SER = P3^4;

//传入四个一字节的参数,按并行输出芯片序号命名,如dat1由第一个芯片并行输出
void hc595_write_data(u8 dat1, u8 dat2, u8 dat3, u8 dat4)
{
	u8 i = 0;
    //输入顺序与输出芯片顺序相反,所以按倒序依次写入四个字节的数据
	for(i = 0;i < 8;i++)
	{
		SER = dat4 >> 7;
		dat4 <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
    for(i = 0;i < 8;i++)
	{
		SER = dat3 >> 7;
		dat3 <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
    for(i = 0;i < 8;i++)
	{
		SER = dat2 >> 7;
		dat2 <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
    for(i = 0;i < 8;i++)
	{
		SER = dat1 >> 7;
		dat1 <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
    //四个字节的数据都写入移位寄存器后传入存储寄存器
	rCLK = 0;
	delay_10us(1);
	rCLK = 1;
}

void main()
{
	u8 i = 0;
	while(1)
	{
        //先通过第一个芯片遍历1~8行,再通过第二个芯片遍历9~16行,后两个芯片控制所有列线路为低电平
		for(i = 0;i < 8;i++)
		{
			hc595_write_data(ghc595_buf[i], 0, 0, 0);
			delay_10us(50000);
		}
        for(i = 0;i < 8;i++)
		{
			hc595_write_data(0, ghc595_buf[i], 0, 0);
			delay_10us(50000);
		}
	}
}

LED点阵

LED点阵介绍

LED点阵是由发光二极管排列组成的显示器件,被广泛应用于汽车报站器、广告屏等。

LED点阵每一行的阳极并联在一起,每一列的阴极并列在一起,行为高电平、列为低电平的二极管被点亮。硬件结构在上一节已经介绍过。

实验8-1 点亮一个点

实现功能:在点阵屏上点亮一个点,以左上角的点为例。

//main函数之外的程序与实验7-1相同
#include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

sbit SRCLK = P3^6;
sbit rCLK = P3^5;
sbit SER = P3^4;

#define LED_COL_PORT P0

void delay_10us(u16 time)
{
	while(time --);
}

void hc595_write_data(u8 dat)
{
	u8 i = 0;
	for(i = 0;i < 8;i++)
	{
		SER = dat >> 7;
		dat <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
	rCLK = 0;
	delay_10us(1);
	rCLK = 1;
}

void main()
{
	LED_COL_PORT = 0x7f; //列线路设为01111111
	while(1)
	{
		hc595_write_data(0x80); //行线路设为10000000
	}
}

实验8-2 显示数字

实现功能:在点阵屏上显示一个数字。

数字、文字等点阵图像较为复杂,可以通过字模提取软件方便地生成点阵数据。

软件界面:

基本操作-新建图像,根据点阵屏大小选择行列数,生成空白点阵:

模拟动画-放大格点:

在点阵上点选需要点亮的点:

参数设置-其他选项,取模方式根据扫描方式选择横向或纵向,横向取模数据为每一行点亮对应列LED的数据,纵向取模数据为每一列点亮对应行LED的数据,字节倒序根据开发板接线顺序按需勾选,输出数据的数位对应点阵上的行和列上高下低、左高右低时不勾选,反之则勾选:

取模方式-C51格式,在点阵生成区生成字模数据。

#include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

sbit SRCLK = P3^6;
sbit rCLK = P3^5;
sbit SER = P3^4;

#define LED_COL_PORT P0

//控制数字0每一列上哪些行的点被点亮的数据,即纵向取模生成的字模数据
u8 gled_row[8] = {0x00, 0x7C, 0x82, 0x82, 0x82, 0x7C, 0x00, 0x00};
//控制点阵依次扫描每一列的数据,即被扫描列为低电平,其余为高电平
u8 gled_col[8] = {0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe};

void delay_10us(u16 time)
{
	while(time --);
}

void hc595_write_data(u8 dat)
{
	u8 i = 0;
	for(i = 0;i < 8;i++)
	{
		SER = dat >> 7;
		dat <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
	rCLK = 0;
	delay_10us(1);
	rCLK = 1;
}

void main()
{
	u8 i = 0;
	while(1)
	{
		for(i = 0;i < 8;i++)
		{
            //扫描相应的列
			LED_COL_PORT = gled_col[i];
            //将该列上的行点亮
			hc595_write_data(gled_row[i]);
			delay_10us(100); //延时控制在人眼视觉暂留范围内
			hc595_write_data(0x00); //消隐
		}
	}
}

实验8-3 显示数字(595级联)

本实验采用横向取模。8×8点阵上面对应595芯片并行输出的高位,下面对应低位,而16×16点阵上面为低位,下面为高位,因此勾选字节倒序,取模参数如下:

当对16×16点阵横向取模时,取模软件对每一行先取前八列数据为一个字节,再取后八列数据为一个字节,从上往下,得到取模结果:

#include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

sbit SRCLK = P3^6;
sbit rCLK = P3^5;
sbit SER = P3^4;

//控制数字0每一行上哪些列的点被点亮的数据,即横向取模生成的字模数据
//根据取模顺序可知,奇数位的数据对应前八列,传给第三个芯片,偶数位的数据对应后八列,传给第四个芯片
//取模的规则为高电平点亮、低电平不点亮,这与列线路点亮规则相反,因此后续使用要取反
u8 gled_col[32] =
{0x00, 0x00, 0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, 0x04, 0x10, 0x04, 0x10, 0x04, 0x10, 0x04, 0x10,
 0x04, 0x10, 0x04, 0x10, 0x04, 0x10, 0x04, 0x10, 0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, 0x00, 0x00}
//控制点阵依次扫描每一行的数据
//我们将前两个芯片的数据写在一个数组里,前十六个传给第一个芯片,后十六个传给第二个芯片
//扫描前八列时,对应后八列的第二个芯片全为0x00;扫描后八列时,对应前八列的第一个芯片全为0x00
u8 gled_row[32] =
{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,}

void hc595_write_data(u8 dat1, u8 dat2, u8 dat3, u8 dat4)
{
	u8 i = 0;
	for(i = 0;i < 8;i++)
	{
		SER = dat4 >> 7;
		dat4 <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
    for(i = 0;i < 8;i++)
	{
		SER = dat3 >> 7;
		dat3 <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
    for(i = 0;i < 8;i++)
	{
		SER = dat2 >> 7;
		dat2 <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
    for(i = 0;i < 8;i++)
	{
		SER = dat1 >> 7;
		dat1 <<= 1;
		SRCLK = 0;
		delay_10us(1);
		SRCLK = 1;
	}
	rCLK = 0;
	delay_10us(1);
	rCLK = 1;
}

void main()
{
	u8 i = 0;
	while(1)
	{
        //依次扫描每一行点亮对应列的数据,对参数的详细解释见定义数组的注释
		for(i = 0;i < 16;i++)
		{
			hc595_write_data(gled_row[i], gled_row[i + 16], ~gled_col[i * 2], ~gled_col[i * 2 + 1]);
			delay_10us(10); //由于传输四个字节的数据的延时较长,所以这里延时较短
		}
	}
}

本篇完