From 2c8881338db818090a738c07489c0fc2934d4036 Mon Sep 17 00:00:00 2001 From: Thomas Kuschel Date: Sat, 16 Jul 2022 23:24:59 +0200 Subject: [PATCH] rework of si5351 driver --- .cproject | 4 +- Core/Inc/si5351.h | 8 +- Core/Src/commands.c | 2 +- Core/Src/main.c | 2 +- Core/Src/si5351.c | 169 +++++++++++++++++++++++----------------- stm32l4a6zg-f0x.at1.ioc | 2 +- 6 files changed, 105 insertions(+), 82 deletions(-) diff --git a/.cproject b/.cproject index 8e9c816..c8e4f01 100644 --- a/.cproject +++ b/.cproject @@ -82,8 +82,8 @@ - + @@ -165,8 +165,8 @@ - + diff --git a/Core/Inc/si5351.h b/Core/Inc/si5351.h index 4e3660a..94035a3 100644 --- a/Core/Inc/si5351.h +++ b/Core/Inc/si5351.h @@ -70,8 +70,6 @@ extern "C" { #endif /* Includes ------------------------------------------------------------------*/ -#include -#include /* register map of the Si5351 */ /* #include could also be included */ /* Private includes ----------------------------------------------------------*/ @@ -105,7 +103,7 @@ typedef enum { /* Exported macros -----------------------------------------------------------*/ #define __SI5351__ 1 -#define __SI5351_MINOR__ 1 +#define __SI5351_MINOR__ 2 #define __SI5351_PATCHLEVEL__ 0 #define SI5351_VERSION (__SI5351__ * 10000 \ @@ -127,7 +125,6 @@ int si5351_deinitialize(void); si5351_inst_t si5351_init(void * i2c_handle, uint8_t i2c_address, uint32_t xtal_frequency); int si5351_deinit(si5351_inst_t si5351_handle); int si5351_i2c_ready(si5351_inst_t inst); -int si5351_program(si5351_inst_t inst); int si5351_enable_output(si5351_inst_t inst, uint8_t clk); int si5351_disable_output(si5351_inst_t inst, uint8_t clk); int si5351_set_clk0(si5351_inst_t inst, uint32_t frequency); @@ -136,7 +133,10 @@ int si5351_set_clk(si5351_inst_t inst, uint8_t clk, uint32_t frequency, si5351_p char * si5351_read_debug_msg(si5351_inst_t inst); char * si5351_read_register_debug(si5351_inst_t inst, char *buf, size_t bufsize, uint8_t regaddr); +/* some functions for getting information */ int si5351_get_instance(si5351_inst_t *inst); +int si5351_get_i2c_address(si5351_inst_t inst); +void* si5351_get_i2c_handle(si5351_inst_t inst); /* under development */ int si5351_set_clk_phase(si5351_inst_t inst, uint8_t clk, uint32_t frequency, double phase, si5351_pll_t pll); diff --git a/Core/Src/commands.c b/Core/Src/commands.c index 11b1b81..7671243 100644 --- a/Core/Src/commands.c +++ b/Core/Src/commands.c @@ -16,7 +16,7 @@ #include //strchr #include //bsearch #include "helper.h" //ltrim -#include "stm32_si5351.h" +#include "si5351.h" #include "commands.h" diff --git a/Core/Src/main.c b/Core/Src/main.c index 2c94fbb..8668ea2 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -24,7 +24,7 @@ /* USER CODE BEGIN Includes */ #include #include "at1_defines.h" -#include "stm32_si5351.h" +#include "si5351.h" #include "commands.h" #include #include "ringbuf.h" diff --git a/Core/Src/si5351.c b/Core/Src/si5351.c index 2311418..9fb5d9d 100644 --- a/Core/Src/si5351.c +++ b/Core/Src/si5351.c @@ -1,9 +1,9 @@ /** - ****************************************************************************** - * @file stm32_si5351.c - * @brief STM32 library/driver for the Si5351 clock chip - from Skyworks Solutions, Inc. (former SiLabs) - ****************************************************************************** + ******************************************************************************* + * @file si5351.c + * @brief STM32 library/driver for the Si5351 clock chip + * from Skyworks Solutions, Inc. (former SiLabs) + ******************************************************************************* * @author: Thomas Kuschel KW4NZ * created 2022-05-11 * @@ -16,11 +16,13 @@ /* Defines for compilation ---------------------------------------------------*/ /* if you want to automatically enable the clk output after setting synthesis */ +/* you can set the AUTOMATICALLY_ENALBE_OUTPUT to 1 (not recommended) */ #define AUTOMATICALLY_ENABLE_OUTPUT 0 /* enable some optimizing, usually set to 1 */ #define OPTIMIZED 1 -/* Includes ------------------------------------------------------------------*/ +/* Includes -----------------------------------------------------------------*/ +#include #include #include #ifdef DEBUG @@ -31,7 +33,8 @@ #include /* including the HAL here */ #include "stm32l4xx_hal.h" -/* Include the header file */ +/* register map of the Si5351 */ +#include "si5351_reg.h" #include "si5351.h" /* Private typedef -----------------------------------------------------------*/ @@ -40,29 +43,29 @@ */ typedef struct __SI5351_HandleTypeDef { void *i2c_handle; /*!< the I2C handle, must not be unique */ - struct __SI5351_HandleTypeDef *next; /*!< next pointer to the following structure when there are several instances */ + struct __SI5351_HandleTypeDef *next; /*!< used for several instances */ uint32_t xtal_frequency; /*!< XTAL or CLKIN frequency */ #if SI5351_DEBUG - char debug_msg[1000]; /*!< debugging messages for extensive tests of the Si5351 chip, not required */ + char debug_msg[1000]; /*!< for debugging msgs, extensive tests, N/A */ #endif uint8_t clk_is_pllb; /*!< assignment of PLLA or PLLB per CLK #, if bit set to 1 ...PLLB */ uint8_t clk_is_disabled; /*!< assignment of Output Enable Control, app. Register 3 */ uint8_t clk_has_phase_shift;/*!< assignment of an output with a phase shift offset */ - uint8_t i2c_address; /*!< I2C address of the datasheet */ - uint8_t interrupt_status_mask; /*!< Reg 2: Interrupt Status Mask */ - uint8_t initialized :1; /*!< mark the driver initialized */ - uint8_t programmed :1; /*!< mark the chip is programmed */ + uint8_t i2c_address; /*!< I2C address of the datasheet */ + uint8_t interrupt_status_mask; /*!< Reg 2: Interrupt Status Mask */ + uint8_t initialized :1; /*!< mark the driver initialized */ + uint8_t programmed :1; /*!< mark the chip is programmed */ } si5351_HandleTypeDef; /* @brief SI5351 synthesis settings */ typedef struct { - uint32_t pll_multiplier; /*!< in datasheet this value corresponds to feedback multisynth (N) a */ - uint32_t pll_numerator; /*!< in datasheet this value corresponds to feedback multisynth (N) b */ - uint32_t pll_denominator; /*!< in datasheet this value corresponds to feedback multisynth (N) c */ - uint32_t out_multiplier; /*!< in datasheet this value corresponds to multisynth (M) a */ - uint32_t out_numerator; /*!< in datasheet this value corresponds to multisynth (M) b */ - uint32_t out_denominator; /*!< in datasheet this value corresponds to multisynth (M) c */ - uint8_t out_r_divider; /*!< R divider, log2 value bit set to 1; 2,4,8,...,128; for frequencies < 500 kHz, otherwise set to 1 i.e. bit 0 */ + uint32_t pll_multiplier; /*!< corresponds to feedback multisynth (N) a */ + uint32_t pll_numerator; /*!< corresponds to feedback multisynth (N) b */ + uint32_t pll_denominator; /*!< corresponds to feedback multisynth (N) c */ + uint32_t out_multiplier; /*!< corresponds to multisynth (M) a */ + uint32_t out_numerator; /*!< corresponds to multisynth (M) b */ + uint32_t out_denominator; /*!< corresponds to multisynth (M) c */ + uint8_t out_r_divider; /*!< R divider, log2 value bit set to 1; 2,4,8,...,128; for frequencies < 500 kHz, otherwise set to 1 i.e. bit 0 */ } synthesis_t; typedef struct { @@ -108,32 +111,33 @@ typedef struct { #endif /* Private variables ---------------------------------------------------------*/ -si5351_HandleTypeDef *first_handle = NULL; /* pointer to the first instance */ -int si5351_errno = 0; /* error_number for functions with return == NULL */ +si5351_HandleTypeDef *first_handle = NULL; /* pointer to the first instance */ +int si5351_errno = 0; /* error_number for functions with return == NULL */ /* Private function prototypes -----------------------------------------------*/ #ifdef DEBUG int __fprintb(FILE *stream, void *value, size_t size); #endif -int calculation(uint32_t frequency, uint32_t xtal, synthesis_t *synth); -int si5351_set_pll(si5351_inst_t inst, synthesis_t *synth); -int si5351_set_output(si5351_inst_t inst, synthesis_t *synth); -int si5351_set_synthesis(si5351_inst_t inst, synthesis_t *synth, uint8_t clk); +static int si5351_set_synthesis(si5351_inst_t inst, synthesis_t *synth, uint8_t clk); +static int si5351_program(si5351_inst_t inst); +static int si5351_error_status_i2c(HAL_StatusTypeDef status); +static int si5351_read(si5351_inst_t inst, uint8_t regaddr, uint8_t *data, uint16_t size); +static int si5351_write(si5351_inst_t inst, uint8_t regaddr, uint8_t *data, uint16_t size); +static int band_select(uint32_t frequency, band_t *band); +static int calculation(uint32_t frequency, uint32_t xtal, synthesis_t *synth); /* Private functions ---------------------------------------------------------*/ -/** Wrapper functions for receiving/transceiving bytes from I2C bus (HAL function set) +/** Wrapper functions for receiving/transceiving bytes from I2C bus + * (HAL function set) */ /** @brief Give better error numbers based on the Linux error_no.h - * @param i2c_handle the handle of the I2C bus from HAL function, e.g. hi2c1 - * @param xtal_frequency either the XTAL frequency (25/27 MHz) or CLock-In - * from 10 MHz to 100 MHz entered in Hz - * @param i2c_address I2C bus address of the device from datasheet typically 0x60 (or 0x61) - * @param datasize reserve an extra area of data space in bytes, access with function si5351_read_data() and si5351_write_data() - * @return si5351_handle Pointer to the si5351 handle, NULL if error, see si5351_errno + * @param status based on HAL library in HAL_StatusTypeDef + * @return error number see si5351_errno */ static int si5351_error_status_i2c(HAL_StatusTypeDef status) { + switch (status) { case HAL_TIMEOUT: return -ETIMEDOUT; @@ -150,32 +154,35 @@ static int si5351_error_status_i2c(HAL_StatusTypeDef status) { } } -/** @brief Read (blocking mode) one ore more data bytes from the I2C bus starting at the regaddr register address - * @param instance instance (handle) of the SI5351 driver +/** @brief Read (blocking mode) one ore more data bytes from the I2C bus + * starting at the regaddr register address + * @param inst instance (handle) of the SI5351 driver * @param regaddr starting register address of the SI5351 * @param data pointer to the first byte of data - * @param datasize datasize of the reading data, sizeof (data), when reading only one register (byte), set to 1 + * @param datasize datasize of the reading data, sizeof (data), + * when reading only one register (byte), set to 1 * @return errno 0 on success, see si5351_errno_t - * @retval -EINVAL when given a NULL handle - * @retval -ETIMEDOUT when HAL_TIMEOUT - * @retval -EIO when HAL_ERROR - * @retval -EBUSY when HAL_BUSY + * @retval -EINVAL when given a NULL handle + * @retval -ETIMEDOUT when HAL_TIMEOUT + * @retval -EIO when HAL_ERROR + * @retval -EBUSY when HAL_BUSY * if applicable use interrupt controlled HAL functions */ -int si5351_read(si5351_inst_t instance, uint8_t regaddr, uint8_t *data, - uint16_t size) { +static int si5351_read(si5351_inst_t inst, uint8_t regaddr, uint8_t *data, uint16_t size) { HAL_StatusTypeDef status; - status = HAL_I2C_Mem_Read(instance->i2c_handle, instance->i2c_address, + status = HAL_I2C_Mem_Read(inst->i2c_handle, inst->i2c_address, (uint16_t) regaddr, I2C_MEMADD_SIZE_8BIT, data, size, 0xffff); return si5351_error_status_i2c(status); } -/** @brief Write (blocking mode) one ore more data bytes to the I2C bus starting at the regaddr register address - * @param instance instance (handle) of the SI5351 driver +/** @brief Write (blocking mode) one ore more data bytes to the I2C bus + * starting at the regaddr register address + * @param inst instance (handle) of the SI5351 driver * @param regaddr starting register address of the SI5351 * @param data pointer to the first byte of data - * @param datasize datasize of the writing data, sizeof (data), when writing to one register (byte), set to 1 + * @param datasize datasize of the writing data, sizeof (data), + * when writing to one register (byte), set to 1 * @return errno 0 on success, see si5351_errno_t * @retval -EINVAL when given a NULL handle * @retval -ETIMEDOUT when HAL_TIMEOUT @@ -183,11 +190,10 @@ int si5351_read(si5351_inst_t instance, uint8_t regaddr, uint8_t *data, * @retval -EBUSY when HAL_BUSY * if applicable use interrupt controlled HAL functions */ -int si5351_write(si5351_inst_t instance, uint8_t regaddr, uint8_t *data, - uint16_t size) { +static int si5351_write(si5351_inst_t inst, uint8_t regaddr, uint8_t *data, uint16_t size) { HAL_StatusTypeDef status; - status = HAL_I2C_Mem_Write(instance->i2c_handle, instance->i2c_address, + status = HAL_I2C_Mem_Write(inst->i2c_handle, inst->i2c_address, (uint16_t) regaddr, I2C_MEMADD_SIZE_8BIT, data, size, 0xffff); return si5351_error_status_i2c(status); } @@ -336,7 +342,7 @@ int si5351_deinitialize(void) { * @retval -EIO when HAL_ERROR * @retval -EBUSY when HAL_BUSY */ -int si5351_program(si5351_inst_t inst) { +static int si5351_program(si5351_inst_t inst) { uint8_t data; int status = 0; @@ -388,8 +394,12 @@ int si5351_program(si5351_inst_t inst) { status = si5351_read(inst, SI5351_FANOUT_ENABLE, &data, 1); if (status) break; - cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE 0x%x\n", cx, data); - cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE %d\n", cx, data); + cx += snprintf(inst->debug_msg + cx, + sizeof(inst->debug_msg) - (size_t)cx, + "(%d) FANOUT_ENABLE 0x%x\n", cx, data); + cx += snprintf(inst->debug_msg + cx, + sizeof(inst->debug_msg) - (size_t)cx, + "(%d) FANOUT_ENABLE %d\n", cx, data); #endif data = SI5351_CLKIN_FANOUT_EN | SI5351_XO_FANOUT_EN | SI5351_MS_FANOUT_EN; // set them to 1b @@ -400,8 +410,12 @@ int si5351_program(si5351_inst_t inst) { status = si5351_read(inst, SI5351_FANOUT_ENABLE, &data, 1); if (status) break; - cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE 0x%x\n", cx, data); - cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) FANOUT_ENABLE %d\n", cx, data); + cx += snprintf(inst->debug_msg + cx, + sizeof(inst->debug_msg) - (size_t)cx, + "(%d) FANOUT_ENABLE 0x%x\n", cx, data); + cx += snprintf(inst->debug_msg + cx, + sizeof(inst->debug_msg) - (size_t)cx, + "(%d) FANOUT_ENABLE %d\n", cx, data); #endif /* Crystal Internal Load Capacitance */ @@ -410,7 +424,9 @@ int si5351_program(si5351_inst_t inst) { status = si5351_write(inst, SI5351_CRYSTAL_INTERNAL_LOAD_CAPACITANCE, &data, 1); if (status) break; - cx += snprintf(inst->debug_msg + cx, sizeof(inst->debug_msg) - (size_t)cx, "(%d) XTAL int. Load Cap: 0x%x (%dd)\n", cx, data, data); + cx += snprintf(inst->debug_msg + cx, + sizeof(inst->debug_msg) - (size_t)cx, + "(%d) XTAL int. Load Cap: 0x%x (%dd)\n", cx, data, data); #endif inst->programmed = 1; /* the device is programmed */ } while (0); @@ -419,24 +435,31 @@ int si5351_program(si5351_inst_t inst) { } /** @brief Select the right band to calculate even divisors for the output - * @param frequency - * @param band pointer of band_t return parameters as a structure min, max, divisor for each band + * @param frequency + * @param band pointer of band_t return parameters as a structure min, + * max, divisor for each band * @return 1 on success, when a frequency band is found - * @retval 0 when frequency not found + * @retval 0 when frequency not found */ -int band_select(uint32_t frequency, band_t *band) { +static int band_select(uint32_t frequency, band_t *band) { static const band_t sband[] = { /* band in meters, frequ_lo, frequ_hi, multiplier, divider_bit */ - { "80", 3000000, 4500000, 200, 0 }, { "40", 5625000, 7500000, 120, 0 }, { - "30", 7500000, 11250000, 80, 0 }, - { "20", 11250000, 15000000, 60, 0 }, { "15", 15000000, 22500000, 40, - 0 }, { "10", 22500000, 32142000, 28, 0 }, { "8", 32142000, - 45000000, 20, 0 }, { "6", 45000000, 64285000, 14, 0 }, { - "4", 64285000, 76000000, 10, 0 }, { "3", 76000000, 11250000, - 8, 0 }, { "2", 11250000, 15000000, 6, 0 }, { "180", 1500000, - 2250000, 400, 0 }, { "120", 2250000, 3000000, 300, 0 }, { - "60", 4500000, 5625000, 160, 0 }, }; - + { "80", 3000000, 4500000, 200, 0 }, + { "40", 5625000, 7500000, 120, 0 }, + { "30", 7500000, 11250000, 80, 0 }, + { "20", 11250000, 15000000, 60, 0 }, + { "15", 15000000, 22500000, 40, 0 }, + { "10", 22500000, 32142000, 28, 0 }, + { "8", 32142000, 45000000, 20, 0 }, + { "6", 45000000, 64285000, 14, 0 }, + { "4", 64285000, 76000000, 10, 0 }, + { "3", 76000000, 11250000, 8, 0 }, + { "2", 11250000, 15000000, 6, 0 }, + {"180", 1500000, 2250000, 400, 0 }, + {"120", 2250000, 3000000, 300, 0 }, + { "60", 4500000, 5625000, 160, 0 }, + }; + /* possibly this could be improved with a bsearch algorithm: */ for (uint32_t i = 0; i < (sizeof(sband) / sizeof(sband[0])); i++) { if (frequency > sband[i].qrg_min && frequency <= sband[i].qrg_max) { memcpy(band, &sband[i], sizeof(*band)); @@ -452,7 +475,7 @@ int band_select(uint32_t frequency, band_t *band) { * @param synth pointer to a structure snythesis_t will be set with the calculated values * @return 0 on success */ -int calculation(uint32_t frequency, uint32_t xtal, synthesis_t *synth) { +static int calculation(uint32_t frequency, uint32_t xtal, synthesis_t *synth) { uint32_t t; band_t band; @@ -689,7 +712,7 @@ int si5351_set_clk_phase(si5351_inst_t inst, uint8_t clk, uint32_t frequency, * @retval -EIO when HAL_ERROR * @retval -EBUSY when HAL_BUSY */ -int si5351_reset_pll(si5351_inst_t inst, uint8_t clk) { +static int si5351_reset_pll(si5351_inst_t inst, uint8_t clk) { /* internal function, no need to check inst nor clk */ @@ -707,8 +730,8 @@ int si5351_reset_pll(si5351_inst_t inst, uint8_t clk) { /** @brief Sets the CLK_x output phase of the si5351 (direct access to register) * @param si5351_instance Given si5351 device handle - * @param phase in uint8_t type value * @param clk The CLK ouput to drive and disable 0...CLK0, 1...CLK1, 2...CLK2, ... + * @param phase in uint8_t type value * @return 0 on success * @retval -EINVAL when given a NULL handle * @retval -ETIMEDOUT when HAL_TIMEOUT @@ -743,7 +766,7 @@ int si5351_set_phase(si5351_inst_t inst, uint8_t clk, uint8_t phase) { * @retval -EIO when HAL_ERROR * @retval -EBUSY when HAL_BUSY */ -int si5351_set_synthesis(si5351_inst_t inst, synthesis_t *synth, uint8_t clk) { +static int si5351_set_synthesis(si5351_inst_t inst, synthesis_t *synth, uint8_t clk) { uint32_t MSNx_P1, MSNx_P2, MSNx_P3; uint32_t MSx_P1, MSx_P2, MSx_P3; diff --git a/stm32l4a6zg-f0x.at1.ioc b/stm32l4a6zg-f0x.at1.ioc index 077114d..149ca69 100644 --- a/stm32l4a6zg-f0x.at1.ioc +++ b/stm32l4a6zg-f0x.at1.ioc @@ -511,4 +511,4 @@ VP_RTC_VS_RTC_Calendar.Signal=RTC_VS_RTC_Calendar VP_SYS_VS_tim6.Mode=TIM6 VP_SYS_VS_tim6.Signal=SYS_VS_tim6 board=NUCLEO-L4A6ZG -isbadioc=true +isbadioc=false