AnsweredAssumed Answered

Stuck STM32F4Discovery I2C with lcd pcf8574F

Question asked by doank.wisnu on Mar 5, 2014
Latest reply on Mar 7, 2014 by sung.chen_chung
Hello,

I'm trying to interface the STM32F4 Discovery with an LCD hd44780 display with pcf8574F through I2C. I'm stuck with my program, i can't write on my LCD. My lcd just blinking..

From the LCD I have SDA connected to PB7 and SCL connected to PB6. 
Any ideas why this is happening?

The I2C driver's code is the following:


#define I2C_ADDR 0x27
// HD44780 controller functions
#define LCD_CLEARDISPLAY        0x01
#define LCD_RETURNHOME          0x02
#define LCD_ENTRYMODESET        0x04
#define LCD_DISPLAYCONTROL      0x08
#define LCD_CURSORSHIFT         0x10
#define LCD_FUNCTIONSET         0x20
#define LCD_SETCGRAMADDR        0x40
#define LCD_SETDDRAMADDR        0x80


// flags for LCD_ENTRYMODESET
#define LCD_ENTRYRIGHT          0x00
#define LCD_ENTRYLEFT           0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00


// flags for LCD_DISPLAYCONTROL
#define LCD_DISPLAYON           0x04
#define LCD_DISPLAYOFF          0x00
#define LCD_CURSORON            0x02
#define LCD_CURSOROFF           0x00
#define LCD_BLINKON             0x01
#define LCD_BLINKOFF            0x00


// flags for LCD_CURSORSHIFT
#define LCD_DISPLAYMOVE         0x08
#define LCD_CURSORMOVE          0x00
#define LCD_MOVERIGHT           0x04
#define LCD_MOVELEFT            0x00


// flags for LCD_FUNCTIONSET
#define LCD_8BITMODE            0x10
#define LCD_4BITMODE            0x00
#define LCD_1LINE               0x00
#define LCD_2LINE               0x08
#define LCD_5x10DOTS            0x04
#define LCD_5x8DOTS             0x00




// master pins
#define LCD_BACKLIGHT_ON   (0,0,0,0,0,0,0,0) // ON  = data is LOW
#define LCD_BACKLIGHT_OFF  (1,0,0,0,0,0,0,0) // OFF = data is HIGH


#define LCD_RS_DATA        (0,1,0,0,0,0,0,0) // DATA (write to CGRAM/DDRAM)
#define LCD_RS_COMMAND     (0,0,0,0,0,0,0,0) // COMMANDS


#define LCD_RW_HIGH        (0,0,1,0,0,0,0,0) // READ from LCD
#define LCD_RW_LOW         (0,0,0,0,0,0,0,0) // WRITE to LCD (character or command)


#define LCD_PULSE_HIGH     (0,0,0,1,0,0,0,0) // Enable Pulse on pin 6
#define LCD_PULSE_LOW      (0,0,0,0,0,0,0,0) // when pulse goes from high to low the data is read



// COMMANDS: set DDRAM address ( display 2 x 16 )
#define LCD_CURSOR_LINE_MAX    15
#define LCD_CURSOR_TO_LINE_1  (1,0,0,0,0,0,0,0)
#define LCD_CURSOR_TO_LINE_2  (1,1,0,0,0,0,0,0)



// ASCII definitions for CR (\r) and LF (\n)
// used in printf
#define ASCII_CARRIAGE_RETURN 0x0d
#define ASCII_LINE_FEED       0x0a




// small buffer for prinf formatting
#define PRINT_BUFFER_SIZE 64



void I2C1_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);    // enable APB1 peripheral clock for I2C1
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);    // enable clock for SCL and SDA pins


GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // use PB6 and PB7
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;     // set pins to alternate function
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;     // set GPIO speed
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;     // set output to open drain --> the line has to be only pulled low, not driven high
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;     // enable pull up resistors
GPIO_Init(GPIOB, &GPIO_InitStruct);     // init GPIOB
// Connect I2C1 pins to AF
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);     // SCL
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // SDA
// configure I2C1
I2C_InitStruct.I2C_ClockSpeed = 100000; // 100kHz
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;     // I2C mode
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;     // 50% duty cycle --> standard
I2C_InitStruct.I2C_OwnAddress1 = 0x00;     // own address, not relevant in master mode
I2C_InitStruct.I2C_Ack = I2C_Ack_Disable;     // disable acknowledge when reading (can be changed later on)
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // set address length to 7 bit addresses
I2C_Init(I2C1, &I2C_InitStruct);     // init I2C1
// enable I2C1
I2C_Cmd(I2C1, ENABLE);
}



void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction){
// wait until I2C1 is not busy any more
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));


// Send I2C1 START condition
I2C_GenerateSTART(I2Cx, ENABLE);


// wait for I2C1 EV5 --> Slave has acknowledged start condition
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));


// Send slave Address for write
I2C_Send7bitAddress(I2Cx, address, direction);


