野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 31473|回复: 33

嵌入式智能万年历设计

[复制链接]
发表于 2013-8-27 14:04:15 | 显示全部楼层 |阅读模式
需要设计万年历,因此网上找来了一个 例子。

任务要求:
用ARM—M3芯片设计一个智能万年历,要求能够正确显示近100 年时间,星期几,时间格式为********日(星期**) **:**:**(时:分:秒),能够识别出闰年(计时范围能够从1970年1月1日到2100年左右)。
该程序设计工程里包含有main.c(相关配置、和终端联系,提醒输出初始时间、输出实时时间)/date.c(计算实时时间)/stm32f10x_it.c三个文件,各文件内容如下。



回复

使用道具 举报

 楼主| 发表于 2013-8-27 14:06:17 | 显示全部楼层

Main.c文件

[code=cpp]
#include "stm32f10x.h"
#include "stdio.h"
#include "date.h"

__IO uint32_t TimeDisplay = 0;

void RCC_Configuration(void);
void NVIC_Configuration(void);
void GPIO_Configuration(void);
void USART_Configuration(void);
int fputc(int ch, FILE *f);
void RTC_Configuration(void);
void Time_Regulate(struct rtc_time *tm);
void Time_Adjust(void);
void Time_Display(uint32_t TimeVar);
void Time_Show(void);
u8 USART_Scanf(u32 value);

#define  RTCClockSource_LSE       

u8 const *WEEK_STR[] = {"日", "一", "二", "三", "四", "五", "六"};

struct rtc_time systmtime;

int main()
{
          RCC_Configuration();
       
          NVIC_Configuration();
       
          GPIO_Configuration();
       
          USART_Configuration();
       
          /*在启动时检查备份寄存器BKP_DR1,如果内容不是0xA5A5,则需重新配置时间并询问用户调整时间*/
        if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
        {
            printf("\r\n\n RTC not yet configured....");
               
            RTC_Configuration();
                printf("\r\n RTC configured....");
               
            Time_Adjust();
               
                BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
        }
        else
        {
            /*启动无需设置新时钟*/
                /*检查是否掉电重启*/
                if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
                {
                    printf("\r\n\n Power On Reset occurred....");
                }
                /*检查是否Reset复位*/
                else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
            {
              printf("\r\n\n External Reset occurred....");
            }
               
                printf("\r\n No need to configure RTC....");
               
                /*等待寄存器同步*/
                RTC_WaitForSynchro();
               
                /*允许RTC秒中断*/
                RTC_ITConfig(RTC_IT_SEC, ENABLE);
               
                /*等待上次RTC寄存器写操作完成*/
                RTC_WaitForLastTask();
        }

        #ifdef RTCClockOutput_Enable

          RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
       
          PWR_BackupAccessCmd(ENABLE);
       
          BKP_TamperPinCmd(DISABLE);
       
          BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock);
        #endif
       
          RCC_ClearFlag();
       
          Time_Show();
}

void RCC_Configuration()
{
                SystemInit();
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
}
[/code]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-8-27 14:07:52 | 显示全部楼层

Main.c文件 2


[code=cpp]

void NVIC_Configuration()
{
          NVIC_InitTypeDef NVIC_InitStructure;
       
          NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
       
          NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
          NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
          NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
          NVIC_Init(&NVIC_InitStructure);
}

void GPIO_Configuration()
{
          GPIO_InitTypeDef GPIO_InitStructure;
       
          GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
          GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
          GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
          GPIO_Init(GPIOA, &GPIO_InitStructure);
            
          GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
          GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
          GPIO_Init(GPIOA, &GPIO_InitStructure);

}

void USART_Configuration()
{
            USART_InitTypeDef USART_InitStructure;
       
                USART_InitStructure.USART_BaudRate = 115200;
                USART_InitStructure.USART_WordLength = USART_WordLength_8b;
                USART_InitStructure.USART_StopBits = USART_StopBits_1;
                USART_InitStructure.USART_Parity = USART_Parity_No ;
                USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
            USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
       
                USART_Init(USART1, &USART_InitStructure);
            USART_Cmd(USART1, ENABLE);
}

