Обратная связь



Авторизироваться


Нет аккаунта?
Присоединяйтесь



Забыли пароль?
Восстановление пароля

  • :
  • :
Реклама на сайте Контакты Отзывы Обратная связь Войти / Зарегистрироваться

Протоколы связи между цифровыми устройствами: I2C

Присоединяйтесь к нам в соц. сетях!

В данной статье я бы хотел рассказать об I2C интерфейсе. Применяют этого зверя при локальном расположении мастера и ведомого. В данной статье буду использовать контроллеры производителя Microchip: PIC18F4520, PIC24HJ128GP504 и компиляторы XC8 и XC16 соответственно.

Пару общих слов:

  1. Любая команда должна начинаться со СТАРТА и заканчиваться СТОПОМ
  2. Чтение происходит в высоком состоянии линии SCL
  3. Запись происходит в низком состоянии линии SCL
  4. Работа по данному интерфесу построена на переходах логических уровней линии SCL и не связана со временем, как RS485.

Общий структурный вид протокола I2C:

  1. ST - Старт бит
  2. SDA + W - Адресс+бит чтения(0)/записи(1)
  3. SAK - Ответ от ведомого устройства(slave acknowledge)
  4. SUB - (обычно это адрес регистра)8-bit sub-address
  5. DATA - сам байт данных
  6. SP - стоп бит
  7. SR - Рестарт
  8. NMASK - No Master Acknowledge(выдается мастером)
  9. SP - Стор бит
i2c протокол i2c протокол

Рассмотрим подробнее.

Запись одного байта, это таблица 11.

1)ST(Start) - Начинает любую последовательность команд работы по i2c.

i2c старт i2c старт тайминг

Внимание:
TBRG это период I2C. Как видно, линия данных опережает линию такта.

2)SDA + W - Адресс + 0-ой бит записи(1).

3)SAK - Ответ от ведомого устройства(slave acknowledge). Интересный и важный момент. Данное дело имеет место если ведомое устройство поняло ваш байт. Т.е мастер отпускает линию данных в высоком уровне, а ведомое устройство должно провалить линию в низкое состояниев с конца 8 до конца 9 SLC пульса.

i2c ack i2c ack тайминги

Внимание! Как видим, данные провалены в низкий уровень т.е ведомый понял команду.

4)SUB - (обычно это адрес регистра)8-bit sub-address. Тут думаю все понятно.

5)DATA - сам байт данных. Тут думаю все понятно.

6)SP - стоп бит

i2c стоп бит i2c стоп тайминги

Внимание!! Последовательную запись не разбираю, так как там просто повторяется часть вышеприведенной последовательности.

Чтение одного байта, это таблица 13.

Для осознания этой последовательности требуется прочитать все выше. Тут только стоит описать

1)SR - Рестарт. Это действие нужно выполнить в последовательности операции чтения(см структуру интерфейса). Делается это путем:

//для PIC18
void RestartI2(void)// генерация бита повторный старт
{
RSEN = 1; //Generate Restart
while (RSEN); //Wait for restart
SSPIF = 0;
}

//для PIC24
void RestartI2C(void)// генерация бита повторный старт
{
I2C1CONbits.RSEN = 1; //Generate Restart
while (I2C1CONbits.RSEN); //Wait for restart
}

2)NMASK - No Master Acknowledge(выдается мастером). Также используется только в операциях чтения и означает конец запросу данных.

i2c noack

Исходники C+

Настройку контроллеров, Осцилятора, частоты I2C и конфигурацию выводов осилите сами.

PIC18F4520, компилятор XC8

void i2cinitial(void) {
TRISC3 = 1; //i2c
TRISC4 = 1; //i2c
SSPIE = 0;
SSPIF = 0;
SSPADD = 0x30; //
SSPM3 = 1;
SSPM2 = 0;
SSPM1 = 0;
SSPM0 = 0;
//------------------
SMP = 0; //Slew rate control disabled for Standard Speed mode (100 kHz and 1 MHz)
CKE = 1;
SSPEN = 1; //Enables the serial port and configures the SDA and SCL pins as the serial port pins
return;
}
//--------------------------------

