Skip to content

Instantly share code, notes, and snippets.

@ksvbka
Last active April 14, 2022 09:43
Show Gist options
  • Save ksvbka/fec1f28ce40c276334fb to your computer and use it in GitHub Desktop.
Save ksvbka/fec1f28ce40c276334fb to your computer and use it in GitHub Desktop.
I2C Bit-Bangging - MSP430
/********************************************************************************
Module : I2C_SW
Author : 05/04/2015, by KienLTb - https://kienltb.wordpress.com/
Description : I2C software using bit-banging.
********************************************************************************/
/*-----------------------------------------------------------------------------*/
/* Header inclusions */
/*-----------------------------------------------------------------------------*/
#include "msp430g2553.h"
#include "i2c_sw.h"
/*-----------------------------------------------------------------------------*/
/* Local Macro definitions */
/*-----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------*/
/* Function prototypes */
/*-----------------------------------------------------------------------------*/
unsigned char Read_SCL(void);
unsigned char Read_SDA(void);
void Clear_SCL(void);
void Clear_SDA(void);
void I2C_Init(void);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_Writebit(unsigned char bit);
unsigned char I2C_ReadByte(void);
void I2C_WriteByte(unsigned char Data);
unsigned char I2C_ReadByte(void);
void I2C_WriteData(unsigned char *Data, unsigned char DevideAddr, unsigned char Register, unsigned char nLength);
void I2C_ReadData(unsigned char *Buff, unsigned char DevideAddr, unsigned char Register, unsigned char nLength);
/*-----------------------------------------------------------------------------*/
/* Function implementations */
/*-----------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------
Function : Read_SCL
Purpose : Set SCL as input and return current Logic level of SCL (0 or 1)
nomal is 1 because pullup by resistor
Parameters : None
Return : Logic level of SCL pin
--------------------------------------------------------------------------------*/
unsigned char Read_SCL(void)
{
I2C_PxDIR &= ~SCL;
return((I2C_PxIN & SCL) != 0);
}
/*--------------------------------------------------------------------------------
Function : Read_SDA
Purpose : Set SDA as input and return current Logic level of SDA (0 or 1),
nomal is 1 because pullup by resistor
Parameters : None
Return : Logic level of SDA pin
--------------------------------------------------------------------------------*/
unsigned char Read_SDA(void)
{
I2C_PxDIR &= ~SDA;
return((I2C_PxIN & SDA) != 0);
}
/*--------------------------------------------------------------------------------
Function : Clear_SCL
Purpose : Set SCL as Out put, logic Low
Parameters : None
Return : None
--------------------------------------------------------------------------------*/
void Clear_SCL(void)
{
I2C_PxDIR |= SCL;
}
/*--------------------------------------------------------------------------------
Function : Clear_SDA
Purpose : Set SDA as Out put, logic LOW
Parameters : None
Return : None
--------------------------------------------------------------------------------*/
void Clear_SDA(void)
{
I2C_PxDIR |= SDA;
}
/*--------------------------------------------------------------------------------
Function : I2C_Init
Purpose : Initialize I2C block
Parameters : None
Return : None
--------------------------------------------------------------------------------*/
void I2C_Init(void)
{
// Conffig SCL and SDA as GPIO
I2C_PxSEL &= ~(SCL + SDA);
I2C_PxSEL2 &= ~(SCL + SDA);
// Set SCL and SDA is logic HIGH
I2C_PxDIR &= ~(SCL + SDA);
I2C_PxOUT &= ~(SCL + SDA);
}
/*--------------------------------------------------------------------------------
Function : I2C_Start
Purpose : Send start signal
Parameters : None
Return : None
--------------------------------------------------------------------------------*/
void I2C_Start(void)
{
Read_SDA(); //set SDA to 1
I2C_DELAY();
Clear_SDA(); //set SDA to 0, currently SCL is 1
I2C_DELAY();
Clear_SCL(); //set SCL to 0
}
/*--------------------------------------------------------------------------------
Function : I2C_Stop
Purpose : Send Stop signal
Parameters : None
Return : None
--------------------------------------------------------------------------------*/
void I2C_Stop(void)
{
Clear_SDA(); //set SDA to 0
I2C_DELAY();
Read_SCL(); //set SCL to 1
I2C_DELAY();
Read_SDA(); //set SDA to 1
}
/*--------------------------------------------------------------------------------
Function : I2C_Writebit
Purpose : Write bit to I2C bus
Parameters : a bit need to write
Return : None
--------------------------------------------------------------------------------*/
void I2C_Writebit(unsigned char bit)
{
if(bit)
Read_SDA();
else
Clear_SDA();
I2C_DELAY();
Read_SCL();
I2C_DELAY();
Clear_SCL();
}
/*--------------------------------------------------------------------------------
Function : I2C_Readbit
Purpose : Read bit to I2C bus
Parameters : None
Return : unsigned char
--------------------------------------------------------------------------------*/
unsigned char I2C_Readbit(void)
{
unsigned char bit;
//Let the slave driver data
Read_SDA();
I2C_DELAY();
Read_SCL();
bit = Read_SDA();
I2C_DELAY();
Clear_SCL();
return bit;
}
/*--------------------------------------------------------------------------------
Function : I2C_WriteByte
Purpose : Write a Byte to I2C bus
Parameters : unsigned char Data
Return : None
--------------------------------------------------------------------------------*/
void I2C_WriteByte(unsigned char Data)
{
unsigned char nBit;
for(nBit = 0; nBit < 8; nBit++)
{
I2C_Writebit((Data & 0x80) != 0);
Data <<= 1;
}
I2C_Readbit(); // Waite NACK
}
/*--------------------------------------------------------------------------------
Function : I2C_ReadByte
Purpose : Read a Byte to I2C bus
Parameters : None
Return : unsigned char
--------------------------------------------------------------------------------*/
unsigned char I2C_ReadByte(void)
{
unsigned char Buff = 0;
unsigned char nBit;
for(nBit = 0; nBit < 8; nBit++)
{
Buff = (Buff << 1) | I2C_Readbit();
}
return Buff;
}
/*--------------------------------------------------------------------------------
Function : I2C_WriteData
Purpose : Write n Byte to I2C bus
Parameters : Data - Pointer to Data need to write
DevideAddr - Devide Address
Register - Register Address
nLength - Number of Byte need to write
Return : None
--------------------------------------------------------------------------------*/
void I2C_WriteData(unsigned char *Data, unsigned char DevideAddr, unsigned char Register, unsigned char nLength)
{
unsigned char nIndex;
I2C_Start();
I2C_WriteByte(DevideAddr << 1); // byDeviceAddr is 7 bit and command is write
I2C_WriteByte(Register);
for(nIndex = 0; nIndex < nLength; nIndex++)
{
I2C_WriteByte(*(Data + nIndex));
}
I2C_Readbit();
I2C_Stop();
}
/*--------------------------------------------------------------------------------
Function : I2C_ReadData
Purpose : Read n Byte from I2C bus
Parameters : Buff - Pointer to Buffer store value
DevideAddr - Devide Address
Register - Register Address
nLength - Number of Byte need to read
Return : None
--------------------------------------------------------------------------------*/
void I2C_ReadData(unsigned char *Buff, unsigned char DevideAddr, unsigned char Register, unsigned char nLength)
{
unsigned char nIndex;
I2C_Start();
I2C_WriteByte(DevideAddr << 1);
I2C_WriteByte(Register);
I2C_Stop();
_NOP(); // Short delay
I2C_Start();
_NOP(); // Short delay
I2C_WriteByte((DevideAddr << 1) | 1);
for(nIndex = 0; nIndex < nLength; nIndex++)
{
*(Buff + nIndex) = I2C_ReadByte();
if(nIndex > 0)I2C_Writebit(ACK);
}
I2C_Writebit(NACK);
I2C_Stop();
}
/********************************************************************************
Module : I2C_SW
Author : 05/04/2015, by KienLTb - https://kienltb.wordpress.com/
Description : I2C software using bit-banging.
********************************************************************************/
#ifndef _I2C_SW_H_
#define _I2C_SW_H_
/*-----------------------------------------------------------------------------*/
/* Macro definitions */
/*-----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------*/
/* Local Macro definitions */
/*-----------------------------------------------------------------------------*/
#define I2C_PxSEL P1SEL
#define I2C_PxSEL2 P1SEL2
#define I2C_PxDIR P1DIR
#define I2C_PxOUT P1OUT
#define I2C_PxIN P1IN
#define SCL BIT6
#define SDA BIT7
#define ACK 0x00
#define NACK 0x01
#define TIME_DELAY 100
#define I2C_DELAY() __delay_cycles(TIME_DELAY)
/*-----------------------------------------------------------------------------*/
/* Function prototypes */
/*-----------------------------------------------------------------------------*/
//NOTE: Need custom Read_SCL(), Read_SDA(), Clear_SCL(), Clear_SDA() to compatible Hardware.
unsigned char Read_SCL(void); // Set SCL as input and return current level of line, 0 or 1, nomal is 1 because pullup by res
unsigned char Read_SDA(void); // Set SDA as input and return current level of line, 0 or 1, nomal is 0 because pull by res
void Clear_SCL(void); // Actively drive SCL signal Low
void Clear_SDA(void); // Actively drive SDA signal Low
void I2C_Init(void);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_Writebit(unsigned char bit);
unsigned char I2C_Readbit(void);
void I2C_WriteByte(unsigned char Data);
unsigned char I2C_ReadByte(void);
void I2C_WriteData(unsigned char *Data, unsigned char DevideAddr, unsigned char Register, unsigned char nLength);
void I2C_ReadData(unsigned char *Buff, unsigned char DevideAddr, unsigned char Register, unsigned char nLength);
#endif // _I2C_SW_H_
@micovo
Copy link

micovo commented Feb 20, 2019

https://gist.github.com/ksvbka/fec1f28ce40c276334fb#file-i2c_sw-c-L222

Additional call of I2C_Readbit on the line 222 is causing issues on some I2C devices such as EEPROM (I wasn't able to write anything into the EEPROM until I've removed this line).
I2C_Readbit is called in the I2C_WriteByte function and doesn't need to be called again...

@micovo
Copy link

micovo commented Feb 21, 2019

https://gist.github.com/ksvbka/fec1f28ce40c276334fb#file-i2c_sw-c-L248

Multibyte read also fails on the I2C EEPROM. This like should look like this:
if(nIndex < nLength - 1) I2C_Writebit(ACK);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment