x-cube-tof1/Drivers/BSP/53L1A2/53l1a2.c

579 lines
16 KiB
C

/**
******************************************************************************
* @file 53l1a2.c
* @author IMG SW Application Team
* @brief This file contains the X-NUCLEO-53L1A2 BSP implementation.
******************************************************************************
* @attention
*
* Copyright (c) 2022 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------ */
#include <string.h>
#include "53l1a2.h"
/** @addtogroup BSP
* @{
*/
/** @addtogroup XNUCLEO_53L1A2
* @{
*/
/** @addtogroup XNUCLEO_53L1A2_COMMON
* @{
*/
/* This macro can be overloaded by the user to report error log messages with printf format */
#define VL53L1A2_ErrLog(...) (void)0
/* These macros can be overloaded by the user to enforce i2c sharing in RTOS context */
#define VL53L1A2_GetI2cBus(...) (void)0
#define VL53L1A2_PutI2cBus(...) (void)0
#define I2C_EXPANDER_ADDR0 ((int)(0x43*2)) /*!< Expander 0 i2c address[7..0] format */
#define I2C_EXPANDER_ADDR1 ((int)(0x42*2)) /*!< Expander 1 i2c address[7..0] format */
/**
* GPIO monitor pin state register
* 16 bit register LSB at lowest offset (little endian)
*/
#define GPMR (0x10)
/**
* STMPE1600 GPIO set pin state register
* 16 bit register LSB at lowest offset (little endian)
*/
#define GPSR (0x12)
/**
* STMPE1600 GPIO set pin direction register
* 16 bit register LSB at lowest offset
*/
#define GPDR (0x14)
/**
* @defgroup VL53L1A2_7Segment 7 segment display
*
* macro use for human readable segment building
* @code
* --s0--
* s s
* 5 1
* --s6--
* s s
* 4 2
* --s3-- . s7 (dp)
* @endcode
*
* @{
*/
#define DP (1U << 7) /*!< decimal point bit mapping */
#define S0 (1U << 3) /*!< segment s0 bit mapping */
#define S1 (1U << 5) /*!< segment s1 bit mapping */
#define S2 (1U << 6) /*!< segment s2 bit mapping */
#define S3 (1U << 4) /*!< segment s3 bit mapping */
#define S4 (1U << 0) /*!< segment s4 bit mapping */
#define S5 (1U << 1) /*!< segment s5 bit mapping */
#define S6 (1U << 2) /*!< segment s6 bit mapping */
/**
* build a character by defining the non lighted segment (not one and no DP)
*
* @param ... literal sum and or combine of any macro to define any segment #S0 .. #S6
*
* example '9' is all segment on but S4
* @code
* ['9']= NOT_7_NO_DP(S4),
* @endcode
*/
#define NOT_7_NO_DP( ... ) (uint8_t) ~( __VA_ARGS__ + DP )
/**
* Ascii to 7 segment lookup table
*
* Most common character are supported and follow http://www.twyman.org.uk/Fonts/
* few extra special \@ ^~ ... etc are present for specific demo purpose
*/
static const uint8_t ascii_to_display_lut[256] =
{
[' '] = 0,
['-'] = S6,
['_'] = S3,
['='] = S3 + S6,
['~'] = S0 + S3 + S6, /* 3 h bar */
['^'] = S0, /* use as top bar */
['?'] = NOT_7_NO_DP(S5 + S3 + S2),
['*'] = NOT_7_NO_DP(),
['['] = S0 + S3 + S4 + S5,
[']'] = S0 + S3 + S2 + S1,
['@'] = S0 + S3,
['0'] = NOT_7_NO_DP(S6),
['1'] = S1 + S2,
['2'] = S0 + S1 + S6 + S4 + S3,
['3'] = NOT_7_NO_DP(S4 + S5),
['4'] = S5 + S1 + S6 + S2,
['5'] = NOT_7_NO_DP(S1 + S4),
['6'] = NOT_7_NO_DP(S1),
['7'] = S0 + S1 + S2,
['8'] = NOT_7_NO_DP(0),
['9'] = NOT_7_NO_DP(S4),
['a'] = S2 + S3 + S4 + S6,
['b'] = NOT_7_NO_DP(S0 + S1),
['c'] = S6 + S4 + S3,
['d'] = NOT_7_NO_DP(S0 + S5),
['e'] = NOT_7_NO_DP(S2),
['f'] = S6 + S5 + S4 + S0, /* same as F */
['g'] = NOT_7_NO_DP(S4), /* same as 9 */
['h'] = S6 + S5 + S4 + S2,
['i'] = S4,
['j'] = S1 + S2 + S3 + S4,
['k'] = S6 + S5 + S4 + S2, /* a h */
['l'] = S3 + S4,
['m'] = S0 + S4 + S2, /* same as */
['n'] = S2 + S4 + S6,
['o'] = S6 + S4 + S3 + S2,
['p'] = NOT_7_NO_DP(S3 + S2), /* same as P */
['q'] = S0 + S1 + S2 + S5 + S6,
['r'] = S4 + S6,
['s'] = NOT_7_NO_DP(S1 + S4),
['t'] = NOT_7_NO_DP(S0 + S1 + S2),
['u'] = S4 + S3 + S2 + S5 + S1, /* U */
['v'] = S4 + S3 + S2, /* is u but u use U */
['w'] = S1 + S3 + S5,
['x'] = NOT_7_NO_DP(S0 + S3), /* similar to H */
['y'] = NOT_7_NO_DP(S0 + S4),
['z'] = S0 + S1 + S6 + S4 + S3, /* same as 2 */
['A'] = NOT_7_NO_DP(S3),
['B'] = NOT_7_NO_DP(S0 + S1), /* as b */
['C'] = S0 + S3 + S4 + S5, /* same as [ */
['E'] = NOT_7_NO_DP(S1 + S2),
['F'] = S6 + S5 + S4 + S0,
['G'] = NOT_7_NO_DP(S4), /* same as 9 */
['H'] = NOT_7_NO_DP(S0 + S3),
['I'] = S1 + S2,
['J'] = S1 + S2 + S3 + S4,
['K'] = NOT_7_NO_DP(S0 + S3), /* same as H */
['L'] = S3 + S4 + S5,
['M'] = S0 + S4 + S2, /* same as m*/
['N'] = S2 + S4 + S6, /* same as n*/
['O'] = NOT_7_NO_DP(S6),
['P'] = NOT_7_NO_DP(S3 + S2),
['Q'] = NOT_7_NO_DP(S3 + S2),
['R'] = S4 + S6,
['S'] = NOT_7_NO_DP(S1 + S4), /* sasme as 5 */
['T'] = NOT_7_NO_DP(S0 + S1 + S2), /* sasme as t */
['U'] = NOT_7_NO_DP(S6 + S0),
['V'] = S4 + S3 + S2, /* is u but u use U */
['W'] = S1 + S3 + S5,
['X'] = NOT_7_NO_DP(S0 + S3), /* similar to H */
['Y'] = NOT_7_NO_DP(S0 + S4),
['Z'] = S0 + S1 + S6 + S4 + S3, /* same as 2 */
};
#undef S0
#undef S1
#undef S2
#undef S3
#undef S4
#undef S5
#undef S6
#undef DP
/**
* @}
*/
/** @defgroup XNUCLEO_53L1A2_COMMON_Private_Variables Private Variables
* @{
*/
static uint32_t InitCounter = 0;
/* cache the full set of expanded GPIO values to avoid i2c reading */
static union CurIOVal_u
{
uint8_t bytes[4]; /*!< 4 bytes array i/o view */
uint32_t u32; /*!< single dword i/o view */
} CurIOVal; /* cache the extended IO values */
/**
* @}
*/
/** @defgroup XNUCLEO_53L1A2_COMMON_Private_Functions_Prototypes Private Functions Prototypes
* @{
*/
static int32_t _I2cFailRecover(void);
static int32_t _ExpanderRd(uint32_t I2cExpAddr, uint32_t index, uint8_t *data, uint32_t n_data);
static int32_t _ExpanderWR(uint32_t I2cExpAddr, uint32_t index, uint8_t *data, uint32_t n_data);
static int32_t _ExpandersSetAllIO(void);
/**
* @}
*/
/**
* @brief Initialize X-NUCLEO-53L1A2 STM32 expansion board
* @note All devices XSDN are asserted and display is turned off
* @return 0 on success
*/
int32_t VL53L1A2_Init(void)
{
int32_t status = 0;
uint8_t ExpanderData[2];
if (InitCounter++ == 0U)
{
status |= _I2cFailRecover();
status |= VL53L1A2_I2C_INIT();
if (status != BSP_ERROR_NONE)
{
goto done_err;
}
status = _ExpanderRd(I2C_EXPANDER_ADDR0, 0, ExpanderData, 2);
if ((status != 0) || (ExpanderData[0] != 0x00U) || (ExpanderData[1] != 0x16U))
{
VL53L1A2_ErrLog("I2C Expander @0x%02X not detected", (int)I2C_EXPANDER_ADDR0);
goto done_err;
}
status = _ExpanderRd(I2C_EXPANDER_ADDR1, 0, ExpanderData, 2);
if ((status != 0) || (ExpanderData[0] != 0x00U) || (ExpanderData[1] != 0x16U))
{
VL53L1A2_ErrLog("I2C Expander @0x%02X not detected", (int)I2C_EXPANDER_ADDR1);
goto done_err;
}
CurIOVal.u32 = 0x0U;
/* setup expander i/o direction all output but exp1 bit 14*/
ExpanderData[0] = 0xFFU;
ExpanderData[1] = 0xFFU;
status = _ExpanderWR(I2C_EXPANDER_ADDR0, GPDR, ExpanderData, 2);
if (status)
{
VL53L1A2_ErrLog("Set Expander @0x%02X DR", I2C_EXPANDER_ADDR0);
goto done_err;
}
ExpanderData[0] = 0xFFU;
ExpanderData[1] = 0xBFU; /* all but bit 14-15 that is pb1 and xhurt */
status = _ExpanderWR(I2C_EXPANDER_ADDR1, GPDR, ExpanderData, 2);
if (status)
{
VL53L1A2_ErrLog("Set Expander @0x%02X DR", I2C_EXPANDER_ADDR1);
goto done_err;
}
/* shut down all segment and all device */
CurIOVal.u32 = 0x7FU + (0x7FU << 7) + (0x7FU << 16) + (0x7FU << (16 + 7));
status = _ExpandersSetAllIO();
if (status)
{
VL53L1A2_ErrLog("Set initial i/o ");
}
}
done_err:
return status;
}
/**
* @brief De-initialize X-NUCLEO-53L1A2 STM32 expansion board
* @return 0 on success
*/
int32_t VL53L1A2_DeInit(void)
{
int32_t status = 0;
if (InitCounter > 0U)
{
if (--InitCounter == 0U)
{
status = VL53L1A2_I2C_DEINIT();
}
}
return status;
}
/**
* @brief Set Reset (XSDN) state of a given "id" device
* @param DevNo The device number, use @ref VL53L1A2_dev_e.
* @param state State of the device reset (xsdn) pin @warning reset pin is active low
* @return 0 on success
*/
int32_t VL53L1A2_ResetId(uint8_t DevNo, uint8_t state)
{
int32_t status;
switch (DevNo)
{
case VL53L1A2_DEV_CENTER :
CurIOVal.bytes[3] &= ~0x80U; /* bit 15 expander 1 => byte #3 */
if (state)
{
CurIOVal.bytes[3] |= 0x80U; /* bit 15 expander 1 => byte #3 */
}
status = _ExpanderWR(I2C_EXPANDER_ADDR1, GPSR + 1, &CurIOVal.bytes[3], 1);
break;
case VL53L1A2_DEV_LEFT :
CurIOVal.bytes[1] &= ~0x40U; /* bit 14 expander 0 => byte #1*/
if (state)
{
CurIOVal.bytes[1] |= 0x40U; /* bit 14 expander 0 => byte #1*/
}
status = _ExpanderWR(I2C_EXPANDER_ADDR0, GPSR + 1, &CurIOVal.bytes[1], 1);
break;
case VL53L1A2_DEV_RIGHT :
CurIOVal.bytes[1] &= ~0x80U; /* bit 15 expander 0 => byte #1 */
if (state)
{
CurIOVal.bytes[1] |= 0x80U; /* bit 15 expander 0 => byte #1*/
}
status = _ExpanderWR(I2C_EXPANDER_ADDR0, GPSR + 1, &CurIOVal.bytes[1], 1);
break;
default:
VL53L1A2_ErrLog("Invalid DevNo %d", DevNo);
status = -1;
goto done;
}
/* error with valid id */
if (status)
{
VL53L1A2_ErrLog("expander i/o error for DevNo %d state %d ", DevNo, state);
}
done:
return status;
}
/**
* @brief Set the 7 segments display
* @param str String to set on display
* @warning When string is less than 4 digits, display is left-justified and lower digits are blanked.
* To display a 2 digits value, left justified on the 4 digits, use "%4d" formatting
* @warning When more than 4 char are present only first 4 are displayed
* @note Characters that do not have 7 segment font matching in ascii_to_display_lut are left blank
* @return 0 on success
*/
int32_t VL53L1A2_SetDisplayString(const char *str)
{
int32_t status;
uint32_t BitPos;
uint32_t i;
uint32_t Segments;
/* lookup table for for digit to bit position in @a CurIOVal u32 */
uint8_t DisplayBitPos[4] = {0, 7, 16, 16 + 7};
for (i = 0; (i < 4U) && (str[i] != 0U); i++)
{
Segments = (uint32_t)ascii_to_display_lut[(uint8_t)str[i]];
Segments = (~Segments) & 0x7FU;
BitPos = DisplayBitPos[i];
CurIOVal.u32 &= ~(0x7FU << BitPos);
CurIOVal.u32 |= Segments << BitPos;
}
/* clear unused digit */
for (; i < 4U; i++)
{
BitPos = DisplayBitPos[i];
CurIOVal.u32 |= 0x7FU << BitPos;
}
status = _ExpandersSetAllIO();
if (status)
{
VL53L1A2_ErrLog("Set i/o");
}
return status;
}
/** @defgroup XNUCLEO_53L1A2_COMMON_Private_Functions Private Functions
* @{
*/
/**
* @brief Expansion board i2c bus recovery
* We may get reset in middle of an i2c access (h/w reset button, debug or f/w load)
* hence some agent on bus may be in middle of a transaction and can create issue or even prevent starting (SDA is low)
* this routine does use gpio to manipulate and recover i2c bus line in all cases.
*/
static int32_t _I2cFailRecover(void)
{
/* We can't assume bus state based on SDA and SCL state (we may be in a data or NAK bit so SCL=SDA=1)
* by setting SDA high and toggling SCL at least 10 time we ensure whatever agent and state
* all agent should end up seeing a "stop" and bus get back to an known idle i2c bus state */
uint8_t i;
uint8_t retry_cnt = 0;
static uint8_t is_already_init = 0U;
GPIO_InitTypeDef GPIO_InitStruct;
if (is_already_init == 1U)
{
return BSP_ERROR_NONE;
}
/* Enable I/O */
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = VL53L1A2_I2C_SCL_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(VL53L1A2_I2C_SCL_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = VL53L1A2_I2C_SDA_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(VL53L1A2_I2C_SDA_GPIO_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(VL53L1A2_I2C_SCL_GPIO_PORT, VL53L1A2_I2C_SCL_GPIO_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(VL53L1A2_I2C_SDA_GPIO_PORT, VL53L1A2_I2C_SDA_GPIO_PIN, GPIO_PIN_SET);
do
{
for (i = 0; i < 10U; i++)
{
HAL_GPIO_WritePin(VL53L1A2_I2C_SCL_GPIO_PORT, VL53L1A2_I2C_SCL_GPIO_PIN, GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(VL53L1A2_I2C_SCL_GPIO_PORT, VL53L1A2_I2C_SCL_GPIO_PIN, GPIO_PIN_SET);
HAL_Delay(1);
}
retry_cnt++;
} while ((HAL_GPIO_ReadPin(VL53L1A2_I2C_SDA_GPIO_PORT, VL53L1A2_I2C_SDA_GPIO_PIN) == GPIO_PIN_RESET) && (retry_cnt < 7U));
if (HAL_GPIO_ReadPin(VL53L1A2_I2C_SCL_GPIO_PORT, VL53L1A2_I2C_SDA_GPIO_PIN) == GPIO_PIN_RESET)
{
/* We are still in a bad i2c state, return error */
return BSP_ERROR_COMPONENT_FAILURE;
}
is_already_init = 1U;
return BSP_ERROR_NONE;
}
/**
* @brief Set all i2c expended gpio in one go
* @return i/o operation status
*/
static int32_t _ExpandersSetAllIO(void)
{
int32_t status;
status = _ExpanderWR(I2C_EXPANDER_ADDR0, GPSR, &CurIOVal.bytes[0], 2);
if (status)
{
goto done_err;
}
status = _ExpanderWR(I2C_EXPANDER_ADDR1, GPSR, &CurIOVal.bytes[2], 2);
done_err:
return status;
}
/**
* @brief STMPE1600 i2c Expander register read
* @param I2cExpAddr Expander address
* @param index register index
* @param data read data buffer
* @param n_data number of byte to read
* @return of if ok else i2c I/O operation status
*/
static int32_t _ExpanderRd(uint32_t I2cExpAddr, uint32_t index, uint8_t *data, uint32_t n_data)
{
int32_t status;
uint8_t RegAddr;
RegAddr = index;
VL53L1A2_GetI2cBus();
do
{
status = HAL_I2C_Master_Transmit(&VL53L1A2_HI2C, I2cExpAddr, &RegAddr, 1, 100);
if (status)
{
break;
}
status = HAL_I2C_Master_Receive(&VL53L1A2_HI2C, I2cExpAddr, data, n_data, n_data * 100);
} while (0);
VL53L1A2_PutI2cBus();
return status;
}
/**
* @brief STMPE1600 i2c Expander register write
* @param I2cExpAddr Expander address
* @param index register index
* @param data data buffer
* @param n_data number of byte to write
* @return of if ok else i2c I/O operation status
*/
static int32_t _ExpanderWR(uint32_t I2cExpAddr, uint32_t index, uint8_t *data, uint32_t n_data)
{
int32_t status;
uint8_t RegAddr[0x10];
RegAddr[0] = index;
memcpy(RegAddr + 1, data, n_data);
VL53L1A2_GetI2cBus();
status = HAL_I2C_Master_Transmit(&VL53L1A2_HI2C, I2cExpAddr, RegAddr, n_data + 1, 100);
VL53L1A2_PutI2cBus();
return status;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/