mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-09-22 11:30:01 +08:00
[BSP][NS800RT7P65] Add WDG driver for NS800RT7xxx#11451
ToolsCI / Tools (push) Has been cancelled
RT-Thread BSP Static Build Check / 🔍 Summary of Git Diff Changes (push) Has been cancelled
pkgs_test / change (push) Has been cancelled
RT-Thread BSP Static Build Check / ${{ matrix.legs.RTT_BSP }} (push) Has been cancelled
RT-Thread BSP Static Build Check / collect-artifacts (push) Has been cancelled
ToolsCI / Tools (push) Has been cancelled
RT-Thread BSP Static Build Check / 🔍 Summary of Git Diff Changes (push) Has been cancelled
pkgs_test / change (push) Has been cancelled
RT-Thread BSP Static Build Check / ${{ matrix.legs.RTT_BSP }} (push) Has been cancelled
RT-Thread BSP Static Build Check / collect-artifacts (push) Has been cancelled
This commit is contained in:
@@ -30,6 +30,11 @@ if GetDepend('BSP_USING_SPI'):
|
||||
if GetDepend('BSP_USING_QSPI'):
|
||||
src += ['drv_qspi.c']
|
||||
|
||||
if GetDepend('BSP_USING_WDT'):
|
||||
src += ['drv_iwdg.c']
|
||||
if GetDepend('BSP_USING_WWDG'):
|
||||
src += ['drv_wwdg.c']
|
||||
|
||||
group = DefineGroup('HAL_Drivers', src, depend = [''], CPPPATH = path)
|
||||
|
||||
Return('group')
|
||||
|
||||
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2026-05-13 Jeffery Yuan first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#include "board.h"
|
||||
#include "drv_iwdg.h"
|
||||
|
||||
#ifdef BSP_USING_IWDG1
|
||||
#include "iwdg1.h"
|
||||
#endif
|
||||
|
||||
#ifdef BSP_USING_IWDG2
|
||||
#include "iwdg2.h"
|
||||
#endif
|
||||
|
||||
#if defined(RT_USING_WDT) && (defined(BSP_USING_IWDG1) || defined(BSP_USING_IWDG2))
|
||||
|
||||
#define DBG_TAG "drv.iwdg"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
#define NS800_USEC_PER_SEC 1000000ULL
|
||||
#define NS800_MSEC_PER_SEC 1000ULL
|
||||
#define NS800_IWDG1_CLK_HZ 10000000ULL
|
||||
#define NS800_IWDG2_CLK_HZ 128000ULL
|
||||
#define NS800_ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
enum ns800_iwdg_id
|
||||
{
|
||||
NS800_IWDG1 = 0,
|
||||
NS800_IWDG2,
|
||||
};
|
||||
|
||||
struct ns800_iwdg
|
||||
{
|
||||
rt_watchdog_t watchdog;
|
||||
enum ns800_iwdg_id id;
|
||||
const char *name;
|
||||
void *base;
|
||||
rt_bool_t started;
|
||||
rt_uint64_t timeout_us;
|
||||
union
|
||||
{
|
||||
#ifdef BSP_USING_IWDG1
|
||||
struct
|
||||
{
|
||||
IWDG1_Prescaler prescaler;
|
||||
IWDG1_Timeout timeout;
|
||||
} iwdg1;
|
||||
#endif
|
||||
#ifdef BSP_USING_IWDG2
|
||||
struct
|
||||
{
|
||||
IWDG2_Prescaler prescaler;
|
||||
rt_uint16_t reload;
|
||||
} iwdg2;
|
||||
#endif
|
||||
} hw;
|
||||
};
|
||||
|
||||
static rt_uint64_t ns800_abs_diff_u64(rt_uint64_t a, rt_uint64_t b)
|
||||
{
|
||||
return (a > b) ? (a - b) : (b - a);
|
||||
}
|
||||
|
||||
static rt_uint32_t ns800_timeout_to_seconds(rt_uint64_t timeout_us)
|
||||
{
|
||||
return (rt_uint32_t)((timeout_us + NS800_USEC_PER_SEC - 1ULL) / NS800_USEC_PER_SEC);
|
||||
}
|
||||
|
||||
static rt_uint32_t ns800_timeout_to_ms(rt_uint64_t timeout_us)
|
||||
{
|
||||
return (rt_uint32_t)((timeout_us + NS800_MSEC_PER_SEC - 1ULL) / NS800_MSEC_PER_SEC);
|
||||
}
|
||||
|
||||
#ifdef BSP_USING_IWDG1
|
||||
|
||||
static struct ns800_iwdg iwdg1_dev;
|
||||
|
||||
static const rt_uint8_t iwdg1_prescaler_code[] =
|
||||
{
|
||||
IWDG1_PRESCALE_DIV1,
|
||||
IWDG1_PRESCALE_DIV2,
|
||||
IWDG1_PRESCALE_DIV4,
|
||||
IWDG1_PRESCALE_DIV8,
|
||||
IWDG1_PRESCALE_DIV16,
|
||||
IWDG1_PRESCALE_DIV32,
|
||||
IWDG1_PRESCALE_DIV128,
|
||||
IWDG1_PRESCALE_DIV256,
|
||||
IWDG1_PRESCALE_DIV512,
|
||||
IWDG1_PRESCALE_DIV1024,
|
||||
IWDG1_PRESCALE_DIV2048,
|
||||
IWDG1_PRESCALE_DIV4096,
|
||||
IWDG1_PRESCALE_DIV8192,
|
||||
IWDG1_PRESCALE_DIV64,
|
||||
};
|
||||
|
||||
static const rt_uint32_t iwdg1_prescaler_div[] =
|
||||
{
|
||||
1U, 2U, 4U, 8U, 16U, 32U, 128U, 256U, 512U, 1024U, 2048U, 4096U, 8192U, 64U,
|
||||
};
|
||||
|
||||
static const rt_uint16_t iwdg1_timeout_count[] =
|
||||
{
|
||||
1024U, 4096U, 8192U, 16384U,
|
||||
};
|
||||
|
||||
static rt_uint64_t iwdg1_calc_timeout_us(rt_uint32_t div, rt_uint32_t count)
|
||||
{
|
||||
return ((rt_uint64_t)count * div * NS800_USEC_PER_SEC + NS800_IWDG1_CLK_HZ / 2ULL) / NS800_IWDG1_CLK_HZ;
|
||||
}
|
||||
|
||||
static void iwdg1_select_config(rt_uint32_t timeout_s, struct ns800_iwdg *dev)
|
||||
{
|
||||
rt_uint64_t target_us = (rt_uint64_t)timeout_s * NS800_USEC_PER_SEC;
|
||||
rt_uint64_t best_diff = (rt_uint64_t)-1;
|
||||
rt_uint32_t best_prescaler = 0;
|
||||
rt_uint32_t best_timeout = 0;
|
||||
rt_uint32_t psc;
|
||||
rt_uint32_t top;
|
||||
|
||||
if (target_us == 0)
|
||||
{
|
||||
target_us = iwdg1_calc_timeout_us(iwdg1_prescaler_div[0], iwdg1_timeout_count[0]);
|
||||
}
|
||||
|
||||
for (psc = 0; psc < NS800_ARRAY_SIZE(iwdg1_prescaler_code); psc++)
|
||||
{
|
||||
for (top = 0; top < NS800_ARRAY_SIZE(iwdg1_timeout_count); top++)
|
||||
{
|
||||
rt_uint64_t actual_us = iwdg1_calc_timeout_us(iwdg1_prescaler_div[psc], iwdg1_timeout_count[top]);
|
||||
rt_uint64_t diff = ns800_abs_diff_u64(actual_us, target_us);
|
||||
|
||||
if (diff < best_diff)
|
||||
{
|
||||
best_diff = diff;
|
||||
best_prescaler = psc;
|
||||
best_timeout = top;
|
||||
dev->timeout_us = actual_us;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev->hw.iwdg1.prescaler = (IWDG1_Prescaler)iwdg1_prescaler_code[best_prescaler];
|
||||
dev->hw.iwdg1.timeout = (IWDG1_Timeout)best_timeout;
|
||||
}
|
||||
|
||||
static void iwdg1_apply_config(struct ns800_iwdg *dev)
|
||||
{
|
||||
IWDG1_configModule((IWDG1_TypeDef *)dev->base,
|
||||
RESET_EN,
|
||||
LPRUN,
|
||||
dev->hw.iwdg1.prescaler,
|
||||
HEAD_PERCENT100,
|
||||
END_PERCENT0,
|
||||
dev->hw.iwdg1.timeout);
|
||||
IWDG1_clearErrorStatus((IWDG1_TypeDef *)dev->base);
|
||||
IWDG1_clearIntStatus((IWDG1_TypeDef *)dev->base);
|
||||
}
|
||||
|
||||
void IWDG1_Handler(void)
|
||||
{
|
||||
rt_interrupt_enter();
|
||||
IWDG1_clearErrorStatus((IWDG1_TypeDef *)iwdg1_dev.base);
|
||||
IWDG1_clearIntStatus((IWDG1_TypeDef *)iwdg1_dev.base);
|
||||
while (IWDG1_getIntStatus((IWDG1_TypeDef *)iwdg1_dev.base))
|
||||
{
|
||||
;
|
||||
}
|
||||
IWDG1_enableModule((IWDG1_TypeDef *)iwdg1_dev.base);
|
||||
rt_interrupt_leave();
|
||||
}
|
||||
|
||||
#endif /* BSP_USING_IWDG1 */
|
||||
|
||||
#ifdef BSP_USING_IWDG2
|
||||
|
||||
static struct ns800_iwdg iwdg2_dev;
|
||||
|
||||
static const rt_uint8_t iwdg2_prescaler_code[] =
|
||||
{
|
||||
IWDG2_PRESCALE_DIV128,
|
||||
IWDG2_PRESCALE_DIV2048,
|
||||
IWDG2_PRESCALE_DIV1024,
|
||||
IWDG2_PRESCALE_DIV512,
|
||||
IWDG2_PRESCALE_DIV256,
|
||||
IWDG2_PRESCALE_DIV64,
|
||||
IWDG2_PRESCALE_DIV32,
|
||||
IWDG2_PRESCALE_DIV16,
|
||||
IWDG2_PRESCALE_DIV8,
|
||||
IWDG2_PRESCALE_DIV4,
|
||||
};
|
||||
|
||||
static const rt_uint32_t iwdg2_prescaler_div[] =
|
||||
{
|
||||
128U, 2048U, 1024U, 512U, 256U, 64U, 32U, 16U, 8U, 4U,
|
||||
};
|
||||
|
||||
static rt_uint64_t iwdg2_calc_timeout_us(rt_uint32_t div, rt_uint32_t reload)
|
||||
{
|
||||
return ((rt_uint64_t)(reload + 1U) * div * NS800_USEC_PER_SEC + NS800_IWDG2_CLK_HZ / 2ULL) / NS800_IWDG2_CLK_HZ;
|
||||
}
|
||||
|
||||
static void iwdg2_select_config(rt_uint32_t timeout_s, struct ns800_iwdg *dev)
|
||||
{
|
||||
rt_uint64_t target_us = (rt_uint64_t)timeout_s * NS800_USEC_PER_SEC;
|
||||
rt_uint64_t best_diff = (rt_uint64_t)-1;
|
||||
rt_uint32_t best_prescaler = 0;
|
||||
rt_uint32_t best_reload = 0;
|
||||
rt_uint32_t psc;
|
||||
|
||||
if (target_us == 0)
|
||||
{
|
||||
target_us = iwdg2_calc_timeout_us(iwdg2_prescaler_div[NS800_ARRAY_SIZE(iwdg2_prescaler_div) - 1], 0U);
|
||||
}
|
||||
|
||||
for (psc = 0; psc < NS800_ARRAY_SIZE(iwdg2_prescaler_code); psc++)
|
||||
{
|
||||
rt_uint64_t ticks;
|
||||
rt_uint64_t actual_us;
|
||||
rt_uint64_t diff;
|
||||
rt_uint64_t tick_us = ((rt_uint64_t)iwdg2_prescaler_div[psc] * NS800_USEC_PER_SEC +
|
||||
NS800_IWDG2_CLK_HZ / 2ULL) / NS800_IWDG2_CLK_HZ;
|
||||
|
||||
if (tick_us == 0)
|
||||
{
|
||||
tick_us = 1;
|
||||
}
|
||||
|
||||
ticks = (target_us + tick_us / 2ULL) / tick_us;
|
||||
if (ticks == 0)
|
||||
{
|
||||
ticks = 1;
|
||||
}
|
||||
|
||||
if (ticks > 4096ULL)
|
||||
{
|
||||
ticks = 4096ULL;
|
||||
}
|
||||
|
||||
actual_us = iwdg2_calc_timeout_us(iwdg2_prescaler_div[psc], (rt_uint32_t)(ticks - 1ULL));
|
||||
diff = ns800_abs_diff_u64(actual_us, target_us);
|
||||
if (diff < best_diff)
|
||||
{
|
||||
best_diff = diff;
|
||||
best_prescaler = psc;
|
||||
best_reload = (rt_uint32_t)(ticks - 1ULL);
|
||||
dev->timeout_us = actual_us;
|
||||
}
|
||||
}
|
||||
|
||||
dev->hw.iwdg2.prescaler = (IWDG2_Prescaler)iwdg2_prescaler_code[best_prescaler];
|
||||
dev->hw.iwdg2.reload = (rt_uint16_t)best_reload;
|
||||
}
|
||||
|
||||
static rt_err_t iwdg2_apply_config(struct ns800_iwdg *dev)
|
||||
{
|
||||
if (!IWDG2_configModule((IWDG2_TypeDef *)dev->base,
|
||||
RESET_SEL,
|
||||
dev->hw.iwdg2.prescaler,
|
||||
dev->hw.iwdg2.reload))
|
||||
{
|
||||
return -RT_ETIMEOUT;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
void IWDG2_Handler(void)
|
||||
{
|
||||
rt_interrupt_enter();
|
||||
if (IWDG2_getIntStatus((IWDG2_TypeDef *)iwdg2_dev.base))
|
||||
{
|
||||
IWDG2_clearIntStatus((IWDG2_TypeDef *)iwdg2_dev.base);
|
||||
while (IWDG2_getIntStatus((IWDG2_TypeDef *)iwdg2_dev.base))
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
rt_interrupt_leave();
|
||||
}
|
||||
|
||||
#endif /* BSP_USING_IWDG2 */
|
||||
|
||||
static rt_err_t ns800_iwdg_init(rt_watchdog_t *wdt)
|
||||
{
|
||||
RT_UNUSED(wdt);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t ns800_iwdg_control(rt_watchdog_t *wdt, int cmd, void *arg)
|
||||
{
|
||||
struct ns800_iwdg *dev;
|
||||
|
||||
RT_ASSERT(wdt != RT_NULL);
|
||||
dev = (struct ns800_iwdg *)wdt->parent.user_data;
|
||||
RT_ASSERT(dev != RT_NULL);
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case RT_DEVICE_CTRL_WDT_KEEPALIVE:
|
||||
#ifdef BSP_USING_IWDG1
|
||||
if (dev->id == NS800_IWDG1)
|
||||
{
|
||||
IWDG1_refreshModule((IWDG1_TypeDef *)dev->base);
|
||||
}
|
||||
#endif
|
||||
#ifdef BSP_USING_IWDG2
|
||||
if (dev->id == NS800_IWDG2)
|
||||
{
|
||||
IWDG2_refreshModule((IWDG2_TypeDef *)dev->base);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
|
||||
if (arg == RT_NULL)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
#ifdef BSP_USING_IWDG1
|
||||
if (dev->id == NS800_IWDG1)
|
||||
{
|
||||
if (dev->started)
|
||||
{
|
||||
LOG_W("%s timeout cannot be reconfigured after start", dev->name);
|
||||
return -RT_EBUSY;
|
||||
}
|
||||
iwdg1_select_config(*(rt_uint32_t *)arg, dev);
|
||||
LOG_I("%s timeout request=%u s actual=%u ms",
|
||||
dev->name, *(rt_uint32_t *)arg, ns800_timeout_to_ms(dev->timeout_us));
|
||||
}
|
||||
#endif
|
||||
#ifdef BSP_USING_IWDG2
|
||||
if (dev->id == NS800_IWDG2)
|
||||
{
|
||||
rt_err_t ret;
|
||||
|
||||
iwdg2_select_config(*(rt_uint32_t *)arg, dev);
|
||||
ret = iwdg2_apply_config(dev);
|
||||
if (ret != RT_EOK)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dev->started)
|
||||
{
|
||||
IWDG2_refreshModule((IWDG2_TypeDef *)dev->base);
|
||||
}
|
||||
LOG_I("%s timeout request=%u s actual=%u ms",
|
||||
dev->name, *(rt_uint32_t *)arg, ns800_timeout_to_ms(dev->timeout_us));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
|
||||
if (arg == RT_NULL)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
*(rt_uint32_t *)arg = ns800_timeout_to_seconds(dev->timeout_us);
|
||||
break;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
|
||||
return -RT_ENOSYS;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_START:
|
||||
if (!dev->started)
|
||||
{
|
||||
#ifdef BSP_USING_IWDG1
|
||||
if (dev->id == NS800_IWDG1)
|
||||
{
|
||||
iwdg1_apply_config(dev);
|
||||
IWDG1_enableModule((IWDG1_TypeDef *)dev->base);
|
||||
}
|
||||
#endif
|
||||
#ifdef BSP_USING_IWDG2
|
||||
if (dev->id == NS800_IWDG2)
|
||||
{
|
||||
rt_err_t ret = iwdg2_apply_config(dev);
|
||||
if (ret != RT_EOK)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
IWDG2_enableModule((IWDG2_TypeDef *)dev->base);
|
||||
IWDG2_refreshModule((IWDG2_TypeDef *)dev->base);
|
||||
}
|
||||
#endif
|
||||
dev->started = RT_TRUE;
|
||||
LOG_I("%s started, timeout=%u ms", dev->name, ns800_timeout_to_ms(dev->timeout_us));
|
||||
}
|
||||
break;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_STOP:
|
||||
return -RT_ENOSYS;
|
||||
|
||||
default:
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static const struct rt_watchdog_ops ns800_iwdg_ops =
|
||||
{
|
||||
.init = ns800_iwdg_init,
|
||||
.control = ns800_iwdg_control,
|
||||
};
|
||||
|
||||
#ifdef BSP_USING_IWDG1
|
||||
int rt_hw_iwdg1_init(void)
|
||||
{
|
||||
iwdg1_dev.id = NS800_IWDG1;
|
||||
iwdg1_dev.name = IWDG1_DEVICE_NAME;
|
||||
iwdg1_dev.base = IWDG1;
|
||||
iwdg1_dev.started = RT_FALSE;
|
||||
iwdg1_select_config(10U, &iwdg1_dev);
|
||||
iwdg1_dev.watchdog.ops = &ns800_iwdg_ops;
|
||||
|
||||
if (rt_hw_watchdog_register(&iwdg1_dev.watchdog,
|
||||
iwdg1_dev.name,
|
||||
RT_DEVICE_FLAG_DEACTIVATE,
|
||||
&iwdg1_dev) != RT_EOK)
|
||||
{
|
||||
LOG_E("%s register failed", iwdg1_dev.name);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
INIT_BOARD_EXPORT(rt_hw_iwdg1_init);
|
||||
#endif /* BSP_USING_IWDG1 */
|
||||
|
||||
#ifdef BSP_USING_IWDG2
|
||||
int rt_hw_iwdg2_init(void)
|
||||
{
|
||||
RCC_unlockRccRegister();
|
||||
RCC_enableLircOscillator();
|
||||
RCC_lockRccRegister();
|
||||
|
||||
iwdg2_dev.id = NS800_IWDG2;
|
||||
iwdg2_dev.name = IWDG2_DEVICE_NAME;
|
||||
iwdg2_dev.base = IWDG2;
|
||||
iwdg2_dev.started = RT_FALSE;
|
||||
iwdg2_select_config(10U, &iwdg2_dev);
|
||||
iwdg2_dev.watchdog.ops = &ns800_iwdg_ops;
|
||||
|
||||
if (rt_hw_watchdog_register(&iwdg2_dev.watchdog,
|
||||
iwdg2_dev.name,
|
||||
RT_DEVICE_FLAG_DEACTIVATE,
|
||||
&iwdg2_dev) != RT_EOK)
|
||||
{
|
||||
LOG_E("%s register failed", iwdg2_dev.name);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
INIT_BOARD_EXPORT(rt_hw_iwdg2_init);
|
||||
#endif /* BSP_USING_IWDG2 */
|
||||
|
||||
#endif /* RT_USING_WDT && (BSP_USING_IWDG1 || BSP_USING_IWDG2) */
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2026-05-13 Jeffery Yuan first version
|
||||
*/
|
||||
|
||||
#ifndef __DRV_IWDG_H__
|
||||
#define __DRV_IWDG_H__
|
||||
|
||||
#include <rtdevice.h>
|
||||
#include <board.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define IWDG1_DEVICE_NAME "iwdg1"
|
||||
#define IWDG2_DEVICE_NAME "iwdg2"
|
||||
|
||||
int rt_hw_iwdg1_init(void);
|
||||
int rt_hw_iwdg2_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DRV_IWDG_H__ */
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2026-05-13 Jeffery Yuan first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#include "board.h"
|
||||
#include "drv_wwdg.h"
|
||||
#include "rcc.h"
|
||||
#include "wwdg.h"
|
||||
|
||||
#ifdef RT_USING_WDT
|
||||
#ifdef BSP_USING_WWDG
|
||||
|
||||
#define DBG_TAG "drv.wwdg"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
#define NS800_WWDG_COUNTER_MIN 0x40U
|
||||
#define NS800_WWDG_COUNTER_MAX 0x7FU
|
||||
#define NS800_WWDG_TICK_BASE 4096ULL /* Fixed WWDG counter clock divider from the reference manual. */
|
||||
#define NS800_USEC_PER_SEC 1000000ULL
|
||||
#define NS800_MSEC_PER_SEC 1000ULL
|
||||
#define NS800_ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
struct ns800_wwdg
|
||||
{
|
||||
rt_watchdog_t watchdog;
|
||||
WWDG_TypeDef *base;
|
||||
WWDG_Prescaler prescaler;
|
||||
rt_uint8_t reload;
|
||||
rt_uint8_t window;
|
||||
rt_uint64_t timeout_us;
|
||||
rt_bool_t started;
|
||||
};
|
||||
|
||||
static struct ns800_wwdg wwdg_dev;
|
||||
|
||||
static const rt_uint8_t wwdg_prescaler_div[] =
|
||||
{
|
||||
1U, 2U, 4U, 8U, 16U, 32U, 64U, 128U,
|
||||
};
|
||||
|
||||
static rt_uint32_t wwdg_get_clock_hz(void)
|
||||
{
|
||||
rt_uint32_t clock_hz = RCC_getPclk5Frequency();
|
||||
|
||||
if (clock_hz == 0U || clock_hz == 0xFFFFFFFFU)
|
||||
{
|
||||
LOG_E("%s clock query failed", WWDG_DEVICE_NAME);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
return clock_hz;
|
||||
}
|
||||
|
||||
static rt_uint64_t wwdg_calc_timeout_us(rt_uint32_t div, rt_uint32_t reload, rt_uint32_t clock_hz)
|
||||
{
|
||||
rt_uint32_t ticks = reload - (NS800_WWDG_COUNTER_MIN - 1U);
|
||||
|
||||
return (rt_uint64_t)ticks * NS800_WWDG_TICK_BASE * div * NS800_USEC_PER_SEC / clock_hz;
|
||||
}
|
||||
|
||||
static rt_uint64_t wwdg_calc_max_timeout_us(rt_uint32_t clock_hz)
|
||||
{
|
||||
return wwdg_calc_timeout_us(wwdg_prescaler_div[NS800_ARRAY_SIZE(wwdg_prescaler_div) - 1],
|
||||
NS800_WWDG_COUNTER_MAX,
|
||||
clock_hz);
|
||||
}
|
||||
|
||||
static rt_uint32_t wwdg_timeout_to_ms(rt_uint64_t timeout_us)
|
||||
{
|
||||
return (rt_uint32_t)(timeout_us / NS800_MSEC_PER_SEC);
|
||||
}
|
||||
|
||||
static void wwdg_select_max_config(struct ns800_wwdg *dev)
|
||||
{
|
||||
rt_uint32_t clock_hz = wwdg_get_clock_hz();
|
||||
rt_uint32_t max_prescaler = NS800_ARRAY_SIZE(wwdg_prescaler_div) - 1U;
|
||||
|
||||
dev->prescaler = (WWDG_Prescaler)max_prescaler;
|
||||
dev->reload = NS800_WWDG_COUNTER_MAX;
|
||||
dev->window = NS800_WWDG_COUNTER_MAX;
|
||||
dev->timeout_us = clock_hz ? wwdg_calc_max_timeout_us(clock_hz) : 0U;
|
||||
}
|
||||
|
||||
static rt_err_t wwdg_select_config(rt_uint32_t timeout_s, struct ns800_wwdg *dev)
|
||||
{
|
||||
rt_uint64_t target_us = (rt_uint64_t)timeout_s * NS800_USEC_PER_SEC;
|
||||
rt_uint64_t best_timeout_us = (rt_uint64_t)-1;
|
||||
rt_uint32_t best_prescaler = 0;
|
||||
rt_uint32_t best_reload = NS800_WWDG_COUNTER_MAX;
|
||||
rt_uint32_t clock_hz = wwdg_get_clock_hz();
|
||||
rt_uint32_t psc;
|
||||
|
||||
if (timeout_s == 0U || clock_hz == 0U)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
if (target_us > wwdg_calc_max_timeout_us(clock_hz))
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
for (psc = 0; psc < NS800_ARRAY_SIZE(wwdg_prescaler_div); psc++)
|
||||
{
|
||||
rt_uint64_t tick_unit = (rt_uint64_t)NS800_WWDG_TICK_BASE * wwdg_prescaler_div[psc] * NS800_USEC_PER_SEC;
|
||||
rt_uint64_t ticks;
|
||||
rt_uint32_t reload;
|
||||
rt_uint64_t actual_us;
|
||||
|
||||
ticks = (target_us * clock_hz + tick_unit - 1ULL) / tick_unit;
|
||||
if (ticks == 0)
|
||||
{
|
||||
ticks = 1;
|
||||
}
|
||||
|
||||
if (ticks > 64ULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
reload = (rt_uint32_t)(ticks + (NS800_WWDG_COUNTER_MIN - 1U));
|
||||
actual_us = wwdg_calc_timeout_us(wwdg_prescaler_div[psc], reload, clock_hz);
|
||||
if (actual_us >= target_us && actual_us < best_timeout_us)
|
||||
{
|
||||
best_prescaler = psc;
|
||||
best_reload = reload;
|
||||
best_timeout_us = actual_us;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_timeout_us == (rt_uint64_t)-1)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
dev->prescaler = (WWDG_Prescaler)best_prescaler;
|
||||
dev->reload = (rt_uint8_t)best_reload;
|
||||
dev->window = (rt_uint8_t)best_reload;
|
||||
dev->timeout_us = best_timeout_us;
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static void wwdg_apply_config(struct ns800_wwdg *dev)
|
||||
{
|
||||
WWDG_configModule(dev->base,
|
||||
WWDG_DIRECT_RESET,
|
||||
dev->prescaler,
|
||||
dev->window);
|
||||
}
|
||||
|
||||
static rt_err_t wwdg_init(rt_watchdog_t *wdt)
|
||||
{
|
||||
RT_UNUSED(wdt);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t wwdg_control(rt_watchdog_t *wdt, int cmd, void *arg)
|
||||
{
|
||||
struct ns800_wwdg *dev;
|
||||
rt_err_t ret;
|
||||
|
||||
RT_ASSERT(wdt != RT_NULL);
|
||||
dev = (struct ns800_wwdg *)wdt->parent.user_data;
|
||||
RT_ASSERT(dev != RT_NULL);
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case RT_DEVICE_CTRL_WDT_KEEPALIVE:
|
||||
WWDG_refreshModule(dev->base, dev->reload);
|
||||
break;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
|
||||
if (arg == RT_NULL)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
ret = wwdg_select_config(*(rt_uint32_t *)arg, dev);
|
||||
if (ret != RT_EOK)
|
||||
{
|
||||
rt_uint32_t clock_hz = wwdg_get_clock_hz();
|
||||
|
||||
LOG_W("%s timeout request=%u s exceeds max=%u ms",
|
||||
WWDG_DEVICE_NAME,
|
||||
*(rt_uint32_t *)arg,
|
||||
clock_hz ? wwdg_timeout_to_ms(wwdg_calc_max_timeout_us(clock_hz)) : 0U);
|
||||
return ret;
|
||||
}
|
||||
if (dev->started)
|
||||
{
|
||||
wwdg_apply_config(dev);
|
||||
WWDG_refreshModule(dev->base, dev->reload);
|
||||
}
|
||||
LOG_W("%s timeout request=%u s actual=%u ms",
|
||||
WWDG_DEVICE_NAME,
|
||||
*(rt_uint32_t *)arg,
|
||||
wwdg_timeout_to_ms(dev->timeout_us));
|
||||
break;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
|
||||
if (arg == RT_NULL)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
*(rt_uint32_t *)arg = (rt_uint32_t)(dev->timeout_us / NS800_USEC_PER_SEC);
|
||||
break;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
|
||||
return -RT_ENOSYS;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_START:
|
||||
if (!dev->started)
|
||||
{
|
||||
wwdg_apply_config(dev);
|
||||
WWDG_clearIntStatus(dev->base);
|
||||
WWDG_enableModule(dev->base, dev->reload);
|
||||
dev->started = RT_TRUE;
|
||||
LOG_I("%s started, timeout=%u ms", WWDG_DEVICE_NAME, wwdg_timeout_to_ms(dev->timeout_us));
|
||||
}
|
||||
break;
|
||||
|
||||
case RT_DEVICE_CTRL_WDT_STOP:
|
||||
return -RT_ENOSYS;
|
||||
|
||||
default:
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static const struct rt_watchdog_ops wwdg_ops =
|
||||
{
|
||||
.init = wwdg_init,
|
||||
.control = wwdg_control,
|
||||
};
|
||||
|
||||
int rt_hw_wwdg_init(void)
|
||||
{
|
||||
wwdg_dev.base = WWDG;
|
||||
wwdg_dev.started = RT_FALSE;
|
||||
if (wwdg_select_config(1U, &wwdg_dev) != RT_EOK)
|
||||
{
|
||||
wwdg_select_max_config(&wwdg_dev);
|
||||
}
|
||||
wwdg_dev.watchdog.ops = &wwdg_ops;
|
||||
|
||||
if (rt_hw_watchdog_register(&wwdg_dev.watchdog,
|
||||
WWDG_DEVICE_NAME,
|
||||
RT_DEVICE_FLAG_DEACTIVATE,
|
||||
&wwdg_dev) != RT_EOK)
|
||||
{
|
||||
LOG_E("%s register failed", WWDG_DEVICE_NAME);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
INIT_BOARD_EXPORT(rt_hw_wwdg_init);
|
||||
|
||||
#endif /* BSP_USING_WWDG */
|
||||
#endif /* RT_USING_WDT */
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2026-05-13 Jeffery Yuan first version
|
||||
*/
|
||||
|
||||
#ifndef DRV_WWDG_H__
|
||||
#define DRV_WWDG_H__
|
||||
|
||||
#include <rtdevice.h>
|
||||
#include <board.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WWDG_DEVICE_NAME "wwdg"
|
||||
|
||||
int rt_hw_wwdg_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DRV_WWDG_H__ */
|
||||
|
||||
@@ -15,4 +15,11 @@ devices.spi:
|
||||
kconfig:
|
||||
- CONFIG_BSP_USING_SPI=y
|
||||
- CONFIG_BSP_USING_SPI1=y
|
||||
- CONFIG_BSP_USING_QSPI=y
|
||||
- CONFIG_BSP_USING_QSPI=y
|
||||
devices.wdt:
|
||||
<<: *scons
|
||||
kconfig:
|
||||
- CONFIG_BSP_USING_WDT=y
|
||||
- CONFIG_BSP_USING_IWDG1=y
|
||||
- CONFIG_BSP_USING_IWDG2=y
|
||||
- CONFIG_BSP_USING_WWDG=y
|
||||
@@ -211,4 +211,22 @@ menu "On-chip Peripheral Drivers"
|
||||
select RT_USING_QSPI
|
||||
default n
|
||||
|
||||
menuconfig BSP_USING_WDT
|
||||
bool "Enable Watchdog"
|
||||
select RT_USING_WDT
|
||||
default n
|
||||
if BSP_USING_WDT
|
||||
config BSP_USING_IWDG1
|
||||
bool "Enable IWDG1"
|
||||
default n
|
||||
|
||||
config BSP_USING_IWDG2
|
||||
bool "Enable IWDG2"
|
||||
default n
|
||||
|
||||
config BSP_USING_WWDG
|
||||
bool "Enable WWDG"
|
||||
default n
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
||||
Reference in New Issue
Block a user