1 /********************* (C) COPYRIGHT 2007 RAISONANCE S.A.S. *******************/
\r
5 * @brief Mems Initialization and management
\r
10 * @version 1.5 various corrections reported by Ron Miller
\r
13 /******************************************************************************/
\r
15 /* Includes ------------------------------------------------------------------*/
\r
20 /* Private define ------------------------------------------------------------*/
\r
21 #define RDOUTXL 0xE8 /*!< Multiple Read from OUTXL */
\r
22 #define WRCTRL_REG1 0x20 /*!< Single Write CTRL_REG */
\r
23 #define RDCTRL_REG1 0xA0 /*!< Single Read CTRL_REG */
\r
24 #define RDID 0x8F /*!< Single Read WHO_AM_I */
\r
25 #define LOW 0x00 /*!< ChipSelect line low */
\r
26 #define HIGH 0x01 /*!< ChipSelect line high */
\r
27 #define DUMMY_BYTE 0xA5
\r
28 #define MEMS_DIVIDER 1
\r
29 #define MEMS_TESTING_DIVIDER 101
\r
31 #define DELAY_REACT 20
\r
32 #define MIN_REACT 15
\r
33 #define DIV_REACT 10
\r
34 #define GRAD_SHOCK 200000
\r
36 /* Private variables ---------------------------------------------------------*/
\r
37 tMEMS_Info MEMS_Info = {0}; // structure definition in circle.h
\r
38 int TestingActive = 0;
\r
39 int StartingFromResetOrShockCounter = 1000;
\r
40 int TimeCounterForDoubleClick = 0;
\r
41 int TimeLastShock = 0;
\r
42 static int divider = 0;
\r
43 static Rotate_H12_V_Match_TypeDef previous_Screen_Orientation;
\r
47 unsigned N_filtering = 0;
\r
55 // each coordinate (X, Y and Z) is described by 3 variables where suffix means:
\r
56 // f = flag to indicate that a move has been done. Cleared by the Ptr Manager when acknowledged.
\r
57 // i = amplitude of the move (Grad / 10)
\r
58 // t = delay to accept the counter reaction
\r
73 /* Private function prototypes -----------------------------------------------*/
\r
74 static void MEMS_ChipSelect( u8 State );
\r
75 static u8 MEMS_SendByte( u8 byte );
\r
76 static void MEMS_WriteEnable( void );
\r
77 static u32 MEMS_ReadOutXY( void );
\r
78 static void MEMS_WakeUp( void );
\r
80 /* Private functions ---------------------------------------------------------*/
\r
82 /*******************************************************************************
\r
86 *******************************************************************************/
\r
91 /******************************************************************************/
\r
92 static void MEMS_WakeUp( void )
\r
96 /* read RDCTRL_REG1 */
\r
98 /* Chip Select low */
\r
99 MEMS_ChipSelect( LOW );
\r
101 /* Send "RDCTRL_REG1" instruction */
\r
102 MEMS_SendByte( RDCTRL_REG1 );
\r
104 reg_val = MEMS_SendByte( DUMMY_BYTE );
\r
106 /* Chip Select high */
\r
107 MEMS_ChipSelect( HIGH );
\r
109 /* SET P0:P1 to '11' */
\r
110 /* 0xC0 to wake up and 0x30 for full speed frequency (640 Hz). */
\r
111 reg_val = reg_val | 0xC0 | 0x30;
\r
113 /* Chip Select low */
\r
114 MEMS_ChipSelect( LOW );
\r
116 /* Send "WRCTRL_REG1" instruction */
\r
117 MEMS_SendByte( WRCTRL_REG1 );
\r
118 MEMS_SendByte( reg_val );
\r
120 /* Chip Select high */
\r
121 MEMS_ChipSelect( HIGH );
\r
124 /*******************************************************************************
\r
128 *******************************************************************************/
\r
130 * Reads X and Y Out.
\r
132 * @return An unsigned 32 bit word with the highest 16 bits containing the Y
\r
133 * and the lowest 16 bits the X.
\r
136 /******************************************************************************/
\r
137 static u32 MEMS_ReadOutXY( void )
\r
146 /* Chip Select low */
\r
147 MEMS_ChipSelect( LOW );
\r
149 /* Send "RDOUTXL" instruction */
\r
150 MEMS_SendByte( RDOUTXL );
\r
153 OutXL = MEMS_SendByte( DUMMY_BYTE );
\r
156 OutXH = MEMS_SendByte( DUMMY_BYTE );
\r
159 OutYL = MEMS_SendByte( DUMMY_BYTE );
\r
162 OutYH = MEMS_SendByte( DUMMY_BYTE );
\r
165 OutZL = MEMS_SendByte( DUMMY_BYTE );
\r
168 OutZH = MEMS_SendByte( DUMMY_BYTE );
\r
170 MEMS_Info.OutX = OutXL + ( OutXH << 8 );
\r
171 MEMS_Info.OutY = OutYL + ( OutYH << 8 );
\r
172 MEMS_Info.OutZ = OutZL + ( OutZH << 8 );
\r
174 /* Chip Select high */
\r
175 MEMS_ChipSelect( HIGH );
\r
177 MEMS_Info.OutX_F4 += ( MEMS_Info.OutX - ( MEMS_Info.OutX_F4 >> 2 ) ); // Filter on 4 values.
\r
178 MEMS_Info.OutY_F4 += ( MEMS_Info.OutY - ( MEMS_Info.OutY_F4 >> 2 ) ); // Filter on 4 values.
\r
179 MEMS_Info.OutZ_F4 += ( MEMS_Info.OutZ - ( MEMS_Info.OutZ_F4 >> 2 ) ); // Filter on 4 values.
\r
181 MEMS_Info.OutX_F16 += ( MEMS_Info.OutX - ( MEMS_Info.OutX_F16 >> 4 ) ); // Filter on 16 values.
\r
182 MEMS_Info.OutY_F16 += ( MEMS_Info.OutY - ( MEMS_Info.OutY_F16 >> 4 ) ); // Filter on 16 values.
\r
183 MEMS_Info.OutZ_F16 += ( MEMS_Info.OutZ - ( MEMS_Info.OutZ_F16 >> 4 ) ); // Filter on 16 values.
\r
185 MEMS_Info.OutX_F64 += ( MEMS_Info.OutX - ( MEMS_Info.OutX_F64 >> 6 ) ); // Filter on 64 values.
\r
186 MEMS_Info.OutY_F64 += ( MEMS_Info.OutY - ( MEMS_Info.OutY_F64 >> 6 ) ); // Filter on 64 values.
\r
187 MEMS_Info.OutZ_F64 += ( MEMS_Info.OutZ - ( MEMS_Info.OutZ_F64 >> 6 ) ); // Filter on 64 values.
\r
189 MEMS_Info.OutX_F256 += ( MEMS_Info.OutX - ( MEMS_Info.OutX_F256 >> 8) ); // Filter on 256 values.
\r
190 MEMS_Info.OutY_F256 += ( MEMS_Info.OutY - ( MEMS_Info.OutY_F256 >> 8) ); // Filter on 256 values.
\r
191 MEMS_Info.OutZ_F256 += ( MEMS_Info.OutZ - ( MEMS_Info.OutZ_F256 >> 8) ); // Filter on 256 values.
\r
193 if( N_filtering < 256 )
\r
195 // Just to validate the calculated average values.
\r
199 return ( MEMS_Info.OutX + ( MEMS_Info.OutY << 16 ) );
\r
202 /*******************************************************************************
\r
206 *******************************************************************************/
\r
208 * Selects or deselects the MEMS device.
\r
210 * @param[in] State Level to be applied on ChipSelect pin.
\r
213 /******************************************************************************/
\r
214 static void MEMS_ChipSelect( u8 State )
\r
216 /* Set High or low the chip select line on PA.4 pin */
\r
217 GPIO_WriteBit( GPIOD, GPIO_Pin_2, (BitAction)State );
\r
220 /*******************************************************************************
\r
224 *******************************************************************************/
\r
226 * Sends a byte through the SPI interface and return the byte received from
\r
229 * @param[in] byte The byte to send to the SPI interface.
\r
231 * @return The byte returned by the SPI bus.
\r
234 /******************************************************************************/
\r
235 static u8 MEMS_SendByte( u8 byte )
\r
237 /* Loop while DR register in not emplty */
\r
238 while( SPI_I2S_GetFlagStatus( SPI2, SPI_I2S_FLAG_TXE ) == RESET );
\r
240 /* Send byte through the SPI2 peripheral */
\r
241 SPI_I2S_SendData( SPI2, byte );
\r
243 /* Wait to receive a byte */
\r
244 while( SPI_I2S_GetFlagStatus( SPI2, SPI_I2S_FLAG_RXNE ) == RESET );
\r
246 /* Return the byte read from the SPI bus */
\r
247 return SPI_I2S_ReceiveData( SPI2 );
\r
250 /* Public functions for CircleOS ---------------------------------------------*/
\r
252 /*******************************************************************************
\r
256 *******************************************************************************/
\r
259 * Initializes the peripherals used by the SPI MEMS driver.
\r
261 * @attention This function must <b>NOT</b> be called by the user.
\r
264 /******************************************************************************/
\r
265 void MEMS_Init(void)
\r
267 SPI_InitTypeDef SPI_InitStructure;
\r
268 GPIO_InitTypeDef GPIO_InitStructure;
\r
270 /* Configure PC6 and PC7 as Output push-pull For MEMS*/
\r
271 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
\r
272 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
\r
273 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
\r
275 GPIO_Init( GPIOC, &GPIO_InitStructure );
\r
277 /* Enable SPI2 and GPIOA clocks */
\r
278 RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );
\r
279 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
\r
280 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOD, ENABLE );
\r
282 /* Configure SPI2 pins: SCK, MISO and MOSI */
\r
283 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
\r
284 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
\r
285 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
\r
287 GPIO_Init( GPIOB, &GPIO_InitStructure );
\r
289 /* Configure PD2 as Output push-pull, used as MEMS Chip select */
\r
290 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
\r
291 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
\r
292 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
\r
294 GPIO_Init( GPIOD, &GPIO_InitStructure );
\r
296 /* SPI2 configuration */
\r
297 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
\r
298 SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
\r
299 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
\r
300 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
\r
301 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
\r
302 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
\r
303 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
\r
304 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
\r
305 SPI_InitStructure.SPI_CRCPolynomial = 7;
\r
307 SPI_Init( SPI2, &SPI_InitStructure );
\r
310 SPI_Cmd( SPI2, ENABLE );
\r
312 if( MEMS_ReadID() != 0x3A )
\r
316 // Try to resynchronize
\r
317 for( i = 0 ; i < 17 ; i++ )
\r
319 /* Configure SPI2 pins: SCK, MISO and MOSI */
\r
320 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
\r
321 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
\r
322 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
\r
324 GPIO_Init( GPIOB, &GPIO_InitStructure );
\r
325 GPIO_WriteBit( GPIOB, GPIO_Pin_15, HIGH );
\r
326 MEMS_ChipSelect( LOW );
\r
328 GPIO_WriteBit( GPIOB, GPIO_Pin_13, LOW );
\r
329 GPIO_WriteBit( GPIOB, GPIO_Pin_13, HIGH );
\r
330 MEMS_ChipSelect( HIGH );
\r
332 /* Configure again PB. SCK as SPI2 pin */
\r
333 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
\r
334 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
\r
335 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
\r
337 GPIO_Init( GPIOB, &GPIO_InitStructure );
\r
338 if ( MEMS_ReadID() == 0x3A )
\r
346 DRAW_DisplayString( 1, 50, "Test MEM ID Failed", 17 );
\r
350 /* Read for the first time */
\r
355 MEMS_Info.OutX_F4 = MEMS_Info.OutX_F16 = MEMS_Info.OutX_F64 = MEMS_Info.OutX_F256 = MEMS_Info.OutX;
\r
356 MEMS_Info.OutY_F4 = MEMS_Info.OutY_F16 = MEMS_Info.OutY_F64 = MEMS_Info.OutY_F256 = MEMS_Info.OutY;
\r
357 MEMS_Info.OutZ_F4 = MEMS_Info.OutZ_F16 = MEMS_Info.OutZ_F64 = MEMS_Info.OutZ_F256 = MEMS_Info.OutZ;
\r
360 MEMS_GetPosition( &XInit, &YInit );
\r
366 /*******************************************************************************
\r
370 *******************************************************************************/
\r
373 * Called by the CircleOS scheduler to manage the MEMS. The Circle beeps if the
\r
376 * @attention This function must <b>NOT</b> be called by the user.
\r
379 /******************************************************************************/
\r
380 void MEMS_Handler( void )
\r
386 if( StartingFromResetOrShockCounter )
\r
388 StartingFromResetOrShockCounter--;
\r
390 TimeCounterForDoubleClick++;
\r
394 // Evaluate gradients
\r
395 GradX = ( MEMS_Info.OutX_F4 >> 2 ) - MEMS_Info.OutX;
\r
396 GradY = ( MEMS_Info.OutY_F4 >> 2 ) - MEMS_Info.OutY;
\r
397 GradZ = ( MEMS_Info.OutZ_F4 >> 2 ) - MEMS_Info.OutZ;
\r
399 // Decide whether a direction is selected
\r
400 if( tMovePtrX == 0 )
\r
402 if( ( GradX > MIN_REACT ) || ( GradX < -MIN_REACT ) )
\r
404 iMovePtrX = GradX / DIV_REACT;
\r
405 tMovePtrX = DELAY_REACT;
\r
414 if( tMovePtrY == 0 )
\r
416 if( ( GradY > MIN_REACT ) || ( GradY < -MIN_REACT ) )
\r
418 iMovePtrY = GradY / DIV_REACT; //FL071012 rrm fix
\r
419 tMovePtrY = DELAY_REACT;
\r
430 if( ( GradZ > MIN_REACT ) || ( GradY < -MIN_REACT ) )
\r
432 iMovePtrZ = GradZ / DIV_REACT;
\r
433 tMovePtrZ = DELAY_REACT;
\r
442 Gradient2 = (s32)GradX * (s32)GradX + (s32)GradY * (s32)GradY + (s32)GradZ * (s32)GradZ;
\r
444 // MEMS is shocked, let's beep!
\r
445 if( ( Gradient2 > GRAD_SHOCK ) && ( BUZZER_GetMode() == BUZZER_OFF ) && ( StartingFromResetOrShockCounter == 0 ) )
\r
447 MEMS_Info.Shocked++;
\r
449 Suggested by Bob Seabrook: a further posiblity is to increment Shocked rather than just setting it
\r
450 So it can still be tested for non zero as before but one can get more
\r
451 info from the int without extra cost. */
\r
453 #define DELAY_BETWEEN_TWO_SHOCK 20
\r
454 #define MAX_DELAY_FOR_DOUBLECLICK 150
\r
455 StartingFromResetOrShockCounter = DELAY_BETWEEN_TWO_SHOCK; //< filter: short delay before detecting the next shock
\r
456 if ( (TimeCounterForDoubleClick - TimeLastShock) < MAX_DELAY_FOR_DOUBLECLICK )
\r
458 MEMS_Info.DoubleClick++;
\r
463 TimeLastShock = TimeCounterForDoubleClick;
\r
465 BUZZER_SetMode( BUZZER_SHORTBEEP );
\r
469 /*******************************************************************************
\r
473 *******************************************************************************/
\r
475 * Reads SPI chip identification.
\r
477 * @return The SPI chip identification.
\r
480 /******************************************************************************/
\r
481 u8 MEMS_ReadID( void )
\r
485 /* Chip Select low */
\r
486 MEMS_ChipSelect( LOW );
\r
488 /* Send "RDID" instruction */
\r
489 MEMS_SendByte( RDID );
\r
491 /* Read a byte from the MEMS */
\r
492 Temp = MEMS_SendByte( DUMMY_BYTE );
\r
494 /* Chip Select low */
\r
495 MEMS_ChipSelect( HIGH );
\r
502 /* Public functions ----------------------------------------------------------*/
\r
504 /*******************************************************************************
\r
508 *******************************************************************************/
\r
511 * Returns the current (relative) position of the Primer.
\r
512 * Only X-Y axis are considered here.
\r
514 * @param[out] pX Current horizontal coordinate.
\r
515 * @param[out] pY Current vertical coordinate.
\r
517 * @warning The (0x0) point in on the low left corner.
\r
518 * @note For absolute position information use MEMS_GetInfo()
\r
521 /******************************************************************************/
\r
522 void MEMS_GetPosition( s16* pX, s16* pY )
\r
524 *pX = MEMS_Info.OutX - XInit;
\r
525 *pY = MEMS_Info.OutY - YInit;
\r
528 /*******************************************************************************
\r
532 *******************************************************************************/
\r
535 * Returns current screen orientation.
\r
537 * @param[out] pH12 Current screen orientation.
\r
540 /******************************************************************************/
\r
541 void MEMS_GetRotation( Rotate_H12_V_Match_TypeDef* pH12 )
\r
543 s16 sX = MEMS_Info.OutX;
\r
544 s16 sY = MEMS_Info.OutY;
\r
546 if( ( ( sX <= -MARGIN ) && ( sY <= 0 ) && (sX<=sY ) ) ||
\r
547 ( ( sX <=- MARGIN ) && ( sY > 0) && (sX <= (-sY ) ) ) )
\r
549 // 1st case: x<0, |x|>y => H12 = V9
\r
552 else if( ( ( sY <= -MARGIN ) && ( sX <= 0 ) && ( sY <= sX ) ) ||
\r
553 ( ( sY <= -MARGIN ) && ( sX > 0 ) && ( sY <= (-sX ) ) ) )
\r
555 // 2nd case: y<0, |y|>x => H12 = V12
\r
558 else if( ( ( sX >= MARGIN ) && ( sY <= 0 ) && ( sX >= (-sY) ) ) ||
\r
559 ( ( sX >= MARGIN ) && ( sY > 0 ) && ( sX >= sY ) ) )
\r
561 // 3rd case: x>0, |x|>y => H12=V3
\r
564 else if( ( ( sY >= MARGIN ) && ( sX <= 0 ) && ( sY >= (-sX ) ) ) ||
\r
565 ( ( sY >= MARGIN ) && ( sX > 0 ) && ( sY >= sX ) ) )
\r
567 // 4th case: y>0, |y|>x => H12=V6
\r
572 /*******************************************************************************
\r
576 *******************************************************************************/
\r
579 * Set current position as "neutral position".
\r
582 /******************************************************************************/
\r
583 void MEMS_SetNeutral( void )
\r
585 // Set Neutral position.
\r
586 MEMS_GetPosition( &XInit, &YInit );
\r
589 /*******************************************************************************
\r
593 *******************************************************************************/
\r
596 * Return the current MEMS information (state, absolute position...).
\r
598 * @return a pointer to tMEMS_Info
\r
601 /******************************************************************************/
\r
602 tMEMS_Info* MEMS_GetInfo( void )
\r