一、设计思路
#include
#include
#define GPIO_KEY P1 //独立键盘用P1口 //--重定义函数变量--// #define uchar unsigned char #define uint unsigned int #define ulong unsigned long //--定义SPI要使用的 IO--// sbit MOSIO = P3^4; sbit R_CLK = P3^5; sbit S_CLK = P3^6;
二、通过I/O口模拟SPI发送数据
(1)传输数据函数与对应模块的仿真
以下是普中科技单片机自带的通过I/O口模拟SPI发送数据的函数,因为有配套的字模生成软件,所以对这个部分我就不写太多东西了,有想要字模软件的可以在文末的百度网盘链接自己下载。
//通过74HC595发送四个字节的数据 void HC595SendData(uchar BT3, uchar BT2,uchar BT1,uchar BT0) {
uchar i; //--发送第一个字节--// for(i=0;i<8;i++) {
MOSIO = BT3 >> 7 ; //从高位到低位 BT3 <<= 1; S_CLK = 0; S_CLK = 1; } //--发送第二个字节--// for(i=0;i<8;i++) {
MOSIO = BT2 >>7; //从高位到低位 BT2 <<= 1; S_CLK = 0; S_CLK = 1; } //--发送第三个字节--// for(i=0;i<8;i++) {
MOSIO = BT1 >> 7; //从高位到低位 BT1 <<= 1; S_CLK = 0; S_CLK = 1; } //--发送第四个字节--// for(i=0;i<8;i++) {
MOSIO = BT0 >> 7; //从高位到低位 BT0 <<= 1; S_CLK = 0; S_CLK = 1; } //--输出--// R_CLK = 0; //set dataline low R_CLK = 1; //片选 R_CLK = 0; //set dataline low }
//点阵显示数组 uchar code tab0[] = {
0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00}; //--张--// uchar code tab1[] = {
128,1,191,49,176,49,176,25,176,13,190,7,134, 1,230,255,134,7,190,13,176,13,176,25,176,49,176,103,158,195,140,1}; //--王--// uchar code tab1[] = {
0,0,252,31,128,0,128,0,128,0,128,0,128,0,248, 15,128,0,128,0,128,0,128,0,128,0,254,63,0,0,0,0}; //--李--// uchar code tab2[] = {
128,0,128,0,254,63,160,2,144,4,136,8,6,48,240, 3,0,1,128,0,254,63,128,0,128,0,224,0,0,0,0,0}; //--任--// uchar code tab3[] = {
16,0,16,28,208,3,8,2,8,2,12,2,10,2,232,63,8,2, 8,2,8,2,8,2,8,2,200,31,0,0,0,0}; //--徐-// uchar code tab4[] = {
16,2,16,2,8,5,132,8,82,16,176,47,8,2,12,2,202, 31,8,2,72,18,72,34,40,34,136,3,0,0,0,0}; //--杨--// uchar code tab5[] = {
24,0,152,63,24,24,24,12,127,6,24,3,156,255,60, 219,126,219,126,219,155,217,152,205,216,204,120,198,24,123,152,49}; //--桃心--// uchar code xin[] = {
0,0,28,56,252,63,254,127,255,255,207,243,135, 225,7,224,15,240,30,120,60,60,120,30,240,15,224,7,192,3,128,1}; //--五角星--// uchar code char1[] = {
128,1,128,1,192,3,192,3,192,3,96,6,127,254,6, 96,28,56,48,12,48,12,152,27,248,30,56,28,12,48,0,0}; //--圆--// uchar code char2[] = {
0,0,224,7,120,30,28,56,28,56,14,112,14,112,14, 112,14,112,14,112,28,56,28,56,120,30,224,7,0,0,0,0}; //--三角--// uchar code char3[] = {
0,0,128,1,128,1,192,3,192,3,96,6,96,6,48,12,48, 12,24,24,24,24,12,48,12,48,254,127,0,0,0,0}; //--菱形--// uchar code char4[] = {
128,0,64,1,32,2,16,4,8,8,4,16,2,32,1,64,2,32, 4,16,8,8,16,4,32,2,64,1,128,0,0,0}; //--箭头--// uchar code char5[] = {
0,0,0,0,0,0,0,2,0,4,0,8,0,16,0,32,126,64,0, 32,0,16,0,8,0,4,0,2,0,0,0,0};
之后会将要显示的数组在混合起来成为一个新的指针数组,这个数组是用来直接在点阵显示的函数中使用的,*p的指针数组中多余很多是被我删掉的名字,只留下了姓。
uchar *p[] = {
tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8, tab9, tab10, tab11, tab12, tab13, tab14}; uchar *c[] = {
char1, char2, char3, char4,char5};
三、检测独立按键
void main(void) {
while(1) {
HC595SendData(0xff,0xff,0,0); //清屏 keyNum=Key_Scan(); //读取键值 switch (keyNum) {
case(0xFE) : //返回按键K1的数据 translation();//文字从上到下平移 break; case(0xFD) : //返回按键K2的数据 twinkle1(); //文字闪烁 break; case(0xFB) : //返回按键K3的数据 twinkle2(); //符号闪烁 break; // case(0xF7) : //返回按键K4的数据 // ; // break; // case(0xEF) : //返回按键K5的数据 // ; // break; // case(0xDF) : //返回按键K6的数据 // ; // break; // case(0xBF) : //返回按键K7的数据 // ; // break; case(0x7F) : //返回按键K8的数据 HC595SendData(0xff,0xff,0,0); //清屏 break; default: break; } } }
读取键值
前面有提到定义P1为独立按键,即程序中的GPIO_KEY,当独立按键按下时GPIO_KEY != 0xFF,这时消除抖动(即延时十毫秒)再检测一次,如果检测为按下按键则将键值保存到keyValue中,之后松开按键或500ms后仍未松开按键,都会将读取到的键值返回。
unsigned char Key_Scan() {
unsigned char keyValue = 0 , i; //保存键值 //--检测按键1--// if (GPIO_KEY != 0xFF) //检测按键K1是否按下 {
Delay10ms(1); //消除抖动 if (GPIO_KEY != 0xFF) //再次检测按键是否按下 {
keyValue = GPIO_KEY; i = 0; while ((i<50) && (GPIO_KEY != 0xFF)) //检测按键是否松开 {
Delay10ms(1); i++; } } } return keyValue; //将读取到键值的值返回 }
四、具体功能
(1)文字自上而下移动
K1按键对应的是文字自上而下移动的功能,以下为对应的程序,其中内嵌的第三个循环for(k = 0; k < 16; k++)中k从0到15遍历,对应LED点阵的1到16行,内嵌的第二个循环for(ms = 10; ms > 0; ms–)决定显示同一个内容的时间,内嵌的第一个循环为while(keyNum!=0x7F),只要keyNum!=0x7F一直成立,就无限循环。
i.如何实现随时按下K8退出该功能,重新回到主函数
在最小的循环中完成内嵌一个检测键值的程序keyNum=Key_Scan();然后判断是否为0x7F(即按下K8时的键值),若是则退出该循环,回到第二个循环,第二个循环中也有判断键值是否为K8的程序,同理,若是,回到第一个循环,while(keyNum!=0x7F)同样会判断键值是否为0x7F,若不是则退出循环,回到主函数。
ii.如何实现文字从上而下移动
刚进入这个功能还未进入循环时,赋值j=0(如果这里不赋值j=0也可以,只是下次进入这个功能会接着这次结束时显示的图像继续向下平移),在内嵌的第三个循环,也就是最小的的那个循环中给LED点阵发送数据,且是一次发送一行数据,循环16次,每次k+1即可显示全整个LED点阵,k加到16时退出最小的循环,第二个循环for(ms = 10; ms > 0; ms–)决定进入最小循环的次数即显示的时间,显示完规定的时间后,退出第二个循环,回到第三个循环,j+1,之后再进入最小循环时发送的前两位数据对应的指针数组中的位置会各自加2j,即显示的文字或字符向上移动一位,最下方空缺出来的一行会由下一个文字或字符的第一行代替,之后同理,则形成了文字向上移动的效果,一个字需要16行,从0开始,当j=15要显示的文字或字符数量时,再令j=0,重新从第一个字开始显示。
以下是文字移动的程序:
//文字由上而下平移 void translation() {
j=0; while(keyNum!=0x7F) {
for(ms = 10; ms > 0; ms--) //移动定格时间设置 {
for(k = 0; k < 16; k++) //显示一个字 {
HC595SendData(~(*(p[0] + 2*(k+j) + 1)),~(*(p[0] + 2*(k+j) )), tab0[2*k],tab0[2*k + 1]); //因为字模软件取的数组是高电平有效,所以列要取反 keyNum=Key_Scan(); if(keyNum==0x7F) break; } HC595SendData(0xff,0xff,0,0); //清屏 if(keyNum==0x7F) break; } j++; if(j == (14*15) ) {
j = 0; } } }
(2)文字闪烁显示
i.如何实现随时按下K8退出该功能,重新回到主函数
与文字从上而下移动的功能同理,只不过文字闪烁功能有四个循环,比文字移动多一个,同样是最小循环中检测键值,若检测到keyNum==0x7F,则逐级退出,最后返回主函数。
ii.如何实现文字闪烁显示
与文字移动有些类似的地方,在最小的循环中发送数据从LED点阵第一行显示到最后一行,上一级循环决定最小循环一共运行几次,即显示一个字的时间,再上一级循环中每次i+1决定显示下一个文字,当i=14时回到最外层循环,之后回到刚才的循环重新从第一个文字开始显示。
//文字闪烁 void twinkle1() {
while(keyNum!=0x7F) {
for(i = 0; i < 14; i++) //总共14个字 {
for(ms = 50; ms > 0; ms--) //显示50次,即肉眼可识别的停留时间 {
for(k = 0; k < 16; k++) //显示一个字 {
//--因为字模软件取的数组是高电平有效,所以列要取反--// HC595SendData(~(*(p[i] + 2*k + 1)),~(*(p[i] + 2*k )), tab0[2*k],tab0[2*k + 1]); keyNum=Key_Scan(); if(keyNum==0x7F) break; } HC595SendData(0xff,0xff,0,0); //清屏 if(keyNum==0x7F) break; } if(keyNum==0x7F) break; } } }
(3)符号闪烁显示
其原理与文字闪烁显示完全相同,只不过这个程序里包含的指针数组里的全是自定义的符号。
以下是符号闪烁的程序:
//符号闪烁 void twinkle2() {
while(keyNum!=0x7F) {
for(i = 0; i <5; i++) //总共5个字 {
for(ms = 50; ms > 0; ms--) //显示50次,即肉眼可识别的停留时间 {
for(k = 0; k < 16; k++) //显示一个字 {
//--因为字模软件取的数组是高电平有效,所以列要取反--// HC595SendData(~(*(c[i] + 2*k + 1)),~(*(c[i] + 2*k )), tab0[2*k],tab0[2*k + 1]); keyNum=Key_Scan(); if(keyNum==0x7F) break; } HC595SendData(0xff,0xff,0,0); //清屏 if(keyNum==0x7F) break; } if(keyNum==0x7F) break; } } }
(4)创新部分
做这个程序的时候快要期末考试了,就没有费心思去做创新部分,但其实很简单,例如,可以把符号也弄成从上而下移动的,可以一会儿闪烁一会移动,比如把要显示的东西横置过来,然后闪烁显示一个人的名字,之后移动显示I LOVE YOU.甚至如果有无源蜂鸣器的话,还可以在显示的同时放首歌。
五、proteus仿真
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/208273.html原文链接:https://javaforall.net
