SHIELD-Malb41k1/OLED-I2C/I2C.h

189 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef I2C_h
#define I2C_h
// этот символ необходим для совместимости с Wire.h. его значение является максимумом для параметра uint8_t используемого в оригинальной библиотеке.
#define BUFFER_LENGTH 255
class I2CLIB {
public:
void begin(void); // инициализация шины
void setClock(uint32_t clock); // ручная установка частоты шины 31-900 kHz (в герцах)
void beginTransmission(uint8_t address); // открыть соединение (для записи данных)
uint8_t endTransmission(bool stop); // закрыть соединение , произвести stop или restart (по умолчанию - stop)
uint8_t endTransmission(void); // закрыть соединение , произвести stop
size_t write(uint8_t data); // отправить в шину байт данных , отправка производится сразу , формат - byte "unsigned char"
uint8_t requestFrom(uint8_t address , uint8_t length , bool stop); //открыть соединение и запросить данные от устройства, отпустить или удержать шину
uint8_t requestFrom(uint8_t address , uint8_t length); //открыть соединение и запросить данные от устройства, отпустить шину
uint8_t read(void); // прочитать байт , БУФЕРА НЕТ!!! , читайте сразу все запрошенные байты , stop или restart после чтения последнего байта, настраивается в requestFrom
uint8_t available(void); // вернет количество оставшихся для чтения байт
// функции добавлены для совместимости с Wire API
inline void beginTransmission(int address){beginTransmission((uint8_t)address);}
uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop);
uint8_t requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop);
uint8_t requestFrom(int address, int quantity);
uint8_t requestFrom(int address, int quantity, int sendStop);
size_t write(const uint8_t *buffer, size_t size);
inline size_t write(unsigned long n) { return write((uint8_t)n); }
inline size_t write(long n) { return write((uint8_t)n); }
inline size_t write(unsigned int n) { return write((uint8_t)n); }
inline size_t write(int n) { return write((uint8_t)n); }
private:
uint8_t _requested_bytes = 0; // переменная хранит количество запрошенных и непрочитанных байт
bool _address_nack = false; // Флаг для отслеживания ошибки при передаче адреса
bool _data_nack = false; // Флаг для отслеживания ошибки при передаче данных
bool _stop_after_request = true; // stop или restart после чтения последнего байта
void start(void); // сервисная функция с нее начинается любая работа с шиной
void stop(void); // сервисная функция ей заканчивается работа с шиной
};
extern I2CLIB Wire;
void setPinMode(uint8_t pin, uint8_t mode) {
if (mode == INPUT) {
DDRD &= ~(1 << pin); // устанавливаем вход
} else {
DDRD |= (1 << pin); // устанавливаем выход
}
}
void I2CLIB::begin()
{ // Инициализация шины в роли master
setPinMode(SDA, INPUT_PULLUP); // Подтяжка шины
setPinMode(SCL, INPUT_PULLUP); // Подтяжка шины
TWBR = 72; // Стандартная скорость - 100kHz
TWSR = 0; // Делитель - /1 , статус - 0;
}
void I2CLIB::setClock(uint32_t clock)
{ // Функция установки частоты шины 31-900 kHz (в герцах)
TWBR = (((long)F_CPU / clock) - 16) / 2; // Расчет baudrate - регистра
}
void I2CLIB::beginTransmission(uint8_t address)
{ // Начать передачу (для записи данных)
I2CLIB::start(); // Старт
I2CLIB::write(address << 1); // Отправка slave - устройству адреса с битом "write"
}
uint8_t I2CLIB::endTransmission(void)
{ // Завершить передачу и отпустить шину
return I2CLIB::endTransmission(true);
}
uint8_t I2CLIB::endTransmission(bool stop)
{ // Завершить передачу (после записи данных)
if (stop) I2CLIB::stop(); // Если задано stop или аргумент пуст - отпустить шину
else I2CLIB::start(); // Иначе - restart (другой master на шине не сможет влезть между сообщениями)
if (_address_nack) { // Если нет ответа при передаче адреса
_address_nack = false; // Обнуляем оба флага
_data_nack = false; // Обнуляем оба флага
return 2; // Возвращаем '2'
} if (_data_nack) { // Если нет ответа при передаче данных
_address_nack = false; // Обнуляем оба флага
_data_nack = false; // Обнуляем оба флага
return 3; // Возвращаем '2'
} return 0; // Если все ОК - возвращаем '0'
}
size_t I2CLIB::write(uint8_t data)
{ // Прямая отправка байта на шину
TWDR = data; // Записать данные в data - регистр
TWCR = _BV(TWEN) | _BV(TWINT); // Запустить передачу
while (!(TWCR & _BV(TWINT))); // Дождаться окончания
uint8_t _bus_status = TWSR & 0xF8; // Чтение статуса шины
if(_bus_status == 0x20) _address_nack = true; // SLA + W + NACK ? - нет ответа при передаче адреса
if(_bus_status == 0x30) _data_nack = true; // BYTE + NACK ? - нет ответа при передаче данных
return 1; // для совместимости с Wire API
}
uint8_t I2CLIB::available()
{ // Вернуть оставшееся количество запрошенных для чтения байт
return _requested_bytes; // Это содержимое этой переменной
}
uint8_t I2CLIB::read()
{ // Прямое чтение байта из шины после запроса
if (--_requested_bytes) { // Если байт не последний
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA); // Запустить чтение шины (с подтверждением "ACK")
while (!(TWCR & _BV(TWINT))); // Дождаться окончания приема данных
return TWDR; // Вернуть принятые данные , это содержимое data - регистра
}
_requested_bytes = 0; // Если читаем последний байт
TWCR = _BV(TWEN) | _BV(TWINT); // Запустить чтение шины (БЕЗ подтверждения "NACK")
while (!(TWCR & _BV(TWINT))); // Дождаться окончания приема данных
if (_stop_after_request) I2CLIB::stop(); // Если в requestFrom не задан аргумент stop , или stop задан как true - отпустить шину
else I2CLIB::start(); // Иначе - restart (другой master на шине не сможет влезть между сообщениями)
return TWDR; // Вернуть принятый ранее байт из data - регистра
}
uint8_t I2CLIB::requestFrom(uint8_t address , uint8_t length)
{ // Запрос n-го кол-ва байт от ведомого устройства и отпускание шины
return I2CLIB::requestFrom(address , length , true);
}
uint8_t I2CLIB::requestFrom(uint8_t address , uint8_t length , bool stop)
{ // Запрос n-го кол-ва байт от ведомого устройства (Читайте все байты сразу!!!)
_stop_after_request = stop; // stop или restart после чтения последнего байта
_requested_bytes = length; // Записать в переменную количество запрошенных байт
I2CLIB::start(); // Начать работу на шине
I2CLIB::write((address << 1) | 0x1); // Отправить устройству адрес + бит "read"
return length; // вернуть длину сообщения для совместимости с оригинальной Wire API
}
uint8_t I2CLIB::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop){
return requestFrom(address , quantity , sendStop!=0);
}
uint8_t I2CLIB::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop){
if (isize > 0) {
// send internal address; this mode allows sending a repeated start to access
// some devices' internal registers. This function is executed by the hardware
// TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers)
beginTransmission(address);
// the maximum size of internal address is 4 bytes
// не понятно почему ограничение размерности регистра в оригинальной Wire API в 3 байта
if (isize > 4){
isize = 4;
}
// write internal register address - most significant byte first
while (isize-- > 0)
write((uint8_t)(iaddress >> (isize*8)));
endTransmission(false);
}
return requestFrom(address, quantity, sendStop);
}
uint8_t I2CLIB::requestFrom(int address, int quantity)
{
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}
uint8_t I2CLIB::requestFrom(int address, int quantity, int sendStop)
{
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop);
}
size_t I2CLIB::write(const uint8_t *buffer, size_t size){
for (size_t i = 0 ; i < size; ++i)
write(buffer[i]);
return size;
}
void I2CLIB::start()
{ // сервисная функция с нее начинается любая работа с шиной
TWCR = _BV(TWSTA) | _BV(TWEN) | _BV(TWINT); // start + I2CLIB enable + установка флага "выполнить задачу"
while (!(TWCR & _BV(TWINT))); // Ожидание завершения
}
void I2CLIB::stop()
{ // сервисная функция ей заканчивается работа с шиной
TWCR = _BV(TWSTO) | _BV(TWEN) | _BV(TWINT); // stop + I2CLIB enable + установка флага "выполнить задачу"
}
I2CLIB Wire = I2CLIB();
#endif