326 lines
14 KiB
C
326 lines
14 KiB
C
/********************************************************/
|
||
// 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位
|
||
-----------+-------------+--------------------------------------------
|
||
| | 0001:300 0111:4800
|
||
b7b6b5b4 | 波特率 | 0100:600 1000:9600
|
||
| | 0101:1200 1001:19200
|
||
| | 0110:2400
|
||
-----------+-------------+--------------------------------------------
|
||
b8 | | 0:不用 注:无协议通讯专用
|
||
-----------+-------------+--------------------------------------------
|
||
b9 | | 0:不用 同上
|
||
-----------+-------------+--------------------------------------------
|
||
b12b11b10 | 通讯接口 | 000:RS485(RS422)接口
|
||
| | 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);
|
||
}
|