int fputc(int ch, FILE *f)
{
          /* 将Printf内容发往串口 */
          USART_SendData(USART1, (unsigned char) ch);

          while (!(USART1->SR & USART_FLAG_TXE));
         
          return (ch);
}

void RTC_Configuration()
{
     /*允许PWR和BKP时钟*/
         RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
         
         /*允许访问BKP域*/
         PWR_BackupAccessCmd(ENABLE);
         
         /*复位备份域*/
         BKP_DeInit();
         
         #ifdef RTCClockSource_LSI
         
         /*允许LSI*/
         RCC_LSICmd(ENABLE);
         
         /*等待LSI准备好*/
         while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY)==RESET)
         {
         }
         
         /*选择LSI作为RTC时钟源*/
         RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
         
         #elif defined  RTCClockSource_LSE
         
         /*允许LSE*/
         RCC_LSEConfig(RCC_LSE_ON);
         
         /*等待LSE准备好*/
         while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET)
         {
         }
         
         /*选择LSE作为RTC时钟源*/
         RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
         #endif
         
         /* Enable RTC Clock */
     RCC_RTCCLKCmd(ENABLE);
         
         #ifdef RTCClockOutput_Enable
         /*禁止Tamper引脚*/
         BKP_TamperPinCmd(DISABLE);/*为了将RTCCLK/64在Tamper引脚输出,Tamper功能必须被禁止*/
         
         /*允许RTC时钟在Tamper引脚上输出*/
         BKP_RTCCalibrationClockOutputCmd(ENABLE);
         #endif
         
         /*等待寄存器同步*/
         RTC_WaitForSynchro();
         
         /*等待上次RTC寄存器写操作完成*/
         RTC_WaitForLastTask();
         
         /*允许RTC秒中断*/
         RTC_ITConfig(RTC_IT_SEC, ENABLE);
         
         /*等待上次RTC寄存器写操作完成*/
         RTC_WaitForLastTask();
         
         #ifdef RTCClockSource_LSI
         /*设置分频系数*/
         RTC_SetPrescaler(31999); /*RTC周期=RTCCLK/RTC_PR=(32.000kHz/(31999+1))*/
         
         #elif defined  RTCClockSource_LSE
         RTC_SetPrescaler(32767); /*RTC周期=RTCCLK/RTC_PR=(32.768kHz/(31767+1))*/
         #endif
         
         /*等待上次RTC寄存器写操作完成*/
         RTC_WaitForLastTask();
       
}

void Time_Regulate(struct rtc_time *tm)
{
          u32 Tmp_YY = 0xFF, Tmp_MM = 0xFF, Tmp_DD = 0xFF, Tmp_HH = 0xFF, Tmp_MI = 0xFF, Tmp_SS = 0xFF;
       
          printf("\r\n=========================Time Settings==================");
       
          printf("\r\n  请输入年份(Please Set Years):  20");

          while (Tmp_YY == 0xFF)
          {
            Tmp_YY = USART_Scanf(99);
          }

          printf("\n\r  年份被设置为:  20%0.2d\n\r", Tmp_YY);

          tm->tm_year = Tmp_YY+2000;
       
          Tmp_MM = 0xFF;

          printf("\r\n  请输入月份(Please Set Months):  ");

          while (Tmp_MM == 0xFF)
          {
            Tmp_MM = USART_Scanf(12);
          }

          printf("\n\r  月份被设置为:  %d\n\r", Tmp_MM);

          tm->tm_mon= Tmp_MM;
       
          Tmp_DD = 0xFF;

          printf("\r\n  请输入日期(Please Set Dates):  ");

          while (Tmp_DD == 0xFF)
          {
            Tmp_DD = USART_Scanf(31);
          }

          printf("\n\r  日期被设置为:  %d\n\r", Tmp_DD);

          tm->tm_mday= Tmp_DD;
       
          Tmp_HH  = 0xFF;

          printf("\r\n  请输入时钟(Please Set Hours):  ");

          while (Tmp_HH == 0xFF)
          {
            Tmp_HH = USART_Scanf(23);
          }

          printf("\n\r  时钟被设置为:  %d\n\r", Tmp_HH );

          tm->tm_hour= Tmp_HH;
            
          Tmp_MI = 0xFF;

          printf("\r\n  请输入分钟(Please Set Minutes):  ");

          while (Tmp_MI == 0xFF)
          {
            Tmp_MI = USART_Scanf(59);
          }

          printf("\n\r  分钟被设置为:  %d\n\r", Tmp_MI);

          tm->tm_min= Tmp_MI;
          
          Tmp_SS = 0xFF;

          printf("\r\n  请输入秒钟(Please Set Seconds):  ");

          while (Tmp_SS == 0xFF)
          {
            Tmp_SS = USART_Scanf(59);
          }

          printf("\n\r  秒钟被设置为:  %d\n\r", Tmp_SS);

          tm->tm_sec= Tmp_SS;
}

