diff --git a/boards/seeedstudio-gd32/doc.txt b/boards/seeedstudio-gd32/doc.txt index a005cb0f0271..c2d25ca6235b 100644 --- a/boards/seeedstudio-gd32/doc.txt +++ b/boards/seeedstudio-gd32/doc.txt @@ -35,7 +35,7 @@ on-board components: | Power Modes | 3 (Sleep, Deep Sleep, Standby) | no | | GPIOs | 80 | yes | | Timers | 5 x 16-bit timer | yes | -| RTC | 1 x 32-bit counter, 20-bit prescaler | no | +| RTC | 1 x 32-bit counter, 20-bit prescaler | yes | | WDT | 2 x 12-bit counter, 3-bit prescaler | yes | | ADC | 2 x 12-bit units, 16 channels, 1 Msps | no | | DAC | 2 x 12-bit channel | no | diff --git a/boards/seeedstudio-gd32/include/periph_conf.h b/boards/seeedstudio-gd32/include/periph_conf.h index 53af5b3f0c90..677e77044e53 100644 --- a/boards/seeedstudio-gd32/include/periph_conf.h +++ b/boards/seeedstudio-gd32/include/periph_conf.h @@ -26,9 +26,12 @@ extern "C" { #endif -/* This board provides an high frequency oscillator */ #ifndef CONFIG_BOARD_HAS_HXTAL -#define CONFIG_BOARD_HAS_HXTAL 1 +#define CONFIG_BOARD_HAS_HXTAL 1 /**< This board provides an high frequency oscillator */ +#endif + +#ifndef CONFIG_BOARD_HAS_LXTAL +#define CONFIG_BOARD_HAS_LXTAL 1 /**< The board provides a low frequency oscillator. */ #endif #ifndef CONFIG_CLOCK_HXTAL diff --git a/cpu/gd32v/Kconfig b/cpu/gd32v/Kconfig index 7e6b245d97f0..ec50dd6ccd02 100644 --- a/cpu/gd32v/Kconfig +++ b/cpu/gd32v/Kconfig @@ -15,6 +15,7 @@ config CPU_FAM_GD32V select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_PM + select HAS_PERIPH_RTC select HAS_PERIPH_TIMER select HAS_PERIPH_TIMER_PERIODIC select HAS_PERIPH_WDT diff --git a/cpu/gd32v/Makefile.features b/cpu/gd32v/Makefile.features index efe6509f76fb..3bca58c77f6e 100644 --- a/cpu/gd32v/Makefile.features +++ b/cpu/gd32v/Makefile.features @@ -2,6 +2,7 @@ CPU_CORE := rv32imac FEATURES_PROVIDED += periph_clic FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_timer_periodic FEATURES_PROVIDED += periph_wdt diff --git a/cpu/gd32v/periph/rtc.c b/cpu/gd32v/periph/rtc.c new file mode 100644 index 000000000000..168aec21e701 --- /dev/null +++ b/cpu/gd32v/periph/rtc.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2019 Alexei Bezborodov + * 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_gd32v + * @{ + * @file + * @brief Low-level RTC driver implementation for GD32VF103 + * + * This driver is a modified copy of the RTC driver for the STM32F1 family. + * + * @author Alexei Bezborodov + * @author Gunar Schorcht + * @} + */ + +#include +#include "cpu.h" +#include "periph/rtc.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#define EXTI_RTC_BIT (1UL << 17) + +static struct { + rtc_alarm_cb_t cb; /**< callback called from RTC interrupt */ + void *arg; /**< argument passed to the callback */ +} isr_ctx; + +/* forward declaration of ISR */ +static void isr_rtc_alarm(unsigned irqn); + +static void _rtc_enter_config_mode(void) +{ + /* enable write access to backup domain registers */ + PMU->CTL |= PMU_CTL_BKPWEN_Msk; + + /* wait until the LWOFF bit is 1 (Last write operation finished). */ + while ((RTC->CTL & RTC_CTL_LWOFF_Msk) == 0) { } + + /* enter configuration mode. */ + RTC->CTL |= RTC_CTL_CMF_Msk; +} + +static void _rtc_exit_config_mode(void) +{ + /* exit configuration mode. */ + RTC->CTL &= ~RTC_CTL_CMF_Msk; + + /* wait until the LWOFF bit is 1 (Last write operation finished). */ + while ((RTC->CTL & RTC_CTL_LWOFF_Msk) == 0) { } + + /* disable write access to backup domain registers */ + PMU->CTL &= ~PMU_CTL_BKPWEN_Msk; +} + +static bool _is_rtc_enable(void) +{ + return ((RCU->BDCTL & RCU_BDCTL_RTCEN_Msk) == RCU_BDCTL_RTCEN_Msk); +} + +#define RCU_BDCTL_RTCSRC_CK_LXTAL 1 +#define RCU_BDCTL_RTCSRC_CK_IRC40K 2 + +static void _rtc_config(void) +{ + DEBUG("[RTC] config\n"); + + /* enable APB1 clocks */ + RCU->APB1EN |= RCU_APB1EN_PMUEN_Msk | RCU_APB1EN_BKPIEN_Msk; + + /* enable write access to backup domain registers */ + PMU->CTL |= PMU_CTL_BKPWEN_Msk; + + /* resets the entire backup domain */ + RCU->BDCTL |= RCU_BDCTL_BKPRST_Msk; + /* reset not activated */ + RCU->BDCTL &= ~RCU_BDCTL_BKPRST_Msk; + +#if CONFIG_BOARD_HAS_LXTAL + /* oscillator clock used as RTC clock */ + RCU->BDCTL |= RCU_BDCTL_RTCSRC_CK_LXTAL << RCU_BDCTL_RTCSRC_Pos; + RCU->BDCTL |= RCU_BDCTL_LXTALEN_Msk; + while ((RCU->BDCTL & RCU_BDCTL_LXTALSTB_Msk) != RCU_BDCTL_LXTALSTB_Msk) { } +#else + RCU->BDCTL |= RCU_BDCTL_RTCSRC_CK_IRC40K << RCU_BDCTL_RTCSRC_Pos; +#endif + + /* enable RTC clock */ + RCU->BDCTL |= RCU_BDCTL_RTCEN_Msk; + + /* calibration clock from 0 to 0x7F */ + BKP->OCTL |= 0; + BKP->OCTL |= BKP_OCTL_ASOEN_Msk; + + /* second interrupt is disabled. */ + RTC->INTEN &= ~RTC_INTEN_SCIE_Msk; + + _rtc_enter_config_mode(); + +#if CONFIG_BOARD_HAS_LXTAL + /* if the input clock frequency (fRTCCLK) is 32.768 kHz, write 7FFFh in + * this register to get a signal period of 1 second. */ + RTC->PSCH = 0; + RTC->PSCL = 0x7FFF; +#else + /* if the input clock frequency (fRTCCLK) is 40 kHz, write 39999 in + * this register to get a signal period of 1 second. */ + RTC->PSCH = 0; + RTC->PSCL = 39999; +#endif + + _rtc_exit_config_mode(); + + /* wait registers synchronize flag */ + RTC->CTL &= (uint16_t)~RTC_CTL_RSYNF_Msk; + while ((RTC->CTL & RTC_CTL_RSYNF_Msk) != RTC_CTL_RSYNF_Msk) { } + + /* disable write access to backup domain registers */ + PMU->CTL &= ~PMU_CTL_BKPWEN_Msk; + + /* configure the EXTI channel, as RTC interrupts are routed through it. + * Needs to be configured to trigger on rising edges. */ + EXTI->FTEN &= ~(EXTI_RTC_BIT); + EXTI->RTEN |= EXTI_RTC_BIT; + EXTI->INTEN |= EXTI_RTC_BIT; + EXTI->PD |= EXTI_RTC_BIT; + + /* enable global RTC interrupt */ + clic_set_handler(RTC_ALARM_IRQn, isr_rtc_alarm); + clic_enable_interrupt(RTC_ALARM_IRQn, CPU_DEFAULT_IRQ_PRIO); +} + +static uint32_t _rtc_get_time(void) +{ + return (RTC->CNTH << 16) | RTC->CNTL; +} + +static void _rtc_set_time(uint32_t counter_val) +{ + _rtc_enter_config_mode(); + RTC->CNTH = (counter_val & 0xffff0000) >> 16; + RTC->CNTL = counter_val & 0x0000ffff; + _rtc_exit_config_mode(); +} + +void rtc_init(void) +{ + /* save current time if RTC already works */ + bool is_rtc_enable = _is_rtc_enable(); + uint32_t cur_time = 0; + if (is_rtc_enable) { + cur_time = _rtc_get_time(); + } + + /* config RTC */ + _rtc_config(); + + /* restore current time after config */ + if (is_rtc_enable) { + _rtc_set_time(cur_time); + } +} + +int rtc_set_time(struct tm *time) +{ + rtc_tm_normalize(time); + + uint32_t timestamp = rtc_mktime(time); + + _rtc_set_time(timestamp); + + DEBUG("%s timestamp=%"PRIu32"\n", __func__, timestamp); + + return 0; +} + +int rtc_get_time(struct tm *time) +{ + uint32_t timestamp = _rtc_get_time(); + rtc_localtime(timestamp, time); + + DEBUG("%s timestamp=%"PRIu32"\n", __func__, timestamp); + return 0; +} + +static void _rtc_enable_alarm(void) +{ + /* clear alarm flag */ + RTC->CTL &= ~RTC_CTL_ALRMIF_Msk; + + _rtc_enter_config_mode(); + RTC->INTEN |= (RTC_INTEN_ALRMIE_Msk); + _rtc_exit_config_mode(); +} + +static void _rtc_disable_alarm(void) +{ + _rtc_enter_config_mode(); + RTC->INTEN &= ~RTC_INTEN_ALRMIE_Msk; + _rtc_exit_config_mode(); +} + +/* RTC->ALRMH and RTC->ALRML are writable only. Therefore the current alarm + * time must be stored separately in a variable for _rtc_get_alarm_time. */ +static uint32_t _rtc_alarm_time = 0; + +static uint32_t _rtc_get_alarm_time(void) +{ + return _rtc_alarm_time; +} + +static void _rtc_set_alarm_time(uint32_t alarm_time) +{ + /* save the current alarm time */ + _rtc_alarm_time = alarm_time; + /* set RTC alarm registers */ + _rtc_enter_config_mode(); + RTC->ALRML = (alarm_time & 0x0000ffff); + RTC->ALRMH = (alarm_time & 0xffff0000) >> 16; + _rtc_exit_config_mode(); +} + +int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) +{ + rtc_tm_normalize(time); + + uint32_t timestamp = rtc_mktime(time); + + /* disable existing alarm (if enabled) */ + rtc_clear_alarm(); + + /* save callback and argument */ + isr_ctx.cb = cb; + isr_ctx.arg = arg; + + /* set wakeup time */ + _rtc_set_alarm_time(timestamp); + + /* enable Alarm */ + _rtc_enable_alarm(); + + DEBUG("%s timestamp=%"PRIu32"\n", __func__, timestamp); + + return 0; +} + +int rtc_get_alarm(struct tm *time) +{ + uint32_t timestamp = _rtc_get_alarm_time(); + rtc_localtime(timestamp, time); + + DEBUG("%s timestamp=%"PRIu32"\n", __func__, timestamp); + + return 0; +} + +void rtc_clear_alarm(void) +{ + _rtc_disable_alarm(); + + isr_ctx.cb = NULL; + isr_ctx.arg = NULL; +} + +void rtc_poweron(void) +{ + /* RTC is always on */ + return; +} + +void rtc_poweroff(void) +{ + /* RTC is always on */ + return; +} + +static void isr_rtc_alarm(unsigned irqn) +{ + (void)irqn; + + if (RTC->CTL & RTC_CTL_ALRMIF_Msk) { + if (isr_ctx.cb != NULL) { + isr_ctx.cb(isr_ctx.arg); + } + RTC->CTL &= ~RTC_CTL_ALRMIF_Msk; + } + EXTI->PD |= EXTI_RTC_BIT; +} diff --git a/sys/include/net/nanocoap_sock.h b/sys/include/net/nanocoap_sock.h index c8dc01f6bd6a..bb0c133dc9c9 100644 --- a/sys/include/net/nanocoap_sock.h +++ b/sys/include/net/nanocoap_sock.h @@ -132,6 +132,7 @@ #include #include +#include "random.h" #include "net/nanocoap.h" #include "net/sock/udp.h" #include "net/sock/util.h" @@ -186,6 +187,7 @@ typedef struct { functions. */ nanocoap_socket_type_t type; /**< Socket type (UDP, DTLS) */ #endif + uint16_t msg_id; /**< next CoAP message ID */ } nanocoap_sock_t; /** @@ -199,6 +201,19 @@ typedef struct { uint8_t blksize; /**< CoAP blocksize exponent */ } coap_block_request_t; +/** + * @brief Get next consecutive message ID for use when building a new + * CoAP request. + * + * @param[in] sock CoAP socket on which the ID is used + * + * @return A new message ID that can be used for a request or response. + */ +static inline uint16_t nanocoap_sock_next_msg_id(nanocoap_sock_t *sock) +{ + return sock->msg_id++; +} + /** * @brief Start a nanocoap server instance * @@ -230,6 +245,7 @@ static inline int nanocoap_sock_connect(nanocoap_sock_t *sock, #if IS_USED(MODULE_NANOCOAP_DTLS) sock->type = COAP_SOCKET_TYPE_UDP; #endif + sock->msg_id = random_uint32(); return sock_udp_create(&sock->udp, local, remote, 0); } diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index 72e176b47e97..b47270d81e45 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -62,13 +62,6 @@ typedef struct { bool more; } _block_ctx_t; -static uint16_t _get_id(void) -{ - __attribute__((section(".noinit"))) - static uint16_t id; - return atomic_fetch_add_u16(&id, 1); -} - #if IS_USED(MODULE_NANOCOAP_DTLS) int nanocoap_sock_dtls_connect(nanocoap_sock_t *sock, sock_udp_ep_t *local, const sock_udp_ep_t *remote, credman_tag_t tag) @@ -456,7 +449,8 @@ ssize_t nanocoap_sock_get(nanocoap_sock_t *sock, const char *path, void *buf, si .iov_len = len, }; - pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET, _get_id()); + pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET, + nanocoap_sock_next_msg_id(sock)); pktpos += coap_opt_put_uri_path(pktpos, 0, path); pkt.payload = pktpos; @@ -488,7 +482,7 @@ ssize_t _sock_put_post(nanocoap_sock_t *sock, const char *path, unsigned code, .iov_len = max_len, }; - pktpos += coap_build_hdr(pkt.hdr, type, NULL, 0, code, _get_id()); + pktpos += coap_build_hdr(pkt.hdr, type, NULL, 0, code, nanocoap_sock_next_msg_id(sock)); pktpos += coap_opt_put_uri_path(pktpos, 0, path); if (response == NULL && type == COAP_TYPE_NON) { @@ -581,7 +575,8 @@ ssize_t nanocoap_sock_delete(nanocoap_sock_t *sock, const char *path) .hdr = (void *)pktpos, }; - pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_DELETE, _get_id()); + pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_DELETE, + nanocoap_sock_next_msg_id(sock)); pktpos += coap_opt_put_uri_path(pktpos, 0, path); pkt.payload = pktpos; @@ -665,7 +660,8 @@ static int _fetch_block(nanocoap_sock_t *sock, uint8_t *buf, size_t len, }; uint16_t lastonum = 0; - buf += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET, _get_id()); + buf += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET, + nanocoap_sock_next_msg_id(sock)); buf += coap_opt_put_uri_pathquery(buf, &lastonum, path); buf += coap_opt_put_uint(buf, lastonum, COAP_OPT_BLOCK2, (num << 4) | blksize); @@ -703,7 +699,8 @@ int nanocoap_sock_block_request(coap_block_request_t *req, uint8_t *pktpos = (void *)pkt.hdr; uint16_t lastonum = 0; - pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, req->method, _get_id()); + pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, req->method, + nanocoap_sock_next_msg_id(req->sock)); pktpos += coap_opt_put_uri_pathquery(pktpos, &lastonum, req->path); pktpos += coap_opt_put_uint(pktpos, lastonum, COAP_OPT_BLOCK1, (req->blknum << 4) | req->blksize | (more ? 0x8 : 0));