xiaoxiaocheng_plc/users/RTC.C

326 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************/
// CPU需要STM32F103--RAM内存不小于64K Flash内存不小于128K
// 本代码已在STM32F103RDT6、VET6测试通过
// 编辑日期20150909
// editor by 小小晟
// 网店shop182385147.taobao.com
/********************************************************
PLC相关的特殊寄存器
专用辅助继电器 描述
M8126 全局标志
M8127 通讯请求握手信号
M8128 出错标志
M8129 通讯请求切换
专用数据寄存器 描述
D8000 = 200; 扫描时间
D8001 = 0X5EF6; 型号版本 FX2N(C)
D8101 = 0X5EF6; 型号版本 FX2N(C)
D8002 = 8; 内存容量
D8102 = 8; 内存容量
D8003 = 0x0010; 内存类型、寄存器类型
D8006 CPU电池电压
D8010 = 10; 扫描当前值
D8011 = 20; 扫描最小时间(0.1MS)
D8012 = 140; 扫描最长时间(0.1MS)
D6030 D6031 D6032 D6034 是模拟量输入
D7030 D7031 是模拟输出
D8120 = 0X4096 通讯格式
D8121 从站号最多16个
D8127 交换数据的首地址
D8128 交换数据量
D8129 网络通讯超时时间确认值
D8000 看门狗
D8019 对应星期
D8018 对应年份
D8017 对应月份
D8016 对应日期
D8015 对应小时
D8014 对应分钟
D8013 对应秒
通讯格式详解D8120
----------------------------------------------------------------------
位号 | 含 义 | 描述
-----------+-------------+--------------------------------------------
b0 | 数据长度 | 0 7位 1 8位
-----------+-------------+--------------------------------------------
b2b1 | 校验方式 | 00不用 01奇校验 11偶校验
-----------+-------------+--------------------------------------------
b3 | 停止位 | 0 1位 1 2位
-----------+-------------+--------------------------------------------
| | 0001300 01114800
b7b6b5b4 | 波特率 | 0100600 10009600
| | 01011200 100119200
| | 01102400
-----------+-------------+--------------------------------------------
b8 | | 0不用 注:无协议通讯专用
-----------+-------------+--------------------------------------------
b9 | | 0不用 同上
-----------+-------------+--------------------------------------------
b12b11b10 | 通讯接口 | 000RS485RS422接口
| | 010 RS232C接口
-----------+-------------+--------------------------------------------
b13 | 求和检查 | 0不加求和码 1自动加上求和码
-----------+-------------+-------------------------------------------
b14 | 协议 | 0无协议通讯 1专用通讯协议
-----------+-------------+--------------------------------------------
b15 | 协议格式 | 0格式1 1格式4
----------------------------------------------------------------------
举例D8120 = 0X4096 通讯波特率是19200
*********************************************************************************/
#include <main.h>
#include "stm32f10x_rtc.h"
#include "PLC_Dialogue.h"
#include "PLC_CONF.H"
/*STM32的RTC只有一个32位的计数器用来计时没有寄存器来存年月日时分秒等。
*通过设置可以让这个计数器1秒加1从0-0XFFFFFFFF大概可以计时136年。程序要设置一个时间起点表示0
*1、可以不使用RTC的秒中断因为在STM32掉电时只有电池供电这时RTC在计数但是STM32的内核中断应该是不能使用的。
*2、不用在备份区保存年月日等参数。
*/
//=======================================================================================================
// 函数名称: u8 Is_Leap_Year(u16 year)
// 功能描述: 判断是否是闰年函数
// 输 入: 年份
// 输 出: 该年份是不是闰年.1,是.0,不是
// 全局变量:
// 调用模块:
// 作 者: 小小晟
// 日 期: 2014年6月5日
// 备 注: 月份 1 2 3 4 5 6 7 8 9 10 11 12
// 闰年 31 29 31 30 31 30 31 31 30 31 30 31 一共366天=31622400秒钟
// 非闰年 31 28 31 30 31 30 31 31 30 31 30 31 一共365天=31536000秒钟
//-------------------------------------------------------------------------------------------------------
// 修改人:
// 日 期:
// 备 注:
//=======================================================================================================
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年的月份日期表
u8 Is_Leap_Year(u16 year)
{
if(year%4==0) //必须能被4整除
{
if(year%100==0)
{
if(year%400==0)return 1;//如果以00结尾,还要能被400整除
else return 0;
}else return 1;
}else return 0;
}
//=======================================================================================================
// 函数名称: u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
// 功能描述: 设置时钟
// 输 入: 年-月-日-时-分-秒
// 输 出: 0,成功;其他:错误代码.
// 全局变量:
// 调用模块:
// 作 者: 小小晟
// 日 期: 2014年6月5日
// 备 注: 以2010年1月1日为基准 2011~2136年为合法年份
//-------------------------------------------------------------------------------------------------------
// 修改人:
// 日 期:
// 备 注:
//=======================================================================================================
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
u16 t;
u32 seccount=0;
if(syear<2016||syear>2136)return 1; //syear范围2016-2136此处设置范围为2000-2136
for(t=2016;t<syear;t++) //把所有 年份的秒钟相加
{
if(Is_Leap_Year(t))seccount+=31622400; //闰年的秒钟数
else seccount+=31536000; //平年的秒钟数
}
smon-=1;
for(t=0;t<smon;t++) //把前面月份的秒钟数相加
{
seccount+=(u32)mon_table[t]*86400; //月份秒钟数相加
if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
}
seccount+=(u32)(sday-1)*86400; //把前面日期的秒钟数相加
seccount+=(u32)hour*3600; //小时秒钟数 因为一小时是3600秒
seccount+=(u32)min*60; //分钟秒钟数 因为一分钟是60秒
seccount+=sec; //最后的秒钟加上去
PWR_BackupAccessCmd(ENABLE); //取消后备区域的写保护<E4BF9D>因为后备寄存器中放的是重要的数据<E695B0>默认是不允许往里面写入值的
RTC_WaitForLastTask(); //等待上次对RTC寄存器配置完成
RTC_SetCounter(seccount); //写入时间
RTC_WaitForLastTask(); //等待上次对RTC寄存器配置完成
return 0;
}
/*************************************星期计算公式******************************************
//(年+年/4+年/400-年/100-年基数+月基数+日)/7……余星期几
注:式中分数均取整
年基数平年1,闰年2,
月基数1、平年一月0, 二月3, 三月3, 四月6, 五月1, 六月4,
七月0, 八月3, 九月5, 十月0, 十一月3,十二月5.
2、闰年一月0, 二月3, 三月4, 四月0, 五月2, 六月5,
七月0, 八月3, 九月6, 十月1, 十一月4, 十二月6.
1949年10月1日是星期几
1949+1949/4+1949/400-1949/100-1+0+1/7
1949+487+4-19-1+0+1/7
345……6
即该日为星期六。
**********************************************************************************************/
//=======================================================================================================
// 函数名称: u8 RTC_Get_Week(u16 year,u8 month,u8 day)
// 功能描述: 输入公历日期得到星期(只允许2000-2136年) 获得现在是星期几
// 输 入: 公历年月日
// 输 出: 星期号
// 全局变量:
// 调用模块:
// 作 者: 小小晟
// 日 期: 2014年6月5日
// 备 注:
//-------------------------------------------------------------------------------------------------------
// 修改人:
// 日 期:
// 备 注:
//=======================================================================================================
u8 week_tab[] ={0,1,4,4,0,2,5,0,3,6,1,4,6};
u8 RTC_Get_Week(u16 Year,u8 Month,u8 Date)
{
if( (Month < 3) && ((!(Year&0x03) && (Year%1000)) || (!(Year%400))))Date--;
return ( Date + Year + Year/4 + Year/400 -Year/100 + week_tab[Month]-2 )%7;
}
//=======================================================================================================
// 函数名称: u8 RTC_Get(void)
// 功能描述: 把秒变成时间 得到当前的时间
// 输 入: 公历年月日
// 输 出: //返回值:0,成功;其他:错误代码.
// 全局变量:
// 调用模块:
// 作 者: 小小晟
// 日 期: 2014年6月5日
// 备 注:
//-------------------------------------------------------------------------------------------------------
// 修改人:
// 日 期:
// 备 注:
//=======================================================================================================
void RTC_Get(void)
{
static u16 daycnt=0;
u32 timecount=0;
u32 temp=0;
u16 temp1=0;
if(PLC_BIT_TEST(M8015))
{RTC_Set(D8018,D8017,D8016,D8015,D8014,D8013);}
else if(PLC_BIT_TEST(M8018))
{
timecount=RTC_GetCounter();//得到计数器中的值(秒钟数)
temp=timecount/86400; //得到天数(秒钟数对应的) 一天=86400秒
if(daycnt!=temp) //超过一天了
{
daycnt=temp;
temp1=2016; //从2016年开始
while(temp>=365) //大于平年的一年
{
if(Is_Leap_Year(temp1)) //是闰年
{
if(temp>=366)temp-=366; //闰年的天数必须
else {temp1++;break;}
}
else temp-=365; //平年的天数
temp1++;
}
D8018 = temp1; //得到年份
temp1=0;
while(temp>=28) //超过了一个月 最小的月天数是28天
{
if(Is_Leap_Year(D8017)&&temp1==1)//当年是不是闰年/2月份
{
if(temp>=29)temp-=29; //闰年月天数29天
else break;
}
else
{
if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
else break;
}
temp1++;
}
D8017 = temp1+1; //得到月份
D8016 = temp+1; //得到日期
}
temp=timecount%86400;
D8015 = temp/3600; //小时
D8014 = (temp%3600)/60; //分钟
D8013 = (temp%3600)%60; //秒钟
D8019 = RTC_Get_Week(D8018,D8017,D8016);//获取星期
}
}
/*
* 函数名<E695B0>RTC_Configuration
* 描述 <20>配置RTC
* 输入 <20>
* 输出 <20>
* 调用 <20>外部调用
*/
void RTC_Config(void)
{
/* 使能PWR和BKP时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
/*取消后备区域的写保护<E4BF9D>因为后备寄存器中放的是重要的数据<E695B0>默认是不允许往里面写入值的 */
PWR_BackupAccessCmd(ENABLE);
/* 将后背寄存器的寄存器值设为默认值 */
BKP_DeInit();
/* 打开外部低速晶振<E699B6>RTC可以选择的时钟源是外部和内部低速晶振及外部高速晶振<E699B6>这里我们选择外部低速晶振32768HZ */
RCC_LSEConfig(RCC_LSE_ON);
/*等待外部低速晶振准备就序*/
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) ;
/*选择外部低速晶振为RTC的时钟源*/
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
/* 使能RTC时钟 */
RCC_RTCCLKCmd(ENABLE);
/* 等待RTC寄存器与RTC的APB时钟同步 */
RTC_WaitForSynchro();
/* 等待上次对RTC寄存器配置完成 */
RTC_WaitForLastTask();
/* 使能RTC中断 */
RTC_ITConfig(RTC_IT_SEC, ENABLE);
/* 等待上次对RTC寄存器配置完成 */
RTC_WaitForLastTask();
/* 设置RTC的预分频值<E9A291>因为外部低速晶振是32768<36>所以选择 */
/* RTC计 数 器 计 数 频 率= RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
RTC_SetPrescaler(32767);
/* 等待上次对RTC寄存器配置完成 */
RTC_WaitForLastTask();
}
void RTC_Init(void)
{
u16 u16_WaitForOscSource;
//我们在BKP的后备寄存器1中存了一个特殊字符0xA5A5
//第一次上电或后备电源掉电后,该寄存器数据丢失,
//表明RTC数据丢失需要重新配置
if(BKP_ReadBackupRegister(BKP_DR1) != 0x5A5A)
{
RTC_Config();//配置完成后向后备寄存器中写特殊字符0xA5A5
BKP_WriteBackupRegister(BKP_DR1, 0x5A5A);
}
else
{
//若后备寄存器没有掉电则无需重新配置RTC
//这里我们可以利用RCC_GetFlagStatus()函数查看本次复位类型
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
for(u16_WaitForOscSource=0;u16_WaitForOscSource<5000;u16_WaitForOscSource++);
if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET); //这是上电复位
else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET){} //这是外部RST管脚复位
//清除RCC中复位标志
RCC_ClearFlag();
/* 使能RTC中断 */
RTC_ITConfig(RTC_IT_SEC, ENABLE);
//等待操作完成
RTC_WaitForLastTask();
}
PLC_BIT_ON(M8018);PLC_BIT_OFF(M8015);
}