I am working on a university satellite team, and we have an issue with our main GNC board which we cannot resolve. Hopefully I can get some input from others on here...
The board in question has three magnetometers (LSM303DLHC) as well as three gyroscopes (FXAS21002C) which are connected across three I2C lines - each line having one mag and one gyro. I've attached a picture of our PCB design for reference.
Just for context: unfortunately due to a short timeline, and component shortages, we were unable to have the gyros and mags placed on the board during the manufacturing process and had to solder them separately by hand after the board was made. Although this was done by someone else at the university who is skilled at soldering, it resulted in a power-ground short, presumably from messy solder. We had these parts desoldered, using fresh components, which fixed the ground-power short, but then found that two of the three I2C lines were now shorted.
The third I2C line, which is not shorted, is able to connect and read from the mag, but the gyro is not returning correct data (the values don't change).
We tried to directly read/write the registers on the gyro, but did not get correct responses. Every register just returned 3F, instead of 1 or 0 or whatever value it should be. We know that the software is not the issue, as it works fine on a gyro breakout board. Scanning the I2C line also does not detect the address to the gyro.
Does anyone have any insight into why the gyros are not working? or why they just return the same value over and over again? The I2C lines are pretty simple, and we've checked all the pin connections, and everything checked out. I am not sure what else the issue could be.
Schematic:
Apologies for the messy naming conventions/organization. I was not the original designer.
PCB Design:
The I2C traces that run off the screen are connected to a molex connector. Traces are all connected to the correct pins on the gyro. It is a 4 layer board that has a ground and power plane.
Sample of Code:
The mag reading is currently commented out from when we were trying to isolate the gyro.
Main
#include "mag_wrap.h"
#include "gyro_wrap.h"
#include <Wire.h>
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.print("begin");
// initMag(&Mag1);
// startMag(&Mag1);
initGyro(&Gyro1);
startGyro(&Gyro1);
}
void loop() {
double t0 = micros();
// Serial.print("Read Mag");
// readMagData(&Mag1);
// Serial.print("Mag: ");
// for (int i = 0; i < 3; i++) {
// Serial.print(" ");
// Serial.print(Mag1.magXYZ[i],7);
// }
// Serial.println();
Serial.print("Read Gyro");
readGyroData(&Gyro1);
Serial.print("Gyro: ");
for (int i = 0; i < 3; i++) {
Serial.print(" ");
Serial.print(Gyro1.gyroXYZ[i],7);
}
Serial.println();
while (micros() - t0 < 1.0 / 25 * 1e6) {
}
}
Gyro Functions
/*
* gyro_wrap.c
*
* Created on: Feb 17, 2021
* Author: Alex Zhen
*/
#include "gyro_wrap.h"
// gyroscope struct.
gyro_t Gyro1;
/*!
* @brief read the value of registers of a gyroscope.
*
*
* @param reg The register want to be read.
* @param value The variable to hold the value of the register.
* @param valueSize The size of the value of the register.
* @param Gyro The gyroscope want to be read.
* @return void
*
*/
void readRegs(uint8_t reg, uint8_t *value, uint8_t valueSize, gyro_t * Gyro)
{
#if ARDUINO_CODE
Wire.beginTransmission(GYRO_ADDRESS);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom(GYRO_ADDRESS, valueSize);
int i = 0;
while (Wire.available()) {
*(value+i) = Wire.read();
i++;
}
#else
I2C_request(Gyro->gyroHandle, GYRO_ADDRESS, reg, value, valueSize);
#endif
}
/*!
* @brief write a value to the registers of a gyroscope.
*
*
* @param reg The register want to be written.
* @param value The value want to assigned to the register.
* @param valueSize The size of The value want to assigned to the register.
* @param Gyro The gyroscope want to be written.
* @return void
*
*/
void writeReg(uint8_t reg, uint8_t value, gyro_t * Gyro)
{
#if ARDUINO_CODE
Wire.beginTransmission(GYRO_ADDRESS);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
#else
I2C_send(Gyro->gyroHandle, GYRO_ADDRESS, reg, &value, 1);
#endif
}
#if ARDUINO_CODE
/*!
* @brief Turn on a gyroscope. Initialize all parameters of a gyroscope.
*
*
* @param Gyro The gyroscope wants to be initialized
* @return void
*
*/
void initGyro(gyro_t * Gyro)
#else
/*!
* @brief Turn on a gyroscope. Initialize all parameters of a gyroscope.
*
*
* @param Gyro The gyroscope wants to be initialized.
* @param gyroHandle The freertos handle of the gyroscope.
* @param gyroTransfer The transfer information of the gyroscope.
* @return void
*
*/
void initGyro(gyro_t * Gyro, lpi2c_rtos_handle_t *gyroHandle)
#endif
{
if (!Gyro->gyroInitialized)
{
#if !ARDUINO_CODE
Gyro->gyroHandle = gyroHandle;
#endif
#if DIFF_TEMP_BIAS_COE
switch (base_Gyro){
case LPI2C1:
static const float gyroBiasValue = {-0.565375, 0.6173333, -0.0121667};
static const float gyroTempBiasCoeValue = {0.02, 0.02, 0.01};
static const float gyroTempSensCoeValue = {0.0008, 0.0008, 0.0001};
Gyro->gyroBias = gyroBiasValue;
Gyro->gyroTempBiasCoe = gyroTempBiasCoeValue;
Gyro->gyroTempSensCoe = gyroTempSensCoeValue;
break;
case LPI2C2:
static const float gyroBiasValue = {0, 0, 0};
static const float gyroTempBiasCoeValue = {0, 0, 0};
static const float gyroTempSensCoeValue = {0, 0, 0};
Gyro->gyroBias = gyroBiasValue;
Gyro->gyroTempBiasCoe = gyroTempBiasCoeValue;
Gyro->gyroTempSensCoe = gyroTempSensCoeValue;
break;
case LPI2C3:
static const float gyroBiasValue = {0, 0, 0};
static const float gyroTempBiasCoeValue = {0, 0, 0};
static const float gyroTempSensCoeValue = {0, 0, 0};
Gyro->gyroBias = gyroBiasValue;
Gyro->gyroTempBiasCoe = gyroTempBiasCoeValue;
Gyro->gyroTempSensCoe = gyroTempSensCoeValue;
break;
}
#else
#if COUNT_ZERO_OFFSET
static const float gyroBiasValue[3] = {-0.565375, 0.6173333, -0.0121667};
static const float gyroTempBiasCoeValue[3] = {0.02, 0.02, 0.01};
static const float gyroTempSensCoeValue[3] = {0.0008, 0.0008, 0.0001};
#else
static const float gyroBiasValue[3] = {.0, .0, .0};
static const float gyroTempBiasCoeValue[3] = {.0, .0, .0};
static const float gyroTempSensCoeValue[3] = {.0, .0, .0};
#endif
memcpy(Gyro->gyroBias,gyroBiasValue, 12);
memcpy(Gyro->gyroTempBiasCoe,gyroTempBiasCoeValue, 12);
memcpy(Gyro->gyroTempSensCoe,gyroTempSensCoeValue, 12);
#endif
#if ARDUINO_CODE
Wire.begin();
#endif
Gyro->gyroInitialized = 1;
}
}
/*!
* @brief Set the gyroscope to desired configurations. Start reading.
*
*
* @param Gyro The gyroscope wants to be set.
* @return void
*
*/
void startGyro(gyro_t * Gyro)
{
if (Gyro->gyroInitialized){
writeReg(GYRO_CTRL_REG0, GYRO_FSR_NUM, Gyro);
writeReg(GYRO_CTRL_REG1, (GYRO_ODR_NUM<<2 | 0b10), Gyro);
}
}
/*!
* @brief initialize the gyroscope and start the gyroscope's reading. This
* is the function going to be used on the FSW for starting the gyroscope.
*
*
* @param Gyro The gyroscope wants to be set.
* @return void
*
*/
#if ARDUINO_CODE
void quickStartGyro(gyro_t * Gyro)
#else
void quickStartGyro(gyro_t * Gyro, lpi2c_rtos_handle_t *gyroHandle)
#endif
{
#if ARDUINO_CODE
initGyro(Gyro);
#else
initGyro(Gyro, gyroHandle);
#endif
startGyro(Gyro);
}
/*!
* @brief Read the temperature of a gyroscope.
*
*
* @param Gyro The gyroscope that its temperature want to be read.
* @return void
*
*/
void readTempData(gyro_t * Gyro)
{
uint8_t rawTempData;
readRegs(GYRO_TEMP, &rawTempData, 1, Gyro);
Gyro->temperature = (int8_t) rawTempData;
}
/*!
* @brief Read the angular rates of a gyroscope (x,y,z axes).
*
*
* @param Gyro The gyroscope that its angular rates want to be read.
* @return void
*
*/
void readGyroData(gyro_t * Gyro)
{
readTempData(Gyro);
unsigned char rawData[6]; // x/y/z gyro register data stored here
readRegs(GYRO_OUT_X_MSB,rawData, 6, Gyro); // Read the six raw data registers into data array
#if COUNT_TEMP_BIAS
int8_t tempDelta = Gyro->temperature - GYRO_TEMP_0;
#endif
for (int i = 0; i<3; i++){
short tempValue = ((short)(((unsigned short)rawData[2*i]) << 8 | ((unsigned short) rawData[2*i + 1])));
#if COUNT_TEMP_BIAS
Gyro->gyroXYZ[i] = tempValue*GYRO_SENSITIVITY*(1 + (Gyro->gyroTempSensCoe[i])*(int16_t) tempDelta) - (Gyro->gyroBias[i]) - Gyro->gyroTempBiasCoe[i]*(int16_t) tempDelta;
#else
Gyro->gyroXYZ[i] = ((float) tempValue)*GYRO_SENSITIVITY - (Gyro->gyroBias[i]);
#endif
}
}
/*!
* @brief Reset a gyroscope. The i2c connection won't be reset.
*
*
* @param Gyro The gyroscope to be reset
* @return void
*
*/
void resetGyro(gyro_t * Gyro){
writeReg(GYRO_CTRL_REG1, 0b1000000, Gyro); // set reset bit to 1 to assert software reset to zero at end of boot process
uint8_t flag;
readRegs(GYRO_INT_SRC_FLAG, &flag, 1, Gyro);
while(!(flag & 0x08)) { // wait for boot end flag to be set
readRegs(GYRO_INT_SRC_FLAG, &flag, 1, Gyro);
}
}
Gyro Header
/*
* gyro_wrap.h
*
* Created on: Feb 17, 2021
* Author: Alex Zhen
*/
#ifndef GYRO_WRAP_H_
#define GYRO_WRAP_H_
#define ARDUINO_CODE 1
#if ARDUINO_CODE
#include <Wire.h>
#else
#include "fsl_lpi2c.h"
#include "fsl_lpi2c_freertos.h"
#include "peripherals.h"
#endif
#define COUNT_ZERO_OFFSET 0
#define COUNT_TEMP_BIAS 0 // if the code count temperature influence on output
#define MULTI_GYROS 0 // if there are multiple gyroscopes(three)
#define DIFF_TEMP_BIAS_COE 0 // if the gyroscopes have different temperature bias and sensitivity coefficients.
// register addresses FXAS21002C_H_
#define GYRO_OUT_X_MSB 0x01
#define GYRO_CTRL_REG0 0x0D
#define GYRO_TEMP 0x12
#define GYRO_CTRL_REG1 0x13
#define GYRO_INT_SRC_FLAG 0x0B
// gyro parameters
#define GYRO_ODR_NUM 0b101
#define GYRO_FSR_NUM 0b11
#define GYRO_ODR_VALUE 25
#define GYRO_FSR_VALUE 250
#define GYRO_SENSITIVITY 7.8125e-3
#define GYRO_TEMP_0 23
#define GYRO_ADDRESS (uint8_t)0x20
/*!
* @brief Structure contains information about one gyroscope
*
*/
typedef struct _Gyro
{
float gyroXYZ[3]; /* measured angular rates*/
int8_t temperature; /* measured temperature*/
#if !ARDUINO_CODE
lpi2c_rtos_handle_t * gyroHandle; /* gyroscope i2c handle?*/
#endif
float gyroBias[3]; /* gyroscope zero-off set(bias)*/
float gyroTempBiasCoe[3]; /* gyroscope temperature bias coefficients*/
float gyroTempSensCoe[3]; /* gyroscope temperature sensitivity coefficients*/
char gyroInitialized; /* gyroscope status */
} gyro_t;
extern gyro_t Gyro1; /* gyroscope 1*/
#if MULTI_GYROS
extern gyro_t Gyro2;
extern gyro_t Gyro3;
#endif
/*!
* @brief read the value of registers of a gyroscope.
*
*
* @param reg The register want to be read.
* @param value The variable to hold the value of the register.
* @param valueSize The size of the value of the register.
* @param Gyro The gyroscope want to be read.
* @return void
*
*/
void readRegs(uint8_t reg, uint8_t *value, uint8_t valueSize, gyro_t * Gyro);
/*!
* @brief write a value to the registers of a gyroscope.
*
*
* @param reg The register want to be written.
* @param value The value want to assigned to the register.
* @param valueSize The size of The value want to assigned to the register.
* @param Gyro The gyroscope want to be written.
* @return void
*
*/
void writeReg(uint8_t reg, uint8_t value, gyro_t * Gyro);
#if ARDUINO_CODE
/*!
* @brief Turn on a gyroscope. Initialize all parameters of a gyroscope.
*
*
* @param Gyro The gyroscope wants to be initialized
* @return void
*
*/
void initGyro(gyro_t * Gyro);
#else
/*!
* @brief Turn on a gyroscope. Initialize all parameters of a gyroscope.
*
*
* @param Gyro The gyroscope wants to be initialized.
* @param gyroHandle The freertos handle of the gyroscope.
* @param gyroTransfer The transfer information of the gyroscope.
* @return void
*
*/
void initGyro(gyro_t * Gyro, lpi2c_rtos_handle_t *gyroHandle);
#endif
/*!
* @brief Set the gyroscope to desired configurations. Start reading.
*
*
* @param Gyro The gyroscope wants to be set.
* @return void
*
*/
void startGyro(gyro_t * Gyro);
/*!
* @brief initialize the gyroscope and start the gyroscope's reading
*
*
* @param Gyro The gyroscope wants to be set.
* @return void
*
*/
/*!
* @brief initialize the gyroscope and start the gyroscope's reading. This
* is the function going to be used on the FSW for starting the gyroscope.
*
*
* @param Gyro The gyroscope wants to be set.
* @return void
*
*/
#if ARDUINO_CODE
void quickStartGyro(gyro_t * Gyro);
#else
void quickStartGyro(gyro_t * Gyro, lpi2c_rtos_handle_t *gyroHandle);
#endif
/*!
* @brief Read the temperature of a gyroscope.
*
*
* @param Gyro The gyroscope that its temperature want to be read.
* @return void
*
*/
void readTempData(gyro_t * Gyro);
/*!
* @brief Read the angular rates of a gyroscope (x,y,z axes).
*
*
* @param Gyro The gyroscope that its angular rates want to be read.
* @return void
*
*/
void readGyroData(gyro_t * Gyro);
/*!
* @brief Reset a gyroscope. The i2c connection won't be reset.
*
*
* @param Gyro The gyroscope to be reset
* @return void
*
*/
void resetGyro(gyro_t * Gyro);
#endif /* GYRO_WRAP_H_ */