2018-06-20 08:39 AM
I've been beating my head against the wall for the last couple of days trying to figure out why I cannot read registers at odd addresses through the LIS3DSH SPI interface with the nRF52832. Byte accesses to even addresses work fine, but byte accesses to odd addresses always return the value of the register at the even address one location lower. 16-bit accesses only work when the address is even. It seems like the LSB of the register address is always read by the LIS3DSH as a zero.
Communicating via the I2C bus works fine - bytes, words, even and odd alignment are all OK.
So, it looks to me like the LIS3DSH has a bug in the SPI interface. My best guess is that the last bit of the address is not correctly latched internally by the rising edge of SCL, and must be held static until the next falling edge of SCL (at which time the LIS3DSH starts to drive the data bus).
Here's my proof:
Shown below is a logic analyzer capture with showing a single byte read of the WHO_AM_I register (address 0x0F). The bottom 4 traces are a delayed (expanded) version of the top four. Here, I drive MOSI (SDI) to zero after the rising edge of the 8th clock in the ''address'' phase of the transfer. The hold time is shown to be about 460nsec. You can see that the data (MISO) returned during the next 8 clocks is 0x00 (corresponding to register 0x0E, INFO2).
In the second capture, I hold the last bit of the address until the falling edge of that same clock. Here, the data returned is correct for WHO_AM_I (0x3F).
The data sheet says the hold time is only 15 nsec, relative to the rising edge of SCL. It looks like you have to hold the 8th bit of the address (and only the 8th bit) until the falling edge of SCL.
So, is this a bug in the chip? I didn't see any mention of this in the errata sheet.
This is not a problem if the last bit of the address is held static through the data transfer. In my case, I'm now bit-banging the I/O ports, so I merely leave the MOSI (SDI) pin in its last state.
Unfortunately the built-in SPI interface of some micro's (like the nRF52832) drive the MOSI line low immediately after the last bit of the address is clocked out during read operations. This means you can't do individual byte reads of registers at odd addresses. The work-around would be to do a word-aligned 16-bit read and select the proper byte. Fortunately the 16-bit X, Y, and Z data values in the LIS3DSH are word aligned and 16-bit SPI reads with chips like the nRF52832 work fine.
2021-01-07 04:31 AM
Hi,
I am still stuck with the same problem. I tried with different sensor too. I am writing CTRL_REG1 exactlt same way as the other registers. I am passing the register number and data value as parameters. That works fine for all the other registers.
Since you successfully enabled the wake up state machine, can u share the sequence in which you are writing the registers, may be that will help me.
Thank you for your help.
2021-01-07 07:33 AM
Here's a simple sketch for the Bluefruit Feather that I wrote to implement the Wake-Up example from AP3393. Give it a try to see what happens.
The LIS3DSH SDA and SCL are connected to the SDA and SCL pins of the Feather board, and I used a pull up resistor on SDA. The LIS3DSH CS pin (to enable I2C) and SDO pin (to select the address) on the LIS3DSH are tied high (+3.3V).
#include <Wire.h>
#define TRUE 1
#define FALSE 0
// Software Timer for blinking RED LED
SoftwareTimer blinkTimer;
// LIS3DSH register addresses
#define LIS_BASE_RD 0x3B // I2C base address with pin 7 pulled up
#define LIS_BASE_WR 0x3A // I2C base address with pin 7 pulled up
#define LIS_BASE 0x1D // I2C base address with pin 7 pulled up
#define INFO1 0x0D
#define INFO2 0x0E
#define WHO_AM_I 0x0F
#define STAT 0x18
#define CTRL_REG1 0x21
#define CTRL_REG3 0x23
#define CTRL_REG4 0x20
#define CTRL_REG5 0x24
#define CTRL_REG6 0x25
#define STATUS 0x27
#define THRS1_1 0x57
#define ST1_1 0x40
#define ST1_2 0x41
#define MASK1_B 0x59
#define MASK1_A 0x5A
#define SETT1 0x5B
#define OUT_X_L 0x28
#define OUT_X_H 0x29
#define OUT_Y_L 0x2A
#define OUT_Y_H 0x2B
#define OUT_Z_L 0x2C
#define OUT_Z_H 0x2D
#define INT_SM1 0x08 // INT1 bit position in STAT register
// Library I2C Routines
void I2C_Write_LIS(uint8_t address, uint8_t value) {
Wire.beginTransmission(LIS_BASE);
Wire.write(address);
Wire.write(value);
Wire.endTransmission();
}
uint8_t I2C8_Read_LIS(uint8_t address) {
uint8_t temp;
// send the register address
Wire.beginTransmission(LIS_BASE);
Wire.write(address);
Wire.endTransmission();
// read back the value (1 byte)
Wire.requestFrom(LIS_BASE, 1);
if (Wire.available()) temp = Wire.read();
return (temp);
}
uint16_t I2C16_Read_LIS(uint8_t address) {
uint16_t temp;
uint8_t lo_byte;
// send the register address
Wire.beginTransmission(LIS_BASE);
Wire.write(address);
Wire.endTransmission();
// read back the value (2 bytes)
Wire.requestFrom(LIS_BASE, 2);
if (Wire.available()) lo_byte = Wire.read();
if (Wire.available()) temp = Wire.read();
temp <<= 8;
temp |= lo_byte;
return (temp);
}
void config_LIS3DSH(void){
I2C_Write_LIS(CTRL_REG4,0x67); // 100Hz update, all axis enabled
I2C_Write_LIS(CTRL_REG6,0x10); // ADD_INC = 1
// read back some register to verify operation
Serial.print("INFO1=");
Serial.println(I2C8_Read_LIS(INFO1),HEX);
Serial.print("INFO2=");
Serial.println(I2C8_Read_LIS(INFO2),HEX);
Serial.print("WHOAMI=");
Serial.println(I2C8_Read_LIS(WHO_AM_I),HEX);
Serial.print("CTRL_REG6=");
Serial.println(I2C8_Read_LIS(CTRL_REG6),HEX);
}
void setup(){
// initialize the I2C interface
Wire.begin();
Wire.setClock(400000);
// configure serial debug port
Serial.begin(115200);
Serial.println("\n\n********* LIS3DSH I2C Demo 1 *********");
config_LIS3DSH();
Serial.println("\nLIS3DSH configured");
Serial.println("\nSetting up Wake-up demo");
I2C_Write_LIS(CTRL_REG1,0x01);
I2C_Write_LIS(CTRL_REG3,0x48);
I2C_Write_LIS(CTRL_REG4,0x67);
I2C_Write_LIS(CTRL_REG5,0x00);
I2C_Write_LIS(THRS1_1,0x55);
I2C_Write_LIS(ST1_1,0x05);
I2C_Write_LIS(ST1_2,0x11);
I2C_Write_LIS(MASK1_B,0xFC);
I2C_Write_LIS(MASK1_A,0xFC);
I2C_Write_LIS(SETT1,0x01);
Serial.println("\nReading back Wake-up demo registers");
Serial.print("CTRL_REG1=");
Serial.println(I2C8_Read_LIS(CTRL_REG1),HEX);
Serial.print("CTRL_REG3=");
Serial.println(I2C8_Read_LIS(CTRL_REG3),HEX);
Serial.print("CTRL_REG4=");
Serial.println(I2C8_Read_LIS(CTRL_REG4),HEX);
Serial.print("CTRL_REG5=");
Serial.println(I2C8_Read_LIS(CTRL_REG5),HEX);
Serial.print("THRS1_1=");
Serial.println(I2C8_Read_LIS(THRS1_1),HEX);
Serial.print("ST1_1=");
Serial.println(I2C8_Read_LIS(ST1_1),HEX);
Serial.print("ST1_2=");
Serial.println(I2C8_Read_LIS(ST1_2),HEX);
Serial.print("MASK1_B=");
Serial.println(I2C8_Read_LIS(MASK1_B),HEX);
Serial.print("MASK1_A=");
Serial.println(I2C8_Read_LIS(MASK1_A),HEX);
Serial.print("SETT1=");
Serial.println(I2C8_Read_LIS(SETT1),HEX);
Serial.print("STAT=");
Serial.println(I2C8_Read_LIS(STAT),HEX);
// Initialize blinkTimer for 1000 ms and start it
blinkTimer.begin(500, blink_timer_callback);
blinkTimer.start();
}
void loop(){
int8_t status_reg;
// read the X, Y, and Z registers and display on terminal
Serial.print("\nX=");
Serial.print(I2C16_Read_LIS(OUT_X_L),DEC);
Serial.print(" Y=");
Serial.print(I2C16_Read_LIS(OUT_Y_L),DEC);
Serial.print(" Z=");
Serial.print(I2C16_Read_LIS(OUT_Z_L),DEC);
// read the status register to tell when an interrupt has occurred
status_reg = I2C8_Read_LIS(STAT);
Serial.print(" STAT=");
Serial.println(status_reg,HEX);
if (status_reg & INT_SM1) {
// interrupt occurred
Serial.println("**** State Machine 1 interrupt ****");
// restart the state machine to clear the interrupt
I2C_Write_LIS(CTRL_REG1,0x01);
}
delay (1000);
}
void blink_timer_callback(TimerHandle_t xTimerID){
(void) xTimerID;
digitalToggle(LED_RED);
}
void rtos_idle_callback(void){
// Don't call any other FreeRTOS blocking API()
// Perform background task(s) here
}