void Time_Adjust()
{
          RTC_WaitForLastTask();
       
          Time_Regulate(&systmtime);
          
          GregorianDay(&systmtime);

          RTC_SetCounter(mktimev(&systmtime));

          RTC_WaitForLastTask();
}


void Time_Display(uint32_t TimeVar)
{          
           to_tm(TimeVar, &systmtime);

           printf("\r   当前时间为: %d年 %d月 %d日 (星期%s)  %0.2d:%0.2d:%0.2d",
                            systmtime.tm_year, systmtime.tm_mon, systmtime.tm_mday,
                            WEEK_STR[systmtime.tm_wday], systmtime.tm_hour,
                            systmtime.tm_min, systmtime.tm_sec);

}

void Time_Show()
{
          printf("\n\r");
       
          /* Infinite loop */
          while (1)
          {
            /* 每过1s */
            if (TimeDisplay == 1)
            {
              Time_Display(RTC_GetCounter());
              TimeDisplay = 0;
            }
          }
}

u8 USART_Scanf(u32 value)
{
          u32 index = 0;
          u32 tmp[2] = {0, 0};
       
          while (index < 2)
          {
            while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)
            {
                }
               
            tmp[index++] = (USART_ReceiveData(USART1));
            if ((tmp[index - 1] < 0x30) || (tmp[index - 1] > 0x39))   /*数字0到9的ASCII码为0x30至0x39*/
            {
                  if((index == 2) && (tmp[index - 1] == '\r'))
                  {
                      tmp[1] = tmp[0];
              tmp[0] = 0x30;
                  }
                  else
                  {
                      printf("\n\rPlease enter valid number between 0 and 9 -->:  ");
              index--;
                  }
            }
          }
          
          /* 计算输入字符的相应ASCII值*/
          index = (tmp[1] - 0x30) + ((tmp[0] - 0x30) * 10);
          /* Checks */
          if (index > value)
          {
            printf("\n\rPlease enter valid number between 0 and %d", value);
            return 0xFF;
          }
          return index;
}
[/code]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-8-27 14:08:36 | 显示全部楼层

Date.c文件

[code=cpp]
#include "date.h"

#define FEBRUARY                2
#define        STARTOFTIME                1970
#define SECDAY                        86400L
#define SECYR                        (SECDAY * 365)
#define        leapyear(year)                ((year) % 4 == 0)
#define        days_in_year(a)         (leapyear(a) ? 366 : 365)
#define        days_in_month(a)         (month_days[(a) - 1])

static int month_days[12] = {        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

/*计算公历*/
void GregorianDay(struct rtc_time * tm)
{
        int leapsToDate;
        int lastYear;
        int day;
        int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };

        lastYear=tm->tm_year-1;

        /*计算到计数的前一年之中一共经历了多少个闰年*/
        leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;      

     /*如若计数的这一年为闰年,且计数的月份在2月之后,则日数加1,否则不加1*/
        if((tm->tm_year%4==0) && ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && (tm->tm_mon>2))
        {
                day=1;
        }
        else
        {
                day=0;
        }

        day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday; /*计算从计数元年元旦到计数日期一共有多少天*/

        tm->tm_wday=day%7;
}

u32 mktimev(struct rtc_time *tm)
{
        if (0 >= (int) (tm->tm_mon -= 2))
        {       
                tm->tm_mon += 12;       
                tm->tm_year -= 1;
        }

        return ((((u32) (tm->tm_year/4 - tm->tm_year/100 + tm->tm_year/400 + 367*tm->tm_mon/12 + tm->tm_mday)
         + tm->tm_year*365 - 719499)*24 + tm->tm_hour )*6+ tm->tm_min )*60 + tm->tm_sec;
}

