首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

基于ARM单片机的软件时钟

基于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 );
}
//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)。
顶了在看。。。。
元件交易网 网络与实体相结合,网罗电子天下
PCB百能网紧急采购

*商机编号:B006812S000002
*采购类型: 常规硬性PCB板
*订单量: 先打样(50pcs),后续大批量
*买家行业:通讯行业
*要求:
1)板材:旺灵F4BM-2、双面板
2)尺寸:19*28.5mm、板厚:0.8mm
3)工艺制程:介电常数2.65
详情请立即拔打服务热线:400---8866---380或登录PCB百能网
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百能网
返回列表