/********************* (C) COPYRIGHT 2007 RAISONANCE S.A.S. *******************/ | |
/** | |
* | |
* @file mems.c | |
* @brief Mems Initialization and management | |
* @author FL | |
* @date 07/2007 | |
* @version 1.1 | |
* @date 10/2007 | |
* @version 1.5 various corrections reported by Ron Miller | |
* | |
**/ | |
/******************************************************************************/ | |
/* Includes ------------------------------------------------------------------*/ | |
#include "circle.h" | |
/// @cond Internal | |
/* Private define ------------------------------------------------------------*/ | |
#define RDOUTXL 0xE8 /*!< Multiple Read from OUTXL */ | |
#define WRCTRL_REG1 0x20 /*!< Single Write CTRL_REG */ | |
#define RDCTRL_REG1 0xA0 /*!< Single Read CTRL_REG */ | |
#define RDID 0x8F /*!< Single Read WHO_AM_I */ | |
#define LOW 0x00 /*!< ChipSelect line low */ | |
#define HIGH 0x01 /*!< ChipSelect line high */ | |
#define DUMMY_BYTE 0xA5 | |
#define MEMS_DIVIDER 1 | |
#define MEMS_TESTING_DIVIDER 101 | |
#define MARGIN 500 | |
#define DELAY_REACT 20 | |
#define MIN_REACT 15 | |
#define DIV_REACT 10 | |
#define GRAD_SHOCK 200000 | |
/* Private variables ---------------------------------------------------------*/ | |
tMEMS_Info MEMS_Info = {0}; // structure definition in circle.h | |
int TestingActive = 0; | |
int StartingFromResetOrShockCounter = 1000; | |
int TimeCounterForDoubleClick = 0; | |
int TimeLastShock = 0; | |
static int divider = 0; | |
static Rotate_H12_V_Match_TypeDef previous_Screen_Orientation; | |
u32 Gradient2; | |
//Filtering | |
unsigned N_filtering = 0; | |
//Gradient | |
s16 GradX = 0; | |
s16 GradY = 0; | |
s16 GradZ = 0; | |
// Pointer move: | |
// each coordinate (X, Y and Z) is described by 3 variables where suffix means: | |
// f = flag to indicate that a move has been done. Cleared by the Ptr Manager when acknowledged. | |
// i = amplitude of the move (Grad / 10) | |
// t = delay to accept the counter reaction | |
int fMovePtrX; | |
int iMovePtrX; | |
int tMovePtrX; | |
int fMovePtrY; | |
int iMovePtrY; | |
int tMovePtrY; | |
int fMovePtrZ; | |
int iMovePtrZ; | |
int tMovePtrZ; | |
s16 XInit = 0; | |
s16 YInit = 0; | |
s16 ZInit = 0; | |
/* Private function prototypes -----------------------------------------------*/ | |
static void MEMS_ChipSelect( u8 State ); | |
static u8 MEMS_SendByte( u8 byte ); | |
static void MEMS_WriteEnable( void ); | |
static u32 MEMS_ReadOutXY( void ); | |
static void MEMS_WakeUp( void ); | |
/* Private functions ---------------------------------------------------------*/ | |
/******************************************************************************* | |
* | |
* MEMS_WakeUp | |
* | |
*******************************************************************************/ | |
/** | |
* Wake Up Mems. | |
* | |
**/ | |
/******************************************************************************/ | |
static void MEMS_WakeUp( void ) | |
{ | |
u8 reg_val; | |
/* read RDCTRL_REG1 */ | |
/* Chip Select low */ | |
MEMS_ChipSelect( LOW ); | |
/* Send "RDCTRL_REG1" instruction */ | |
MEMS_SendByte( RDCTRL_REG1 ); | |
reg_val = MEMS_SendByte( DUMMY_BYTE ); | |
/* Chip Select high */ | |
MEMS_ChipSelect( HIGH ); | |
/* SET P0:P1 to '11' */ | |
/* 0xC0 to wake up and 0x30 for full speed frequency (640 Hz). */ | |
reg_val = reg_val | 0xC0 | 0x30; | |
/* Chip Select low */ | |
MEMS_ChipSelect( LOW ); | |
/* Send "WRCTRL_REG1" instruction */ | |
MEMS_SendByte( WRCTRL_REG1 ); | |
MEMS_SendByte( reg_val ); | |
/* Chip Select high */ | |
MEMS_ChipSelect( HIGH ); | |
} | |
/******************************************************************************* | |
* | |
* MEMS_ReadOutXY | |
* | |
*******************************************************************************/ | |
/** | |
* Reads X and Y Out. | |
* | |
* @return An unsigned 32 bit word with the highest 16 bits containing the Y | |
* and the lowest 16 bits the X. | |
* | |
**/ | |
/******************************************************************************/ | |
static u32 MEMS_ReadOutXY( void ) | |
{ | |
u8 OutXL; | |
u8 OutXH; | |
u8 OutYL; | |
u8 OutYH; | |
u8 OutZL; | |
u8 OutZH; | |
/* Chip Select low */ | |
MEMS_ChipSelect( LOW ); | |
/* Send "RDOUTXL" instruction */ | |
MEMS_SendByte( RDOUTXL ); | |
/* Read a byte */ | |
OutXL = MEMS_SendByte( DUMMY_BYTE ); | |
/* Read a byte */ | |
OutXH = MEMS_SendByte( DUMMY_BYTE ); | |
/* Read a byte */ | |
OutYL = MEMS_SendByte( DUMMY_BYTE ); | |
/* Read a byte */ | |
OutYH = MEMS_SendByte( DUMMY_BYTE ); | |
/* Read a byte */ | |
OutZL = MEMS_SendByte( DUMMY_BYTE ); | |
/* Read a byte */ | |
OutZH = MEMS_SendByte( DUMMY_BYTE ); | |
MEMS_Info.OutX = OutXL + ( OutXH << 8 ); | |
MEMS_Info.OutY = OutYL + ( OutYH << 8 ); | |
MEMS_Info.OutZ = OutZL + ( OutZH << 8 ); | |
/* Chip Select high */ | |
MEMS_ChipSelect( HIGH ); | |
MEMS_Info.OutX_F4 += ( MEMS_Info.OutX - ( MEMS_Info.OutX_F4 >> 2 ) ); // Filter on 4 values. | |
MEMS_Info.OutY_F4 += ( MEMS_Info.OutY - ( MEMS_Info.OutY_F4 >> 2 ) ); // Filter on 4 values. | |
MEMS_Info.OutZ_F4 += ( MEMS_Info.OutZ - ( MEMS_Info.OutZ_F4 >> 2 ) ); // Filter on 4 values. | |
MEMS_Info.OutX_F16 += ( MEMS_Info.OutX - ( MEMS_Info.OutX_F16 >> 4 ) ); // Filter on 16 values. | |
MEMS_Info.OutY_F16 += ( MEMS_Info.OutY - ( MEMS_Info.OutY_F16 >> 4 ) ); // Filter on 16 values. | |
MEMS_Info.OutZ_F16 += ( MEMS_Info.OutZ - ( MEMS_Info.OutZ_F16 >> 4 ) ); // Filter on 16 values. | |
MEMS_Info.OutX_F64 += ( MEMS_Info.OutX - ( MEMS_Info.OutX_F64 >> 6 ) ); // Filter on 64 values. | |
MEMS_Info.OutY_F64 += ( MEMS_Info.OutY - ( MEMS_Info.OutY_F64 >> 6 ) ); // Filter on 64 values. | |
MEMS_Info.OutZ_F64 += ( MEMS_Info.OutZ - ( MEMS_Info.OutZ_F64 >> 6 ) ); // Filter on 64 values. | |
MEMS_Info.OutX_F256 += ( MEMS_Info.OutX - ( MEMS_Info.OutX_F256 >> 8) ); // Filter on 256 values. | |
MEMS_Info.OutY_F256 += ( MEMS_Info.OutY - ( MEMS_Info.OutY_F256 >> 8) ); // Filter on 256 values. | |
MEMS_Info.OutZ_F256 += ( MEMS_Info.OutZ - ( MEMS_Info.OutZ_F256 >> 8) ); // Filter on 256 values. | |
if( N_filtering < 256 ) | |
{ | |
// Just to validate the calculated average values. | |
N_filtering++; | |
} | |
return ( MEMS_Info.OutX + ( MEMS_Info.OutY << 16 ) ); | |
} | |
/******************************************************************************* | |
* | |
* MEMS_ChipSelect | |
* | |
*******************************************************************************/ | |
/** | |
* Selects or deselects the MEMS device. | |
* | |
* @param[in] State Level to be applied on ChipSelect pin. | |
* | |
**/ | |
/******************************************************************************/ | |
static void MEMS_ChipSelect( u8 State ) | |
{ | |
/* Set High or low the chip select line on PA.4 pin */ | |
GPIO_WriteBit( GPIOD, GPIO_Pin_2, (BitAction)State ); | |
} | |
/******************************************************************************* | |
* | |
* MEMS_SendByte | |
* | |
*******************************************************************************/ | |
/** | |
* Sends a byte through the SPI interface and return the byte received from | |
* the SPI bus. | |
* | |
* @param[in] byte The byte to send to the SPI interface. | |
* | |
* @return The byte returned by the SPI bus. | |
* | |
**/ | |
/******************************************************************************/ | |
static u8 MEMS_SendByte( u8 byte ) | |
{ | |
/* Loop while DR register in not emplty */ | |
while( SPI_I2S_GetFlagStatus( SPI2, SPI_I2S_FLAG_TXE ) == RESET ); | |
/* Send byte through the SPI2 peripheral */ | |
SPI_I2S_SendData( SPI2, byte ); | |
/* Wait to receive a byte */ | |
while( SPI_I2S_GetFlagStatus( SPI2, SPI_I2S_FLAG_RXNE ) == RESET ); | |
/* Return the byte read from the SPI bus */ | |
return SPI_I2S_ReceiveData( SPI2 ); | |
} | |
/* Public functions for CircleOS ---------------------------------------------*/ | |
/******************************************************************************* | |
* | |
* MEMS_Init | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Initializes the peripherals used by the SPI MEMS driver. | |
* | |
* @attention This function must <b>NOT</b> be called by the user. | |
* | |
**/ | |
/******************************************************************************/ | |
void MEMS_Init(void) | |
{ | |
SPI_InitTypeDef SPI_InitStructure; | |
GPIO_InitTypeDef GPIO_InitStructure; | |
/* Configure PC6 and PC7 as Output push-pull For MEMS*/ | |
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; | |
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; | |
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; | |
GPIO_Init( GPIOC, &GPIO_InitStructure ); | |
/* Enable SPI2 and GPIOA clocks */ | |
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE ); | |
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); | |
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOD, ENABLE ); | |
/* Configure SPI2 pins: SCK, MISO and MOSI */ | |
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; | |
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; | |
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; | |
GPIO_Init( GPIOB, &GPIO_InitStructure ); | |
/* Configure PD2 as Output push-pull, used as MEMS Chip select */ | |
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; | |
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; | |
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; | |
GPIO_Init( GPIOD, &GPIO_InitStructure ); | |
/* SPI2 configuration */ | |
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; | |
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; | |
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; | |
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; | |
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; | |
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; | |
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; | |
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; | |
SPI_InitStructure.SPI_CRCPolynomial = 7; | |
SPI_Init( SPI2, &SPI_InitStructure ); | |
/* Enable SPI2 */ | |
SPI_Cmd( SPI2, ENABLE ); | |
if( MEMS_ReadID() != 0x3A ) | |
{ | |
int i; | |
// Try to resynchronize | |
for( i = 0 ; i < 17 ; i++ ) | |
{ | |
/* Configure SPI2 pins: SCK, MISO and MOSI */ | |
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15; | |
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; | |
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; | |
GPIO_Init( GPIOB, &GPIO_InitStructure ); | |
GPIO_WriteBit( GPIOB, GPIO_Pin_15, HIGH ); | |
MEMS_ChipSelect( LOW ); | |
GPIO_WriteBit( GPIOB, GPIO_Pin_13, LOW ); | |
GPIO_WriteBit( GPIOB, GPIO_Pin_13, HIGH ); | |
MEMS_ChipSelect( HIGH ); | |
/* Configure again PB. SCK as SPI2 pin */ | |
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15; | |
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; | |
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; | |
GPIO_Init( GPIOB, &GPIO_InitStructure ); | |
if ( MEMS_ReadID() == 0x3A ) | |
{ | |
break; | |
} | |
} | |
if( i == 17 ) | |
{ | |
DRAW_DisplayString( 1, 50, "Test MEM ID Failed", 17 ); | |
} | |
} | |
/* Read for the first time */ | |
N_filtering = 0; | |
MEMS_ReadOutXY(); | |
MEMS_Info.OutX_F4 = MEMS_Info.OutX_F16 = MEMS_Info.OutX_F64 = MEMS_Info.OutX_F256 = MEMS_Info.OutX; | |
MEMS_Info.OutY_F4 = MEMS_Info.OutY_F16 = MEMS_Info.OutY_F64 = MEMS_Info.OutY_F256 = MEMS_Info.OutY; | |
MEMS_Info.OutZ_F4 = MEMS_Info.OutZ_F16 = MEMS_Info.OutZ_F64 = MEMS_Info.OutZ_F256 = MEMS_Info.OutZ; | |
/* Init X and Y*/ | |
MEMS_GetPosition( &XInit, &YInit ); | |
/* Wake Up Mems*/ | |
MEMS_WakeUp(); | |
} | |
/******************************************************************************* | |
* | |
* MEMS_Handler | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Called by the CircleOS scheduler to manage the MEMS. The Circle beeps if the | |
* MEMS is shocked. | |
* | |
* @attention This function must <b>NOT</b> be called by the user. | |
* | |
**/ | |
/******************************************************************************/ | |
void MEMS_Handler( void ) | |
{ | |
char buffer [20]; | |
int i; | |
int ofs_disp = 0; | |
if( StartingFromResetOrShockCounter ) | |
{ | |
StartingFromResetOrShockCounter--; | |
} | |
TimeCounterForDoubleClick++; | |
MEMS_ReadOutXY(); | |
// Evaluate gradients | |
GradX = ( MEMS_Info.OutX_F4 >> 2 ) - MEMS_Info.OutX; | |
GradY = ( MEMS_Info.OutY_F4 >> 2 ) - MEMS_Info.OutY; | |
GradZ = ( MEMS_Info.OutZ_F4 >> 2 ) - MEMS_Info.OutZ; | |
// Decide whether a direction is selected | |
if( tMovePtrX == 0 ) | |
{ | |
if( ( GradX > MIN_REACT ) || ( GradX < -MIN_REACT ) ) | |
{ | |
iMovePtrX = GradX / DIV_REACT; | |
tMovePtrX = DELAY_REACT; | |
fMovePtrX = 1; | |
} | |
} | |
else | |
{ | |
tMovePtrX--; | |
} | |
if( tMovePtrY == 0 ) | |
{ | |
if( ( GradY > MIN_REACT ) || ( GradY < -MIN_REACT ) ) | |
{ | |
iMovePtrY = GradY / DIV_REACT; //FL071012 rrm fix | |
tMovePtrY = DELAY_REACT; | |
fMovePtrY = 1; | |
} | |
} | |
else | |
{ | |
tMovePtrY--; | |
} | |
if( tMovePtrZ==0 ) | |
{ | |
if( ( GradZ > MIN_REACT ) || ( GradY < -MIN_REACT ) ) | |
{ | |
iMovePtrZ = GradZ / DIV_REACT; | |
tMovePtrZ = DELAY_REACT; | |
fMovePtrZ = 1; | |
} | |
} | |
else | |
{ | |
tMovePtrZ--; | |
} | |
Gradient2 = (s32)GradX * (s32)GradX + (s32)GradY * (s32)GradY + (s32)GradZ * (s32)GradZ; | |
// MEMS is shocked, let's beep! | |
if( ( Gradient2 > GRAD_SHOCK ) && ( BUZZER_GetMode() == BUZZER_OFF ) && ( StartingFromResetOrShockCounter == 0 ) ) | |
{ | |
MEMS_Info.Shocked++; | |
/*FL071007 = 1; | |
Suggested by Bob Seabrook: a further posiblity is to increment Shocked rather than just setting it | |
So it can still be tested for non zero as before but one can get more | |
info from the int without extra cost. */ | |
#define DELAY_BETWEEN_TWO_SHOCK 20 | |
#define MAX_DELAY_FOR_DOUBLECLICK 150 | |
StartingFromResetOrShockCounter = DELAY_BETWEEN_TWO_SHOCK; //< filter: short delay before detecting the next shock | |
if ( (TimeCounterForDoubleClick - TimeLastShock) < MAX_DELAY_FOR_DOUBLECLICK ) | |
{ | |
MEMS_Info.DoubleClick++; | |
TimeLastShock = 0; | |
} | |
else | |
{ | |
TimeLastShock = TimeCounterForDoubleClick; | |
} | |
BUZZER_SetMode( BUZZER_SHORTBEEP ); | |
} | |
} | |
/******************************************************************************* | |
* | |
* MEMS_ReadID | |
* | |
*******************************************************************************/ | |
/** | |
* Reads SPI chip identification. | |
* | |
* @return The SPI chip identification. | |
* | |
**/ | |
/******************************************************************************/ | |
u8 MEMS_ReadID( void ) | |
{ | |
u8 Temp = 0; | |
/* Chip Select low */ | |
MEMS_ChipSelect( LOW ); | |
/* Send "RDID" instruction */ | |
MEMS_SendByte( RDID ); | |
/* Read a byte from the MEMS */ | |
Temp = MEMS_SendByte( DUMMY_BYTE ); | |
/* Chip Select low */ | |
MEMS_ChipSelect( HIGH ); | |
return Temp; | |
} | |
/// @endcond | |
/* Public functions ----------------------------------------------------------*/ | |
/******************************************************************************* | |
* | |
* MEMS_GetPosition | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Returns the current (relative) position of the Primer. | |
* Only X-Y axis are considered here. | |
* | |
* @param[out] pX Current horizontal coordinate. | |
* @param[out] pY Current vertical coordinate. | |
* | |
* @warning The (0x0) point in on the low left corner. | |
* @note For absolute position information use MEMS_GetInfo() | |
* | |
**/ | |
/******************************************************************************/ | |
void MEMS_GetPosition( s16* pX, s16* pY ) | |
{ | |
*pX = MEMS_Info.OutX - XInit; | |
*pY = MEMS_Info.OutY - YInit; | |
} | |
/******************************************************************************* | |
* | |
* MEMS_GetRotation | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Returns current screen orientation. | |
* | |
* @param[out] pH12 Current screen orientation. | |
* | |
**/ | |
/******************************************************************************/ | |
void MEMS_GetRotation( Rotate_H12_V_Match_TypeDef* pH12 ) | |
{ | |
s16 sX = MEMS_Info.OutX; | |
s16 sY = MEMS_Info.OutY; | |
if( ( ( sX <= -MARGIN ) && ( sY <= 0 ) && (sX<=sY ) ) || | |
( ( sX <=- MARGIN ) && ( sY > 0) && (sX <= (-sY ) ) ) ) | |
{ | |
// 1st case: x<0, |x|>y => H12 = V9 | |
*pH12 = V9; | |
} | |
else if( ( ( sY <= -MARGIN ) && ( sX <= 0 ) && ( sY <= sX ) ) || | |
( ( sY <= -MARGIN ) && ( sX > 0 ) && ( sY <= (-sX ) ) ) ) | |
{ | |
// 2nd case: y<0, |y|>x => H12 = V12 | |
*pH12 = V12; | |
} | |
else if( ( ( sX >= MARGIN ) && ( sY <= 0 ) && ( sX >= (-sY) ) ) || | |
( ( sX >= MARGIN ) && ( sY > 0 ) && ( sX >= sY ) ) ) | |
{ | |
// 3rd case: x>0, |x|>y => H12=V3 | |
*pH12 = V3; | |
} | |
else if( ( ( sY >= MARGIN ) && ( sX <= 0 ) && ( sY >= (-sX ) ) ) || | |
( ( sY >= MARGIN ) && ( sX > 0 ) && ( sY >= sX ) ) ) | |
{ | |
// 4th case: y>0, |y|>x => H12=V6 | |
*pH12 = V6; | |
} | |
} | |
/******************************************************************************* | |
* | |
* MEMS_SetNeutral | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Set current position as "neutral position". | |
* | |
**/ | |
/******************************************************************************/ | |
void MEMS_SetNeutral( void ) | |
{ | |
// Set Neutral position. | |
MEMS_GetPosition( &XInit, &YInit ); | |
} | |
/******************************************************************************* | |
* | |
* MEMS_GetInfo | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Return the current MEMS information (state, absolute position...). | |
* | |
* @return a pointer to tMEMS_Info | |
* | |
**/ | |
/******************************************************************************/ | |
tMEMS_Info* MEMS_GetInfo( void ) | |
{ | |
return &MEMS_Info; | |
} |