From cf3e2a9ff72b007027c0642c4dea666727c998e3 Mon Sep 17 00:00:00 2001 From: Michal Lenc Date: Tue, 28 May 2024 11:01:40 +0200 Subject: [PATCH] timers/mcp794xx: add option to digital trimming MCP794XX supports digital trimming that periodically adds or subtracts clock cycles, resulting in small adjustments in the internal timing. This way inaccuracies of clock source can be compensated. This commit adds option to set the trimming register for MCP794XX. Signed-off-by: Michal Lenc --- drivers/timers/mcp794xx.c | 107 +++++++++++++++++++++++++++++++- include/nuttx/timers/mcp794xx.h | 24 +++++++ 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/drivers/timers/mcp794xx.c b/drivers/timers/mcp794xx.c index 54953a0c735d3..2734c87f5a2bd 100644 --- a/drivers/timers/mcp794xx.c +++ b/drivers/timers/mcp794xx.c @@ -77,6 +77,7 @@ struct mcp794xx_dev_s { FAR struct i2c_master_s *i2c; /* Contained reference to the I2C bus driver. */ uint8_t addr; /* The I2C device address. */ + bool coarse_trim; /* Coarse trim mode */ }; /**************************************************************************** @@ -182,6 +183,105 @@ static int rtc_bcd2bin(uint8_t value) * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: mcp794xx_rtc_set_trim + * + * Description: + * Sets the digital trimming to correct for inaccuracies of clock source. + * Digital trimming consists of the MCP794XX periodically adding or + * subtracting clock cycles, resulting in small adjustments in the internal + * timing. + * + * Input Parameters: + * trim_val - Calculated trimming value, refer to MCP794XX reference + * manual. + * rtc_slow - True indicates RTC is behind real clock, false otherwise. + * This has to be set to ensure correct trimming direction. + * coarse_mode - MCP794XX allows coarse mode that trims every second + * instead of every minute. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int mcp794xx_rtc_set_trim(uint8_t trim_val, bool rtc_slow, bool coarse_mode) +{ + struct i2c_msg_s msg[2]; + uint8_t buffer[2]; + uint8_t address; + uint8_t ctrl; + int ret; + + if (g_mcp794xx.coarse_trim != coarse_mode) + { + address = MCP794XX_REG_CONTROL; + msg[0].frequency = CONFIG_MCP794XX_I2C_FREQUENCY; + msg[0].addr = g_mcp794xx.addr; + msg[0].flags = I2C_M_NOSTOP; + msg[0].buffer = &address; + msg[0].length = 1; + + msg[1].frequency = CONFIG_MCP794XX_I2C_FREQUENCY; + msg[1].addr = g_mcp794xx.addr; + msg[1].flags = I2C_M_READ; + msg[1].buffer = &ctrl; + msg[1].length = 1; + + ret = I2C_TRANSFER(g_mcp794xx.i2c, msg, 2); + if (ret < 0) + { + rtcerr("ERROR: I2C_TRANSFER failed: %d\n", ret); + return ret; + } + + ctrl &= ~MCP794XX_CONTROL_CRSTRIM; + if (coarse_mode) + { + ctrl |= MCP794XX_CONTROL_CRSTRIM; + } + + buffer[0] = MCP794XX_REG_CONTROL; + buffer[1] = ctrl; + msg[0].frequency = CONFIG_MCP794XX_I2C_FREQUENCY; + msg[0].addr = g_mcp794xx.addr; + msg[0].flags = 0; + msg[0].buffer = buffer; + msg[0].length = 2; + + ret = I2C_TRANSFER(g_mcp794xx.i2c, msg, 1); + if (ret < 0) + { + rtcerr("ERROR: I2C_TRANSFER failed: %d\n", ret); + return ret; + } + + g_mcp794xx.coarse_trim = coarse_mode; + } + + buffer[0] = MCP794XX_REG_OSCTRIM; + buffer[1] = trim_val & 0x7; + if (rtc_slow) + { + buffer[1] |= MCP794XX_OSCTRIM_SIGN; + } + + msg[0].frequency = CONFIG_MCP794XX_I2C_FREQUENCY; + msg[0].addr = g_mcp794xx.addr; + msg[0].flags = 0; + msg[0].buffer = buffer; + msg[0].length = 2; + + ret = I2C_TRANSFER(g_mcp794xx.i2c, msg, 1); + if (ret < 0) + { + rtcerr("ERROR: I2C_TRANSFER failed: %d\n", ret); + return ret; + } + + return OK; +} + /**************************************************************************** * Name: mcp794xx_rtc_initialize * @@ -211,9 +311,10 @@ int mcp794xx_rtc_initialize(FAR struct i2c_master_s *i2c, uint8_t addr) { /* Remember the i2c device and claim that the RTC is enabled */ - g_mcp794xx.i2c = i2c; - g_mcp794xx.addr = addr; - g_rtc_enabled = true; + g_mcp794xx.i2c = i2c; + g_mcp794xx.addr = addr; + g_mcp794xx.coarse_trim = false; + g_rtc_enabled = true; return OK; } diff --git a/include/nuttx/timers/mcp794xx.h b/include/nuttx/timers/mcp794xx.h index 7b8abf1f9bb56..d75c63356c78e 100644 --- a/include/nuttx/timers/mcp794xx.h +++ b/include/nuttx/timers/mcp794xx.h @@ -41,6 +41,30 @@ extern "C" #define EXTERN extern #endif +/**************************************************************************** + * Name: mcp794xx_rtc_set_trim + * + * Description: + * Sets the digital trimming to correct for inaccuracies of clock source. + * Digital trimming consists of the MCP794XX periodically adding or + * subtracting clock cycles, resulting in small adjustments in the internal + * timing. + * + * Input Parameters: + * trim_val - Calculated trimming value, refer to MCP794XX reference + * manual. + * rtc_slow - True indicates RTC is behind real clock, false otherwise. + * This has to be set to ensure correct trimming direction. + * coarse_mode - MCP794XX allows coarse mode that trims every second + * instead of every minute. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int mcp794xx_rtc_set_trim(uint8_t trim_val, bool rtc_slow, bool coarse_mode); + /**************************************************************************** * Name: mcp794xx_rtc_initialize *