void StartI2(void) // генерация стартового бита
{
SEN = 1; //Generate Start COndition
while (SEN);
NOP();
SSPIF = 0;
}
//-------------------------------

void RestartI2(void)// генерация бита повторный старт
{
RSEN = 1; //Generate Restart
while (RSEN); //Wait for restart
SSPIF = 0;
}
//------------------------------------------------------------------------------

void StopI2(void)// генераци¤ бита stop
{
PEN = 1; //Generate Stop Condition
while (PEN); //Wait for Stop
SSPIF = 0;
}
//------------------------------

unsigned char WriteI2(unsigned char byte) // отправка одного байта
{
unsigned char ack = 1;
ACKSTAT = 1;
SSPIF = 0;
SSPBUF = byte; //Load byte to I2C1 Transmit buffer
while (!SSPIF)//1byte transfer total finish
{
if (ack)//was not ack from slave
{
if (!ACKSTAT)//ack got
ack = 0;
}
}
SSPIF = 0;
return (ack);
}
//--------------------------------

void IdleI2(void) // ожидание освобождения линии
{
while (RW && BF); //Wait for bus Idle
}
//------------------------------

void NotAckI2(void)// генерация бита NoASK
{
ACKDT = 1; //Set for NotACk
ACKEN = 1;
while (ACKEN); //wait for ACK to complet
ACKDT = 0;
SSPIF = 0;
}
//-------------------------------

void AckI2(void)// генерация бита ASK
{
ACKDT = 1; //Set for ACk
ACKEN = 1;
while (ACKEN); //wait for ACK to complete
}
//------------------------------

unsigned char getI2(void) // функция приема байта
{
RCEN = 1; //Enable Master receive
while (RCEN); //Wait for receive buffer to be full
SSPIF = 0;
return (SSPBUF); //Return data in buffer
}
//------------------------------

void i2cwrite(char devaddr, char regaddr, unsigned char data)// функция записи байта
{
unsigned char i2_error = 0;
IdleI2(); //Wait for bus Idle
StartI2(); //Generate Start condition
i2_error = WriteI2(devaddr); //Write Control byte
if (i2_error > 0)
goto skip;
i2_error = WriteI2(regaddr); //Write Low Address
if (i2_error > 0)
goto skip;
i2_error = WriteI2(data); //Write Data
skip:
switch (devaddr) {
case 0x3C:
if (i2_error > 0)
M.i2_error = 1;
else
M.i2_error = 0;
break;
case 0x30:
if (i2_error > 0)
A.i2_error = 1;
else
A.i2_error = 0;
break;
case 0x32:
if (i2_error > 0)
A.i2_error = 1;
else
A.i2_error = 0;
break;
}
StopI2(); //Initiate Stop Condition
}
//--------------------------------

unsigned char i2cread(unsigned char devaddr, unsigned char regaddr)// чтение одного байта
{
unsigned char buff=0xFF;
unsigned char i2_error = 0;
IdleI2(); //Wait for bus Idle
StartI2(); //Generate Start condition
i2_error = WriteI2(devaddr); //send adress device
if (i2_error > 0)
goto skip;
i2_error = WriteI2(regaddr); //send adress regist
if (i2_error > 0)
goto skip;
RestartI2(); //Generate Restart
i2_error = WriteI2(devaddr | 0x01); //send control byte for Read
if (i2_error > 0)
goto skip;
buff = getI2(); //Read Length number of bytes to Data
skip:
switch (devaddr) {
case 0x3C:
if (i2_error > 0)
M.i2_error = 1;
else
M.i2_error = 0;
break;
case 0x30:
if (i2_error > 0)
A.i2_error = 1;
else
A.i2_error = 0;
break;
case 0x32:
if (i2_error > 0)
A.i2_error = 1;
else
A.i2_error = 0;
break;
}
NotAckI2(); //send Not Ack
StopI2(); //Send Stop Condition
return buff;
}

PIC24HJ128GP504, XC16

void i2cinitial(void)// инициализация I2C порта
{
I2C1BRG = ((FCY / i2cF)-(FCY / 1111111)); //300 кГц
I2C1CON = 0x1200;

I2C1RCV = 0x0000;
I2C1TRN = 0x0000;

I2C1CON = 0x9200;
return;
}
//------------------------------

void StartI2C(void) // генерация стартового бита
{
I2C1CONbits.SEN = 1; //Generate Start COndition
while (I2C1CONbits.SEN); //Wait for Start COndition
}
//--------------------------------

void RestartI2C(void)// генерация бита повторный старт
{
I2C1CONbits.RSEN = 1; //Generate Restart
while (I2C1CONbits.RSEN); //Wait for restart
}
//--------------------------------

void StopI2C(void)// генерация бита stop
{
I2C1CONbits.PEN = 1; //Generate Stop Condition
while (I2C1CONbits.PEN); //Wait for Stop
}
//--------------------------------

void WriteI2C(unsigned char byte) // отправка одного байта
{
unsigned int i = 0;
rs485.i2c_error = 0;
I2C1TRN = byte; //Load byte to I2C1 Transmit buffer
while (I2C1STATbits.TBF) //wait for data transmission
{
i++;
if (i >= 600)
{
rs485.i2c_error = 1;
break;
}
}
}
//--------------------------------

void IdleI2C(void) // ожидание освобождения линии
{
while (I2C1STATbits.TRSTAT); //Wait for bus Idle
}
//--------------------------------

void NotAckI2C(void)// генерация бита NoASK
{
unsigned int i = 0;
I2C1CONbits.ACKDT = 1; //Set for NotACk
I2C1CONbits.ACKEN = 1;
while (I2C1CONbits.ACKEN) //wait for ACK to complete
{
i++;
if (i >= 600)
{
rs485.i2c_error = 1;
break;
}
}
I2C1CONbits.ACKDT = 0; //Set for NotACk
}
//-------------------------------

void AckI2C(void)// генерация бита ASK
{
unsigned int i = 0;
I2C1CONbits.ACKDT = 0; //Set for ACk
I2C1CONbits.ACKEN = 1;
while (I2C1CONbits.ACKEN) //wait for ACK to complete
{
i++;
if (i >= 600)
{
rs485.i2c_error = 1;
break;
}
}
}
//--------------------------------

unsigned char getI2C(void) // функция приема байта
{
rs485.i2c_error = 0;
unsigned int i = 0;
I2C1CONbits.RCEN = 1; //Enable Master receive
Nop();
while (!I2C1STATbits.RBF) //Wait for receive bufer to be full
{
i++;
if (i >= 600)
{
rs485.i2c_error = 1;
break;
}
}
return (I2C1RCV); //Return data in buffer
}

void i2cwrite(char devaddr, char regaddr, char buff)// функция записи байта
{
IdleI2C(); //Ensure Module is Idle
StartI2C(); //Generate Start Condition
WriteI2C(devaddr); //Write Control byte
IdleI2C();
if (rs485.i2c_error)
goto skip;
WriteI2C(regaddr); //Write Low Address
IdleI2C();
if (rs485.i2c_error)
goto skip;
WriteI2C(buff); //Write Data
IdleI2C();
if (rs485.i2c_error)
goto skip;
skip:
StopI2C(); //Initiate Stop Condition
return;
}
//--------------------------------

unsigned char i2cread(unsigned char devaddr, unsigned char regaddr)// чтение одного байта
{
unsigned char buff;

IdleI2C(); //Wait for bus Idle
StartI2C(); //Generate Start condition
WriteI2C(devaddr); //send control byte for write
IdleI2C(); //Wait for bus Idle
if (rs485.i2c_error)
goto skip;
WriteI2C(regaddr); //send control byte for write
IdleI2C();
if (rs485.i2c_error)
goto skip;
RestartI2C(); //Generate Restart
WriteI2C(devaddr | 0x01); //send control byte for Read
IdleI2C(); //Wait for bus Idle
if (rs485.i2c_error)
goto skip;
buff = getI2C(); //Read Length number of bytes to Data
if (!rs485.i2c_error)
goto skip;
NotAckI2C(); //send Not Ack
skip:
StopI2C(); //Send Stop Condition
return buff;
}

Надеюсь это спасет чью-то жизнь.)