原创 基于ARM单片机的软件时钟

2010-8-6 18:02 3074 10 10 分类: MCU/ 嵌入式

基于ARM单片机的软件时钟


ARM单片机的功能要大大优于51单片机,而其价格已经很低了,生产厂家不论是出于广告宣传的需要,还是出于利润的角度考虑(嵌入ARM单片机将会卖更高的价格),他们更希望在自己的产品中嵌入ARM单片机。


本人使用的是周立功公司推出的Easy ARM1138开发板,它除了具有32位ARM Cortex-M3内核的LM3S1138单片机之外,还内嵌有USB接口的仿真调试器,开发调试非常方便,不愧为广大师生和工程师的首选。如下图所示。


094290c0-c328-4158-9629-0a4251b83fe1.jpg


1.    硬件设计


利用LM3S1138内部的定时器,0.5秒产生一次中断,再用一个时间计数器进行计时;然后再利用1602LCD进行显示;按键sw1为时间“选择”键,按键sw2为时间“+1”键,它们用于对于时间的调整;从而实现完整的软件时钟。电位器用于调整LCD的对比度。完整的电原理图如下所示。



 2376f09d-4232-4be5-86f0-8c4ebf100de2.jpg


目标板是利用原有的一块印制板,在其上焊接有两只按键、两只电阻、一只电位器和连接1602LCD的插座(参见后面的实物照片),目标板与1138开发板之间通过15根杜邦线相连。


 


⒉    软件设计


程序一开始要进行系统初始化、定时器设置初值、开中断、LCD初始化、调用显示子函数。然后进入大循环;首先判断是否是“选择”键按下,若是则进行消抖处理,再判断原为时间的哪一位在闪烁,并使得下一位闪烁,同时使闪烁计时为零; 然后判断是否是“+1”键按下,若是则进行消抖处理,再判断原为时间的哪一位在闪烁,并使得该位+1,同时使闪烁计时为零;然后循环判断是否是“选择”键按下……。


定时器T0每0.5秒中断一次,在定时器中断函数中,首先中断计数器+1,若中断次数为奇数,则根据闪烁字控制某位不显示;若中断次数为偶数(一秒),则时间计数器+1,当时间计数器的值超过23点59分59秒时时间计数器=0,再调显示子函数,并使闪烁计时器+1,然后判断闪烁字是否不为零,若是则判断闪烁是否已达30秒?若是则停止闪烁,并中断返回。详细的流程如下图所示。



 e15b03a8-8312-47ef-990a-f8706bc5c044.jpg


基于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 );


}


 


//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)。实物图片如下所示。


2b512041-a868-4770-98ce-cd990a5ce089.JPG

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
10
关闭 站长推荐上一条 /3 下一条