Stm32 I2C PCF8563 Blockup

After almost 27days of my current project, yesterday I was reviewing my efforts, thought I did a neat job and did it really quick.

-Interfaced a 320x240 TFT and SRAM with DMA and FSMC,
-Wrote the Driver and the filtering logic for handling the touch screen IC,
-Wrote the Drivers for handling internal and external Flash ( IAP & SPI ),
-Managed to remove the TFT flickering issue whenever a SRAM access was done, since the CS of TFT did not work and the WR/ signal was shared between both the peripherals, so wrote a pretty neat state machine and filtered out the unwanted clock pulses from the TFT.
-Mixed C and C++ to produce a pretty neat GUI and event handling mechanism for the widgets.
-Managed to interface my android phone along with the board on bluetooth and get the ECG reproduction on my phone.

I managed to take on all the above relatively quicker with respect to my deadline requirements, until i realised that I took almost 6 days upon this stupid problem of interfacing an I2C based commonly used RTC chip - PCF8563....man this was the greatest shocker!!
That was when I decided to write this article, I know it's not something you write about, I2C interfacing is one of the most common things that an embedded guy does......But still I will write and explain whats so special with STM32 especially when you are doing a lot of things with the small guy!!

Here's the source code, look out for something special, if you can guess it, then trust me you are doing good. i know the codes not perfect with all the redundant loops, but in my case, no worries I have a watchdog, and also i do very less transactions on i2c bus -----


#include "includes.h"


TypedefRTCStruct RTCStruct;


/*********************************************************************************************************************
Function Name : InitI2C
Parameters : None
Description : Configures the I2C to be used for interfacing with the RTC
Author : Som
**********************************************************************************************************************/
void InitI2C( void )
{
I2C_InitTypeDef I2C_InitStructure;

// Enable the clock for I2C1
RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE );

I2C_DeInit( I2C1 );

/* I2C configuration */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = PCF8563_SLAVE_ADDRESS;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;

/* I2C Peripheral Enable */
I2C_Cmd(I2C1, ENABLE);

/* Apply I2C configuration after enabling it */
I2C_Init(I2C1, &I2C_InitStructure);
}