/* wait for I2Cx EV6, check if
* either Slave has acknowledged Master transmitter or
* Master receiver mode, depending on the transmission
* direction
*/
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));
}
}


/* This function transmits one byte to the slave device
* Parameters:
* I2Cx --> the I2C peripheral e.g. I2C1
* data --> the data byte to be transmitted
*/




void I2C_write(I2C_TypeDef* I2Cx, uint8_t data)
{
// wait for I2C1 EV8 --> last byte is still being transmitted (last byte in SR, buffer empty), next byte can already be written
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING));
I2C_SendData(I2Cx, data);
}



void I2C_stop(I2C_TypeDef* I2Cx){


// Send I2C1 STOP Condition after last byte has been transmitted
I2C_GenerateSTOP(I2Cx, ENABLE);
// wait for I2C1 EV8_2 --> byte has been transmitted
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}






void lcd_simple_print( const char *str )
{
     uint8_t _cursor_position = 0;


  lcd_command_clear();


  while( *str ) {


          _cursor_position++;
          if( _cursor_position > LCD_CURSOR_LINE_MAX | *str == ASCII_LINE_FEED )
          {
               lcd_command( LCD_CURSOR_TO_LINE_2 );
               _cursor_position = 0;
               str++;
          } else
          {
             lcd_data( *str );
             str++;
          }


     }
}




void lcd_printf( const char *format, ... )
{
     va_list args;
     char *buf = ( char *) malloc( PRINT_BUFFER_SIZE * sizeof( char ) );


     if( buf != 0 )
     {
          va_start( args, format );


          if( vsnprintf( buf, PRINT_BUFFER_SIZE * sizeof( char ), format, args ) > 0 )
          {
               lcd_simple_print( buf );
          } else
          {
               lcd_simple_print( buf );
               lcd_simple_print( "lcd_printf: maxlen \
                    character limit was reached or some other error, \
                    such as an invalid format specification\n" );
          }


          va_end( args );
          free( buf );


   } else
   {
      lcd_simple_print( "lcd_printf: not enough memory\n" );
   }
}




// some simple delay, should use timer
//
void delay_ms( uint32_t _time )
{
      _time = _time * 420;


  while( _time-- )
  {
  }
}


// abstract functions
// concept taken from arduino I2C LCD library
//
//
void lcd_command( uint8_t value )
{
   lcd_send( value, LCD_RS_COMMAND );
}


void lcd_command_clear( void )
{
     lcd_command( LCD_CLEARDISPLAY );
     delay_ms( 100 );
}


void lcd_data( uint8_t value )
{
   lcd_send( value, LCD_RS_DATA );
}


// convert function for 4-bit communication
// writes either command or data
//
//
void lcd_send( uint8_t value, uint8_t mode )
{
   uint8_t high_nibble = ( value>>4 ) & 0x0f;
   uint8_t low_nibble  = ( value    ) & 0x0f;


   lcd_i2c_pulse( high_nibble | mode );
   lcd_i2c_pulse( low_nibble  | mode );
}


// the "pulse" function simulates the ramp ON/OFF
// the controller reads data when the LCD_PULSE pin
// goes from HIGH to LOW
//
void lcd_i2c_pulse( uint8_t _data )
{
      I2C_start( I2C1, I2C_ADDR<<1, I2C_Direction_Transmitter );
      I2C_write( I2C1, (uint8_t) ( _data ) );
      I2C_stop( I2C1 );


      I2C_start( I2C1, I2C_ADDR<<1, I2C_Direction_Transmitter );
      I2C_write( I2C1, (uint8_t) ( _data ) | LCD_PULSE_HIGH );
   I2C_stop( I2C1 );


   I2C_start( I2C1, I2C_ADDR<<1, I2C_Direction_Transmitter );
   I2C_write( I2C1, (uint8_t) ( _data ) );
      I2C_stop( I2C1 );
}


// sets the controller: 4-bit communication
//                      interaction with 1602 LCD
//
//
void lcd_i2c_init( void )
{
   // switch controller to 8bit mode, regardless the mode
      // the  controller is in. there should be waits added
      // 5ms for command writes and 200us for data writes.

   lcd_i2c_pulse( 0x03 );
   lcd_i2c_pulse( 0x03 );


   //  LCD in 8-bit mode


   lcd_i2c_pulse( 0x03 );


   // switch to 4-bit mode
   lcd_i2c_pulse( 0x02 );


   // controller is in 4-bit mode, configure its behaviour


      lcd_command( LCD_FUNCTIONSET | \
                LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS );


      lcd_command( LCD_DISPLAYCONTROL | \
                LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF );


   lcd_command( LCD_ENTRYMODESET | \
                LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT );
};

int main(void)
{     unsigned char *text;
     uint32_t n;
while(1)
    {
       /* msTicks = 0;
    while ( msTicks < 1000 );
*/

    lcd_i2c_pulse();
    delay_ms( 1000 )





    lcd_simple_print("lcd test");
     delay_ms( 1000 )

I would really appreciate any yout suggestions. :) 


Thank you.











Outcomes