同上进行MDK工程和Proteus电路的搭建,在文章的末尾给出了数码管动态扫描实现的MDK工程以及Proteus工程,读者自行学习验证。

  1. 搭建MDK的LPC2103工程,由于我们需要用c来实现代码,在提示是否加入启动代码时,选择加入,并设置不要开启PLL(因Proteus仿真12M都已经无法实时仿真了)。

  2. 加入数码管驱动模块文件DigitalTube.h和DigitalTube.c和数码管秒表计时源码main.c,并进行接口方面的修改,数据管动态扫描设计实现请参考笔者51单片机开发系列三/数码管动态扫描显示。

数码管驱动模块实现DigitalTube.c内容如下:

#include"LPC210x.h"
#include"DigitalTube.h"
// 数值相对应的段码,共阳极
static unsigned char const DigitalTubeTable[12]= {
    // 共阳LED段码表
    0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0xff, 0xbf
    //"0" "1"   "2"   "3"   "4"   "5"   "6"   "7"   "8"   "9" "不亮"  "-"
}
;
// 每个数码管需一个字节的内存保存对应数码管数据            
staticunsigned char FrameBuffer[DigitalTubeNumber];
unsigned char*DigitalTube_GetBuffer() {
    return FrameBuffer;
}
// Proteus仿真,数码管扫描时需消隐,Proteus是无法等同实际硬件运行的
// 加入DigitalTube_EN为Proteus仿真的演示,若实际硬件运行
// 请删掉下面四行
// #define DigitalTubeEnable() {IOSET= (1<<11);}
// #defineDigitalTubeDisable() {IOCLR = (1<<11);}
// DigitalTubeDisable();
//DigitalTubeEnable();
// 实际硬件不需要,Proteus仿真用来消隐
#defineDigitalTubeEnable() {
    IOSET =(1<<11);
}
#defineDigitalTubeDisable() {
    IOCLR =(1<<11);
}
voidDigitalTube_Scan() {
    static unsigned char Select = 0;
    // 记录扫描的选择线
    unsigned char Code;
    DigitalTubeDisable();
    // 实际硬件不需要,Proteus仿真用来消隐
    // 从对应选择线中找到显存数据,并得到相应的段码
    Code =DigitalTubeTable[FrameBuffer[Select]];
    // 段码实际输出到数码管接口
    DigitalTube_Data(Code);
    // 位选实际输出到数码管接口
    DigitalTube_Select(Select);
    DigitalTubeEnable();
    // 实际硬件不需要,Proteus仿真用来消隐
    Select++;
    // 进入到下一位选扫描
    if (Select >=DigitalTubeNumber) {
        Select= 0;
        // 所有数码管已扫描,从第一个数码管再次开始扫描
    }
}
void DigitalTube_Init() {
    // 清除引脚配置寄存器[23:0],P0.0~P0.11作为GPIO口功能
    PINSEL0 &= ~0xffffff;
    // 置位IO口方向寄存器[11:0],P0.0~P0.11作为IO口输出
    IODIR |= 0xfff;
    IOSET = 0xfff;
    // P0.0~P0.11输出高电平
}

我们在数码管模块头文件DigitalTube.h中实现模块的接口访问宏实现,使之方便移植及修改接口配置。模块头文件同时也引出模块的接口函数,void DigitalTube_Scan(void)为数码管刷新函数,需周期性调用刷新数码管显示。unsigned char *DigitalTube_GetBuffer(void)用来获得数码管显存,从而更新数码管显存数据。其内容如下:

#ifndef__DigitalTube_H__
#define__DigitalTube_H__
#ifdef__cplusplus
extern"C" {
    #endif
    // 数码管模块中的个数,最大为8
    #defineDigitalTubeNumber   4
    // 输出数码管位选,位选为P0.8~P0.10
    // 先读出P0的值再清0 P0.8~P0.10再写入相应位选
    #defineDigitalTube_Select(Select) {
        IOPIN = (IOPIN&(~(7<<8))) +((Select)<<8);
    }
    // 输出数码管段码,段码为P0.0~P0.7
    // 先读出P0的值再清0 P0.0~P0.7再写入段码
    #defineDigitalTube_Data(Dat) {
        IOPIN = (IOPIN&(~0xff)) | (Dat);
    }
    // 数码管初始化函数,引脚配置
    void DigitalTube_Init(void);
    // 数码管刷新函数,必须保证以一定周期调用刷新
    void DigitalTube_Scan(void);
    // 获得数码管显存,以作显示的数据更新
    unsigned char*DigitalTube_GetBuffer(void);
    #ifdef__cplusplus
}
#endif
#endif
/*__DigitalTube_H__*/

