007-变量的作用域和LED点阵
阅读原文时间:2023年07月11日阅读:2

一、局部变量和全局变量

  • 局部变量:函数内申明的变量,只在函数内有效。

  • 全局变量:函数外部申明的变量。一个源程序文件有一个或者多个函数,全局变量对他们都起作用。

    备注:全局变量有副作用,降低了函数的独立性,降低函数的通用性,能用局部变量不用全局变量。

二、变量的存储类型

  • 自动变量:局部变量不加static修饰即属于自动变量,也叫动态变量。
  • 静态变量:所有的全局变量都是静态变量,局部变量加了static关键字也属于静态变量。

例如:

void main()
{
      unsigned char i;            //自动变量
      static unsigned char j;     //静止变量
}

由于普中A2开发板的8×8点阵LED由74HC595控制DPa~DPh,所以先介绍74HC595芯片。

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

  • 74HC595 是具有 8 位移位寄存器和一个存储器,三态输出功能。移位寄存器和存储器是分别的时钟。数据SCK的上升沿输入,在 RCK 的上升沿进入到存储器中。如果两个时钟连在一起,则移位寄存器总是比存储器早一个脉冲。移位寄存器有一个串行输入(DS),和一个串行输出(Q7 非),和一个异步的低电平复位, 存储寄存器有一个并行 8 位的, 具有三态的总线输出, 当 MR 为高电平,OE 为低电平时,数据在 SHCP 上升沿进入移位寄存器,在 STCP 上升沿输出到并行端口。

  • 74HC595管脚图

    备注:疑似管脚名称有误,为了方便学习普中A2板子,所以依据提供的图来看

  • 15和1至7脚:QA~QH——并行数据输出,简记为并出;

    9脚:QH'——串行数据输出,简记为串出;

    10脚:SRCLR非( MR)——低电平复位引脚

    11脚:SRCLK(SHCP)——移位寄存器时钟输入,简记为输入开关;

    12脚:RCLK(STCP)——存储寄存器时钟输入,简记为输出开关;

    13脚:OE非——输出有效

    14脚:SER(DS)——串行数据输入,简记为串入,输入的八位数字由此进入;

  • 工作过程(个人理解):

  1. 初始化:SRCLK为0,RCLK为1;此时SER口有八位数字,从高位到低位依次为“76543210”;
  2. 写入:第7位数字写入SER;
  3. SRCLK由0->1(上升沿到达),SER中的第7位数字通过移位寄存器到达QH;
  4. SRCLK由1->0(下降沿到达),第6位数字写入SER;
  5. SRCLK由0->1(上升沿到达),SER中的第6位数字通过移位寄存器到达QG;
  6. 循环移动八位数字由高位至低位到达QH至QA,但数据还没有被输出;
  7. RCLK由0->1,QH至QA中的全部数据一次性被输出;
  • 正确的74HC595管脚图应该为下图:

    #include "reg51.h" //此文件中定义了单片机的一些特殊功能寄存
    #include "intrins.h"

    //功能:点亮8×8点阵LED左上角的小灯
    //目标:P0^7 = 0;DPh = 1,其他为0

    typedef unsigned int u16; //对数据类型进行声明定义
    typedef unsigned char u8;
    //定义使用的IO口
    sbit SRCLK=P3^6;
    sbit RCLK=P3^5;
    sbit SER=P3^4;
    sbit LED=P0^7;

    /*

    • 函 数 名 : Hc595SendByte(u8 dat)
    • 函数功能 : 通过 595 发送 1 个字节的数据
    • 输 入 : dat: 1 个 595 输出数值
    • 输 出 : 无
      */

    void Hc595SendByte(u8 dat)
    {
    u8 a;
    SRCLK = 1;
    RCLK = 1;
    for(a=0;a<8;a++) //发送 8 位数 { SER = dat >> 7; //从最高位开始发送
    dat <<= 1;
    SRCLK = 0; //发送时序
    nop();
    nop();
    SRCLK = 1;
    }
    RCLK = 0;
    nop();
    nop();
    RCLK = 1;
    }

    /*

    • 函 数 名 : main
    • 函数功能 : 主函数
    • 输 入 : 无
    • 输 出 : 无
      */

    void main()
    {
    LED=0; //使第一列为低电平。
    while(1)
    {
    Hc595SendByte(0x80); //1000 0000
    }
    }

