O7/vl53lx_uld/X-NUCLEO-53L1A1.c

609 lines
17 KiB
C
Raw Normal View History

2024-08-28 12:10:00 +08:00
/**
* @file X-NUCLEO-53L1A1.c
*
* implement X-NUCLEO-53L1A1 Nucleo BSP
*/
#include "main.h"
#include <string.h>
#include "X-NUCLEO-53L1A1.h"
#include "stm32wlxx_hal.h"
#ifndef HAL_I2C_MODULE_ENABLED
#define HAL_I2C_MODULE_ENABLED
#pragma message("hal conf should enable i2c")
#endif
/* when not customized by application define dummy one */
#ifndef XNUCLEO53L1A1_GetI2cBus
/**
* macro that can be overloaded by user to enforce i2c sharing in RTOS context
*/
#define XNUCLEO53L1A1_GetI2cBus(...) (void)0
#endif
#ifndef XNUCLEO53L1A1_PutI2cBus
/** macro can be overloaded by user to enforce i2c sharing in RTOS context
*/
# define XNUCLEO53L1A1_PutI2cBus(...) (void)0
#endif
/**
* Expander 0 i2c address[7..0] format
*/
#define I2cExpAddr0 ((int)(0x43*2))
/**
* Expander 1 i2c address[7..0] format
*/
#define I2cExpAddr1 ((int)(0x42*2))
/** @} XNUCLEO53L1A1_I2CExpanders*/
/**
* 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 XNUCLEO53L1A1_Board */
/****************************************************
*@defgroup XNUCLEO53L1A1_globals
*@{
*/
/**
* i2c handle to be use of all i2c access
* end user shall provide it to
* can be @a XNUCLEO53L1A1_I2C1Configure() @sa XNUCLEO53L1A1_usage
* @warning do not use any XNUCLEO53L1A1_xxx prior to a first init with valid i2c handle
*/
I2C_HandleTypeDef XNUCLEO53L1A1_hi2c;
/**
* 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 */
}
/** cache the extended IO values */
CurIOVal;
/**
* lookup table for for digit to bit position in @a CurIOVal u32
*/
static int DisplayBitPos[4]={0, 7, 16, 16+7};
/** @} XNUCLEO53L1A1_globals*/
/* Forward definition of private function */
static int _ExpanderRd(int I2cExpAddr, int index, uint8_t *data, int n_data);
static int _ExpanderWR(int I2cExpAddr, int index, uint8_t *data, int n_data);
static int _ExpandersSetAllIO(void);
/**
* 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 void _I2cFailRecover(){
GPIO_InitTypeDef GPIO_InitStruct;
int i, nRetry=0;
// 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
// Enable I/O
__GPIOA_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
//TODO we could do this faster by not using HAL delay 1ms for clk timing
do{
for( i=0; i<10; i++){
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
HAL_Delay(1);
}
// if( HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9) == 0 ){
// static int RetryRecover;
// RetryRecover++;
// }
}while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0 && nRetry++<7);
if( HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0 ){
__GPIOB_CLK_ENABLE();
//We are still in bad i2c state warm user by blinking led but stay here
GPIO_InitStruct.Pin = LED1_Pin ;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(LED3_GPIO_Port, &GPIO_InitStruct);
do{
HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);
HAL_Delay(33);
HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);
HAL_Delay(33);
HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);
HAL_Delay(33);
HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);
HAL_Delay(33*20);
}while(1);
}
}
int XNUCLEO53L1A1_I2C2Configure() {
int status;
GPIO_InitTypeDef GPIO_InitStruct;
_I2cFailRecover();
/* Peripheral clock enable */
__GPIOA_CLK_ENABLE();
__I2C2_CLK_ENABLE();
/**I2C1 GPIO Configuration\n
PB8 ------> I2C1_SCL\n
PB9 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
XNUCLEO53L1A1_hi2c.Instance = I2C2;
//XNUCLEO53L1A1_hi2c.Init.Timing = 0x00300F38; /* set 400KHz fast mode i2c*/
XNUCLEO53L1A1_hi2c.Init.Timing = 0x2010091A; //0x2010091A = 400K Fast Mode, 0x20303E5D, 100K Standard mode, 0x20000209 Fast Mode Plus, 1Mbps
XNUCLEO53L1A1_hi2c.Init.OwnAddress1 = 0;
XNUCLEO53L1A1_hi2c.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
XNUCLEO53L1A1_hi2c.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
XNUCLEO53L1A1_hi2c.Init.OwnAddress2 = 0;
XNUCLEO53L1A1_hi2c.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
XNUCLEO53L1A1_hi2c.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
status = HAL_I2C_Init(&XNUCLEO53L1A1_hi2c);
return status;
}
int XNUCLEO53L1A1_SetIntrStateId(int EnableIntr, int DevNo){
int status;
IRQn_Type IntrNo;
int IntrPin;
switch( DevNo ){
case XNUCLEO53L1A1_DEV_CENTER :
case 'c' :
IntrNo = VL53L1A1_GPIO1_C_INTx;
IntrPin= VL53L1A1_GPIO1_C_GPIO_PIN;
status = 0;
break;
case XNUCLEO53L1A1_DEV_LEFT :
case 'l' :
break;
case 'r' :
case XNUCLEO53L1A1_DEV_RIGHT :
break;
default:
XNUCLEO53L1A1_ErrLog("Invalid DevNo %d",DevNo);
status = -1;
goto done;
}
if( EnableIntr ){
__HAL_GPIO_EXTI_CLEAR_IT(IntrPin);
NVIC_ClearPendingIRQ(IntrNo);
HAL_NVIC_EnableIRQ(IntrNo);
/**
* @note When enabling interrupt end user shall check actual state of the line and soft trigger event if active
* Alternatively user can use API and device feature to clear device Interrupt status to possibly generates a new edge.
* on shared pin configuration this must be repeated for all device.
* The same shall be done after clearing a condition in device and interrupt remain active.
*/
}
else{
HAL_NVIC_DisableIRQ(IntrNo);
__HAL_GPIO_EXTI_CLEAR_IT(IntrPin);
NVIC_ClearPendingIRQ(IntrNo);
}
done:
return status;
}
int XNUCLEO53L1A1_Init(void) {
int status;
// uint8_t ExpanderData[2];
// XNUCLEO53L1A1_USART2_UART_Init();
status = XNUCLEO53L1A1_I2C2Configure();
return status;
}
int XNUCLEO53L1A1_GetPB1(int *state) {
int status;
uint8_t PortValue;
status= _ExpanderRd(I2cExpAddr1, GPMR+1, &PortValue,1);
if( status == 0){
if( PortValue&=0x40 )
PortValue=1;
else
PortValue=0;
}
else{
XNUCLEO53L1A1_ErrLog("i/o error");
}
*state = PortValue;
return status;
}
int XNUCLEO53L1A1_ResetId(int DevNo, int state) {
int status;
switch( DevNo ){
case XNUCLEO53L1A1_DEV_CENTER :
case 'c' :
CurIOVal.bytes[3]&=~0x80; /* bit 15 expender 1 => byte #3 */
if( state )
CurIOVal.bytes[3]|=0x80; /* bit 15 expender 1 => byte #3 */
status= _ExpanderWR(I2cExpAddr1, GPSR+1, &CurIOVal.bytes[3], 1);
break;
case XNUCLEO53L1A1_DEV_LEFT :
case 'l' :
CurIOVal.bytes[1]&=~0x40; /* bit 14 expender 0 => byte #1*/
if( state )
CurIOVal.bytes[1]|=0x40; /* bit 14 expender 0 => byte #1*/
status= _ExpanderWR(I2cExpAddr0, GPSR+1, &CurIOVal.bytes[1], 1);
break;
case 'r' :
case XNUCLEO53L1A1_DEV_RIGHT :
CurIOVal.bytes[1]&=~0x80; /* bit 15 expender 0 => byte #1 */
if( state )
CurIOVal.bytes[1]|=0x80; /* bit 15 expender 0 => byte #1*/
status= _ExpanderWR(I2cExpAddr0, GPSR+1, &CurIOVal.bytes[1], 1);
break;
default:
XNUCLEO53L1A1_ErrLog("Invalid DevNo %d",DevNo);
status = -1;
goto done;
}
//error with valid id
if( status ){
XNUCLEO53L1A1_ErrLog("expander i/o error for DevNo %d state %d ",DevNo, state);
}
done:
return status;
}
void VL53L1A1_EXTI_IOConfigure(int DevNo, int IntPriority, int SubPriority){
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = VL53L1A1_INTR_PIN_PUPD;
switch (DevNo ) {
case XNUCLEO53L1A1_DEV_CENTER:
case 'c':
VL53L1A1_GPIO1_C_CLK_ENABLE();
/*Configure GPIO pin : PA4 */
GPIO_InitStruct.Pin = VL53L1A1_GPIO1_C_GPIO_PIN;
XNUCLEO53L1A1_SetIntrStateId(1,XNUCLEO53L1A1_DEV_CENTER);
HAL_GPIO_Init(VL53L1A1_GPIO1_C_GPIO_PORT, &GPIO_InitStruct);
HAL_NVIC_SetPriority((IRQn_Type)VL53L1A1_GPIO1_C_GPIO_PIN, IntPriority, SubPriority);
break;
#if VL53L1A1_GPIO1_SHARED == 0
case XNUCLEO53L1A1_DEV_LEFT:
case 'l':
break;
case XNUCLEO53L1A1_DEV_RIGHT:
break;
#endif
}
}
void VL53L1A1_EXTI_IOUnconfigure(int DevNo){
switch (DevNo ) {
case XNUCLEO53L1A1_DEV_CENTER:
case 'c':
//XNUCLEO53L1A1_SetIntrStateId(0,XNUCLEO53L1A1_DEV_TOP);
HAL_GPIO_DeInit(VL53L1A1_GPIO1_C_GPIO_PORT, VL53L1A1_GPIO1_C_GPIO_PIN);
__HAL_GPIO_EXTI_CLEAR_IT(VL53L1A1_GPIO1_C_GPIO_PIN);
break;
#if VL53L1A1_GPIO1_SHARED == 0
case XNUCLEO53L1A1_DEV_LEFT:
case 'l':
// XNUCLEO53L1A1_SetIntrStateId(0,XNUCLEO53L1A1_DEV_LEFT);
HAL_GPIO_DeInit(VL53L1A1_GPIO1_L_GPIO_PORT, VL53L1A1_GPIO1_L_GPIO_PIN);
__HAL_GPIO_EXTI_CLEAR_IT(VL53L1A1_GPIO1_L_GPIO_PIN);
break;
case XNUCLEO53L1A1_DEV_RIGHT:
case 'r':
HAL_GPIO_DeInit(VL53L1A1_GPIO1_R_GPIO_PORT, VL53L1A1_GPIO1_R_GPIO_PIN);
__HAL_GPIO_EXTI_CLEAR_IT(VL53L1A1_GPIO1_R_GPIO_PIN);
//XNUCLEO53L1A1_SetIntrStateId(0,XNUCLEO53L1A1_DEV_RIGHT);
break;
#endif
}
}
/**
* Set all i2c expended gpio in one go
* @return i/o operation status
*/
static int _ExpandersSetAllIO(void){
int status;
status = _ExpanderWR(I2cExpAddr0, GPSR, &CurIOVal.bytes[0], 2);
if( status ){
goto done_err;
}
status = _ExpanderWR(I2cExpAddr1, GPSR, &CurIOVal.bytes[2], 2);
done_err:
return status;
}
/**
* STMPE1600 i2c Expender register read
* @param I2cExpAddr Expender 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 int _ExpanderRd(int I2cExpAddr, int index, uint8_t *data, int n_data) {
int status;
uint8_t RegAddr;
RegAddr = index;
XNUCLEO53L1A1_GetI2cBus();
do {
status = HAL_I2C_Master_Transmit(&XNUCLEO53L1A1_hi2c, I2cExpAddr, &RegAddr, 1, 100);
if (status)
break;
status = HAL_I2C_Master_Receive(&XNUCLEO53L1A1_hi2c, I2cExpAddr, data, n_data, n_data * 100);
} while (0);
XNUCLEO53L1A1_PutI2cBus();
return status;
}
/**
* STMPE1600 i2c Expender register write
* @param I2cExpAddr Expender 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 int _ExpanderWR(int I2cExpAddr, int index, uint8_t *data, int n_data) {
int status;
uint8_t RegAddr[0x10];
RegAddr[0] = index;
memcpy(RegAddr + 1, data, n_data);
XNUCLEO53L1A1_GetI2cBus();
status = HAL_I2C_Master_Transmit(&XNUCLEO53L1A1_hi2c, I2cExpAddr, RegAddr, n_data + 1, 100);
XNUCLEO53L1A1_PutI2cBus();
return status;
}
/**
* @defgroup XNUCLEO53L1A1_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
*
* @{
*/
/** decimal point bit mapping* */
#define DP (1<<7)
//VL6180 shield
//#define S0 (1<<0)
//#define S1 (1<<1)
//#define S2 (1<<2)
//#define S3 (1<<3)
//#define S4 (1<<4)
//#define S5 (1<<5)
//#define S6 (1<<6)
/** sgement s0 bit mapping*/
#define S0 (1<<3)
/** sgement s1 bit mapping*/
#define S1 (1<<5)
/** sgement s2 bit mapping*/
#define S2 (1<<6)
/** sgement s3 bit mapping*/
#define S3 (1<<4)
/** sgement s4 bit mapping*/
#define S4 (1<<0)
/** sgement s5 bit mapping*/
#define S5 (1<<1)
/** sgement s6 bit mapping*/
#define S6 (1<<2)
/**
* 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
/** @} */
int XNUCLEO53L1A1_SetDisplayString(const char *str) {
int status;
uint32_t Segments;
int BitPos;
int i;
for( i=0; i<4 && str[i]!=0; i++){
Segments = (uint32_t)ascii_to_display_lut[(uint8_t)str[i]];
Segments =(~Segments)&0x7F;
BitPos=DisplayBitPos[i];
CurIOVal.u32 &=~(0x7F<<BitPos);
CurIOVal.u32 |= Segments<<BitPos;
}
/* clear unused digit */
for( ; i<4;i++){
BitPos=DisplayBitPos[i];
CurIOVal.u32 |=0x7F<<BitPos;
}
status = _ExpandersSetAllIO();
if( status ){
XNUCLEO53L1A1_ErrLog("Set i/o");
}
return status;
}
/**
*
* @} XNUCLEO53L1A1_top
*/