How to configure GPIO pin as an I2C pin?

Hello

I am using AT32UC3C microcontroller. I want to use its pins PB01 as SCL and PA04 as SDA pin. What should be the configuration of GPIO pins. Currently I have configured it to (GPIO_OPEN_DRAIN | GPIO_DIR_OUTPUT). But as per gpio spec, when using a pin as gpio pin GPIO_OPEN_DRAIN is not implemented. Also when I will be reading from a sensor, the gpio pin should be an input pin. So should I change the configuration while reading?

While reading I have tried these configurations:

GPIO_OPEN_DRAIN | GPIO_DIR_INPUT

GPIO_DIR_INPUT

GPIO_DIR_INPUT | GPIO_PULL_UP

GPIO_DIR_INPUT | GPIO_PULL_DOWN

But none gave correct results?

However keeping it GPIO_OPEN_DRAIN | GPIO_DIR_OUTPUT while reading worked for some sensors but not all sensors. So what should I do to read from those sensors?

Have you got pull-up resistors?

yes

Well since it looks like you have the basics done. We are going to need some more info.

What sensors?

All the sensors the same?

How many…?

What is the ratio for working to non-working?

Post your entire code in code tags.

Related issue? https://forum.sparkfun.com/viewtopic.php?f=14&t=41371

@Valen. Yes. They are related. But this is a more specific question so I made a separate thread.

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);
	}
}

ks190995:
@Valen. Yes. They are related. But this is a more specific question so I made a separate thread.

Makes sense, this one is more generically aimed. But I figured in the other thread you mentioned specific voltages, which could give more insight about what is going wrong at the port.

Yes. But can these two threads be combined?