void to_tm(u32 tim, struct rtc_time * tm)
{
        register u32    i;
        register long   hms, day;

        day = tim / SECDAY;
        hms = tim % SECDAY;

        tm->tm_hour = hms / 3600;
        tm->tm_min = (hms % 3600) / 60;
        tm->tm_sec = (hms % 3600) % 60;

/*算出当前年份,起始的计数年份为1970年*/
        for (i = STARTOFTIME; day >= days_in_year(i); i++)
        {
                day -= days_in_year(i);
        }
        tm->tm_year = i;

/*计算当前的月份*/
        if (leapyear(tm->tm_year))
        {
                days_in_month(FEBRUARY) = 29;
        }
        for (i = 1; day >= days_in_month(i); i++)
        {
                day -= days_in_month(i);
        }
        days_in_month(FEBRUARY) = 28;
        tm->tm_mon = i;

/*计算当前日期*/
        tm->tm_mday = day + 1;

        GregorianDay(tm);
       
}
[/code]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-8-27 14:09:26 | 显示全部楼层

stm32f10x_it.c文件


[code=cpp]
#include "stm32f10x_it.h"

extern uint32_t TimeDisplay;

void NMI_Handler(void)
{
}

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval : None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Memory Manage exception.
  * @param  None
  * @retval : None
  */