外部模块通过引入数码管的模块头文件DigitalTube.h来实现调用数码管驱动函数,简单测试调用(秒表数码管显示计数)main.c实现如下:

#include"LPC210x.h"
#include"DigitalTube.h"
// 不开启PLL,cpu频率为晶体频率12M,外设时钟为cpu/4
#define  PCLK  (12000000 / 4)
// 以定时器时间为计时标准,记录时间间隔
staticvolatile unsigned int SystemTick = 0;
// 定时器2ms中断处理进行数码管刷新
// 此处用__irq告诉编译器函数用中断方式入出栈及返回
static voidTime0_ISR() __irq {
    SystemTick++;
    // 记录时间间隔
    DigitalTube_Scan();
    // 刷新数码管
    T0IR = 0x1;
    // 清除中断标志
    VICVectAddr = 0;
    // 通知中断控制器中断结束
}
voidTime0_Init() {
    // 写1清除定时器0的对应中断标志
    T0IR=0xff;
    // 预分频计数值初始化为0
    T0PC=0;
    // 预分频值为0
    T0PR=0;
    // 定时器计数器初始化为0
    T0TC=0;
    // 设置匹配模式,复位并中断
    T0MCR=0x03;
    // 2ms中断一次
    T0MR0=(PCLK / 500);
    // 选择为IRQ中断
    VICIntSelect = 0x00;
    // 清除定时器0的中断使能
    VICIntEnClr =  (1 << 4);
    // 中断向量指向时钟中断处理函数
    VICVectAddr0 = (unsignedint)Time0_ISR;
    // 中断优先级向量0分配给定时器0,并开启该优先级中断
    VICVectCntl0 = (0x20 | 0x04);
    // 开启定时器0中断
    VICIntEnable= (1 << 4);
}
int main() {
    unsigned char *pBuffer;
    unsigned char i;
    // 定时器初始化
    Time0_Init();
    // 数码管引脚配置初始化
    DigitalTube_Init();
    // 获得数码管显存,以作更新数据显示
    pBuffer = DigitalTube_GetBuffer();
    // 数据管显存初始化显示0
    for (i=0; i<DigitalTubeNumber; i++) {
        pBuffer[i] = 0;
    }
    // 开启定时器进行计时以及数码管刷新
    T0TCR = 0x01;
    while(1) {
        // SystemTick读数到500时为1s间隔到
        if (SystemTick > 500) {
            SystemTick =0;
            // 重新计秒
            // 更新数码管秒表计数显存  
            for (i=0; i<DigitalTubeNumber;i++) {
                pBuffer[DigitalTubeNumber-1-i]++;
                if(pBuffer[DigitalTubeNumber-1-i] < 10) {
                    break;
                    // 未到10,不用进位更新高位显存,退出
                } else {
                    // 到10,这一位清0,并继续循环更新高位显存
                    pBuffer[DigitalTubeNumber-1-i]= 0;
                }
            }
        }
    }
}
  1. 在工程属性中点选Create HEX File,编译后会生成hex文件。

  2. 正确生成hex代码文件后,可通过MDK的Debug进行仿真查看代码的运行。

  3. 通过Proteus搭建电路,加载进生成的hex文件,可以很直观的观察秒表计数,Proteus只作软件仿真,搭建的电路完全不能用在实际的电路中,设置12M的仿真时钟时,已无法实时仿真了,仿真的时间有一定的延长,也不能代表实际的软件计秒延时情况。

5_入门数码管动态扫描c实现 - 图1

Proteus仿真ARM7数码管秒表计数