标题:
基于ARM单片机的软件时钟
[打印本页]
作者:
luoman
时间:
2010-8-18 09:22
标题:
基于ARM单片机的软件时钟
基于ARM单片机的软件时钟
ARM单片机的功能要大大优于51单片机,而其价格已经很低了,生产厂家不论是出于广告宣传的需要,还是出于利润的角度考虑(嵌入ARM单片机将会卖更高的价格),他们更希望在自己的产品中嵌入ARM单片机。
本人使用的是周立功公司推出的Easy ARM1138开发板,它除了具有32位ARM Cortex-M3内核的LM3S1138单片机之外,还内嵌有USB接口的仿真调试器,开发调试非常方便,不愧为广大师生和工程师的首选。
1. 硬件设计
利用LM3S1138内部的定时器,0.5秒产生一次中断,再用一个时间计数器进行计时;然后再利用1602LCD进行显示;按键sw1为时间“选择”键,按键sw2为时间“+1”键,它们用于对于时间的调整;从而实现完整的软件时钟。电位器用于调整LCD的对比度。
目标板是利用原有的一块印制板,在其上焊接有两只按键、两只电阻、一只电位器和连接1602LCD的插座(参见后面的实物照片),目标板与1138开发板之间通过15根杜邦线相连。
⒉ 软件设计
程序一开始要进行系统初始化、定时器设置初值、开中断、LCD初始化、调用显示子函数。然后进入大循环;首先判断是否是“选择”键按下,若是则进行消抖处理,再判断原为时间的哪一位在闪烁,并使得下一位闪烁,同时使闪烁计时为零; 然后判断是否是“+1”键按下,若是则进行消抖处理,再判断原为时间的哪一位在闪烁,并使得该位+1,同时使闪烁计时为零;然后循环判断是否是“选择”键按下……。
定时器T0每0.5秒中断一次,在定时器中断函数中,首先中断计数器+1,若中断次数为奇数,则根据闪烁字控制某位不显示;若中断次数为偶数(一秒),则时间计数器+1,当时间计数器的值超过23点59分59秒时时间计数器=0,再调显示子函数,并使闪烁计时器+1,然后判断闪烁字是否不为零,若是则判断闪烁是否已达30秒?若是则停止闪烁,并中断返回。
基于ARM单片机LM3S1138软件时钟的详细程序如下所示(基于《Stellaris外设驱动库》)。
/*****************************************************
T0+LCD1602程序 ,一行显示Easy ARM1138,另一行显示时间
PA口--数据,PF0--RS,PF1--RW,PF2--E,PF6-sw1,PF7-sw2
******************************************************/
// 包含必要的头文件
#include <hw_types.h>
#include <hw_memmap.h>
#include <hw_ints.h>
#include <hw_sysctl.h>
#include <hw_gpio.h>
#include <hw_timer.h>
#include <interrupt.h>
#include <sysctl.h>
#include <gpio.h>
#include <timer.h>
// 将较长的标识符定义成较短的形式
#define SysCtlPeriEnable SysCtlPeripheralEnable
#define SysCtlPeriDisable SysCtlPeripheralDisable
#define GPIOPinTypeIn GPIOPinTypeGPIOInput
#define GPIOPinTypeOut GPIOPinTypeGPIOOutput
#define RS GPIO_PORTF_BASE,GPIO_PIN_0 //LCD寄存器选择
#define RW GPIO_PORTF_BASE,GPIO_PIN_1 //LCD读写控制
#define E GPIO_PORTF_BASE,GPIO_PIN_2 //LCD使能
#define AIO GPIO_PORTA_BASE,0xff //LCD数据口
#define sw1 GPIO_PORTF_BASE,GPIO_PIN_6 //选择键
#define sw2 GPIO_PORTF_BASE,GPIO_PIN_7 //+1键
#define uint unsigned int
#define uchar unsigned char
uint sjjs=3600*8+60*15; //时间初值为8:15
uint zdjs=0; //中断计数
uchar SJ=0; //闪烁计时
uchar sz=0; //闪烁字 (小时十位=08,个位=04,分钟十位=02,个位闪烁=01)
// 定义全局的系统时钟变量
unsigned long TheSysClock = 12000000UL;
// 延时
void Delay(unsigned long ulVal)
{
while ( --ulVal != 0 );
}
作者:
luoman
时间:
2010-8-18 09:23
//LCD初始化
void LCD_Init(void)
{
int i;
GPIOPinWrite(AIO,0x30); //GPIOA口出0x30
GPIOPinWrite(RS,0) ; //RS0
GPIOPinWrite(RW,0) ; //RW0
for(i=0;i<3;i++)
{
GPIOPinWrite(E,0x01<<2) ; //E1
GPIOPinWrite(E,0) ; //E=0
Delay(500* (TheSysClock / 4000000)); // 延时约500us
}
}
//LCD命令写
void Write_Com(uchar a)
{
GPIOPinTypeOut(AIO); //GPIOA为输出
GPIOPinWrite(RS,0) ; //RS=0
GPIOPinWrite(RW,0x01<<1) ; //RW=1
GPIOPinWrite(E,0x01<<2) ; //E=1
GPIOPinTypeIn(AIO); //GPIOA为输入
while (0x80==(GPIOPinRead(AIO)&0x80)) //测试忙闲
;
GPIOPinWrite(E,0) ; //E=0
GPIOPinWrite(RW,0) ; //RW=0
GPIOPinTypeOut(AIO); //GPIOA为输出
GPIOPinWrite(AIO,a); //GPIOA口出a
GPIOPinWrite(E,0x01<<2) ; //E=1
GPIOPinWrite(E,0) ; //E=0
}
//LCD数据写
void Write_Data(uchar a)
{
GPIOPinTypeOut(AIO); //GPIOA为输出
GPIOPinWrite(RS,0) ; //RS=0
GPIOPinWrite(RW,0x01<<1) ; //RW=1
GPIOPinWrite(E,0x01<<2) ; //E=1
GPIOPinTypeIn(AIO); //GPIOA为输入
while (0x80==(GPIOPinRead(AIO)&0x80)) //测试忙闲
;
GPIOPinWrite(E,0) ; //E=0
GPIOPinWrite(RS,1) ; //RS=1
GPIOPinWrite(RW,0) ; //RW=0
GPIOPinTypeOut(AIO); //GPIO A为输出
GPIOPinWrite(AIO,a); //GPIO A口出a
GPIOPinWrite(E,0x01<<2) ; //E=1
GPIOPinWrite(E,0) ; //E=0
}
// 系统初始化
void SystemInit(void)
{
SysCtlLDOSet(SYSCTL_LDO_2_50V); // 设置LDO输出电压
SysCtlClockSet(SYSCTL_USE_OSC | // 系统时钟设置,采用主振荡器
SYSCTL_OSC_MAIN |
SYSCTL_XTAL_6MHZ |
SYSCTL_SYSDIV_1);
TheSysClock = SysCtlClockGet(); // 获取系统时钟,单位:Hz
}
// 计算并显示时间
void Timer_Disp( void )
{
unsigned int a,b,f;
// 计算并显示小时
a=(sjjs/3600);
b=a%10;
a=a/10;
Write_Com(0xc4); //小时十位
Write_Data(0X30+a);
Write_Data(0X30+b);
Write_Data(0x3a);
// 计算并显示分钟
f=sjjs%3600;
a=f/60;
b=a%10;
a=a/10;
Write_Data(0X30+a);
Write_Data(0X30+b);
Write_Data(0x3a);
// 计算并显示秒
f=sjjs%60;
b=f%10;
a=f/10;
Write_Data(0X30+a);
Write_Data(0X30+b);
}
// 主函数(程序入口)
int main(void)
{
SystemInit(); // 系统初始化
uint c;
SysCtlPeriEnable(SYSCTL_PERIPH_TIMER0); //使能定时器模块
TimerConfigure(TIMER0_BASE , TIMER_CFG_32_BIT_PER); //配置定时器为32位周期定时器
TimerLoadSet(TIMER0_BASE , TIMER_A , 3000000UL); //设置定时器初值(0.5S)
TimerIntEnable(TIMER0_BASE , TIMER_TIMA_TIMEOUT); //使能定时器超时中断
IntEnable(INT_TIMER0A); //使能定时器中断
IntMasterEnable(); //使能处理器中断
TimerEnable(TIMER0_BASE , TIMER_A); //使能定时器计数
SysCtlPeriEnable(SYSCTL_PERIPH_GPIOA); //使能GPIOA口外设
GPIOPinTypeOut(GPIO_PORTA_BASE ,0xFF); //GPIOA为输出
SysCtlPeriEnable(SYSCTL_PERIPH_GPIOF); //使能GPIOF口外设
GPIOPinTypeOut(GPIO_PORTF_BASE ,0x07); //PF0,1,2为输出
GPIOPinTypeIn(GPIO_PORTF_BASE ,0xC0); //PF6,7为输入
LCD_Init(); //LCD初始化
Write_Com(0x38); //设置工作方式
Write_Com(0x01); //清除显示
Write_Com(0x06); //设置输入方式
Write_Com(0x0c); //设置显示方式
Write_Com(0x82);
Write_Data(0x45); //显示Easy ARM1138
Write_Data(0x61);
Write_Data(0x73);
Write_Data(0x79);
Write_Data(0x20);
Write_Data(0x41);
Write_Data(0x52);
Write_Data(0x4d);
Write_Data(0x31);
Write_Data(0x31);
Write_Data(0x33);
Write_Data(0x38);
Timer_Disp(); //显示时间
while(1)
{
if(GPIOPinRead(sw1)==0)
{
Delay(50* (TheSysClock / 4000)); //消抖,延时约50ms
if(GPIOPinRead(sw1)==0)
{ //选择键按下
for(;;)
{
if(sz==0x08)
{ //原为小时十位闪烁
sz=0x04; //改为小时个位闪烁
SJ=0;
for(;;)
if(GPIOPinRead(sw1)) //选择键松开
goto loop;
}
if(sz==0x04)
{ //原为小时个位闪烁
sz=0x02; //改为分钟十位闪烁
SJ=0;
for(;;)
if(GPIOPinRead(sw1)) //选择键松开
goto loop;
}
if(sz==0x02)
{ //原为分钟十位闪烁
sz=0x01; //改为分钟个位闪烁
SJ=0;
for(;;)
if(GPIOPinRead(sw1)) //选择键松开
goto loop;
}
{ //原无闪烁或为分钟个位闪烁
sz=0x08; //改为小时十位闪烁
SJ=0;
for(;;)
if(GPIOPinRead(sw1)) //选择键松开
goto loop;
}
}
}
}
loop:if(GPIOPinRead(sw2)==0)
{ //+1键按下
Delay(50* (TheSysClock / 4000)); //消抖,延时约50ms
if(GPIOPinRead(sw2)==0)
{
for(;;)
{
if(sz==0x08)
{ //小时十位闪烁
if(sjjs/36000<2)
sjjs+=36000 ; //小时十位+1
else
sjjs%=36000;
SJ=0;
for(;;)
if(GPIOPinRead(sw2)) //+1键松开
goto loop;
}
if(sz==0x04)
{ //小时个位闪烁
c=sjjs%36000;
if(c/3600<9)
sjjs+=3600 ; //小时个位+1
else
sjjs-=3600*9;
SJ=0;
for(;;)
if(GPIOPinRead(sw2)) //+1键松开
goto loop;
}
if(sz==0x02)
{ //分钟十位闪烁
c=sjjs%36000;
c%=3600;
if(c/600<5)
sjjs=sjjs+600; //分钟十位+1
else
sjjs-=600*5;
SJ=0;
for(;;)
if(GPIOPinRead(sw2)) //+1键松开
goto loop;
}
if(sz==0x01)
{ //分钟个位闪烁
c=sjjs%36000;
c%=3600;
c=c%600;
if(c/60<9)
sjjs+=60 ; //分钟个位+1
else
sjjs-=60*9;
SJ=0;
for(;;)
if(GPIOPinRead(sw2)) //+1键松开
goto loop;
}
}
}
}
}
}
// 定时器的中断服务函数
void Timer0A_ISR(void)
{
unsigned long ulStatus;
ulStatus = TimerIntStatus(TIMER0_BASE , true); //读取中断状态
TimerIntClear(TIMER0_BASE , ulStatus); //清除中断状态,重要!
if ( ulStatus & TIMER_TIMA_TIMEOUT ) //如果是定时器超时中断
{
zdjs+=1;
if(zdjs&0x01)
{ //0.5秒
if(sz)
{
switch(sz)
{
case 1:Write_Com(0xc8);Write_Data(0x20);break; //分钟个位灭
case 2:Write_Com(0xc7);Write_Data(0x20);break; //分钟十位灭
case 4:Write_Com(0xc5);Write_Data(0x20);break; //小时个位灭
case 8:Write_Com(0xc4);Write_Data(0x20);break; //小时十位灭
}
}
}
else
{ //一秒
sjjs+=1;
if(sjjs>23*3600+59*60+59)
sjjs=0;
Timer_Disp(); // 显示时间
SJ+=1;
if(sz)
{
if(SJ>29)
{
sz=0; //停止闪烁
}
}
}
}
}
本人使用的是IAR EWARM(IAR Embedded Workbench for ARM)集成开发环境,利用周立功公司提供的工程模板,输入以上的源程序,点击菜单“Project”→“Mark”进行编译,或按F7键,根据提示修改错误,再进行编译直至无错误为止 。
3 软件调试
编译无误后即可点击菜单“Project”→“Dbuge”下载源程序的机器码至开发板(或按Ctrl+D),同时在桌面上出现了几个调试用的快捷按钮——运行(go)、运行到光标处(Run to Cursor)、步出(Step Out)、步入(Step Into)、步越(Step Over)、停止(Break)、复位(Reset)等。通过选择上述不同的快捷按钮,以及观察变量的值(点击菜单“View”→“Watch”在Expression中输入变量名,即可在Value中看到该变量的值;或将光标悬停在某变量上,即可看到该变量的值),来调试程序,直至一切无误后即可全速运行了(点击快捷按钮go)。
作者:
元器件交易网
时间:
2010-8-18 10:42
顶了在看。。。。
作者:
bnbv
时间:
2010-8-18 18:22
PCB百能网紧急采购
*商机编号:B006812S000002
*采购类型: 常规硬性PCB板
*订单量: 先打样(50pcs),后续大批量
*买家行业:通讯行业
*要求:
1)板材:旺灵F4BM-2、双面板
2)尺寸:19*28.5mm、板厚:0.8mm
3)工艺制程:介电常数2.65
详情请立即拔打服务热线:400---8866---380或登录PCB百能网
作者:
bnbv
时间:
2010-8-18 18:23
PCB百能网紧急采购
*商机编号:B006433S000002
*采购类型: 常规硬性PCB板
*订单量: 先打样(10pcs),后续大批量
*买家行业: 航天航空,医疗及卫星通讯电源
*要求:
1)材料:FR4的材料
2)尺寸:双面板和四层板尺寸相同(100.00*100.00mm)、板厚:1.6mm
3)工艺制程:可以做2OZ和4OZ的铜厚3.00oz;红胶工艺
4)表面处理:化金
5)检验按IPC-600的标准制作,请以10pcs打样报美元价格,必须以美元报价
6)仅限华东的供应商
详情请立即拔打服务热线:400---8866---380或登录PCB百能网
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0