资源描述
目录
摘要…………………………………………………1
一、设计任务和要求………………………………2
二、设计过程……………………………………………2
三、设计流程图……………………………………………6
四、源程序清单及说明……………………………………7
五、调试过程………………………………………………11
六、设计心得………………………………………………11
七、参考文献………………………………………………11
摘要
今年来,32位嵌入式系统发展很快,32位单片机已逐渐占领8位机的市场,并以很高的性价比赢得了越来越多的用户的青睐。随着科技和工艺的进步,32位单片机的优势会更加突显出来,将会成为嵌入式系统的主流。在32位嵌入式系统中,基于ARM的应用占据了大部分份额。
EasyARM2131开发板是广州周立功公司设计的EasyARM系列开发套件之一,采用了PHILIPS公司基于ARM7TDMI-S 核、单电源供电、LQFP64封装的LPC2131,具有JTAG
仿真调试、ISP编程等功能。
本次设计是采用LPC2138的32位芯片设计的可调万年历,具有调时、调分,60秒倒计时和准点报时的功能。同时当倒计时至0时,LED亮;采用的是PWM输出音乐准点报时。
关键词:ARM LPC2138 LED PWM
一、设计任务和要求:
1、 课程设计目的
(1) 了解嵌入式芯片的工作原理,从而学会制作相关的东西。
(2) 通过课程设计进一步的了解各种在设计中常用到的程序的作用及程序的调试方法。
(3) 进一步了解嵌入式系统以及嵌入式在实际生活中的应用
2、 课程设计要求
(1) 本次设计要包含ARM板上的几个模块
(2) 本次设计要有一定有实际意义(有应用价值)
二、设计过程:
本设计实现了时钟、星期、日期(月、日)的显示,校时校分,60秒倒计时以及准点报时等功能。各功能原理描述如下:
(一)、时钟、星期、日期(月、日)的显示:
LPC2138的RTC时钟可由独立的32.768kHz振荡器或基于VPB时钟的可编程预分频器来提供。RTC提供秒、分、小时、日、月、年和星期。
RTC功能结构方框图:
RTC包含了许多寄存器,按照功能可分:混合寄存器、时间寄存器、时间计数器、报警寄存器和预分频器。
如下图所示,设置时钟控制寄存器(CCR)可以选择RTC的计数时钟,实时时钟CTC计数器的时钟源使用由PCLK通过基准时钟分频器(PREINT、PREFRAC)调整出的 32768Hz时钟信号,或者直接使用独立的外部 32.768KHz振荡器产生的时钟信号。CTC是一个 15 位的计数器,它位于秒计数器之前,CTC每秒计数 32768个时钟;当有CTC秒进位时,完整时间CTME0~2、RTC时间寄存器(如SEC、MIN等)将会更新。RTC时钟控制寄存器CCR用于使能实时时钟和CTC复位控制等。
(二) 、校时校分:
本功能采用按键控制。EasyARM2131开发板上的6个按键,电路图如下图所示:
先要设置IODIR使口线成为输入方式,然后读取IOPIN的值,判断是否有键按下,若KEY1按下,则小时计数器HOUR加1,若KEY2按下,则分钟计数器加1,从而实现了校时校分的功能。
(三)、60秒倒计时:
定义一个外部变量yhy且初始值为59,当RTC产生增量中断时,yhy减1。当yhy为0时,8个LED全亮,否则全暗。再赋予yhy等于59进行下一次倒计时。Yhy的显示取代RTC的年。即RTC显示年的四个数码管中选两个用来显示yhy的值。Yhy的值将和秒、分、日、月、星期一起送到上位机显示。
EasyARM2131开发板上的8路LED(LED8~LED1)分别可选择P1[25:18]进行控制。当跳线JP12全部选择LED8~LED1后,P1.25~P1.18分别控制这8路LED。当P1[25:18]都输出低电平时,8个 LED点亮,当P1[25:18]都输出高电平时,8个LED熄灭。实验程序首先设置管脚连接GPIO,接着设置P1[25:18]口为输出模式,然后通过IO1CLR和IO1SET控制P1[25:18],驱动8个LED的亮灭,
8路LED电路如下图所示:
(四)、准点报时:
当时钟计数至准点时,PWM2输出音乐,报时时间为15秒。
LPC2138的脉宽调制器(PWM)建立在标准定时器0/1之上。两个匹配寄存器可用控制单边沿PWM输出。PWMMR0控制PWM周期率,另一个匹配寄存器(PWMMR1~PWMMR6)控制PWM边沿的位置。每个额外的单边沿PWM输出只需要一个匹配寄存器,因为所有PWM输出的重复率速率是相同的。多个单边沿控制PWM输出在每个PWM周期的开始,当PWMMR0发生匹配时,都有一个上升沿。
通过改变PWMMR0的值,可以改变PWM输出的频率。实验设定PWM输出单边沿PWM方波,控制蜂鸣器BEEP发声,改变PWM的频率,蜂鸣器发出不同频率的声音。进行PWM音乐输出实验,需要了解一些简谱和频率的关系。
单边沿控制PWM输出规则示意图:
PWM输出音乐流程图:
三、设计流程图:
四、源程序清单及说明:
#include "config.h"
#include "music.h"
const uint32 KEY1 = 1 << 16; // P0.16连接KEY1
const uint32 KEY3 = 1 << 18; // P0.18连接KEY3
const uint32 LEDS8 = (0XFF << 18); // P1[25:18]控制LED8~LED1,低电平点亮
int yhy=59;
uint8 hour_flag=0;
// 定义串口模式设置的数据结构
typedef struct UartMode
{ uint8 datab; // 字长度 5/6/7/8
uint8 stopb; // 停止位 1/2
uint8 parity; // 奇偶校验 0-无校验, 1-奇校验, 2-偶校验
} UARTMODE;
/* 歌曲曲谱 - 虹彩妹妹*/
const uint32 HCMM[] =
{ _LA, _SO, _MI, _LA, _SO, _MI,
_LA, _LA, _SO, _LA,
_LA, _SO, _MI, _LA, _SO, _MI,
};
/* 歌曲节拍 */
const uint32 HCMM_L[] =
{ _4, _8, _8, _4, _8, _8,
_8, _4, _8, _2,
_4, _8, _8, _4, _8, _8,
};
/******************************************************************************
**函数名称 :UART0_Init()
** 函数功能 :初始化串口:设置工作模式和波特率。
******************************************************************************/
uint8 UART0_Init (uint32 baud, UARTMODE set)
{ uint32 bak;
// 参数过滤
if ((0 == baud) || (baud > 115200)) return (0);
if ((set.datab < 5) || (set.datab > 8)) return (0);
if ((0 == set.stopb) || (set.stopb > 2)) return (0);
if (set.parity > 4) return (0);
// 设置串口波特率
U0LCR = 0x80; // DLAB=1
bak = (Fpclk >> 4) / baud;
U0DLM = bak >> 8;
U0DLL = bak & 0xff;
// 设置串口模式
bak = set.datab - 5;
if (2 == set.stopb) bak |= 0x04;
if (0 != set.parity)
{ set.parity = set.parity - 1;
bak |= 0x08;
}
bak |= set.parity << 4;
U0LCR = bak;
return (0);
}
/******************************************************************************
** 函数名称 :SendByte()
** 函数功能 :向串口UART0发送字节数据,并等待发送完毕。
******************************************************************************/
void SendByte (uint8 data)
{ U0THR = data;
while ((U0LSR & 0X20) == 0); // 等待数据发送
}
/******************************************************************************
** 函数名称 :PC_DispChar()
** 函数功能 :向PC机发送显示字符。
******************************************************************************/
void PC_DispChar (uint8 no, uint8 chr)
{ SendByte(0xff);
SendByte(0x81);
SendByte(no);
SendByte(chr);
SendByte(0x00);
}
uint8 const SHOWTABLE[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
/******************************************************************************
** 函数名称 :SendTimeRtc()
** 函数功能 :读取RTC的时间值,并将读出的时分秒值通过串口送到上位机显示。
******************************************************************************/
void SendTimeRtc (void)
{ uint32 datas;
uint32 times;
uint32 bak;
times = CTIME0; // 读取完整的时钟寄存器
datas = CTIME1;
PC_DispChar(1, SHOWTABLE[yhy/10]);
PC_DispChar(2, SHOWTABLE[yhy % 10]);
bak = (datas >> 8) & 0x0f; // 获取 月
PC_DispChar(4, SHOWTABLE[bak / 10]);
PC_DispChar(5, SHOWTABLE[bak % 10]);
bak = datas & 0x1f; // 获取 日
PC_DispChar(6, SHOWTABLE[bak / 10]);
PC_DispChar(7, SHOWTABLE[bak % 10]);
bak = (times >> 24) & 0x07; // 获取 星期
PC_DispChar(8, SHOWTABLE[bak]);
bak = (times >> 16) & 0x1f; // 获取 小时
PC_DispChar(9, SHOWTABLE[bak / 10]);
PC_DispChar(10, SHOWTABLE[bak % 10]);
bak = (times >> 8) & 0x3f; // 获取 分钟
PC_DispChar(11, SHOWTABLE[bak / 10]);
PC_DispChar(12, SHOWTABLE[bak % 10]);
bak = times & 0x3f; // 获取 秒钟
PC_DispChar(13, SHOWTABLE[bak / 10]);
PC_DispChar(14, SHOWTABLE[bak % 10]);
}
/******************************************************************************
** 函数名称 :DelayNS()
** 函数功能 :长软件延时
******************************************************************************/
void DelayNS (uint32 dly)
{ uint32 i;
for ( ; dly>0; dly--)
for (i=0; i<500000; i++);
}
/******************************************************************************
** 函数名称 :RTCInit()
** 函数功能 :初始化实时时钟
******************************************************************************/
void RTCInit (void)
{ PREINT = Fpclk / 32768 - 1; // 设置基准时钟分频器
PREFRAC = Fpclk - (Fpclk / 32768) * 32768;
CCR = 0x00; // 禁止时间计数器
MONTH = 01;
DOM = 10;
DOW = 4;
HOUR = 8;
MIN =59;
SEC = 50;
CIIR = 0x01; // 设置秒值的增量产生1次中断
CCR = 0x01; // 启动RTC
}
/******************************************************************************
** 函数名称 :main()
** 函数功能 :读取实时时钟的值,通过串口发送出去。
******************************************************************************/
int main (void)
uint8 s_cnt=0;
UARTMODE uart0_set;
PINSEL0 = 0x00000005; // 连接IO到UART0
PINSEL1 = 0x00000000;
PINSEL2 = PINSEL2 & (~0x08);// P1[25:16]连接GPIO
IO1DIR = LEDS8; // 设置LEDS8控制口为输出
uart0_set.datab = 8;
uart0_set.stopb = 1;
uart0_set.parity = 0;
UART0_Init(115200, uart0_set);
U0FCR = 0x01; // FIFO使能
/* PWM初始化 */
PWMPR = 0x00; // 不分频,计数频率为Fpclk
PWMMCR = 0x02; // 设置PWMMR0匹配时复位PWMTC
PWMPCR = 0x0400; // 允许PWM2输出,单边PWM
PWMMR0 = Fpclk / 450;
PWMMR2 = PWMMR0 / 2; // 50%占空比
PWMLER = 0x05; // PWM0和PWM2匹配锁存
PWMTCR = 0x02; // 复位PWMTC
RTCInit();
while (1)
{ if ((IO0PIN & KEY1) == 0)
{ CCR = 0x00; // 禁止时间计数器
DelayNS(1);
HOUR=(HOUR+1)%24; // 如果KEY1按下,
CCR = 0x01; // 启动RTC
}
if ((IO0PIN & KEY3) == 0)
{ CCR = 0x00; // 禁止时间计数器
DelayNS(1);
MIN=(MIN+1)%60; // 如果KEY3按下,
CCR = 0x01; // 启动RTC
}
while (0 == (ILR & 0x01)) // 等待RTC增量中断
{ if((SEC==0)&&(MIN==0))
{ hour_flag=1;}
}
ILR = 0x01; // 清除中断标志
yhy=yhy-1;
if(yhy==0)
IO1CLR = LEDS8; // 8个LED点亮
else
IO1SET = LEDS8; // 8个LED熄灭
if(yhy==(-1))
yhy=59;
SendTimeRtc();
if (hour_flag) //每秒一次
{ if (s_cnt<16)
{ PINSEL0 = 0x00000005 | (0x02 << 14); // 连接IO到UART0
PWMMR0 = Fpclk / HCMM[s_cnt]; // 设置输出频率
PWMTCR = 0x09; // 启动PWM输出
PWMLER = 0x05; // 更新匹配值后,必须锁存
s_cnt++;
}
else
{ PINSEL0 = 0x00000005 ; // 连接IO到UART0
s_cnt=0;
hour_flag=0;
}
}
}
return (0);
}
/******************************************************************************** End Of File
*******************************************************************************
修改,即引入外部变量yhy来代替年份的显示即每次增量中断产生时yhy减1。将修改的程序进行编译然后下载到芯片,然后运行。观察开发板的现象,看是否能五、调试过程:
整个设计的调试过程是分步进行的。
1)、 60秒倒计时的调试:对深入浅出ARM7课本中的万年历源程序进行实现倒计时功能。倒计时功能实现后再添加LED的提示,即当倒计时至0时所有LED亮否则灭。同样将添加好的程序进行编译并下载到芯片,然后运行,观察开发板现象看看是否能实现所需功能。这一项相对简单,所以调试过程也很快成功。
2)、 校时、校分的调试:在能够实现倒计时功能的程序中添加校时校分的程序。KEY1按下计数器停止计数小时(HOUR)加1,KEY3按下计数器停止计数分钟(MIN)加1。程序添加完之后编译,编译无错之后下载到芯片,然后运行。按下KEY1或KEY3,观察是否能实现所需功能。这个过程运用到了按键,所以存在一个消抖的问题,若消抖不合理可能可能会造成按键按一下,小时或分钟可能不止加1。
3)、 准点报时:在前面的程序当中添加准点报时的程序。准点报时采用的是PWM2输出15秒的音乐。先将PWM2输出音乐的程序单独调试运行,反复修改频率直至输出音乐达到最好的效果。然后将程序添加到前面的程序当中。这个过程遇到了一个问题:当PWM2输出音乐的时候计数器会停止计数。说明添加不合理。将流程图重新画了一遍,再引入了一个标志位,反复修改程序并调试,直至准点报时的时候计数器仍能计数。具体见源程序。
六、设计心得:
在实训的这五天里,前三天老师每天上午都在讲课,我基本都听不懂,感觉自己都学得好差。下午在图书馆查找资料,形成设计思想,写出大概的程序。最后两天时间就是呆在实验室,经常忘了时间,在实验室反复修改,调试。与同学相互讨论,咨询老师。在此,我要特别感谢我的同学廖艳霞和华永金,还有指导老师涂老师,谢谢他们的指点。
通过这次实训,感觉自己的动手能力还有待提高,当然,在这次实训中,我能够独立的思考。以前摸棱两可的知识变得更加清晰了。对ARM7感觉没有没有以前那么恐怖了。这门课程的学习最主要的还是自学,终于理解师傅领进门,修行在个人着句话了。
七、参考文献:
[1]:周立功等 深入浅出ARM7 北京航天航空出版社
[2]: 薛钧义 微机原理与接口技术 高等教育出版社
12
展开阅读全文