/* * @file x-nucleo-6180xa1.c * $Date: 2015-10-21 16:55:39 +0200 (Wed, 21 Oct 2015) $ * $Revision: 2560 $ */ #include /** Configure VL6180 pins to Output */ #include "stm32xxx_hal.h" #ifndef HAL_I2C_MODULE_ENABLED #define HAL_I2C_MODULE_ENABLED #pragma message("hal conf should enable i2c") #endif #include "x-nucleo-6180xa1.h" #define V1_CHIP_SWITCH_PORT GPIOA #define V1_CHIP_SWITCH_PIN GPIO_PIN_7 #define V1_CHIP_ENABLE_PORT GPIOA #define V1_CHIP_ENABLE_PIN GPIO_PIN_5 #define V1_IRQ_PIN GPIO_PIN_6 #define V2_IRQ_PIN GPIO_PIN_0 #ifdef __STM32F4xx_HAL_H #define V1_IRQ EXTI9_5_IRQn #define V2_IRQ EXTI0_IRQn #else #ifdef __STM32L4xx_HAL_H #define V1_IRQ EXTI9_5_IRQn #define V2_IRQ EXTI0_IRQn #else #define V1_IRQ EXTI4_15_IRQn #define V2_IRQ EXTI0_1_IRQn #endif #endif /* local private prototypes */ static void _V1_GPIO_Init(void); static void _V1_Set7SegmentDP( int dp); static void _V1_Set7Segment(uint8_t value); static void _V1_DisplayOff(void); static void _V2_GPIO_Init(void); static int _V2_GetSwicth(void); #define EXPANDER_I2C_ADDRESS (0x42*2) #define GPMR 0x10 #define GPSR 0x12 #define GPDR 0x14 #define V2_D1 (1<<7) // second byte or word MSB #define V2_D2 (1<<8) #define V2_D3 (1<<9) #define V2_D4 (1<<10) #define V2_DISP_SEL (1<<11) #define V2_CHIPEN (1<<12) #define V2_CHIPEN_B (1<<13) #define V2_CHIPEN_L (1<<14) #define V2_CHIPEN_R (1<<15) static int IsV2=0; I2C_HandleTypeDef *_hi2c; uint16_t _V2PadVal; /* gpio SR value caching to avoid reading on each new bit set */ static int I2cExpAddr=EXPANDER_I2C_ADDRESS; static int _err=0; static void _V2_DisplayOff(void); static void _V2_SetChipEn( int No, int state ); int XNUCLEO6180XA1_IsV2(void){ return IsV2; } int XNUCLEO6180XA1_EXTI_CallBackHandle(uint16_t GPIO_Pin){ int IsVL6180XIntPin = IsV2 ? GPIO_Pin == V2_IRQ_PIN : GPIO_Pin == V1_IRQ_PIN; if( IsVL6180XIntPin){ XNUCLEO6180XA1_UserIntHandler(); } return IsVL6180XIntPin; } void XNUCLEO6180XA1_Reset(int state){ if( !IsV2) HAL_GPIO_WritePin(V1_CHIP_ENABLE_PORT, V1_CHIP_ENABLE_PIN , (GPIO_PinState)state); else{ _V2_SetChipEn(0, state); } } int XNUCLEO6180XA1_ResetId(int state, int id) { int status; if( IsV2 && id>=0 && id<4){ _V2_SetChipEn(id, state); status=0; } else{ /* Error case */ status=-1; } return status; } int XNUCLEO6180XA1_GetSwitch(void){ GPIO_PinState state ; if( !IsV2) state = HAL_GPIO_ReadPin(V1_CHIP_SWITCH_PORT, V1_CHIP_SWITCH_PIN); else{ state= _V2_GetSwicth() ? GPIO_PIN_SET : GPIO_PIN_RESET ; } return state; } /* public API */ void XNUCLEO6180XA1_GPIO_Init(void) { // this must be done after i2c init _V1_GPIO_Init(); } int V2_ExpanderRd(int index, uint8_t *data, int n_data){ int status; uint8_t RegAddr; RegAddr=index; do{ status=HAL_I2C_Master_Transmit(_hi2c, I2cExpAddr, &RegAddr, 1, 100); if( status ) break; status =HAL_I2C_Master_Receive(_hi2c, I2cExpAddr, data, n_data, n_data*100); }while(0); return status; } int V2_ExpanderWR( int index, uint8_t *data, int n_data){ int status; uint8_t RegAddr[0x10]; RegAddr[0]=index; memcpy(RegAddr+1, data, n_data); status=HAL_I2C_Master_Transmit(_hi2c, I2cExpAddr, RegAddr, n_data+1, 100); return status; } /** * * @param No 0= top , 1= Left, 2=Bottom 3=Right * @param state */ static void _V2_SetChipEn( int No, int state ){ //int mask = V2_CHIPEN << No; int mask = (No==3) ? V2_CHIPEN_R : ((No==2) ? V2_CHIPEN_B : ((No==1) ? V2_CHIPEN_L : V2_CHIPEN)); if( state) _V2PadVal|=mask ; else _V2PadVal&=~mask; V2_ExpanderWR(GPSR, (uint8_t*)&_V2PadVal,2); } int _V2_GetSwicth(){ int status; uint16_t Value; status=V2_ExpanderRd(GPMR, (uint8_t*)&Value,2); if(status ==0 ){ Value&=V2_DISP_SEL; } else{ //TODO ("VL6180x Expander rd fail"); _err++; Value=0; } return Value; } static void _I2cFailRecover(){ GPIO_InitTypeDef GPIO_InitStruct; int i, nRetry=0; // We may get reset after in middle of an i2c access (h/w reset button use or debug load) // hence some agent on bus may be in middle of a a transaction and will create issue or even prevent starting (SDA is low) // 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 will end up seeing a "stop" end bus we back to an known idle i2c bus state // Enable I/O __GPIOB_CLK_ENABLE(); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9 ; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOB, &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(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); HAL_Delay(1); } // if( HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9) == 0 ){ // static int RetryRecover; // RetryRecover++; // } }while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9) == 0 && nRetry++<7); if( HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9) == 0 ){ __GPIOA_CLK_ENABLE(); //We are still in bad i2c state warm user by blinking led but stay here GPIO_InitStruct.Pin = GPIO_PIN_5 ; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); do{ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); HAL_Delay(33); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); HAL_Delay(33); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); HAL_Delay(33); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); HAL_Delay(33*20); }while(1); } } /** * F401 valid may not work for over device */ void XNUCLEO6180XA1_I2C1_Init(I2C_HandleTypeDef *hi2c1) { GPIO_InitTypeDef GPIO_InitStruct; uint8_t ExpanderID[2]; int status; _I2cFailRecover(); /* Peripheral clock enable */ __GPIOB_CLK_ENABLE(); __I2C1_CLK_ENABLE(); /**I2C1 GPIO Configuration PB8 ------> I2C1_SCL PB9 ------> I2C1_SDA */ GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_LOW; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); hi2c1->Instance = I2C1; #ifdef __STM32F4xx_HAL_H hi2c1->Init.ClockSpeed = 400000; hi2c1->Init.DutyCycle = I2C_DUTYCYCLE_2; #else hi2c1->Init.Timing = 0x00300F38; /* set 400KHz fast mode i2c*/ #endif hi2c1->Init.OwnAddress1 = 0; hi2c1->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1->Init.DualAddressMode = I2C_DUALADDRESS_DISABLED; hi2c1->Init.OwnAddress2 = 0; hi2c1->Init.GeneralCallMode = I2C_GENERALCALL_DISABLED; hi2c1->Init.NoStretchMode = I2C_NOSTRETCH_DISABLED; HAL_I2C_Init(hi2c1); /* try to detect v1 v2 by reading i2c expander register * if rd i2c fail assume it is a v1 shield it may be a v2 with an i2c bus hangs (happen some time)*/ _hi2c=hi2c1; status = V2_ExpanderRd( 0, ExpanderID, 2); if( status == 0 && ExpanderID[0]==0 && ExpanderID[1]==0x16){ IsV2=1; _V2_GPIO_Init(); } else{ _V1_GPIO_Init(); } } /** * @brief Default do nothing interrupt callback */ #pragma weak XNUCLEO6180XA1_UserIntHandler void XNUCLEO6180XA1_UserIntHandler(void) { } void XNUCLEO6180XA1_ClearInterrupt(void){ if( !IsV2 ) NVIC_ClearPendingIRQ(V1_IRQ); else NVIC_ClearPendingIRQ(V2_IRQ); } void XNUCLEO6180XA1_DisableInterrupt(void){ if( !IsV2){ HAL_NVIC_DisableIRQ(V1_IRQ); __HAL_GPIO_EXTI_CLEAR_IT(V1_IRQ_PIN); NVIC_ClearPendingIRQ(V1_IRQ); } else{ HAL_NVIC_DisableIRQ(V2_IRQ); __HAL_GPIO_EXTI_CLEAR_IT(V2_IRQ_PIN); NVIC_ClearPendingIRQ(V2_IRQ); } } void XNUCLEO6180XA1_EnableInterrupt(void){ if( !IsV2 ){ __HAL_GPIO_EXTI_CLEAR_IT(V1_IRQ_PIN); NVIC_ClearPendingIRQ(V1_IRQ); HAL_NVIC_EnableIRQ(V1_IRQ); } else{ __HAL_GPIO_EXTI_CLEAR_IT(V2_IRQ_PIN); NVIC_ClearPendingIRQ(V2_IRQ); HAL_NVIC_EnableIRQ(V2_IRQ); } } /** * Display digit enable pad/pin LUT * D1 is left most digit (ms nibble) on the display * D3 is right most digit (ls nibble) on the display */ #define PA 0 #define PB 1 #define PC 2 #define MAKE_PAD(port, pin) (((port)<<4)|pin) /* digit enable pad/pin LUT */ static uint8_t DisplayEn[] = { MAKE_PAD( PB, 0), /* D1 0 */ MAKE_PAD( PA, 4), /* D2 1 */ MAKE_PAD( PA, 1), /* D3 2 */ MAKE_PAD( PA, 0), /* D4 3 */ }; /* 7 segment code to pad/pin LUT */ static uint8_t DisplaySeg[] = { MAKE_PAD( PB, 6 ), /* 0 A */ MAKE_PAD( PC, 7 ), /* 1 B */ MAKE_PAD( PA, 9 ), /* 2 C */ MAKE_PAD( PA, 8 ), /* 3 D */ MAKE_PAD( PB, 10), /* 4 E */ MAKE_PAD( PB, 4 ), /* 5 F */ MAKE_PAD( PB, 5 ), /* 6 G */ MAKE_PAD( PB, 3 ), /* 7 DP */ /* Note that PB3 these is conflicting wit jtag/SWO used for trace * activating PB3 as ooutput will prevent trace to work */ }; #undef MAKE_PAD #undef PA #undef PB #undef PC #define pad_write(pad, state) HAL_GPIO_WritePin( (GPIO_TypeDef*)((char*)GPIOA + (pad>>4)*((char*)GPIOB-(char*)GPIOA)), 1<<(pad&0x0F), state); /* --s0-- s s 5 1 --s6-- s s 4 2 --s3-- s7 */ #define DP (1<<7) #define NOT_7_NO_DP( ... ) (uint8_t) ~( __VA_ARGS__ + DP ) #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) /* refer to http://www.twyman.org.uk/Fonts/ */ 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']= S0+S1+S2+S5+S6, // sames as 'q' ['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 static void _V1_DisplayString(const char *str, int SegDelayMs){ int i; const char *pc; for( i=0, pc=str; i<4 && *pc!=0 ; i++, pc++){ _V1_Set7Segment( ascii_to_display_lut[(uint8_t)*pc]); if( *(pc+1)== '.'){ _V1_Set7SegmentDP(1); pc++; } /* digit on */ pad_write(DisplayEn[i], GPIO_PIN_RESET); /* activate led */ XNUCLEO6180XA1_WaitMilliSec(SegDelayMs); /* digit off */ pad_write(DisplayEn[i], GPIO_PIN_SET); /* turn off led */ } } static void _V2_Set7Segment( int Leds, int digit ){ _V2PadVal |= 0x7F; /* clear 7 seg bits */ _V2PadVal |= V2_D1|V2_D2|V2_D3|V2_D4; /* all segment off */ _V2PadVal &= ~(V2_D1<