I2C clock stays low after sending address (scope picture included)[solved]

Share code examples or discuss embedded software, including device drivers, interrupt handlers, middleware and application code.

Moderators: Markus Girdland, Mattias Norlander

didier9
Posts: 10
Joined: Sat Jan 05, 2019 1:48 am

I2C clock stays low after sending address (scope picture included)[solved]

Postby didier9 » Thu Jan 10, 2019 1:00 am

I am running this simple example below from Erwin Ouyang

Simply import it into a new CMSIS project. It compiled immediately with no error (which was encouraging).

The example originally included a 2x16 LCD but I am not using it so I stripped the code to the bare minimum just to check the I2C.

The problem is that the I2C hangs after sending the address. The clock line never goes back up. There is no device attached to the I2C port, just pull-up resistors and the scope.

Image

The code uses the standard libraries with no modification.

Any help appreciated.

Here is the code:

Code: Select all

#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_i2c.h"


#define I2Cx_RCC         RCC_APB1Periph_I2C2
#define I2Cx            I2C2
#define I2C_GPIO_RCC      RCC_APB2Periph_GPIOB
#define I2C_GPIO         GPIOB
#define I2C_PIN_SDA         GPIO_Pin_11
#define I2C_PIN_SCL         GPIO_Pin_10

#define SLAVE_ADDRESS      0x08

void i2c_init( void );
void i2c_start( void );
void i2c_stop( void );
void i2c_address_direction( uint8_t address, uint8_t direction );
void i2c_transmit( uint8_t byte );
uint8_t i2c_receive_ack( void );
uint8_t i2c_receive_nack( void );
void i2c_write( uint8_t address, uint8_t data );
void i2c_read( uint8_t address, uint8_t* data );

uint8_t receivedByte;
   
int main( void ){
   int i;
   
   i2c_init();
   
   while( 1 ){
      // Write 0x01 to slave (turn on LED blinking)
      i2c_write( SLAVE_ADDRESS, 0x01 );

      // small delay
      for( i=0; i<100000; i++ );
      
      // Read data from slave
      i2c_read( SLAVE_ADDRESS, &receivedByte );

      // small delay
      for( i=0; i<100000; i++ );
   }
}

void i2c_init(){
   // Initialization struct
   I2C_InitTypeDef I2C_InitStruct;
   GPIO_InitTypeDef GPIO_InitStruct;
   
   // Step 1: Initialize I2C
   RCC_APB1PeriphClockCmd( I2Cx_RCC, ENABLE );
   I2C_InitStruct.I2C_ClockSpeed = 100000;
   I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
   I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
   I2C_InitStruct.I2C_OwnAddress1 = 0x00;
   I2C_InitStruct.I2C_Ack = I2C_Ack_Disable;
   I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
   I2C_Init( I2Cx, &I2C_InitStruct );
   I2C_Cmd( I2Cx, ENABLE );
   
   // Step 2: Initialize GPIO as open drain alternate function
   RCC_APB2PeriphClockCmd( I2C_GPIO_RCC, ENABLE );
   GPIO_InitStruct.GPIO_Pin = I2C_PIN_SCL | I2C_PIN_SDA;
   GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
   GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
   GPIO_Init( I2C_GPIO, &GPIO_InitStruct );
}

void i2c_start(){
   // Wait until I2Cx is not busy anymore
   while( I2C_GetFlagStatus( I2Cx, I2C_FLAG_BUSY ));
   
   // Generate start condition
   I2C_GenerateSTART( I2Cx, ENABLE );
   
   // Wait for I2C EV5.
   // It means that the start condition has been correctly released
   // on the I2C bus (the bus is free, no other devices is communicating))
   while( !I2C_CheckEvent( I2Cx, I2C_EVENT_MASTER_MODE_SELECT ));
}

void i2c_stop(){
   // Generate I2C stop condition
   I2C_GenerateSTOP( I2Cx, ENABLE );
   // Wait until I2C stop condition is finished
   while( I2C_GetFlagStatus( I2Cx, I2C_FLAG_STOPF ));
}

