189 lines
12 KiB
C++
189 lines
12 KiB
C++
#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 |