/*******************************************************
* Name:I2C_8563_PageWrite
* input:unsigned char* pBuffer, unsigned char WriteAddr, unsigned char NumByteToWrite
* output:none
* Author : Somdutta roy
*********************************************************/
void I2C_8563_BufferWrite(unsigned char *pBuffer, unsigned char WriteAddr, unsigned short NumByteToWrite)
{
/* FSMC Clock Disable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, DISABLE);


// Initialise the I2C Bus
InitI2C();

/* Wait if bus is busy */
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

/* Send STRAT condition */
I2C_GenerateSTART(I2C1, ENABLE);

/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

/* Send address for write */
I2C_Send7bitAddress(I2C1, PCF8563_SLAVE_ADDRESS, I2C_Direction_Transmitter);

/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

/* Send the EEPROM's internal address to write to */
I2C_SendData(I2C1, WriteAddr);

/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

/* Send the byte to be written */

for (short i = 0; i < NumByteToWrite; i++) { I2C_SendData(I2C1, pBuffer[i]); /* Test on EV8 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); } /* Send STOP condition */ I2C_GenerateSTOP(I2C1, ENABLE); /*De inittialise the I2C Bus*/ I2C_DeInit( I2C1 ); // Disable the clock for I2C1 RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, DISABLE ); /* FSMC Clock Enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);

}


/******************************************************************************
* name: 2C_8563_BufferRead
* input: pBuffer read to it,ReadAddr read address,NumByteToRead the number
* of ready to read
* output: None
* Author : Somdutta Roy
*******************************************************************************/
void I2C_8563_BufferRead(unsigned char* pBuffer, unsigned char ReadAddr, u16 NumByteToRead)
{
/* FSMC Clock Disable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, DISABLE);


// Initialise the I2C Bus
InitI2C();

/* Wait if bus is busy */
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

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

/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

/* Send EEPROM address for write */
I2C_Send7bitAddress(I2C1, PCF8563_SLAVE_ADDRESS, I2C_Direction_Transmitter);

/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

/* Clear EV6 by setting again the PE bit */
I2C_Cmd(I2C1, ENABLE);

/* Send the EEPROM's internal address to write to */
I2C_SendData(I2C1, ReadAddr);

/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

/* Send STRAT condition a second time */
I2C_GenerateSTART(I2C1, ENABLE);

/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

/* Send 8563 address for read */
I2C_Send7bitAddress(I2C1, PCF8563_SLAVE_ADDRESS, I2C_Direction_Receiver);

/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

/* While there is data to be read */
while(NumByteToRead)
{
if(NumByteToRead == 1)
{
/* Disable Acknowledgement */
I2C_AcknowledgeConfig(I2C1, DISABLE);

/* Send STOP Condition */
I2C_GenerateSTOP(I2C1, ENABLE);
}

/* Test on EV7 and clear it */
if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
{
/* Read a byte from the EEPROM */
*pBuffer = I2C_ReceiveData(I2C1);

/* Point to the next location where the byte read will be saved */
pBuffer++;

/* Decrement the read bytes counter */
NumByteToRead--;
}
}

/*De inittialise the I2C Bus*/
I2C_DeInit( I2C1 );

// Disable the clock for I2C1
RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, DISABLE );

/* FSMC Clock Enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);

}

/*******************************************************
* Name:ReadRTCDateTime
* input:None
* output:none
* Author : Somdutta roy
*********************************************************/
void ReadRTCDateTime( void )
{
unsigned char ucaBuf[16];

I2C_8563_BufferRead( ucaBuf, 2, 7 );

RTCStruct.ucSec = BcdToBin(ucaBuf[0]);
RTCStruct.ucMin = BcdToBin(ucaBuf[1] & 0x7F );
RTCStruct.ucHour = BcdToBin(ucaBuf[2] & 0x3F );
RTCStruct.ucDay = BcdToBin(ucaBuf[3] & 0x3F );
RTCStruct.ucMonth = BcdToBin(ucaBuf[5] & 0x1F );
RTCStruct.ucYear = BcdToBin(ucaBuf[6] & 0xFF );

GlobalBlockingDelay( 2 );
}

/*******************************************************
* Name:SetRTCDateTime
* input:None
* output:none
* Author : Somdutta roy
*********************************************************/
unsigned char SetRTCDateTime( void )
{
unsigned char ucaBuf[16];

if (IsValidDateTime( &RTCStruct ) == FALSE)
{
return 0;
}

ucaBuf[0] = 0;
ucaBuf[1] = 0;
ucaBuf[2] = 0;
ucaBuf[3] = BinToBcd( RTCStruct.ucMin );
ucaBuf[4] = BinToBcd( RTCStruct.ucHour );
ucaBuf[5] = BinToBcd( RTCStruct.ucDay );
ucaBuf[6] = 0;
ucaBuf[7] = BinToBcd( RTCStruct.ucMonth );
ucaBuf[8] = BinToBcd( RTCStruct.ucYear ) ;

ucaBuf[9] = 0x00;
ucaBuf[10] = 0x00;
ucaBuf[11] = 0x01;
ucaBuf[12] = 0x00;

ucaBuf[13] = 0x00;
ucaBuf[14] = 0x00;
ucaBuf[15] = 0x00;

I2C_8563_BufferWrite( ucaBuf, 0, 16);

GlobalBlockingDelay( 2 );

return 1;
}


/*******************************************************
* Name:IsValidDateTime
* input:None
* output:none
* Author : Somdutta roy
*********************************************************/
unsigned char IsValidDateTime(TypedefRTCStruct *pRTCStruct)
{
unsigned char RTC_MonthVal[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

if( ( short )pRTCStruct->ucYear < RTC_YEAR_MIN || pRTCStruct->ucYear > RTC_YEAR_MAX)
{
return 0;
}

if (pRTCStruct->ucYear == RTC_YEAR_MIN)
{
if ( ( short )pRTCStruct->ucMonth < 0) { return 0; } else if( ( short )pRTCStruct->ucDay < 0) { return 0; } } if((pRTCStruct->ucMonth < 1) || (pRTCStruct->ucMonth > 12))
{
return 0;
}

if((pRTCStruct->ucDay < 1) || (pRTCStruct->ucDay > 31))
{
return FALSE;
}

if( pRTCStruct->ucDay > RTC_MonthVal[pRTCStruct->ucMonth - 1])
{
if(pRTCStruct->ucMonth == 2)
{
//if (IsLeapYear( pRTCStruct->ucYear))
if( pRTCStruct->ucYear % 4 != 0 )
{
if (pRTCStruct->ucDay > 29 )
{
return 0;
}
}
else if (pRTCStruct->ucDay > 28 )
{
return 0;
}
}
else
{
return 0;
}
}

if ((pRTCStruct->ucHour > 23) || (pRTCStruct->ucMin > 59) || (pRTCStruct->ucSec > 59) )
{
return 0;
}

return 1;
}

Well looks like a very simple snippet of code doesn't it, but reality check is that i took 6 working days on this, even after implementing the same stuff so many times starting from 8051 to arm9 linux and wince based boards.

Notice the code in italics, i forgot to stop the FSMC clock, and stm32 has its limitation that I2C and FSMC do not work together, you need to time multiplex them. The fact is as we embedded systems developers move ahead with more faster and powerful micro controllers, we tend to forget our roots, stuffs we learnt when we had the humble 8051 cores to play with, Timing Timing and more Timing!! Bus Timings are the way to go in embedded systems.

No comments:

Post a Comment