void i2c_address_direction( uint8_t address, uint8_t direction ){
   // Send slave address
   I2C_Send7bitAddress( I2Cx, address, direction );
   
   // Wait for I2C EV6
   // It means that a slave acknowledges his address
   if( direction == I2C_Direction_Transmitter ){
      while( !I2C_CheckEvent( I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ));
   }else if( direction == I2C_Direction_Receiver ){
      while( !I2C_CheckEvent( I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ));
   }
}

void i2c_transmit( uint8_t byte ){
   // Send data byte
   I2C_SendData( I2Cx, byte );
   // Wait for I2C EV8_2.
   // It means that the data has been physically shifted out and
   // output on the bus)
   while( !I2C_CheckEvent( I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED ));
}

uint8_t i2c_receive_ack(){
   // Enable ACK of received data
   I2C_AcknowledgeConfig( I2Cx, ENABLE );
   // Wait for I2C EV7
   // It means that the data has been received in I2C data register
   while( !I2C_CheckEvent( I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED ));
   
   // Read and return data byte from I2C data register
   return I2C_ReceiveData( I2Cx );
}

uint8_t i2c_receive_nack(){
   // Disable ACK of received data
   I2C_AcknowledgeConfig( I2Cx, DISABLE );
   // Wait for I2C EV7
   // It means that the data has been received in I2C data register
   while( !I2C_CheckEvent( I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED ));
   
   // Read and return data byte from I2C data register
   return I2C_ReceiveData( I2Cx );
}

void i2c_write( uint8_t address, uint8_t data ){
   i2c_start();
   i2c_address_direction( address << 1, I2C_Direction_Transmitter );
   i2c_transmit( data );
   i2c_stop();
}

void i2c_read( uint8_t address, uint8_t* data ){
   i2c_start();
   i2c_address_direction( address << 1, I2C_Direction_Receiver );
   *data = i2c_receive_nack();
   i2c_stop();
}
Last edited by didier9 on Thu Jan 10, 2019 3:04 am, edited 1 time in total.

didier9
Posts: 10
Joined: Sat Jan 05, 2019 1:48 am

Re: I2C clock stays low after sending address (scope picture included)

Postby didier9 » Thu Jan 10, 2019 1:44 am

Found the issue...

It turns out the driver actually hangs until a slave acknowledges.
That was unexpected.
I looked at the code more closely and indeed the i2c_address_direction() function call waits for an acknowledgment.
I modified the function as shown below and now it properly exits with an error code if no slave acknowledges.

It is pretty crude, I should use a timer but in a pinch it will work.

Code: Select all

uint8_t i2c_address_direction( uint8_t address, uint8_t direction ){
   int i=10000;
   // Send slave address
   I2C_Send7bitAddress( I2Cx, address, direction );

   // Wait for I2C EV6
   // It means that a slave acknowledges his address
   if( direction == I2C_Direction_Transmitter ){
      while( !I2C_CheckEvent( I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) ){
         if( i--==0 ){
            return( FALSE );
         }
      }
   }else if( direction == I2C_Direction_Receiver ){
      while( !I2C_CheckEvent( I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) ){
         if( i--==0 ){
            return( FALSE );
         }
      }
   }
   return( TRUE );
} // i2c_address_direction()


and I have added code to catch the error in the calling functions:

Code: Select all

void i2c_write( uint8_t address, uint8_t data ){
   i2c_start();
   if( !i2c_address_direction( address << 1, I2C_Direction_Transmitter )){
      i2c_stop();
      return;
   }
   i2c_transmit( data );
   i2c_stop();
} // i2c_write()

/* this is the high level read byte function */
void i2c_read( uint8_t address, uint8_t* data ){
   i2c_start();
   if( !i2c_address_direction( address << 1, I2C_Direction_Receiver )){
      i2c_stop();
      return;
   }
   *data = i2c_receive_nack();
   i2c_stop();
} // i2c_read()


It is necessary because in my project, I use optional I2C peripherals so the processor must make a note of which I2C slaves are responding and not needlessly hang when one is not responding.


Return to “Embedded target code development discussions”

Who is online

Users browsing this forum: No registered users and 1 guest