Работаем с шиной I2C (SMBus)

Пн 29 Март 2010 by admin

Шина I2C представляет собой последовательную шину данных для связи внутренних компонентов устройств. Разработана фирмой Phillips в 1980-х годах.

Шина SMBus основана на шине I2C и широко применяется в современных компьютерах. Основное отличие от оригинальной I2C - ограничение минимальной рабочей частоты за счет введения таймаутов операций передачи данных.

I2C для своей работы использует две двунаправленные линии передачи данных, которые подтянуты к напряжению питания и управляются схемой открытого коллектора или открытого стока.

SDA - последовательная линия данных;

SCL - последовательная линия тактирования.

Обе линии нагружены резисторами, стандартное напряжение на шине +5В или +3,3В. Однако, оно может быть и другим.

Адресация осуществляется в 7 или 10-битном адресном пространстве, на одну шину возможно подключение до 112 или 1008 устройств соответственно. Основной режим работы - на скорости 100 кБит/с или в высокоскоростном режиме 400 кБит/с.

Версия протокола 2.0, выпущенная в 1998 году предусматривает возможность работы на скоростях до 3,4 МБит/с.

[caption id="attachment_212" align="aligncenter" width="425" caption="Пример схемотехники"]Пример схемотехники[/caption]

Не буду рассматривать алгоритм работы шины, т.к. подобной информации очень много в сети, в том числе на русском языке. Предлагаю ознакомиться со статьей в русской Wiki.

Приведу пример библиотеки на языке C, реализующей программную работу по протоколу I2C. Код совершенно не привязан ни к каким конкретным микроконтроллерам (более того, вообще к микроконтроллерам как таковым) и может быть использован где угодно. Достаточно откорректировать строки, осветственные за управление линиями SDA и SCL. Код снабжен комментариями.

Скачать исходный текст:

i2c-soft.h

i2c-soft.c

Смотреть исходный текст:

i2c-soft.h:

[c]/*
* i2c-soft.h
*
* Created on: 19.12.2009
* Author: Pavel V. Gololobov
*/
#ifndef I2C_H_
#define I2C_H_
#include <io.h>
#include <stdint.h>
#define NO_I2C_ACK 0
#define OK_I2C_ACK 1
#ifndef SDA
#define I2COUT P3OUT // Write to Port
#define I2CIN P3IN // Read from Port
#define I2CDIR P3DIR // Set Port Direction
#define I2CSEL P3SEL // Alternative Port Fuctions
#define SDA 0x02 // Serial Data Line PIN
#define SCL 0x08 // Serial Clock Line PIN
#endif
// Init Bus
void i2c_Init();
// Start Transfer
void i2c_Start();
// Stop Transfer
void i2c_Stop();
// Write Transfer
int16_t i2c_Write(uint8_t);
// Read Transfer
uint8_t i2c_Read(int16_t);
// Read Byte
uint8_t i2c_ReadByte(uint8_t, uint8_t);
// Write Byte
void i2c_WriteByte(uint8_t, uint8_t, uint8_t);

#endif[/c]

i2c-soft.c

[c]/*
* i2c-soft.c
*
* Created on: 19.12.2009
* Author: Pavel V. Gololobov
*/
#include <io.h>
#include <stdint.h>
#include "i2c-soft.h"
//------------------------------------------------------------------
// I2C Speed Down
//------------------------------------------------------------------
#define I2CWAIT i2c_Wait(100);
//------------------------------------------------------------------
// I2C Delay
//------------------------------------------------------------------
void i2c_Wait(int32_t n)
{
do
{
nop();
nop();
nop();
nop();
nop();
}
while(--n);
}
//------------------------------------------------------------------
// I2C SDA SCL Control
//------------------------------------------------------------------
static void SetLowSDA()
{
I2CDIR |= SDA;
I2CWAIT
}
static void SetHighSDA()
{
I2CDIR &= ~SDA;
I2CWAIT
}
static void SetLowSCL()
{
I2COUT &= ~SCL;
I2CWAIT
}
static void SetHighSCL()
{
I2COUT |= SCL;
I2CWAIT
}
//------------------------------------------------------------------
// I2C Initialize Bus
//------------------------------------------------------------------
void i2c_Init()
{
I2CSEL &= ~SDA;
I2CSEL &= ~SCL;
I2COUT &= ~SCL;
I2COUT &= ~SDA;
I2CDIR |= SCL;
I2CDIR &= ~SDA;
SetHighSCL();
SetLowSDA();
SetHighSDA();
}
//------------------------------------------------------------------
// I2C Start Data Transfer
//------------------------------------------------------------------
void i2c_Start()
{
SetHighSCL();
SetHighSDA();
SetHighSCL();
SetLowSDA();
SetLowSCL();
SetHighSDA();
}
//------------------------------------------------------------------
// I2C Stop Transfer
//------------------------------------------------------------------
void i2c_Stop()
{
SetLowSCL();
SetLowSDA();
SetHighSCL();
SetLowSDA();
SetHighSCL();
SetHighSDA();
}
//------------------------------------------------------------------
// I2C Write Transfer
//------------------------------------------------------------------
int16_t i2c_Write(uint8_t a)
{
int16_t i;
int16_t return_ack;
for (i = 0; i < 8; i++)
{
SetLowSCL();
if (a & 0x80)
SetHighSDA();
else
SetLowSDA();
SetHighSCL();
a <<= 1;
}
SetLowSCL();
/* ack Read */
SetHighSDA();
SetHighSCL();
if (I2CIN & SDA)
return_ack = NO_I2C_ACK;
else
return_ack = OK_I2C_ACK;
SetLowSCL();
return (return_ack);
}
//------------------------------------------------------------------
// I2C Read Transfer
//------------------------------------------------------------------
uint8_t i2c_Read(int16_t ack)
{
int16_t i;
uint8_t caracter = 0x00;
SetLowSCL();
SetHighSDA();
for (i = 0; i < 8; i++)
{
caracter = caracter << 1;
SetHighSCL();
if (I2CIN & SDA)
caracter = caracter + 1;
SetLowSCL();
}
if (ack)
{
SetLowSDA();
}
SetHighSCL();
SetLowSCL();
return (caracter);
}
//------------------------------------------------------------------
// I2C Read Byte
//------------------------------------------------------------------
uint8_t i2c_ReadByte(uint8_t nAddress, uint8_t nRegister)
{
i2c_Start();
i2c_Write(nAddress);
i2c_Write(nRegister);
i2c_Start();
i2c_Write(nAddress | 0x01);
uint8_t ret = i2c_Read(0);
i2c_Stop();
return ret;
}
//------------------------------------------------------------------
// I2C Write Byte
//------------------------------------------------------------------
void i2c_WriteByte(uint8_t nAddress, uint8_t nRegister, uint8_t nValue)
{
i2c_Start();
i2c_Write(nAddress);
i2c_Write(nRegister);
i2c_Write(nValue);
i2c_Stop();
}[/c]