579 lines
19 KiB
C
579 lines
19 KiB
C
#include "zh_avr_i2c.h"
|
||
|
||
avr_err_t zh_avr_i2c_master_init(void)
|
||
{
|
||
}
|
||
|
||
avr_err_t zh_avr_i2c_master_transmit(const uint8_t addr, uint8_t *data, uint8_t size, size_t delay)
|
||
{
|
||
}
|
||
|
||
avr_err_t zh_avr_i2c_master_receive(const uint8_t addr, uint8_t *data, uint8_t size, size_t delay)
|
||
{
|
||
}
|
||
|
||
avr_err_t zh_avr_i2c_master_transmit_receive(const uint8_t addr, uint8_t *write_data, uint8_t write_size, uint8_t *read_data, uint8_t read_size, size_t delay)
|
||
{
|
||
}
|
||
|
||
void DoNothing(void);
|
||
|
||
uint8_t i2c_Do; // Переменная состояния передатчика IIC
|
||
uint8_t i2c_InBuff[i2c_MasterBytesRX]; // Буфер прием при работе как Slave
|
||
uint8_t i2c_OutBuff[i2c_MasterBytesTX]; // Буфер передачи при работе как Slave
|
||
uint8_t i2c_SlaveIndex; // Индекс буфера Slave
|
||
|
||
uint8_t i2c_Buffer[i2c_MaxBuffer]; // Буфер для данных работы в режиме Master
|
||
uint8_t i2c_index; // Индекс этого буфера
|
||
uint8_t i2c_ByteCount; // Число байт передаваемых
|
||
|
||
uint8_t i2c_SlaveAddress; // Адрес подчиненного
|
||
|
||
uint8_t i2c_PageAddress[i2c_MaxPageAddrLgth]; // Буфер адреса страниц (для режима с sawsarp)
|
||
uint8_t i2c_PageAddrIndex; // Индекс буфера адреса страниц
|
||
uint8_t i2c_PageAddrCount; // Число байт в адресе страницы для текущего Slave
|
||
|
||
// Указатели выхода из автомата:
|
||
IIC_F MasterOutFunc = &DoNothing; // в Master режиме
|
||
IIC_F SlaveOutFunc = &DoNothing; // в режиме Slave
|
||
IIC_F ErrorOutFunc = &DoNothing; // в результате ошибки в режиме Master
|
||
|
||
uint8_t WorkLog[100]; // Лог пишем сюда
|
||
uint8_t WorkIndex = 0; // Индекс лога
|
||
|
||
ISR(TWI_vect) // Прерывание TWI Тут наше все.
|
||
{
|
||
/*
|
||
PORTB ^= 0x01; // Дрыгаем ногой порта, для синхронизации логического анализатора и отметок вызова TWI
|
||
|
||
|
||
// Отладочный кусок. Вывод лога работы конечного автомата в буфер памяти, а потом. По окончании работы через UART на волю
|
||
if (WorkIndex <99) // Если лог не переполнен
|
||
{
|
||
if (TWSR) // Статус нулевой?
|
||
{
|
||
WorkLog[WorkIndex]= TWSR; // Пишем статус в лог
|
||
WorkIndex++;
|
||
}
|
||
else
|
||
{
|
||
WorkLog[WorkIndex]= 0xFF; // Если статус нулевой то вписываем FF
|
||
WorkIndex++;
|
||
}
|
||
}
|
||
*/
|
||
switch (TWSR & 0xF8) // Отсекаем биты прескалера
|
||
{
|
||
case 0x00: // Bus Fail (автобус сломался)
|
||
{
|
||
i2c_Do |= i2c_ERR_BF;
|
||
|
||
TWCR = 0 << TWSTA |
|
||
1 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Go!
|
||
|
||
MACRO_i2c_WhatDo_ErrorOut break;
|
||
}
|
||
|
||
case 0x08: // Старт был, а затем мы:
|
||
{
|
||
i2c_index = 0; // Обнуляем индекс буфера.
|
||
|
||
if ((i2c_Do & i2c_type_msk) == i2c_sarp) // В зависимости от режима
|
||
{
|
||
i2c_SlaveAddress |= 0x01; // Шлем Addr+R
|
||
}
|
||
else // Или
|
||
{
|
||
i2c_SlaveAddress &= 0xFE; // Шлем Addr+W
|
||
}
|
||
|
||
TWDR = i2c_SlaveAddress; // Адрес слейва
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Go!
|
||
break;
|
||
}
|
||
|
||
case 0x10: // Повторный старт был, а затем мы
|
||
{
|
||
if ((i2c_Do & i2c_type_msk) == i2c_sawsarp) // В зависимости от режима
|
||
{
|
||
i2c_SlaveAddress |= 0x01; // Шлем Addr+R
|
||
}
|
||
else
|
||
{
|
||
i2c_SlaveAddress &= 0xFE; // Шлем Addr+W
|
||
}
|
||
|
||
// To Do: Добавить сюда обработку ошибок
|
||
|
||
TWDR = i2c_SlaveAddress; // Адрес слейва
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Go!
|
||
break;
|
||
}
|
||
|
||
case 0x18: // Был послан SLA+W получили ACK, а затем:
|
||
{
|
||
if ((i2c_Do & i2c_type_msk) == i2c_sawp) // В зависимости от режима
|
||
{
|
||
TWDR = i2c_Buffer[i2c_index]; // Шлем байт данных
|
||
i2c_index++; // Увеличиваем указатель буфера
|
||
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Go!
|
||
}
|
||
|
||
if ((i2c_Do & i2c_type_msk) == i2c_sawsarp)
|
||
{
|
||
i2c_PageAddrIndex = 0; // Обнулили указатель буфера адреса страницы.
|
||
|
||
TWDR = i2c_PageAddress[i2c_PageAddrIndex]; // Или шлем адрес странцы (по сути тоже байт данных)
|
||
i2c_PageAddrIndex++;
|
||
// Увеличиваем указатель буфера страницы
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Go!
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 0x20: // Был послан SLA+W получили NACK - слейв либо занят, либо его нет дома.
|
||
{
|
||
i2c_Do |= i2c_ERR_NA;
|
||
// Код ошибки
|
||
TWCR = 0 << TWSTA |
|
||
1 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Шлем шине Stop
|
||
|
||
MACRO_i2c_WhatDo_ErrorOut // Обрабатываем событие ошибки;
|
||
break;
|
||
}
|
||
|
||
case 0x28: // Байт данных послали, получили ACK! (если sawp - это был байт данных. если sawsarp - байт адреса страницы)
|
||
{ // А дальше:
|
||
if ((i2c_Do & i2c_type_msk) == i2c_sawp) // В зависимости от режима
|
||
{
|
||
if (i2c_index == i2c_ByteCount) // Если был байт данных последний
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
1 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Шлем Stop
|
||
|
||
MACRO_i2c_WhatDo_MasterOut // И выходим в обработку стопа
|
||
}
|
||
else
|
||
{
|
||
TWDR = i2c_Buffer[i2c_index]; // Либо шлем еще один байт
|
||
i2c_index++;
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Go!
|
||
}
|
||
}
|
||
|
||
if ((i2c_Do & i2c_type_msk) == i2c_sawsarp) // В другом режиме мы
|
||
{
|
||
if (i2c_PageAddrIndex == i2c_PageAddrCount) // Если последний байт адреса страницы
|
||
{
|
||
TWCR = 1 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Запускаем Повторный старт!
|
||
}
|
||
else
|
||
{ // Иначе
|
||
TWDR = i2c_PageAddress[i2c_PageAddrIndex]; // шлем еще один адрес страницы
|
||
i2c_PageAddrIndex++; // Увеличиваем индекс счетчика адреса страниц
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Go!
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 0x30: // Байт ушел, но получили NACK причин две. 1я передача оборвана слейвом и так надо. 2я слейв сглючил.
|
||
{
|
||
i2c_Do |= i2c_ERR_NK; // Запишем статус ошибки. Хотя это не факт, что ошибка.
|
||
|
||
TWCR = 0 << TWSTA |
|
||
1 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Шлем Stop
|
||
|
||
MACRO_i2c_WhatDo_MasterOut // Отрабатываем событие выхода
|
||
|
||
break;
|
||
}
|
||
|
||
case 0x38: // Коллизия на шине. Нашелся кто то поглавней
|
||
{
|
||
i2c_Do |= i2c_ERR_LP; // Ставим ошибку потери приоритета
|
||
|
||
// Настраиваем индексы заново.
|
||
i2c_index = 0;
|
||
i2c_PageAddrIndex = 0;
|
||
|
||
TWCR = 1 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Как только шина будет свободна
|
||
break; // попробуем передать снова.
|
||
}
|
||
|
||
case 0x40: // Послали SLA+R получили АСК. А теперь будем получать байты
|
||
{
|
||
if (i2c_index + 1 == i2c_ByteCount) // Если буфер кончится на этом байте, то
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
0 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Требуем байт, а в ответ потом пошлем NACK(Disconnect)
|
||
} // Что даст понять слейву, что мол хватит гнать. И он отпустит шину
|
||
else
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Или просто примем байт и скажем потом ACK
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case 0x48: // Послали SLA+R, но получили NACK. Видать slave занят или его нет дома.
|
||
{
|
||
i2c_Do |= i2c_ERR_NA;
|
||
// Код ошибки No Answer
|
||
TWCR = 0 << TWSTA |
|
||
1 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Шлем Stop
|
||
|
||
MACRO_i2c_WhatDo_ErrorOut // Отрабатываем выходную ситуацию ошибки
|
||
break;
|
||
}
|
||
|
||
case 0x50: // Приняли байт.
|
||
{
|
||
i2c_Buffer[i2c_index] = TWDR; // Забрали его из буфера
|
||
i2c_index++;
|
||
|
||
// To Do: Добавить проверку переполнения буфера. А то мало ли что юзер затребует
|
||
|
||
if (i2c_index + 1 == i2c_ByteCount) // Если остался еще один байт из тех, что мы хотели считать
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
0 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Затребываем его и потом пошлем NACK (Disconnect)
|
||
}
|
||
else
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Если нет, то затребываем следующий байт, а в ответ скажем АСК
|
||
}
|
||
break;
|
||
}
|
||
|
||
case 0x58: // Вот мы взяли последний байт, сказали NACK слейв обиделся и отпал.
|
||
{
|
||
i2c_Buffer[i2c_index] = TWDR; // Взяли байт в буфер
|
||
TWCR = 0 << TWSTA |
|
||
1 << TWSTO |
|
||
1 << TWINT |
|
||
i2c_i_am_slave << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Передали Stop
|
||
|
||
MACRO_i2c_WhatDo_MasterOut // Отработали точку выхода
|
||
|
||
break;
|
||
}
|
||
|
||
// IIC Slave ============================================================================
|
||
|
||
case 0x68: // RCV SLA+W Low Priority // Словили свой адрес во время передачи мастером
|
||
case 0x78: // RCV SLA+W Low Priority (Broadcast) // Или это был широковещательный пакет. Не важно
|
||
{
|
||
i2c_Do |= i2c_ERR_LP | i2c_Interrupted; // Ставим флаг ошибки Low Priority, а также флаг того, что мастера прервали
|
||
|
||
// Restore Trans after.
|
||
i2c_index = 0; // Подготовили прерваную передачу заново
|
||
i2c_PageAddrIndex = 0;
|
||
} // И пошли дальше. Внимание!!! break тут нет, а значит идем в "case 60"
|
||
|
||
case 0x60: // RCV SLA+W Incoming? // Или просто получили свой адрес
|
||
case 0x70: // RCV SLA+W Incoming? (Broascast) // Или широковещательный пакет
|
||
{
|
||
|
||
i2c_Do |= i2c_Busy; // Занимаем шину. Чтобы другие не совались
|
||
i2c_SlaveIndex = 0; // Указатель на начало буфера слейва, Неважно какой буфер. Не ошибемся
|
||
|
||
if (i2c_MasterBytesRX == 1) // Если нам суждено принять всего один байт, то готовимся принять его
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
0 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Принять и сказать пошли все н... NACK!
|
||
}
|
||
else
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // А если душа шире чем один байт, то сожрем и потребуем еще ACK!
|
||
}
|
||
break;
|
||
}
|
||
|
||
case 0x80: // RCV Data Byte // И вот мы приняли этот байт. Наш или широковещательный. Не важно
|
||
case 0x90: // RCV Data Byte (Broadcast)
|
||
{
|
||
i2c_InBuff[i2c_SlaveIndex] = TWDR; // Сжираем его в буфер.
|
||
|
||
i2c_SlaveIndex++; // Сдвигаем указатель
|
||
|
||
if (i2c_SlaveIndex == i2c_MasterBytesRX - 1) // Свободно место всего под один байт?
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
0 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Приянть его и сказать NACK!
|
||
}
|
||
else
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Места еще дофига? Принять и ACK!
|
||
}
|
||
break;
|
||
}
|
||
|
||
case 0x88: // RCV Last Byte // Приянли последний байт
|
||
case 0x98: // RCV Last Byte (Broadcast)
|
||
{
|
||
i2c_InBuff[i2c_SlaveIndex] = TWDR; // Сожрали его в буфер
|
||
|
||
if (i2c_Do & i2c_Interrupted) // Если у нас был прерываный сеанс от имени мастера
|
||
{
|
||
TWCR = 1 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Влепим в шину свой Start поскорей и сделаем еще одну попытку
|
||
}
|
||
else
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Если не было такого факта, то просто отвалимся и будем ждать
|
||
}
|
||
|
||
MACRO_i2c_WhatDo_SlaveOut // И лениво отработаем наш выходной экшн для слейва
|
||
break;
|
||
}
|
||
|
||
case 0xA0: // Ой, мы получили Повторный старт. Но чо нам с ним делать?
|
||
{
|
||
|
||
// Можно, конечно, сделать вспомогательный автомат, чтобы обрабатывать еще и адреса внутренних страниц, подобно еепромке.
|
||
// Но я не стал заморачиваться. В этом случае делается это тут.
|
||
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // просто разадресуемся, проигнорировав этот посыл
|
||
break;
|
||
}
|
||
|
||
case 0xB0: // Поймали свой адрес на чтение во время передачи Мастером
|
||
{
|
||
i2c_Do |= i2c_ERR_LP | i2c_Interrupted; // Ну чо, коды ошибки и флаг прерваной передачи.
|
||
|
||
// Восстанавливаем индексы
|
||
i2c_index = 0;
|
||
i2c_PageAddrIndex = 0;
|
||
|
||
} // Break нет! Идем дальше
|
||
|
||
case 0xA8: // Либо просто словили свой адрес на чтение
|
||
{
|
||
i2c_SlaveIndex = 0; // Индексы слейвовых массивов на 0
|
||
|
||
TWDR = i2c_OutBuff[i2c_SlaveIndex]; // Чтож, отдадим байт из тех что есть.
|
||
|
||
if (i2c_MasterBytesTX == 1)
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
0 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Если он последний, мы еще на NACK в ответ надеемся
|
||
}
|
||
else
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // А если нет, то ACK ждем
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case 0xB8: // Послали байт, получили ACK
|
||
{
|
||
|
||
i2c_SlaveIndex++; // Значит продолжаем дискотеку. Берем следующий байт
|
||
TWDR = i2c_OutBuff[i2c_SlaveIndex]; // Даем его мастеру
|
||
|
||
if (i2c_SlaveIndex == i2c_MasterBytesTX - 1) // Если он последний был, то
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
0 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Шлем его и ждем NACK
|
||
}
|
||
else
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
0 << TWEN |
|
||
1 << TWIE; // Если нет, то шлем и ждем ACK
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case 0xC0: // Мы выслали последний байт, больше у нас нет, получили NACK
|
||
case 0xC8: // или ACK. В данном случае нам пох. Т.к. больше байтов у нас нет.
|
||
{
|
||
if (i2c_Do & i2c_Interrupted) // Если там была прерваная передача мастера
|
||
{ // То мы ему ее вернем
|
||
i2c_Do &= i2c_NoInterrupted;
|
||
// Снимем флаг прерваности
|
||
TWCR = 1 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Сгенерим старт сразу же как получим шину.
|
||
}
|
||
else
|
||
{
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
1 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Если мы там одни, то просто отдадим шину
|
||
}
|
||
|
||
MACRO_i2c_WhatDo_SlaveOut // И отработаем выход слейва. Впрочем, он тут
|
||
// Не особо то нужен. Разве что как сигнал, что мастер
|
||
break; // Нас почтил своим визитом.
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void DoNothing(void) // Функция пустышка, затыкать несуществующие ссылки
|
||
{
|
||
}
|
||
|
||
void Init_i2c(void) // Настройка режима мастера
|
||
{
|
||
i2c_PORT |= 1 << i2c_SCL | 1 << i2c_SDA; // Включим подтяжку на ноги, вдруг юзер на резисторы пожмотился
|
||
i2c_DDR &= ~(1 << i2c_SCL | 1 << i2c_SDA);
|
||
|
||
TWBR = 0x80; // Настроим битрейт
|
||
TWSR = 0x00;
|
||
}
|
||
|
||
void Init_Slave_i2c(IIC_F Addr) // Настройка режима слейва (если нужно)
|
||
{
|
||
TWAR = i2c_MasterAddress; // Внесем в регистр свой адрес, на который будем отзываться.
|
||
// 1 в нулевом бите означает, что мы отзываемся на широковещательные пакеты
|
||
SlaveOutFunc = Addr; // Присвоим указателю выхода по слейву функцию выхода
|
||
|
||
TWCR = 0 << TWSTA |
|
||
0 << TWSTO |
|
||
0 << TWINT |
|
||
1 << TWEA |
|
||
1 << TWEN |
|
||
1 << TWIE; // Включаем агрегат и начинаем слушать шину.
|
||
} |