备注及说明:

  • 所实现的结果有Bug,表现为DPh控制的小灯错误规律闪烁,问题暂未解决。

    #include //头文件
    #include //头文件

    sbit SRCLK = P3^6; //74HC595使用的IO定义
    sbit LCLK = P3^5; //LCLK即为RCLK,如果使用#include,则无需更改
    sbit SER = P3^4;

    //74HC595发送函数
    void Hc595SendByte(char date) //定义74HC595的发送函数
    {
    unsigned char a;

    SRCLK = 1;
    LCLK = 1;
    
    for(a=0;a<8;a++)          //发送8位数
    {
        SER = date >> 7;      //从最高位开始发送
        date <<= 1;
    SRCLK = 0;      //发送时序
    _nop_();
    _nop_();
    SRCLK = 1;
    } LCLK = 0; _nop_(); _nop_(); LCLK = 1;

    }

    //延时函数
    void delay(int i)
    {
    while(i--);
    }

    //主函数
    void main()
    {
    unsigned char ledNum;
    unsigned char direction = 0; //0左移

    P0 = 0x7F;  //保证第一列为低电平
    ledNum = 0xEF;  //1111 1110
    while(1)
    {
        Hc595SendByte(ledNum);      //1111 1110
        delay(50000);
        ledNum = _crol_(ledNum, 1); //_corl_为循环左移
    }

    }

