2017-09-02 01:41 PM
Hello:
I have been fighting with this accelerometer for a couple of months and it has now become imperative that I get it functioning.
I am using it with an STM32F405 controller. I have it set up in interrupt mode. When I get an interrupt, I read and process the X,Y,Z registers but the values are always (or almost nearly) the same: -20, -3, -6.
I have absolutely not a clue what is going on. Can anyone please help me with this? Here is the code to process the data after the interrupt. Directly below is the call to SPI read that the handler uses
uint8_t SPI_read(uint8_t address)
{ GPIO_SetPinLow(GPIOD, ACCHI_CS); address = 0x80 | address; // 0b10 - reading and clearing status while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI3, address); while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET); SPI_I2S_ReceiveData(SPI3); while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI3, 0x00); while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET); GPIO_SetPinHigh(GPIOD, ACCHI_CS); return SPI_I2S_ReceiveData(SPI3);}/* Interrupt handler for EXT1 */void EXTI1_IRQHandler(void) { volatile uint8_t x = 0, y = 0, z = 0, reg; volatile float32_t cX = 0, cY = 0, cZ = 0; volatile int8_t accVal, xF,yF,zF; if (EXTI_GetITStatus(EXTI_Line1) != RESET ) { EXTI_ClearITPendingBit(EXTI_Line1);// who dunnit?
reg = SPI_read( LIS200DL_INT1SRC ); if( reg & 0x40 ) { x = SPI_read( LIS200DL_OUT_X ); y = SPI_read( LIS200DL_OUT_Y ); z = SPI_read( LIS200DL_OUT_Z ); } if( x & 0x80 ) { //it's negative, invert result accVal = x ^ 1; accVal += 0x1; cX = (accVal * 0.780); //printf('-X acc value = %.1f\n', cX); } else { cX = (x * 0.780) * -1.0; //printf('+X acc value = %.1f\n', cX); } if( y & 0x80 ) { //invert result accVal = y ^ 1; accVal += 0x1; cY = (accVal * 0.780); //printf('+Y acc value = %.1f\n', cY); } else { cY = (y * 0.780) * -1.0; //printf('-Y acc value = %.1f\n', cY); } if( z & 0x80 ) { //invert result accVal = z ^ 1; accVal += 0x1; cZ = (accVal * 0.780); //printf('+Z acc value = %.1f\n', cZ); } else { cZ = (z * 0.780) * -1.0; //printf('-Z acc value = %.1f\n', cZ); } xF = (int8_t)cX; yF = (int8_t)cY; zF = (int8_t)cZ;if( (abs(xF) > 3) || (abs(yF) > 3) || (abs(zF) > 3) )
{ myDataX = xF; myDataY = yF; myDataZ = zF; } }}
Please advise as to where I am making a mistake. If no one on this forum can help, how can I get official ST support? This device will be used on quite a few systems and right now, it is holding the project up.]\
Roger
Solved! Go to Solution.
2017-09-06 02:41 AM
I have several notes:
- yes, the XOR operation was not correct
- if you fix the XOR operation the conversion will be correct except the sign. You are inverting the value. If the accelerometer output is positive you get negative value and vice versa. But maybe you are doing this intentionally.
- you are loosing some precision due to conversion from float to int (in this command
xF = (int8_t)cX;)
- As C use two's complement for signed variables you can simply replace the whole if structure by one command: xF = (int8_t)x * 0.780f;
2017-09-04 02:52 AM
Can you please provide your sensor configuration (register settings)?
2017-09-04 07:29 PM
Here is the setup that includes the SPI and registers. Below that is the register definitions.
uint8_t configH3LIS200DL( void )
{ uint8_t rtnValue; GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE); 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_NSSInternalSoft_Set; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI3, &SPI_InitStructure);//Initialize pins for SPI functionality
GPIO_InitStructure.GPIO_Pin = ACCHI_SCK | ACCHI_MISO | ACCHI_MOSI; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOC, &GPIO_InitStructure); //Initialize Chip Select pin GPIO_InitStructure.GPIO_Pin = ACCHI_CS; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOD, &GPIO_InitStructure); //interrupt 1 configured in void Configure_PD1_EXTI(void) below... GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SPI3); GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SPI3); GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SPI3); // set initially HIGH GPIO_SetPinHigh(GPIOD, ACCHI_CS); // enable SPI3 SPI_Cmd(SPI3, ENABLE); SPI_send( LIS200DL_WHO_AM_I, 0x0 ); rtnValue = SPI_read( LIS200DL_WHO_AM_I ); //printf('ID2 = %x\n', rtnValue); SPI_send( LIS200DL_CTRL_REG1, 0x2F ); rtnValue = SPI_read( LIS200DL_CTRL_REG1 ); //printf('ctl1 = %x\n', rtnValue); SPI_send( LIS200DL_CTRL_REG3, 0x0 ); rtnValue = SPI_read( LIS200DL_CTRL_REG3 ); //printf('ctl1 = %x\n', rtnValue); SPI_send( LIS200DL_CTRL_REG4, 0x0 );//configure interrupt register
SPI_send( LIS200DL_INT1CFG, 0x2A ); rtnValue = SPI_read( LIS200DL_INT1CFG ); //printf('int1 reg = %x\n', rtnValue); //set threshold register = 3 SPI_send( LIS200DL_INT1THS, 0x3 ); rtnValue = SPI_read( LIS200DL_INT1THS ); //printf('thr reg = %x\n', rtnValue); return rtnValue;}REGISTER DEFS:
#define LIS200DL_WHO_AM_I 0x0F
#define LIS200DL_CTRL_REG1 0x20
#define LIS200DL_CTRL_REG2 0x21#define LIS200DL_CTRL_REG3 0x22#define LIS200DL_CTRL_REG4 0x23#define LIS200DL_CTRL_REG5 0x24#define LIS200DL_HP_RESET 0x25#define LIS200DL_STATUS_REG 0x27#define LIS200DL_OUT_X 0x29#define LIS200DL_OUT_Y 0x2B#define LIS200DL_OUT_Z 0x2D#define LIS200DL_INT1CFG 0x30#define LIS200DL_INT1SRC 0x31#define LIS200DL_INT1THS 0x32#define LIS200DL_INT1DUR 0x33#define LIS200DL_INT2CFG 0x34#define LIS200DL_INT2SRC 0x35#define LIS200DL_INT2THS 0x36#define LIS200DL_INT2DUR 0x37// CTRL_REG1#define LIS200DL_XEN 0x01#define LIS200DL_YEN 0x02#define LIS200DL_ZEN 0x04#define LIS200DL_XYZ_NEW 0x08#define LIS200DL_XOR 0x10#define LIS200DL_YOR 0x20#define LIS200DL_ZOR 0x40#define LIS200DL_XYZOR 0x80// CTRL_REG2
#define LIS200DL_HP_COEFF1 0x01#define LIS200DL_HP_COEFF2 0x02#define LIS200DL_HP_FF_WU1 0x04#define LIS200DL_HP_FF_WU2 0x08#define LIS200DL_FDS 0x10#define LIS200DL_BOOT 0x40#define LIS200DL_SIM 0x80Thanks,
Roger
2017-09-05 12:53 AM
The configuration seems OK to me, it works as expected. You configured the sensor to generate the interrupt if acceleration in any axis is above 2.3g.
How do you stimulate the interrupt?
If you say: 'I read and process the X,Y,Z registers but the values are always (or almost nearly) the same: -20, -3, -6.' are the values in LSB or in g?
What do you want to achieve? What is your target application?
2017-09-05 08:00 AM
Hi,
Thanks for taking the time to look at this and reply. First and most importantly, can you please advise if the conversions from raw data to values look correct? They should be converting from raw values to 'g' values. (in reference to your question about LSB or g values)
With respect to your other question - if the values were almost always '
-20, -3, -6.'
, wouldn't that look suspicious to you? I am interested in those values from +/-3g to +/-100g. The application is on a PCB in an industrial machine and there are all kinds of shake values in there that could potentially go to 100g. So why would it always produce these values?Regards...
2017-09-05 08:58 AM
Ok, if you could check and verify this, I would be grateful - it may be where some of the problem is.
Also, values xF, yF and zF are declared as int8_t already. Is that the variables you were referring to ('you can simply change the type from uint8_t to int8_t ...') or did you mean something different?
2017-09-05 10:23 AM
At the first glance, I think it is not correct, but I will simulate exactly your code tomorrow.
You don't have to convert the raw value by so complex function, you can simply change the type from uint8_t to int8_t and multiply by sensitivity.
xF = (int_8)x * 0.780f;
2017-09-05 08:25 PM
This:
x = (x ^ 0xFF) + 1; // Two's complement
Fixed it. It was nonsense!
Thanks
2017-09-05 08:39 PM
if( x & 0x80 )
{
//it's negative, invert result
accVal = x ^ 1;
accVal += 0x1;
cX = (accVal * 0.780);
//printf('-X acc value = %.1f\n', cX);
}
What's the XOR ONE nonsense?
if (x & 0x80)
x = (x ^ 0xFF) + 1; // Two's complement
Or is it in sign-magnitude format?
2017-09-06 02:41 AM
I have several notes:
- yes, the XOR operation was not correct
- if you fix the XOR operation the conversion will be correct except the sign. You are inverting the value. If the accelerometer output is positive you get negative value and vice versa. But maybe you are doing this intentionally.
- you are loosing some precision due to conversion from float to int (in this command
xF = (int8_t)cX;)
- As C use two's complement for signed variables you can simply replace the whole if structure by one command: xF = (int8_t)x * 0.780f;