I am using 4 MPU9150’s. Each MPU9150 is a combination of two sensors MPU6050(inertial sensor) and AK8975(magnetometer).
I am able to access MPU6050 but not AK8975. SDA and SCL of MPU6050 is connected to primary SDA and SCL of MPU9150 whereas SDA and SCL of AK8975 are connected to auxiliary SDA and SCL of MPU9150. In bypass mode, auxiliary pins and primary pins gets connected together. In that case AK8975 can be accessed directly via primary SDA and SCL by microcontroller. But this is not happening.
A more insightful description can be found here:
http://electronics.stackexchange.com/qu … those-pins
My code is:
/*
* mag_interface.c
*
* Created: 6/16/2015 3:15:58 PM
* Author: Kuldeep Soni
*/
#include "mag_interface.h"
#include <stdint.h>
#include <asf.h>
#include <string.h>
#include "MIMU22BT.h"
#define MPU9150_I2C_ADDRESS_COMPASS 0x0C
// Functions pacing I2C bitbanging
static int32_t last_tick;
#define QUATER_CC_COUNT 160
// I2C bus speed is approximately CLOCK_FREQ/QUATER_CC_COUNT * 220 [kHz]
// 35 gives 400kHz
__always_inline static void start_tick(void){
last_tick = Get_system_register(AVR32_COUNT);
}
__always_inline static void half_tick(void){
int32_t wait_to = last_tick + 2*QUATER_CC_COUNT;
while (Get_system_register(AVR32_COUNT) - wait_to < 0);
last_tick = Get_system_register(AVR32_COUNT);
}
__always_inline static void quater_tick(void){
int32_t wait_to = last_tick + QUATER_CC_COUNT;
while (Get_system_register(AVR32_COUNT) - wait_to < 0);
last_tick = Get_system_register(AVR32_COUNT);
}
__always_inline static void mag_tick(void){
int32_t wait_to = last_tick + 2048000;
while (Get_system_register(AVR32_COUNT) - wait_to < 0);
last_tick = Get_system_register(AVR32_COUNT);
}
__always_inline static void eight_thousand_tick(void){
int32_t wait_to = last_tick + 210000;
while (Get_system_register(AVR32_COUNT) - wait_to < 0);
last_tick = Get_system_register(AVR32_COUNT);
}
// Function that reads all the values of a port
//#define gpio_get_port_value(port) (AVR32_GPIO.port[port].pvr)
// TODO: Use above function instead
static uint32_t gpio_get_port_value(uint32_t port,uint32_t mask)
{
volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[port];
return (gpio_port->pvr ) & mask;
// TODO: Remove this masking. It is not used.
}
//Transpose Matrix
void transpose(uint32_t A[32]){
int32_t j, k;
uint32_t m, t;
m = 0x0000FFFF;
for (j = 16; j != 0; j = j >> 1, m = m ^ (m << j)) {
for (k = 0; k < 32; k = (k + j + 1) & ~j) {
t = (A[k] ^ (A[k+j] >> j)) & m;
A[k] = A[k] ^ t;
A[k+j] = A[k+j] ^ (t << j);
}
}
}
// Function that initializes the I2C communication pins
static void I2C_init(void){
// Initialize the IMU pins to open-drain outputs
gpio_configure_group(CLK_PORT,CLK_PINS,(GPIO_OPEN_DRAIN | GPIO_DIR_OUTPUT));
gpio_configure_group(SDA_PORT,SDA_PINS,(GPIO_OPEN_DRAIN | GPIO_DIR_OUTPUT));
}
// Function that sends the I2C start command
static void I2C_start_mag(void){
start_tick();
// Now pull the SDA lines low will keeping the SCL high
gpio_set_group_low(SDA_PORT,SDA_PINS);
quater_tick();
}
static void I2C_start_read_mag(void){
// Short delay (1/2 clock cycle)
gpio_set_group_low(CLK_PORT,CLK_PINS);
half_tick();
// Make sure all communication lines are in the start state
gpio_set_group_high(CLK_PORT,CLK_PINS);
gpio_set_group_high(SDA_PORT,SDA_PINS);
// Short delay (1/4 clock cycle)
quater_tick();
// Now pull the SDA lines low will keeping the SCL high
gpio_set_group_low(SDA_PORT,SDA_PINS);
quater_tick();
}
// Function that sends the I2C start command
static void I2C_stop_mag(void)
{
// Pull the SCL lines low
gpio_set_group_low(CLK_PORT,CLK_PINS);
quater_tick();
// Pull the SDA lines low
gpio_set_group_low(SDA_PORT,SDA_PINS);
quater_tick();
// Pull the SCL lines high
gpio_set_group_high(CLK_PORT,CLK_PINS);
quater_tick();
// Pull the SDA lines high
gpio_set_group_high(SDA_PORT,SDA_PINS);
}
// Function that clocks out a single byte on the I2C bus and that returns ACK (NACK) responds of the sensors
static void I2C_write_byte_mag(uint8_t data)
{
uint8_t ctr;
for (ctr=8;ctr>0;ctr--)
{
// Pull clk low
gpio_set_group_low(CLK_PORT,CLK_PINS);
quater_tick();
// Set SDA 1
if ((data>>(ctr-1)) & 0x01)
{
gpio_set_group_high(SDA_PORT,SDA_PINS);
}
else
{
gpio_set_group_low(SDA_PORT,SDA_PINS);
}
quater_tick();
// Pull clk high
gpio_set_group_high(CLK_PORT,CLK_PINS);
half_tick();
}
// ACK
// Pull clk low
gpio_set_group_low(CLK_PORT,CLK_PINS);
quater_tick();
// Set SDA 1
gpio_set_group_high(SDA_PORT,SDA_PINS);
quater_tick();
// Pull clk high
gpio_set_group_high(CLK_PORT,CLK_PINS);
quater_tick();
quater_tick();
// Pull SCL low (gives more even clock at higher rates)
gpio_set_group_low(CLK_PORT,CLK_PINS);
}
// Function that reads a single byte on the I2C bus. The input flag signals if a ACK should be read or a NACK outputted.
static void I2C_read_byte_mag(uint32_t *data_port0,Bool ack_flag){
uint8_t ctr;
gpio_configure_group(SDA_PORT,SDA_PINS,(GPIO_DIR_INPUT|GPIO_OPEN_DRAIN));
for (ctr=0;ctr<8;ctr++)
{
// Pull clk low
gpio_set_group_low(CLK_PORT,CLK_PINS);
half_tick();
// Pull clk high
gpio_set_group_high(CLK_PORT,CLK_PINS);
quater_tick();
data_port0[ctr]=gpio_get_port_value(SDA_PORT,SDA_PINS);
quater_tick();
}
gpio_configure_group(SDA_PORT,SDA_PINS,(GPIO_PULL_DOWN|GPIO_OPEN_DRAIN));
if (ack_flag)
{
// ACK
// Pull clk low
gpio_set_group_low(CLK_PORT,CLK_PINS);
quater_tick();
// Set SDA 1
gpio_set_group_low(SDA_PORT,SDA_PINS);
quater_tick();
// Pull clk high
gpio_set_group_high(CLK_PORT,CLK_PINS);
half_tick();
}
else
{
// Send NACK
// Pull clk low
gpio_set_group_low(CLK_PORT,CLK_PINS);
quater_tick();
// Set SDA 1
gpio_set_group_high(SDA_PORT,SDA_PINS);
quater_tick();
// Pull clk high
gpio_set_group_high(CLK_PORT,CLK_PINS);
half_tick();
}
// Pull SCL low
gpio_set_group_low(CLK_PORT,CLK_PINS);
// Release data pins
gpio_set_group_high(SDA_PORT,SDA_PINS);
}
static void single_byte_write_magnetometer(uint8_t address,uint8_t data)
{
// Send I2C start command
I2C_start_mag();
// Send device address with write command
I2C_write_byte_mag(((0x0C<<1) & 0xFE));
// Send register address
I2C_write_byte_mag(address);
// Send data
I2C_write_byte_mag(data);
// Send stop
I2C_stop_mag();
}
static void single_byte_read_magnetometer(uint8_t address , uint32_t *data_port0)
{
// Send I2C start command
I2C_start_read_mag();
// Send device address with write command
I2C_write_byte_mag(((0x0C<<1) & 0xFE));
// Send register address
I2C_write_byte_mag(address);
// Send I2C start command
I2C_start_read_mag();
// Send device address with read command
I2C_write_byte_mag(((0x0C<<1) | 0x01));
// Read byte (false=NACK)
I2C_read_byte_mag(data_port0,false);
// Send stop
I2C_stop_mag();
}
//Function that reads multiple bytes in a burst
static void burst_read(uint8_t address,uint32_t *data_port0,uint8_t nr_of_bytes)
{
uint8_t ctr;
// Send I2C start command
I2C_start_read_mag();
// Send device address with write command
I2C_write_byte_mag(((0x0C<<1) & 0xFE));
// Send register address
I2C_write_byte_mag(address);
// Send I2C start command
I2C_start_read_mag();
// Send device address with read command
I2C_write_byte_mag(((0x0C<<1) | 0x01));
// Read bytes (true=ACK)
for (ctr=0;ctr<nr_of_bytes-1;ctr++)
{
if(ctr%2 == 0)
I2C_read_byte_mag(data_port0+(ctr+1)*8,true);
else
I2C_read_byte_mag(data_port0+(ctr-1)*8,true);
}
// Read last byte (false=NACK)
I2C_read_byte_mag(data_port0+ctr*8,false);
// Send stop
I2C_stop_mag();
}
void mag_interface_init(void){
}
// Magnetometer array data
int16_t mag_data[32][3];
// Buffers for reading data (64 bits) from the IMUs
static uint32_t mag_matrix[64];
// Map of the I/O-pin positions of the IMUs(Magnetometer also accessed from those pins only)
static const uint8_t mag_map[NR_IMUS]=IMU_POS;
uint32_t ts_mag;
void mag_read(void){
ts_mag = Get_system_register(AVR32_COUNT);
single_byte_write_magnetometer(0x0A,0x01);//Single measurement mode
start_tick();
mag_tick();//delay of 8 milli seconds for data to be ready.
burst_read(0x03,mag_matrix,7);
transpose(mag_matrix);//bitwise transpose of an uint32_t type array
transpose(mag_matrix+32);
uint8_t i;
for (i=0; i<NR_IMUS; i++) {
memcpy(mag_data[i],mag_matrix+(32-1)-mag_map[i],4);
memcpy(mag_data[i]+2,mag_matrix+(64-1)-mag_map[i],2);
}
}