Skip to content

Commit 75bd7f1

Browse files
Merge pull request #144 from martinayotte/master
Initial push of RTClock for STM32F4
2 parents acf956a + ef98377 commit 75bd7f1

3 files changed

Lines changed: 583 additions & 0 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name=RTClock
2+
version=1.0
3+
author=Various
4+
email=
5+
sentence=Real Time Clock
6+
paragraph=Real Time Clock for STM32F4
7+
url=
8+
architectures=STM32F4
Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
/******************************************************************************
2+
* The MIT License
3+
*
4+
* Copyright (c) 2010 LeafLabs LLC.
5+
*
6+
* Permission is hereby granted, free of charge, to any person
7+
* obtaining a copy of this software and associated documentation
8+
* files (the "Software"), to deal in the Software without
9+
* restriction, including without limitation the rights to use, copy,
10+
* modify, merge, publish, distribute, sublicense, and/or sell copies
11+
* of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be
15+
* included in all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21+
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22+
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
* SOFTWARE.
25+
*****************************************************************************/
26+
27+
/**
28+
Inspired of the F1xx version adapted for the F4xx, not much F1xx left.
29+
author : Martin Ayotte, 2015.
30+
*/
31+
32+
#include "RTClock.h"
33+
34+
static rtc_dev rtc = {
35+
.regs = RTC_BASE,
36+
// .handlers = { [NR_RTC_HANDLERS - 1] = 0 },
37+
};
38+
39+
rtc_dev *RTC = &rtc;
40+
41+
42+
RTClock::RTClock() {
43+
RTClock(RTCSEL_HSE, 7999, 124);
44+
}
45+
46+
RTClock::RTClock(rtc_clk_src src) {
47+
RTClock(src, 0, 0);
48+
}
49+
50+
RTClock::RTClock(rtc_clk_src src, uint16 sync_prescaler, uint16 async_prescaler) {
51+
uint32 t = 0;
52+
RCC_BASE->APB1ENR |= RCC_APB1RSTR_PWRRST;
53+
dbg_printf("RCC_BASE->APB1ENR = %08X\r\n", RCC_BASE->APB1ENR);
54+
dbg_printf("before bkp_init\r\n");
55+
bkp_init(); // turn on peripheral clocks to PWR and BKP and reset the backup domain via RCC registers.
56+
// (we reset the backup domain here because we must in order to change the rtc clock source).
57+
dbg_printf("before bkp_disable_writes\r\n");
58+
bkp_disable_writes();
59+
dbg_printf("before bkp_enable_writes\r\n");
60+
bkp_enable_writes(); // enable writes to the backup registers and the RTC registers via the DBP bit in the PWR control register
61+
dbg_printf("RCC_BASE->CFGR = %08X\r\n", RCC_BASE->CFGR);
62+
RCC_BASE->CFGR |= (0x08 << 16); // Set the RTCPRE to HSE / 8.
63+
dbg_printf("RCC_BASE->CFGR = %08X\r\n", RCC_BASE->CFGR);
64+
65+
switch (src) {
66+
case RTCSEL_LSE :
67+
dbg_printf("Preparing RTC for LSE mode\r\n");
68+
if ((RCC_BASE->BDCR & 0x00000300) != 0x0100)
69+
RCC_BASE->BDCR = 0x00010000; // Reset the entire Backup domain
70+
RCC_BASE->BDCR = 0x00008101;
71+
dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR);
72+
while (!(RCC_BASE->BDCR & 0x00000002)) {
73+
if (++t > 1000000) {
74+
dbg_printf("RCC_BASE->BDCR.LSERDY Timeout !\r\n");
75+
dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR);
76+
return;
77+
}
78+
}
79+
dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR);
80+
rtc_enter_config_mode();
81+
if (sync_prescaler == 0 && async_prescaler == 0)
82+
RTC_BASE->PRER = 255 | (127 << 16);
83+
else
84+
RTC_BASE->PRER = sync_prescaler | (async_prescaler << 16);
85+
break;
86+
case RTCSEL_LSI :
87+
dbg_printf("Preparing RTC for LSI mode\r\n");
88+
if ((RCC_BASE->BDCR & 0x00000300) != 0x0200)
89+
RCC_BASE->BDCR = 0x00010000; // Reset the entire Backup domain
90+
RCC_BASE->BDCR = 0x00008204;
91+
RCC_BASE->CSR |= 0x00000001;
92+
dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR);
93+
while (!(RCC_BASE->CSR & 0x00000002)) {
94+
if (++t > 1000000) {
95+
dbg_printf("RCC_BASE->CSR.LSIRDY Timeout !\r\n");
96+
dbg_printf("RCC_BASE->CSR = %08X\r\n", RCC_BASE->CSR);
97+
return;
98+
}
99+
}
100+
dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR);
101+
rtc_enter_config_mode();
102+
if (sync_prescaler == 0 && async_prescaler == 0)
103+
RTC_BASE->PRER = 249 | (127 << 16);
104+
else
105+
RTC_BASE->PRER = sync_prescaler | (async_prescaler << 16);
106+
break;
107+
case RTCSEL_DEFAULT:
108+
case RTCSEL_HSE :
109+
dbg_printf("Preparing RTC for HSE mode\r\n");
110+
if ((RCC_BASE->BDCR & 0x00000300) != 0x0300)
111+
RCC_BASE->BDCR = 0x00010000; // Reset the entire Backup domain
112+
RCC_BASE->BDCR = 0x00008304;
113+
dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR);
114+
rtc_enter_config_mode();
115+
if (sync_prescaler == 0 && async_prescaler == 0)
116+
RTC_BASE->PRER = 7999 | (124 << 16);
117+
else
118+
RTC_BASE->PRER = sync_prescaler | (async_prescaler << 16);
119+
break;
120+
case RTCSEL_NONE:
121+
dbg_printf("Preparing RTC for NONE mode\r\n");
122+
if ((RCC_BASE->BDCR & 0x00000300) != 0x0000)
123+
RCC_BASE->BDCR = 0x00010000; // Reset the entire Backup domain
124+
RCC_BASE->BDCR = RCC_BDCR_RTCSEL_NONE;
125+
//do nothing. Have a look at the clocks to see the diff between NONE and DEFAULT
126+
break;
127+
}
128+
RCC_BASE->CR |= 0x00000040; // Turn to 24hrs mode
129+
// dbg_printf("before rtc_clear_sync\r\n");
130+
// rtc_clear_sync();
131+
// dbg_printf("before rtc_wait_sync\r\n");
132+
// rtc_wait_sync();
133+
rtc_exit_config_mode();
134+
dbg_printf("end of rtc_init\r\n");
135+
}
136+
137+
/*
138+
RTClock::~RTClock() {
139+
//to implement
140+
}
141+
*/
142+
143+
void RTClock::setTime (time_t time_stamp) {
144+
unsigned char years = 0;
145+
unsigned char months = 0;
146+
unsigned char monthLength = 0;
147+
unsigned char wdays = 0;
148+
unsigned char hours = 0;
149+
unsigned char mins = 0;
150+
unsigned char secs = 0;
151+
unsigned long days;
152+
153+
secs = time_stamp % 60;
154+
time_stamp /= 60; // now it is minutes
155+
mins = time_stamp % 60;
156+
time_stamp /= 60; // now it is hours
157+
hours = time_stamp % 24;
158+
time_stamp /= 24; // now it is days
159+
wdays = ((time_stamp + 4) % 7) + 1; // Sunday is day 1
160+
161+
while((unsigned)(days += (LEAP_YEAR(years) ? 366 : 365)) <= time_stamp) {
162+
years++;
163+
}
164+
165+
days -= LEAP_YEAR(years) ? 366 : 365;
166+
time_stamp -= days; // now it is days in this year, starting at 0
167+
168+
for (months = 0; months < 12; months++) {
169+
if (months == 1) { // february
170+
if (LEAP_YEAR(years)) {
171+
monthLength = 29;
172+
} else {
173+
monthLength = 28;
174+
}
175+
} else {
176+
monthLength = monthDays[months];
177+
}
178+
179+
if (time_stamp >= monthLength) {
180+
time_stamp -= monthLength;
181+
} else {
182+
break;
183+
}
184+
}
185+
months++; // jan is month 1
186+
days = time_stamp + 1; // day of month
187+
rtc_enter_config_mode();
188+
RTC_BASE->TR = ((hours / 10) << 20) | ((hours % 10) << 16) | ((mins / 10) << 12) | ((mins % 10) << 8) | ((secs / 10) << 4) | (secs % 10);
189+
RTC_BASE->DR = ((years / 10) << 20) | ((years % 10) << 16) | (wdays << 13) | ((months / 10) << 12) | ((months % 10) << 8) | ((days / 10) << 4) | (days % 10);
190+
rtc_exit_config_mode();
191+
}
192+
193+
void RTClock::setTime (struct tm* tm_ptr) {
194+
rtc_enter_config_mode();
195+
RTC_BASE->TR = ((tm_ptr->tm_hour / 10) << 20) | ((tm_ptr->tm_hour % 10) << 16) |
196+
((tm_ptr->tm_min / 10) << 12) | ((tm_ptr->tm_min % 10) << 8) |
197+
((tm_ptr->tm_sec / 10) << 4) | (tm_ptr->tm_sec % 10);
198+
RTC_BASE->DR = ((tm_ptr->tm_year / 10) << 20) | ((tm_ptr->tm_year % 10) << 16) | (tm_ptr->tm_wday << 13) |
199+
((tm_ptr->tm_mon / 10) << 12) | ((tm_ptr->tm_mon % 10) << 8) |
200+
((tm_ptr->tm_mday / 10) << 4) | (tm_ptr->tm_mday % 10);
201+
rtc_exit_config_mode();
202+
}
203+
204+
time_t RTClock::getTime() {
205+
int years = 10 * ((RTC_BASE->DR & 0x00F00000) >> 20) + ((RTC_BASE->DR & 0x000F0000) >> 16);
206+
int months = 10 * ((RTC_BASE->DR & 0x00001000) >> 12) + ((RTC_BASE->DR & 0x00000F00) >> 8);
207+
int days = 10 * ((RTC_BASE->DR & 0x00000030) >> 4) + (RTC_BASE->DR & 0x000000F);
208+
int hours = 10 * ((RTC_BASE->TR & 0x00300000) >> 20) + ((RTC_BASE->TR & 0x000F0000) >> 16);
209+
int mins = 10 * ((RTC_BASE->TR & 0x00007000) >> 12) + ((RTC_BASE->TR & 0x0000F00) >> 8);
210+
int secs = 10 * ((RTC_BASE->TR & 0x00000070) >> 4) + (RTC_BASE->TR & 0x0000000F);
211+
// seconds from 1970 till 1 jan 00:00:00 of the given year
212+
time_t t = (years + 30) * SECS_PER_DAY * 365;
213+
for (int i = 0; i < years; i++) {
214+
if (LEAP_YEAR(i)) {
215+
t += SECS_PER_DAY; // add extra days for leap years
216+
}
217+
}
218+
// add days for this year, months start from 1
219+
for (int i = 1; i < months; i++) {
220+
if ( (i == 2) && LEAP_YEAR(years)) {
221+
t += SECS_PER_DAY * 29;
222+
} else {
223+
t += SECS_PER_DAY * monthDays[i - 1]; //monthDays array starts from 0
224+
}
225+
}
226+
t += (days - 1) * SECS_PER_DAY + hours * SECS_PER_HOUR + mins * SECS_PER_MIN + secs;
227+
return t;
228+
}
229+
230+
struct tm* RTClock::getTime(struct tm* tm_ptr) {
231+
tm_ptr->tm_year = 10 * ((RTC_BASE->DR & 0x00F00000) >> 20) + ((RTC_BASE->DR & 0x000F0000) >> 16);
232+
tm_ptr->tm_mon = 10 * ((RTC_BASE->DR & 0x00001000) >> 12) + ((RTC_BASE->DR & 0x00000F00) >> 8);
233+
tm_ptr->tm_mday = 10 * ((RTC_BASE->DR & 0x00000030) >> 4) + (RTC_BASE->DR & 0x000000F);
234+
tm_ptr->tm_hour = 10 * ((RTC_BASE->TR & 0x00300000) >> 20) + ((RTC_BASE->TR & 0x000F0000) >> 16);
235+
tm_ptr->tm_min = 10 * ((RTC_BASE->TR & 0x00007000) >> 12) + ((RTC_BASE->TR & 0x0000F00) >> 8);
236+
tm_ptr->tm_sec = 10 * ((RTC_BASE->TR & 0x00000070) >> 4) + (RTC_BASE->TR & 0x0000000F);
237+
return tm_ptr;
238+
}
239+
240+
void RTClock::createAlarm(voidFuncPtr function, time_t alarm_time_t) {
241+
// rtc_set_alarm(alarm_time_t); //must be int... for standardization sake.
242+
// rtc_attach_interrupt(RTC_ALARM_SPECIFIC_INTERRUPT, function);
243+
}
244+
245+
void RTClock::createAlarm(voidFuncPtr function, tm* alarm_tm) {
246+
// time_t alarm = mktime(alarm_tm);//convert to time_t
247+
// createAlarm(function, alarm);
248+
}
249+
250+
void RTClock::attachSecondsInterrupt(voidFuncPtr function) {
251+
// rtc_attach_interrupt(RTC_SECONDS_INTERRUPT, function);
252+
}
253+
254+
void RTClock::detachSecondsInterrupt() {
255+
// rtc_detach_interrupt(RTC_SECONDS_INTERRUPT);
256+
}
257+
258+
void RTClock::setAlarmATime (tm * tm_ptr, bool hours_match, bool mins_match, bool secs_match, bool date_match) {
259+
rtc_enter_config_mode();
260+
unsigned int bits = ((tm_ptr->tm_mday / 10) << 28) | ((tm_ptr->tm_mday % 10) << 24) |
261+
((tm_ptr->tm_hour / 10) << 20) | ((tm_ptr->tm_hour % 10) << 16) |
262+
((tm_ptr->tm_min / 10) << 12) | ((tm_ptr->tm_min % 10) << 8) |
263+
((tm_ptr->tm_sec / 10) << 4) | (tm_ptr->tm_sec % 10);
264+
if (!date_match) bits |= (1 << 31);
265+
if (!hours_match) bits |= (1 << 23);
266+
if (!mins_match) bits |= (1 << 15);
267+
if (!secs_match) bits |= (1 << 7);
268+
RTC_BASE->ALRMAR = bits;
269+
RTC_BASE->CR |= (1 << RTC_CR_ALRAIE_BIT); // turn on ALRAIE
270+
rtc_exit_config_mode();
271+
}
272+
273+
274+
void RTClock::setAlarmATime (time_t alarm_time, bool hours_match, bool mins_match, bool secs_match, bool date_match) {
275+
struct tm* tm_ptr = gmtime(&alarm_time);
276+
setAlarmATime(tm_ptr, hours_match, mins_match, secs_match, date_match);
277+
}
278+
279+
280+
void RTClock::turnOffAlarmA() {
281+
rtc_enter_config_mode();
282+
RTC_BASE->CR &= ~(1 << RTC_CR_ALRAIE_BIT); // turn off ALRAIE
283+
rtc_exit_config_mode();
284+
}
285+
286+
287+
void RTClock::setAlarmBTime (tm * tm_ptr, bool hours_match, bool mins_match, bool secs_match, bool date_match) {
288+
rtc_enter_config_mode();
289+
unsigned int bits = ((tm_ptr->tm_mday / 10) << 28) | ((tm_ptr->tm_mday % 10) << 24) |
290+
((tm_ptr->tm_hour / 10) << 20) | ((tm_ptr->tm_hour % 10) << 16) |
291+
((tm_ptr->tm_min / 10) << 12) | ((tm_ptr->tm_min % 10) << 8) |
292+
((tm_ptr->tm_sec / 10) << 4) | (tm_ptr->tm_sec % 10);
293+
if (!date_match) bits |= (1 << 31);
294+
if (!hours_match) bits |= (1 << 23);
295+
if (!mins_match) bits |= (1 << 15);
296+
if (!secs_match) bits |= (1 << 7);
297+
RTC_BASE->ALRMBR = bits;
298+
RTC_BASE->CR |= (1 << RTC_CR_ALRBIE_BIT); // turn on ALRBIE
299+
rtc_exit_config_mode();
300+
}
301+
302+
303+
void RTClock::setAlarmBTime (time_t alarm_time, bool hours_match, bool mins_match, bool secs_match, bool date_match) {
304+
struct tm* tm_ptr = gmtime(&alarm_time);
305+
setAlarmBTime(tm_ptr, hours_match, mins_match, secs_match, date_match);
306+
}
307+
308+
309+
void RTClock::turnOffAlarmB() {
310+
rtc_enter_config_mode();
311+
RTC_BASE->CR &= ~(1 << RTC_CR_ALRBIE_BIT); // turn off ALRBIE
312+
rtc_exit_config_mode();
313+
}
314+
315+
316+
void RTClock::setPeriodicWakeup(uint16 period) {
317+
rtc_enter_config_mode();
318+
dbg_printf("before setting RTC_BASE->WUTR\r\n");
319+
RTC_BASE->WUTR = period; // set the period
320+
dbg_printf("before setting RTC_BASE->CR.WUCKSEL\r\n");
321+
RTC_BASE->CR &= ~(3); RTC_BASE->CR |= 4; // Set the WUCKSEL to 1Hz (0x00000004)
322+
if (period == 0)
323+
RTC_BASE->CR &= ~(1 << RTC_CR_WUTIE_BIT); // if period is 0, turn off periodic wakeup interrupt.
324+
else {
325+
dbg_printf("before turn ON RTC_BASE->CR.WUTIE\r\n");
326+
RTC_BASE->CR |= (1 << RTC_CR_WUTIE_BIT); // turn on WUTIE
327+
}
328+
dbg_printf("RCC_BASE->CR = %08X\r\n", RCC_BASE->CR);
329+
rtc_exit_config_mode();
330+
rtc_enable_wakeup_event();
331+
nvic_irq_enable(NVIC_RTC);
332+
nvic_irq_enable(NVIC_RTCALARM);
333+
dbg_printf("setPeriodicWakeup() done !\r\n");
334+
}
335+
336+
extern "C" {
337+
void __irq_rtc(void) {
338+
dbg_printf("__irq_rtc() called !\r\n");
339+
*bb_perip(&EXTI_BASE->PR, EXTI_RTC_WAKEUP_BIT) = 1;
340+
}
341+
void __irq_rtcalarm(void) {
342+
dbg_printf("__irq_rtcalarm() called !\r\n");
343+
*bb_perip(&EXTI_BASE->PR, EXTI_RTC_ALARM_BIT) = 1;
344+
}
345+
}

0 commit comments

Comments
 (0)