rework of si5351 driver

This commit is contained in:
Thomas Kuschel 2022-07-16 23:24:59 +02:00
parent f3682d7103
commit 2c8881338d
6 changed files with 105 additions and 82 deletions

View File

@ -82,8 +82,8 @@
</folderInfo>
<sourceEntries>
<entry excluding="external_sources" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Middlewares"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
</sourceEntries>
</configuration>
</storageModule>
@ -165,8 +165,8 @@
</folderInfo>
<sourceEntries>
<entry excluding="external_sources" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Middlewares"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
</sourceEntries>
</configuration>
</storageModule>

View File

@ -70,8 +70,6 @@ extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <si5351_reg.h> /* register map of the Si5351 */
/* #include <errno.h> 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);

View File

@ -16,7 +16,7 @@
#include <string.h> //strchr
#include <stdlib.h> //bsearch
#include "helper.h" //ltrim
#include "stm32_si5351.h"
#include "si5351.h"
#include "commands.h"

View File

@ -24,7 +24,7 @@
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "at1_defines.h"
#include "stm32_si5351.h"
#include "si5351.h"
#include "commands.h"
#include <string.h>
#include "ringbuf.h"

View File

@ -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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef DEBUG
@ -31,7 +33,8 @@
#include <assert.h>
/* 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;

View File

@ -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