void MemManage_Handler(void)
{
  /* Go to infinite loop when Memory Manage exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Bus Fault exception.
  * @param  None
  * @retval : None
  */
void BusFault_Handler(void)
{
  /* Go to infinite loop when Bus Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Usage Fault exception.
  * @param  None
  * @retval : None
  */
void UsageFault_Handler(void)
{
  /* Go to infinite loop when Usage Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval : None
  */
void SVC_Handler(void)
{
}

/**
  * @brief  This function handles Debug Monitor exception.
  * @param  None
  * @retval : None
  */
void DebugMon_Handler(void)
{
}

/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval : None
  */
void PendSV_Handler(void)
{
}

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval : None
  */
void SysTick_Handler(void)
{
}

void RTC_IRQHandler(void)
{
          if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
          {
            /* Clear the RTC Second interrupt */
            RTC_ClearITPendingBit(RTC_IT_SEC);
       
            /* Enable time update */
            TimeDisplay = 1;
       
            /* Wait until last write operation on RTC registers has finished */
            RTC_WaitForLastTask();

            /* Reset RTC Counter when Time is 23:59:59 */
            if (RTC_GetCounter() == 0x00015180)
            {
              RTC_SetCounter(0x0);
              /* Wait until last write operation on RTC registers has finished */
              RTC_WaitForLastTask();
            }
          }
}
[/code]
回复 支持 反对

使用道具 举报

发表于 2013-8-31 21:22:56 | 显示全部楼层
真棒!!非常感谢
回复 支持 反对

使用道具 举报

发表于 2013-11-26 11:22:19 | 显示全部楼层
真棒!!非常感谢
回复 支持 反对

使用道具 举报

发表于 2013-12-13 10:09:23 | 显示全部楼层
收藏了先!真棒!!非常感谢
回复 支持 反对

使用道具 举报

发表于 2014-3-25 15:35:08 | 显示全部楼层
哥们  野火历程是串口显示的   你的能在LCD上显示吗?  不用超级终端显示  怎么改啊强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-4-12 14:50:29 | 显示全部楼层
帅呆了,赞一个
回复 支持 反对

使用道具 举报

发表于 2014-5-9 20:48:57 | 显示全部楼层
yu940916 发表于 2014-3-25 15:35
哥们  野火历程是串口显示的   你的能在LCD上显示吗?  不用超级终端显示  怎么改啊强烈支持,非常感谢哥们 ...

今天刚做了一个,实在野火ISO开发板例程的基础上改的,支持串口和LCD显示,要的话留邮箱
回复 支持 1 反对 0

使用道具 举报

发表于 2014-5-10 09:32:15 | 显示全部楼层
yu940916 发表于 2014-3-25 15:35
哥们  野火历程是串口显示的   你的能在LCD上显示吗?  不用超级终端显示  怎么改啊强烈支持,非常感谢哥们 ...

调用LCD函数,应该不是很难的,移植一下
回复 支持 反对

使用道具 举报

发表于 2014-7-21 15:05:28 | 显示全部楼层
回复 支持 反对

使用道具 举报

发表于 2014-8-6 10:22:36 | 显示全部楼层
15637749583@163.com  希望能更详细一点  ,把你的实物也让我看看吧!
回复 支持 反对

使用道具 举报

发表于 2014-8-23 06:41:43 | 显示全部楼层
调用LCD函数强烈支持,非常感谢哥们强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-8-23 09:09:56 | 显示全部楼层
楼主  这不就是 RTC  实时时钟吗!   走过路过,不能错过
回复 支持 反对

使用道具 举报

发表于 2014-8-23 09:29:50 | 显示全部楼层
不错呦 帅呆了,赞一个
回复 支持 反对

使用道具 举报

发表于 2014-8-23 17:07:16 | 显示全部楼层
673261839@qq.com 能发给我一份么?谢了!
回复 支持 反对

使用道具 举报

发表于 2014-9-23 13:02:08 | 显示全部楼层
310744285@qq.com 希望给我发一份 谢谢
回复 支持 反对

使用道具 举报

发表于 2014-10-15 10:28:19 | 显示全部楼层
Animal 发表于 2014-5-9 20:48
今天刚做了一个,实在野火ISO开发板例程的基础上改的,支持串口和LCD显示,要的话留邮箱

我想要一个 可以不 QQ:1019646952
回复 支持 反对

使用道具 举报

发表于 2014-10-15 10:55:15 | 显示全部楼层
1321310827@qq.com,楼主送我一份可好强烈支持,非常感谢哥们
回复 支持 反对

使用道具 举报

发表于 2014-10-22 01:01:53 | 显示全部楼层
673261839@qq.com 能发给我一份么?谢了!
回复 支持 反对

使用道具 举报

发表于 2015-4-26 16:26:35 | 显示全部楼层
Animal 发表于 2014-5-9 20:48
今天刚做了一个,实在野火ISO开发板例程的基础上改的,支持串口和LCD显示,要的话留邮箱

加我一个,谢了,qq:3227825176
回复 支持 反对

使用道具 举报

发表于 2015-12-14 13:28:39 | 显示全部楼层
Animal 发表于 2014-5-9 20:48
今天刚做了一个,实在野火ISO开发板例程的基础上改的,支持串口和LCD显示,要的话留邮箱

850738964@qq.com 求发 急用!非常感谢0.0
回复 支持 反对

使用道具 举报

发表于 2016-11-19 11:17:05 | 显示全部楼层
907283414@qq.com 大神发我一份谢谢哈!
回复 支持 反对

使用道具 举报

发表于 2016-12-19 20:58:36 | 显示全部楼层
这个  请问 还有程序么  可以给一份么
回复 支持 反对

使用道具 举报

发表于 2021-1-14 20:48:38 | 显示全部楼层
643825040@QQ.com谢谢!
回复 支持 反对

使用道具 举报

发表于 2021-1-17 19:28:46 | 显示全部楼层
发邮箱可好1013059924@qq.com,加好友就更好了
回复 支持 反对

使用道具 举报

发表于 2021-1-17 19:29:38 | 显示全部楼层
Arive 发表于 2014-8-6 10:22
希望能更详细一点  ,把你的实物也让我看看吧!

楼主发你邮箱了吗
回复 支持 反对

使用道具 举报

发表于 2021-1-25 16:11:55 | 显示全部楼层
147385835@qq.com,想参考学习版主的程序。谢谢大佬
回复 支持 反对

使用道具 举报

发表于 2021-1-25 16:29:25 | 显示全部楼层
yu940916 发表于 2014-3-25 15:35
哥们  野火历程是串口显示的   你的能在LCD上显示吗?  不用超级终端显示  怎么改啊强烈支持,非常感谢哥们 ...

加个LCD驱动 直接输出就行了
回复 支持 反对

使用道具 举报

发表于 2021-1-29 11:55:50 | 显示全部楼层
可以发个百度网盘吗?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-9-22 23:21 , Processed in 0.172801 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表