备注及说明:

  • #include<51.h>#include<reg52.h>的区别是:获取变量地址不同、寄存器地址不同、扩展不同。

    1.获取变量地址不同

    (1)reg51:reg51声明变量后,不能用取地址运算符&获取其地址,编译无法通过,编译器会提示非法操作。

    (2)reg52:reg52声明变量后,能用取地址运算符&获取其地址,编译能通过,编译器不会提示非法操作。

    2.寄存器地址不同

    (1)reg51:reg51有任何一种型号的51单片机都有的基本SFR寄存器的地址,没有T2和DPTR1寄存器的地址。

    (2)reg52:reg52是对REG51进行扩充,增加了T2和DPTR1寄存器的地址。

  • #include<intrins.h>应用于程序设计,一般出C51单片机编程中,一般程序中需要使用到空指令_nop_();字符循环移位指令_crol_等时使用。

  • 这里用到了_crol_这个外部函数,表达的意思是:循环左移。这个函数是包含在"intrins.h"中。

  • 关于_corl_(cror)和<<(>>)的区别:

    注意循环左移和LED = LED<<1;的区别:

    如果这里LED = 0xfe;也就是0x1111 1110;

    LED = LED << 1;后LED的结果为0x1111 1100,最右一位填入了0。

    LED = _crol_(LED,1);面的有两个参数,LED是将要左移的数据,1表示将要左移的位数,如果为2就是左移2位。

    LED = _crol_ (LED,1);后LED的结果为0x1111 1101,最右一位是左移消逝的第7位。

    #include //头文件
    #include

    //功能:利用中断函数实现点阵LED的全亮效果

    //定义使用的IO口
    sbit SER = P3^4;
    sbit LCLK = P3^5;
    sbit SRCLK = P3^6;

    //定义8×8点阵LED的列选通信号,一次亮一列
    unsigned char dian_zhen_LED[8] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};

    //申明74HC595发送函数
    void HC595Send(char date);

    //主函数
    void main()
    {
    TMOD = 0x01; //设置定时器,晶振频率12Mhz,计次间隔1ms
    TH0 = 0xFC;
    TL0 = 0x18;
    TR0 =1;

    EA = 1;     //打开中断总开关
    ET0 = 1;    //打开T0中断开关
    while(1);   //保持持续刷新

    }

    //定义74HC595发送函数
    void HC595Send(char date)
    {
    unsigned char a;

    SRCLK = 1;  //74HC595初始状态定义
    LCLK = 1;   //74HC595初始状态定义
    
    for(a=0; a<8; a++)  //从高位到低位依次发送8位数字
    {
        SER = date >> 7;
        date <<= 1;
    //SRCLK由0-&gt;1(上升沿到达),一位数字通过移位寄存器到达QH至QA中某一位,但数据还没有被输出
    SRCLK = 0;
    _nop_();
    _nop_();
    SRCLK = 1;
    } LCLK = 0; //RCLK由0->1,QH至QA中的全部数据一次性被输出 _nop_(); _nop_(); LCLK = 1;

    }

    //中断函数
    void InterruptTime0() interrupt 1 //中断类型为T0中断,1为中断编号
    {
    static unsigned char i = 0; //此处要加static定义为静态变量,否则每次只执行“case 0”语句

    TH0 = 0xFC;     //设置定时器,晶振频率12Mhz,计次间隔1ms
    TL0 = 0x18;     //注意:右移TF0溢出时,进入定时器中断硬件自动清零,不在需要软件清零,省略语句TF0=0;
    
    P0 = 0xFF;      //消除鬼影
    HC595Send(0xFF);    //使DPh至DPa保持高电平
    
    switch(i)       //
    {
        case 0: P0 = dian_zhen_LED[i];i++;break;
        case 1: P0 = dian_zhen_LED[i];i++;break;
        case 2: P0 = dian_zhen_LED[i];i++;break;
        case 3: P0 = dian_zhen_LED[i];i++;break;
        case 4: P0 = dian_zhen_LED[i];i++;break;
        case 5: P0 = dian_zhen_LED[i];i++;break;
        case 6: P0 = dian_zhen_LED[i];i++;break;
        case 7: P0 = dian_zhen_LED[i];i=0;break;
        default:break;
    }

    }

    #include //头文件
    #include

    //定义使用的IO口
    sbit SER = P3^4;
    sbit LCLK = P3^5;
    sbit SRCLK = P3^6;

    //定义图案“爱心”取模参数
    unsigned char ai_xin[8] = {0x30,0x78,0x7C,0x3E,0x3E,0x7C,0x78,0x30,};
    //定义8×8点阵LED的列选通信号,一次亮一列
    unsigned char dian_zhen_LED[8] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};

    //申明74HC595发送函数
    void HC595Send(char date);
    //申明延时函数
    void delay(int i);

    //主函数
    void main()
    {
    unsigned char i;

    while(1)
    {
        for(i=0; i<8; i++)  //图案动态刷新
        {
            P0 = dian_zhen_LED[i];  //点阵LED从左至右依次列低电平
            HC595Send(ai_xin[i]);   //第一列图案显示
            delay(100);        //延时
            HC595Send(0x00);   //消除鬼影,普中A2好像不需要消除
        }
    }

    }

    //定义74HC595发送函数
    void HC595Send(char date)
    {
    unsigned char a;

    SRCLK = 1;  //74HC595初始状态定义
    LCLK = 1;   //74HC595初始状态定义
    
    for(a=0; a<8; a++)  //从高位到低位依次发送8位数字
    {
        SER = date >> 7;
        date <<= 1;
    //SRCLK由0-&gt;1(上升沿到达),一位数字通过移位寄存器到达QH至QA中某一位,但数据还没有被输出
    SRCLK = 0;
    _nop_();
    _nop_();
    SRCLK = 1;
    } LCLK = 0; //RCLK由0->1,QH至QA中的全部数据一次性被输出 _nop_(); _nop_(); LCLK = 1;

    }

    //定义延时函数
    void delay(int i)
    {
    while(i--);
    }

    #include //头文件
    #include

    //定义使用的IO口
    sbit SER = P3^4;
    sbit LCLK = P3^5;
    sbit SRCLK = P3^6;

    //申明74HC595发送函数
    void hc595Send(char date);

    //定义图案“我爱你”取模参数
    unsigned char wo_ai_ni[40] =
    {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x66,0x66,0x7E,0x7E,0x66,0x66,0x00,
    0x30,0x78,0x7C,0x3E,0x3E,0x7C,0x78,0x30,
    0x00,0x7C,0x7E,0x06,0x06,0x7E,0x7C,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    };
    //定义8×8点阵LED的列选通信号,一次亮一列
    unsigned char dian_zhen_LED[8] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};

    //主函数
    void main()
    {
    TMOD = 0;
    TH0 = 0xFC;
    TL0 = 0x18;
    TR0 = 1;

    EA = 1;
    ET0 = 1;
    while(1);

    }

    //定义74HC595发送函数
    void hc595Send(char date)
    {
    unsigned char a = 0;

    SRCLK = 1;
    LCLK = 1;
    
    for(a=0; a<8; a++)
    {
        SER = date >> 7;
        date <<= 1;
    SRCLK = 0;
    _nop_();
    _nop_();
    SRCLK = 1;
    } LCLK = 0; _nop_(); _nop_(); LCLK = 1;

    }
    //中断函数
    InterruptTime0() interrupt 1
    {
    static unsigned int cnt = 0;
    static unsigned char index = 0;
    static unsigned char i = 0;

    TH0 = 0xFC;
    TL0 = 0x18;
    
    hc595Send(0x00);    //消除鬼影
    
    switch(i)
    //动态刷新:整个40×8的图片切割成32张图,从index位置开始依次刷新一遍
    {
        case 0: hc595Send(wo_ai_ni[index + 0]); P0 = dian_zhen_LED[0]; i++; break;  //显示第index张图片第一列
        case 1: hc595Send(wo_ai_ni[index + 1]); P0 = dian_zhen_LED[1]; i++; break;
        case 2: hc595Send(wo_ai_ni[index + 2]); P0 = dian_zhen_LED[2]; i++; break;
        case 3: hc595Send(wo_ai_ni[index + 3]); P0 = dian_zhen_LED[3]; i++; break;
        case 4: hc595Send(wo_ai_ni[index + 4]); P0 = dian_zhen_LED[4]; i++; break;
        case 5: hc595Send(wo_ai_ni[index + 5]); P0 = dian_zhen_LED[5]; i++; break;
        case 6: hc595Send(wo_ai_ni[index + 6]); P0 = dian_zhen_LED[6]; i++; break;
        case 7: hc595Send(wo_ai_ni[index + 7]); P0 = dian_zhen_LED[7]; i=0; break;
        default:break;
    }
    cnt++;  //计次加1,一次时间间隔为1ms
    if(cnt == 100)  //时间到达0.1秒
    {
        cnt = 0;    //计次清零
        index++;    //下一张图片
        if(index == 32) //刷新至第32张图
        {
            index = 0;  //从头开始
        }
    }

    }

备注及说明:

  • 普中开发攻略有误,正确的取模软件参数为下图所示:

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章