2016-08-14 18:37:58 +02:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
/** library to get time from a DCF77 module (code)
* @ file rtc_dcf77 . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2017-10-08 18:36:32 +02:00
* @ date 2016 - 2017
2016-08-14 18:37:58 +02:00
* @ note peripherals used : GPIO @ ref rtc_dcf77_gpio , timer @ ref rtc_dcf77_timer
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
2017-10-08 17:30:22 +02:00
# include <libopencmsis/core_cm3.h> // Cortex M3 utilities
2016-08-14 18:37:58 +02:00
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/spi.h> // SPI library
# include <libopencm3/stm32/timer.h> // timer library
# include <libopencm3/cm3/nvic.h> // interrupt handler
2017-10-08 17:30:22 +02:00
# include <libopencm3/stm32/exti.h> // external interrupt utilities
2016-08-14 18:37:58 +02:00
# include "rtc_dcf77.h" // RTC DCF77 library API
# include "global.h" // common methods
2017-10-08 18:36:32 +02:00
/** @defgroup rtc_dcf77_gpio output to enable DCF module and input to capture DCF signal
* @ {
*/
# define RTC_DCF77_ENABLE_PORT A /**< GPIO port to enable the module */
# define RTC_DCF77_ENABLE_PIN 2 /**< GPIO pinto enable the module */
# define RTC_DCF77_SIGNAL_PORT A /**< GPIO port to capture the DCF signal */
# define RTC_DCF77_SIGNAL_PIN 3 /**< GPIO pin to capture the DCF signal */
/** @} */
/** @defgroup rtc_dcf77_timer timer to measure signal puls
* @ {
*/
# define RTC_DCF77_TIMER 4 /**< timer peripheral */
# define RTC_DCF77_TIMER_MAX_TIME 2200 /**< the maximum time in ms the timer can count. DCF77 have pulses < 2s */
/** @} */
2016-08-14 18:37:58 +02:00
volatile bool rtc_dcf77_time_flag = false ;
2017-10-08 17:30:22 +02:00
static volatile uint64_t rtc_dcf77_frame = 0 ; /**< the received DCF77 frame bits */
2016-08-14 18:37:58 +02:00
void rtc_dcf77_setup ( void )
{
// setup enable output
2017-10-08 18:36:32 +02:00
rcc_periph_clock_enable ( RCC_GPIO ( RTC_DCF77_ENABLE_PORT ) ) ; // enable clock GPIO peripheral
gpio_set_mode ( GPIO ( RTC_DCF77_ENABLE_PORT ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( RTC_DCF77_ENABLE_PIN ) ) ; // set pin to output push-pull to be able to enable the module
2016-08-14 18:37:58 +02:00
rtc_dcf77_off ( ) ; // disable module at start
// setup signal input
2017-10-08 18:36:32 +02:00
rcc_periph_clock_enable ( RCC_GPIO ( RTC_DCF77_SIGNAL_PORT ) ) ; // enable clock for signal input peripheral
gpio_set_mode ( GPIO ( RTC_DCF77_SIGNAL_PORT ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , GPIO ( RTC_DCF77_SIGNAL_PIN ) ) ; // set signal pin to input
2016-08-14 18:37:58 +02:00
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable alternate function clock for external interrupt
2017-10-08 18:36:32 +02:00
exti_select_source ( EXTI ( RTC_DCF77_SIGNAL_PIN ) , GPIO ( RTC_DCF77_SIGNAL_PORT ) ) ; // mask external interrupt of this pin only for this port
exti_set_trigger ( EXTI ( RTC_DCF77_SIGNAL_PIN ) , EXTI_TRIGGER_BOTH ) ; // trigger on both edges
exti_enable_request ( EXTI ( RTC_DCF77_SIGNAL_PIN ) ) ; // enable external interrupt
nvic_enable_irq ( NVIC_EXTI_IRQ ( RTC_DCF77_SIGNAL_PIN ) ) ; // enable interrupt
2016-08-14 18:37:58 +02:00
// setup timer to measure pulses
2017-10-08 18:36:32 +02:00
rcc_periph_clock_enable ( RCC_TIM ( RTC_DCF77_TIMER ) ) ; // enable clock for timer peripheral
timer_reset ( TIM ( RTC_DCF77_TIMER ) ) ; // reset timer state
timer_set_mode ( TIM ( RTC_DCF77_TIMER ) , TIM_CR1_CKD_CK_INT , TIM_CR1_CMS_EDGE , TIM_CR1_DIR_UP ) ; // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
timer_set_prescaler ( TIM ( RTC_DCF77_TIMER ) , RTC_DCF77_TIMER_MAX_TIME * ( rcc_ahb_frequency / 1000 ) / ( 1 < < 16 ) ) ; // set prescaler to count up to the maximum time
timer_enable_counter ( TIM ( RTC_DCF77_TIMER ) ) ; // start timer to measure time
2016-08-14 18:37:58 +02:00
}
void rtc_dcf77_on ( void )
{
2017-10-08 18:36:32 +02:00
gpio_clear ( GPIO ( RTC_DCF77_ENABLE_PORT ) , GPIO ( RTC_DCF77_ENABLE_PIN ) ) ; // enable module by pull pin low
2016-08-14 18:37:58 +02:00
}
void rtc_dcf77_off ( void )
{
2017-10-08 18:36:32 +02:00
gpio_set ( GPIO ( RTC_DCF77_ENABLE_PORT ) , GPIO ( RTC_DCF77_ENABLE_PIN ) ) ; // disable module by pull pin high
2016-08-14 18:37:58 +02:00
}
uint8_t * rtc_dcf77_time ( void )
{
static uint8_t to_return [ 6 ] = { 0 } ; // arrays with date values to return
uint8_t parity = 0 ; // to check parity
if ( rtc_dcf77_frame = = 0 ) { // no time received yet
return NULL ;
}
if ( ! ( rtc_dcf77_frame & ( ( uint64_t ) 1 < < 20 ) ) ) { // start of encode time should always be 1
return NULL ;
}
// check minute parity
parity = 0 ;
for ( uint8_t bit = 21 ; bit < = 28 ; bit + + ) {
if ( rtc_dcf77_frame & ( ( uint64_t ) 1 < < bit ) ) {
parity + + ; // count the set bits
}
}
if ( parity % 2 ) { // parity should be even
return NULL ;
}
to_return [ 0 ] = 1 * ( ( rtc_dcf77_frame > > 21 ) & ( 0x1 ) ) + 2 * ( ( rtc_dcf77_frame > > 22 ) & ( 0x1 ) ) + 4 * ( ( rtc_dcf77_frame > > 23 ) & ( 0x1 ) ) + 8 * ( ( rtc_dcf77_frame > > 24 ) & ( 0x1 ) ) + 10 * ( ( rtc_dcf77_frame > > 25 ) & ( 0x1 ) ) + 20 * ( ( rtc_dcf77_frame > > 26 ) & ( 0x1 ) ) + 40 * ( ( rtc_dcf77_frame > > 27 ) & ( 0x1 ) ) ; // read minute (00-59)
if ( to_return [ 0 ] > 59 ) { // minutes should not be more than 59
return NULL ;
}
// check hour parity
parity = 0 ;
for ( uint8_t bit = 29 ; bit < = 35 ; bit + + ) {
if ( rtc_dcf77_frame & ( ( uint64_t ) 1 < < bit ) ) {
parity + + ; // count the set bits
}
}
if ( parity % 2 ) { // parity should be even
return NULL ;
}
2017-10-08 17:30:22 +02:00
to_return [ 1 ] = 1 * ( ( rtc_dcf77_frame > > 29 ) & ( 0x1 ) ) + 2 * ( ( rtc_dcf77_frame > > 30 ) & ( 0x1 ) ) + 4 * ( ( rtc_dcf77_frame > > 31 ) & ( 0x1 ) ) + 8 * ( ( ( rtc_dcf77_frame > > 30 ) > > 2 ) & ( 0x1 ) ) + 10 * ( ( ( rtc_dcf77_frame > > 30 ) > > 3 ) & ( 0x1 ) ) + 20 * ( ( ( rtc_dcf77_frame > > 30 ) > > 4 ) & ( 0x1 ) ) ; // read hour (00-23)
2016-08-14 18:37:58 +02:00
if ( to_return [ 1 ] > 23 ) { // hours should not be more than 23
return NULL ;
}
// check date parity
parity = 0 ;
for ( uint8_t bit = 36 ; bit < = 58 ; bit + + ) {
if ( rtc_dcf77_frame & ( ( uint64_t ) 1 < < bit ) ) {
parity + + ; // count the set bits
}
}
if ( parity % 2 ) { // parity should be even
return NULL ;
}
2017-10-08 17:30:22 +02:00
to_return [ 2 ] = 1 * ( ( ( rtc_dcf77_frame > > 30 ) > > 6 ) & ( 0x1 ) ) + 2 * ( ( ( rtc_dcf77_frame > > 30 ) > > 7 ) & ( 0x1 ) ) + 4 * ( ( ( rtc_dcf77_frame > > 30 ) > > 8 ) & ( 0x1 ) ) + 8 * ( ( ( rtc_dcf77_frame > > 30 ) > > 9 ) & ( 0x1 ) ) + 10 * ( ( ( rtc_dcf77_frame > > 30 ) > > 10 ) & ( 0x1 ) ) + 20 * ( ( ( rtc_dcf77_frame > > 30 ) > > 11 ) & ( 0x1 ) ) ; // read day of the month (01-31)
2016-08-14 18:37:58 +02:00
if ( to_return [ 2 ] = = 0 | | to_return [ 2 ] > 31 ) { // day of the month should be 1-31
return NULL ;
}
2017-10-08 17:30:22 +02:00
to_return [ 3 ] = 1 * ( ( ( rtc_dcf77_frame > > 30 ) > > 12 ) & ( 0x1 ) ) + 2 * ( ( ( rtc_dcf77_frame > > 30 ) > > 13 ) & ( 0x1 ) ) + 4 * ( ( ( rtc_dcf77_frame > > 30 ) > > 14 ) & ( 0x1 ) ) ; // read day of the week (1=Monday - 7=Sunday)
2016-08-14 18:37:58 +02:00
if ( to_return [ 3 ] = = 0 | | to_return [ 3 ] > 7 ) { // day of the week should be 1-7
return NULL ;
}
2017-10-08 17:30:22 +02:00
to_return [ 4 ] = 1 * ( ( ( rtc_dcf77_frame > > 30 ) > > 15 ) & ( 0x1 ) ) + 2 * ( ( ( rtc_dcf77_frame > > 30 ) > > 16 ) & ( 0x1 ) ) + 4 * ( ( ( rtc_dcf77_frame > > 30 ) > > 17 ) & ( 0x1 ) ) + 8 * ( ( ( rtc_dcf77_frame > > 30 ) > > 18 ) & ( 0x1 ) ) + 10 * ( ( ( rtc_dcf77_frame > > 30 ) > > 19 ) & ( 0x1 ) ) ; // read month of the year (01-12)
2016-08-14 18:37:58 +02:00
if ( to_return [ 4 ] = = 0 | | to_return [ 4 ] > 12 ) { // month of the year should be 1-12
return NULL ;
}
2017-10-08 17:30:22 +02:00
to_return [ 5 ] = 1 * ( ( ( rtc_dcf77_frame > > 30 ) > > 20 ) & ( 0x1 ) ) + 2 * ( ( ( rtc_dcf77_frame > > 30 ) > > 21 ) & ( 0x1 ) ) + 4 * ( ( ( rtc_dcf77_frame > > 30 ) > > 22 ) & ( 0x1 ) ) + 8 * ( ( ( rtc_dcf77_frame > > 30 ) > > 23 ) & ( 0x1 ) ) + 10 * ( ( ( rtc_dcf77_frame > > 30 ) > > 24 ) & ( 0x1 ) ) + 20 * ( ( ( rtc_dcf77_frame > > 30 ) > > 25 ) & ( 0x1 ) ) + 40 * ( ( ( rtc_dcf77_frame > > 30 ) > > 26 ) & ( 0x1 ) ) + 80 * ( ( ( rtc_dcf77_frame > > 30 ) > > 27 ) & ( 0x1 ) ) ; // read year of the century (00-99)
2016-08-14 18:37:58 +02:00
if ( to_return [ 5 ] > 99 ) { // year should be <100
return NULL ;
}
return to_return ;
}
/** interrupt service routine called when signal edge is detected, decoding the received DCF77 frame (composed by high pulses) */
2017-10-08 18:36:32 +02:00
void EXTI_ISR ( RTC_DCF77_SIGNAL_PIN ) ( void )
2016-08-14 18:37:58 +02:00
{
2017-10-08 18:36:32 +02:00
exti_reset_request ( EXTI ( RTC_DCF77_SIGNAL_PIN ) ) ; // reset interrupt
2016-08-14 18:37:58 +02:00
static uint16_t old_state = 0 ; // save last port state to detect difference
static uint8_t pulse = 0 ; // next pulse number in the DCF77 frame
static uint16_t pulse_edge = 0 ; // time on when the last pulse (rising edge) has been detected
static uint64_t rtc_dcf77_frame_tmp = 0 ; // the DCF77 frame bits as they get filled
2017-10-08 18:36:32 +02:00
uint16_t time = timer_get_counter ( TIM ( RTC_DCF77_TIMER ) ) ; // get timer value
2016-08-14 18:37:58 +02:00
2017-10-08 18:36:32 +02:00
uint16_t new_state = gpio_get ( GPIO ( RTC_DCF77_SIGNAL_PORT ) , GPIO ( RTC_DCF77_SIGNAL_PIN ) ) ; // save last port state to detect difference
2016-08-14 18:37:58 +02:00
if ( old_state ! = new_state ) { // pulse edge detected
time = ( uint32_t ) ( time - pulse_edge ) * RTC_DCF77_TIMER_MAX_TIME / ( 1 < < 16 ) ; // get time since last rising edge (integer underflow possible)
if ( new_state ) { // rising edge
if ( time < 980 ) { // glitch
goto end ; // ignore glitch
} else if ( time < 1030 ) { // a normal pulse
pulse + + ; // go to next pulse
if ( pulse > 58 ) { // something wrong happened
pulse = 0 ; // restart
}
} else if ( time < 1980 ) { // glitch
goto end ; // ignore glitch
} else if ( time < 2130 ) { // first pulse of a frame
if ( pulse = = 58 ) { // full frame received
rtc_dcf77_frame = rtc_dcf77_frame_tmp ; // save received complete frame
rtc_dcf77_time_flag = true ; // notify user
}
pulse = 0 ;
} else { // something is wrong, restart
pulse = 0 ;
}
pulse_edge = 0 ; // save new edge
2017-10-08 18:36:32 +02:00
timer_set_counter ( TIM ( RTC_DCF77_TIMER ) , 0 ) ; // reset timer to count
2016-08-14 18:37:58 +02:00
} else { // falling edge
if ( time < 90 ) { // glitch
goto end ; // ignore glitch
} else if ( time < 120 ) { // 0 received
rtc_dcf77_frame_tmp & = ~ ( ( uint64_t ) 1 < < pulse ) ; // save 0 bit
} else if ( time < 190 ) { // glitch
goto end ; // ignore glitch
} else if ( time < 220 ) { // 1 received
rtc_dcf77_frame_tmp | = ( ( uint64_t ) 1 < < pulse ) ; // save 1 bit
}
}
}
end :
old_state = new_state ; // save new state
}