同上进行MDK工程和Proteus电路的搭建,在文章的末尾给出了数码管动态扫描实现的MDK工程以及Proteus工程,读者自行学习验证。
搭建MDK的LPC2103工程,由于我们需要用c来实现代码,在提示是否加入启动代码时,选择加入,并设置不要开启PLL(因Proteus仿真12M都已经无法实时仿真了)。
加入数码管驱动模块文件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;
}
}
}
}
}
在工程属性中点选Create HEX File,编译后会生成hex文件。
正确生成hex代码文件后,可通过MDK的Debug进行仿真查看代码的运行。
通过Proteus搭建电路,加载进生成的hex文件,可以很直观的观察秒表计数,Proteus只作软件仿真,搭建的电路完全不能用在实际的电路中,设置12M的仿真时钟时,已无法实时仿真了,仿真的时间有一定的延长,也不能代表实际的软件计秒延时情况。