G1KuL1N

Программный I2C для STM32

14 сообщений в этой теме

G1KuL1N    100

В общем понадобилось тут подвесить FRAM к STM32, а пины аппаратных I2C были уже заняты, ну решил не менять пины, а добавить себе на вооружение на всякий, программный I2C. То что удалось нагуглить либо работало криво, либо приходилось допиливать и допиливать. Ну и написал свой с блекджеком и ш.. Так как в сети примеров мало программного I2C на STM32 то решил поделиться. Вдруг кому пригодится. Работает с библиотекой HAL, поправить нужно только под  свой камень и свои порты и все работает.

I2C.h

#ifndef __I2C_H_H__
#define __I2C_H_H__
// G1KuL1N
// ПОДКЛЮЧИТЬ БИБЛИОТЕКУ СВОЕГО КОНТРОЛЛЕРА

#include "stm32f4xx_hal.h"

// В CUBE MX порты I2C настроить на выход (либо в main.c вручну подать тактирование на нужны GPIO) 

//---подключение шины к пинам-----------------------------------------------------
#define SCL_I HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);   
#define SDA_I HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_14);
#define SCL_O HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET);
#define SDA_O HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET);
//--------------------------------------------------------------------------------
void i2c_init (void);               // Инициализация шины
void i2c_start_cond (void);        // Генерация условия старт
void i2c_restart_cond (void);      // Генерация условия рестарт
void i2c_stop_cond (void) ;       // Генерация условия стоп  
uint8_t i2c_send_byte (uint8_t data) ;      //Передать байт (вх. аргумент передаваемый байт) (возвращает 0 - АСК, 1 - NACK) 
uint8_t i2c_get_byte (uint8_t last_byte) ;  //Принять байт (если последний байт то входной аргумент = 1, если будем считывать еще то 0)(возвращает принятый байт)
//--------------------------------------------------------------------------------
// ПРИМЕР ИСПОЛЬЗОВАНИЯ
//=========================================================================================
//   Запись uint16_t во внешнюю еепром (FRAM FM24CL64) или любой другой 24LC памяти, 
//	 две ячейки,  указывается адрес первой ячейки, следующая идет adr++
//=========================================================================================
//
//void FRAM_W_INT(uint16_t adr, uint16_t dat){
//i2c_start_cond ();
//i2c_send_byte (0xA2); //адрес чипа + что будем делать (записывать)
//i2c_send_byte    ((adr & 0xFF00) >> 8);  
//i2c_send_byte    (adr & 0x00FF);
//i2c_send_byte    ((dat & 0xFF00) >> 8);  
//i2c_send_byte    (dat & 0x00FF);
//i2c_stop_cond();
//}

//=========================================================================================
//   Считывание uint16_t из внешней еепром (FRAM FM24CL64) или любой другой 24LC памяти, 
//	 две ячейки,  указывается адрес первой ячейки, следующая идет adr++
//=========================================================================================
//uint16_t FRAM_R_INT(uint16_t adr){
//uint16_t dat;
//i2c_start_cond ();
//i2c_send_byte (0xA2);
//i2c_send_byte    ((adr & 0xFF00) >> 8);  
//i2c_send_byte    (adr & 0x00FF);
//i2c_restart_cond ();
//i2c_send_byte (0xA3);
//dat =  i2c_get_byte(0);	
//dat <<= 8; 
//dat |= i2c_get_byte(1);
//i2c_stop_cond();
//return dat;
//}

// G1KuL1N

#endif /* __I2C_H_H__ */

i2C.c

#include "I2C.h"

volatile uint8_t i2c_frame_error=0; 

//-----------------------------------------------------------
__STATIC_INLINE void Delay_us (uint32_t __IO us) //Функция задержки в микросекундах us
{
us *=(SystemCoreClock/1000000)/5;
	while(us--);
}

//----------------------------------------------------
void SCL_in (void) //функция отпускания SCL в 1, порт на вход (необходимо установить используемый порт) 
	{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	}
//----------------------------------------------------
void SCL_out (void) //функция притягивания SCL в 0 (необходимо установить используемый порт) 
	{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	SCL_O;
	}
//----------------------------------------------------
void SDA_in (void) //функция отпускания SDA в 1, порт на вход (необходимо установить используемый порт) 
	{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	}
//----------------------------------------------------
void SDA_out (void) //функция притягивания SDA в 0 (необходимо установить используемый порт) 
	{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	SDA_O
	}

//----------------------------------------------------
void i2c_stop_cond (void)  // функция генерации условия стоп 
{
    uint16_t SCL, SDA;
		SCL_out(); // притянуть SCL (лог.0)
    Delay_us(10);
    SDA_out(); // притянуть SDA (лог.0)
    Delay_us(10);

    SCL_in(); // отпустить SCL (лог.1)
    Delay_us(10);
    SDA_in(); // отпустить SDA (лог.1)
    Delay_us(10);
    		
    // проверка фрейм-ошибки
    i2c_frame_error=0;		// сброс счётчика фрейм-ошибок
    SCL=SCL_I;
		SDA=SDA_I;
		if (SCL == 0) i2c_frame_error++;   // проберяем, чтобы на ноге SDA была лог.1, иначе выдаём ошибку фрейма
    if (SDA == 0) i2c_frame_error++;   // проберяем, чтобы на ноге SCL была лог.1, иначе выдаём ошибку фрейма
    Delay_us(40);
   }

void i2c_init (void) // функция инициализации шины
{
    i2c_stop_cond();   // стоп шины
    i2c_stop_cond();   // стоп шины
}
//----------------------------------------------------
void i2c_start_cond (void)  // функция генерации условия старт
{
		SDA_out(); // притянуть SDA (лог.0)
    Delay_us(10);
    SCL_out(); // притянуть SCL (лог.0)
    Delay_us(10);
}
//----------------------------------------------------
void i2c_restart_cond (void)   // функция генерации условия рестарт
{
    SDA_in(); // отпустить SDA (лог.1)
    Delay_us(10);
    SCL_in(); // отпустить SCL (лог.1)
    Delay_us(10);
    SDA_out(); // притянуть SDA (лог.0)
    Delay_us(10);
    SCL_out(); // притянуть SCL (лог.0)
    Delay_us(10);
}
//----------------------------------------------------
uint8_t i2c_send_byte (uint8_t data)  // функция  отправки байта  
{   
 uint8_t i;
 uint8_t ack=1;           //АСК, если АСК=1 – произошла ошибка
uint16_t SDA;   
	for (i=0;i<8;i++)
    {
        if (data & 0x80) 
				{
				SDA_in(); // лог.1
        }
				else 
				{
				SDA_out(); // Выставить бит на SDA (лог.0
				}
        Delay_us(10);
        SCL_in();   // Записать его импульсом на SCL       // отпустить SCL (лог.1)
        Delay_us(10);
        SCL_out(); // притянуть SCL (лог.0)
        data<<=1; // сдвигаем на 1 бит влево
					
    }
    SDA_in(); // отпустить SDA (лог.1), чтобы ведомое устройство смогло сгенерировать ACK
     Delay_us(10);
    SCL_in(); // отпустить SCL (лог.1), чтобы ведомое устройство передало ACK
     Delay_us(10);
    SDA=SDA_I;
		if (SDA==0x00) ack=1; else ack=0;    // Считать ACK

    SCL_out(); // притянуть SCL (лог.0)  // приём ACK завершён

    return ack; // вернуть ACK (0) или NACK (1)   

}
//----------------------------------------------------
uint8_t i2c_get_byte (uint8_t last_byte) // функция принятия байта
{
 uint8_t i, res=0;
	uint16_t SDA;
    SDA_in(); // отпустить SDA (лог.1)

    for (i=0;i<8;i++)
    {
        res<<=1;
        SCL_in(); // отпустить SCL (лог.1)      //Импульс на SCL
        Delay_us(10);
				SDA_in();
				SDA=SDA_I;
				if (SDA==1) res=res|0x01; // Чтение SDA в переменную  Если SDA=1 то записываем 1
        SCL_out(); // притянуть SCL (лог.0)
        Delay_us(10);
    }

    if (last_byte==0){ SDA_out();} // притянуть SDA (лог.0)     // Подтверждение, ACK, будем считывать ещё один байт
    else {SDA_in();} // отпустить SDA (лог.1)                 // Без подтверждения, NACK, это последний считанный байт
    Delay_us(10);
    SCL_in(); // отпустить SCL (лог.1)
    Delay_us(10);
    SCL_out(); // притянуть SCL (лог.0)
    Delay_us(10);
    SDA_in(); // отпустить SDA (лог.1)

    return res; // вернуть считанное значение
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
G1KuL1N    100

Это решение "в лоб", но работает, просто и быстро, написано за 20 минут так как горело, и с ходу рабочего решения не нашел. Задержку на таймере конечно лучше но необязательно, для ценителей программного времени там переделать 10 минут на таймер (хотя тот кто считает такты никогда в здравом уме не  будет использовать программный  i2C, когда есть аппаратный :D). На HAL так как это встроено в большую программу которая работает с HAL. Понадобилось просто подвесить в проект FRAM но уже все было готово и переделывать было бы долго и нудно. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
UTSource

Найдите миллионы труднодоступных

электронных компонентов

Alex    551

10 us - много. Практически все I2C-девайсы работают на тактовой в 100 КГц, так что можно раза в 2 поднимать частоту смело. Или задефайнить её, чтобы пользователь сам указывал.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
mail_robot    1 420

обычно для софтового i2c высокой скорости и не требуется. Но я думаю что не такая уж и проблема чуть допилить предложенное решение. А на HAL оно или нет, вообще никакой разницы. Да и сколько там HALа то? Записать пин, да прочитать. Переписать это на регистры 5 минут, да и того не стоит вообще

Изменено пользователем mail_robot

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Alex    551
33 минуты назад, mail_robot сказал:

обычно для софтового i2c высокой скорости и не требуется

Прочтите, пожалуйста, ещё раз мой пост и попытайтесь понять, о чём я там написал.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Alex    551

Если Вы отвечали не мне - прошу пардону... Ибо между моим постом и Вашим нет никакой логической связи.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

интересно сколько за время передачи пакета по i2c, F4 успеет прос..ть полезной работы на блокирующих Delay. Неужели нельзя столь медленный интерфейс реализовать на таймере. выдавливать биты в прерывании таймера, самый лучший вариант это битбандинг для размотки параллельных данных в последовательность бит.

Изменено пользователем MasterElectric

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
G1KuL1N    100

Уважаемый MasterElectric я выше уже ответил по этому поводу, не спорю с таймером лучше, но если ценим процессорное время - юзаем аппаратный I2C, и нет больше никаких "самых лучших вариантов". Во-вторых когда мне понадобился срочно программный I2C, если-б я залез сюда на форум, наткнулся бы на  Ваш пост с программным I2C с таймерами и бит-бандингом, я бы включил его себе с мыслью: вот чел молодец какой,  выложил не поленился, спасибо ему. Так что как есть, так есть.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
mail_robot    1 420

у человека в первом посте все конкретно расписано что и зачем и в каких случаях. Чего накинулись то? Узкая специальная задача, применение которой - раз в сто лет заткнуть аппаратную дырку

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ваша публикация должна быть проверена модератором

Гость
Вы не авторизованы. Если у вас есть аккаунт, пожалуйста, войдите.
Ответить в тему...

×   Вставлено в виде отформатированного текста.   Восстановить форматирование

  Разрешено не более 75 смайлов.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставить изображения напрямую. Загрузите или вставьте изображения по ссылке.

Загрузка...

  • Похожие публикации

    • Автор: User_1
      Всем доброго времени суток!
      Кто-нибудь может поделиться рабочим примером инициализации I2C в STM32F030? Ну или может увидите в моём коде ошибку
      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); // Включаем тактирование RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); I2C_InitTypeDef I2C_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3; // Speed 50 MHz GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // Альтернативная функция GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // Без подтяжки GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; // Открытый сток GPIO_Init(GPIOA, &GPIO_InitStructure); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; // Режим работы (I2C) I2C_InitStructure.I2C_OwnAddress1 = 0x11; // Адрес (не важен, если устройство - мастер) I2C_InitStructure.I2C_Timing = 0x00300619; // Значение для записи в регистр I2C_TIMINGR I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Cmd(I2C1, ENABLE); I2C_Init(I2C1, &I2C_InitStructure); Полдня уже вожусь, никак не заводится. Компилится без ошибок, светодиод мигает (ну в основном цикле написана простая светодиодная мигалка и раз в секунду - отправка сообщения в I2C), но смотрю логическим анализатором - на ножках тишина (ну в смысле единичка всегда, они же подтянуты резисторами к плюсу)
      I2C_Timing рассчитывается калькулятором от ST (I2C Timing Configuration) на основе частоты тактирования модуля I2C,  желаемой скорости шины и ещё пары параметров, калькулятор прикрепил к сообщению
      I2C_Timing_Configuration_V1.0.1.xls
    • Автор: Sattva
      Тема: Реверс-инжиниринг: необходимо декодировать протокол связи контроллера пром. насоса и дисплея
      Общая цель: сделать управление кондиционером, вести мониторинг его работы.
      Время на работу: около 12 часов (2 дня).
      Задание для исполнителя:
      1. Выполнить обратное декодирование протокола связи контроллера промышленного
      кондиционера и его дисплея с сенсорными кнопками.
      2. Запаять ATMega 328P или ATMega 2560. Вероятно, придется резать дорожки,
      допаять резисторы в разрыв.
      3. Совместно с заказчиком написать короткий код ввода/вывода данных для Arduino (C++).
      Могут понадобиться функции передачи данных на второй контроллер ATMega 2560 по UART.
      4. Совместно с заказчиком составить простое описание.
      5. Работа дисплея, состояние контроллера кондиционера и мониторинг на Arduino
      должны быть синхронны.
      Программировать микроконтроллер и отправлять данные на вебсервер по
      Ethernet (W5500) умею. Паять умею. Работать с микроконтроллером на уровне
      регистров процессора не умею, потому и ставлю задание.

      Протокол: неизвестен
      Шина: неизвестна
      Чип дисплея: Holtek HT1621B
      Контроллер "пульта управления": Holtek BS84B08A-3
      Фото прилагается.
      Вероятно, применяется шина SPI или I2C, что должно облегчить задачу.

      Имеется пример подобного дисплея 2009 года с выполненной
      работой. У предыдущего исполнителя ушло около 2 часов на разбор
      команд и еще столько же на написание кода, *когда поняли, как
      разбирать команды*.

      Фото с реализацией задания прилагается, но на дисплее другого типа.



      Метод выполненной работы в прошлый раз:
      1. Для получения данных с Holtek-HT46F49E по 8-ми битной шине данных использован Arduino Pro Mini 328P.
      2. Подключение: 5 дорожек перерезано и допаяно 5 резисторов по 1 кОм. Так же использовано еще 3 линии (reset и данные).
      3. Для удаленного управления использована Arduino Mega 2560 + Ethernet Shield W5100.
      4. Общение между 328P и 2560 идет по UART.
       
      Наилучший вариант, если кто-то откликнется из Киева, чтобы можно было вместе
      встретиться и поработать. Но если в Киеве никого нет, то куплю недорогой логический
      анализатор уровней и будем общаться удаленно. Готов к любому варианту.
    • Автор: Nitro N
      Здравствуйте! 
      Дано: 
      BLE модуль JDY-10 Гироскоп + Акселерометр модуль GY-521  USB UART CH340G USB bluetooth адаптер Цель:
      передавать сигналы датчика гироскопа и акселерометра через bluetooth модуль на ПК. Мои рассуждения:
      Модуль GY-521 имеет I2C интерфейс, на JDY-10 стоит МК CC2541 который тоже имеет I2C интерфейс. Надо как-то их соединить и заставить отправлять показания на ПК. Как это сделать пока мне не понятно. Иногда приходят мысли что без перепрошивки контроллера не обойтись. 

      Подскажите пожалуйста как это реализовать?

      П.С. В идеале вообще конечно использовать один контроллер для снятия значений и отправки на ПК, но пока так. 
    • Автор: Дмитрий Мартынов
      Здравствуйте!
      Пишу программу на C++ для управления LCD (HD44780) по I2C через модуль расширения портов ввода/вывода PCF8574AT.
      void LCD_I2C::readBF() { transmitByte(0b00001110); //transmits E, RW set to HIGH and RS, set to LOW _delay_ms(5); transmitByte(0b00001010); //transmits E, RS set to LOW and RW set to HIGH _delay_ms(5); initRestart(); transmitAddrRW(0b01111111); //sends PCF8574AT address + SLA+R do { receiveDataAck(); PORTA = storage; } while((storage & (1 << BF)) != 0); //wait until BF is 0 initRestart(); transmitAddrRW(0b01111110); } Метод void transmitByte(uint8_t data) после инициализации состояния "Старт" и отправки адреса устройства + SLA+W отправляет байт данных по TWI с ожиданием бита подтверждения (команды работают верно, проверял по регистру статуса TWI - 0x08, 0x10 и 0x24).
      Метод void transmitAddrRW(uint8_t address) отправляет соответственно SLA+W/R + адрес устройства (команда работает также верно). Нареканий к работе TWI у меня нет, т.к. недавно с его помощью успешно запустил часы DS1307 с интеграцией LCD.
      После передачи запроса на чтение флага занятости инициализируется состояние "ПОВСТАРТ", отправляется адрес устройства + SLA+R, далее идет цикл - запрос байта данных (состояние выводов PCF8574AT) с отправкой бита подтверждения uint8_t receiveDataAck() (команды работают также верно, возвращает storage = TWDR) и вывод storage на порт А микроконтроллера (там установлены светодиоды).
      Чтение регистра данных TWDR после принятия байта данных (receiveDataAck()) дает следующий результат - 0b00000010 - установлен только бит RW микросхемы. Таким образом, флаг занятости BF = DB7 = 7й бит оказывается сразу же сброшенным, происходит мгновенный выход из цикла - контроллер дисплея не успевает скушать информацию, и инициализация не выполняется (неудачную инициализацию определяю по отсутствию курсора). Ожидалось, что флаг занятости будет установлен в единицу и произойдет несколько итераций перед выходом из цикла.
      При замене метода ожидания сброса флага занятости BF на программную задержку в 250 мс везде, где это требует datasheet - инициализация происходит успешно (появляется курсор, как и должно быть).

      Вопрос: что можно сделать, чтобы вместо _delay_ms(250) использовать readBF(), т.к. этот путь мне кажется более верным (уж очень не хочется использовать задержку .__.)? Возможно, проблема в микросхеме, которая неверно выдает информацию при чтении? (Имеется вторая микросхема, она вообще не работает:D)

      З.Ы. На фото виден результат чтения флага BF и Adress Counter - установлен только бит RW.  



      З.Ы.Ы Кому интересно - вот функция main(). Повторюсь - проблема только в методе readBF():
      void LCD_I2C::init() { setBitRate(20000); initStart(); transmitAddrRW(0b01111110); //send PCF8574AT address + SLA+W _delay_ms(60); sendInstruction(0b00110000); //function set 8-bit operation _delay_ms(20); sendInstruction(0b00110000); //function set 8-bit operation _delay_ms(5); sendInstruction(0b00110000); //function set 8-bit operation _delay_ms(5); sendInstruction(0b00100000); //function set 4-bit operation readBF(); //_delay_ms(250); //debug!! sendInstruction(0b00100000); //function set 4-bit operation, 2 lines, 5x8 dots sendInstruction(0b10000000); readBF(); //_delay_ms(250); //debug!! sendInstruction(0b00000000); //display off, cursor off, blinking off sendInstruction(0b10000000); readBF(); //_delay_ms(250); //debug!! sendInstruction(0b00000000); //display clear sendInstruction(0b00010000); readBF(); //_delay_ms(250); //debug!! sendInstruction(0b00000000); //entry mode set increment, display shift off sendInstruction(0b01100000); readBF(); //_delay_ms(250); //debug!! sendInstruction(0b00000000); //display on, cursor off, blinking off sendInstruction(0b11100000); readBF();/ }  
    • Автор: ximik_se
      Всем привет.

      Решил сделать небольшую домашнюю метеостанцию.

      Есть приемник с экраном, куда выводится инфа (построено на ATMEGA 328P) и есть передатчик, который посылает инфу по возудху (построено на ATtiny85).

      В передатчике использую обычные DHT22. В принципе качество чуть ниже среднего. Но главный недостаток - порой сбоит, присылая данные в 2 раза больше предидущих, потом опять приходит в себя. и так повторяется постоянно.

      При этом время между измерениями не меньше 20 сек.

      В общем заказал я себе с Китаюшки более точные датчики - 

      CJMCU-1080 HDC1080
      Вещица прекрасная, но общается по I2C.

      А библиотеку рабочую под нее я смог найти только одну "ClosedCube_HDC1080.h" и никаких модификаций под ATtiny я найти не смог.

      Для ее работы соответственно нужен Wire.h

      В общем решил я его поковырять самостоятельно, хоть и не прогер. Забрался во внутренности ClosedCube_HDC1080.cpp и везде заменил Wire на TinyWireM (некий аналог Wire для ATtiny).

      И у меня даже получилось считывать валжность, но вот вместо температуры приходит гадость. Ибо при компиляции Arduino IDE ругалась на строку (я ее ниже в коде закомментировал)
      uint8_t buf[4]; for (int i = 1; i < (seconds*66); i++) { TinyWireM.beginTransmission(_address); TinyWireM.write(0x00); TinyWireM.endTransmission(); delay(20); TinyWireM.requestFrom(_address, (uint8_t)4); // TinyWireM.readBytes(buf, (size_t)4); } Ошибку пишет следующую:

      \ClosedCube_HDC1080.cpp: In member function 'void ClosedCube_HDC1080::heatUp(uint8_t)':

      \ClosedCube_HDC1080.cpp:81:13: error: 'class USI_TWI' has no member named 'readBytes'

      TinyWireM.readBytes(buf, (size_t)4);
      Может есть ребята более понимающие в коде и сумеющие победить эту проблему, чтобы и температуру этот датчик смог передавать через ATtiny85.

      Вот полный текст файла ClosedCube_HDC1080.cpp (уже замененный ну и строчка закоментирована):
      #include <TinyWireM.h> #include "ClosedCube_HDC1080.h" ClosedCube_HDC1080::ClosedCube_HDC1080() { } void ClosedCube_HDC1080::begin(uint8_t address) { _address = address; TinyWireM.begin(); // Heater off, 14 bit Temperature and Humidity Measurement Resolution TinyWireM.beginTransmission(_address); TinyWireM.write(CONFIGURATION); TinyWireM.write(0x0); TinyWireM.write(0x0); TinyWireM.endTransmission(); } HDC1080_Registers ClosedCube_HDC1080::readRegister() { HDC1080_Registers reg; reg.rawData = (readData(CONFIGURATION) >> 8); return reg; } void ClosedCube_HDC1080::writeRegister(HDC1080_Registers reg) { TinyWireM.beginTransmission(_address); TinyWireM.write(CONFIGURATION); TinyWireM.write(reg.rawData); TinyWireM.write(0x00); TinyWireM.endTransmission(); delay(10); } void ClosedCube_HDC1080::heatUp(uint8_t seconds) { HDC1080_Registers reg = readRegister(); reg.Heater = 1; reg.ModeOfAcquisition = 1; writeRegister(reg); uint8_t buf[4]; for (int i = 1; i < (seconds*66); i++) { TinyWireM.beginTransmission(_address); TinyWireM.write(0x00); TinyWireM.endTransmission(); delay(20); TinyWireM.requestFrom(_address, (uint8_t)4); // TinyWireM.readBytes(buf, (size_t)4); } reg.Heater = 0; reg.ModeOfAcquisition = 0; writeRegister(reg); } float ClosedCube_HDC1080::readT() { return readTemperature(); } float ClosedCube_HDC1080::readTemperature() { uint16_t rawT = readData(TEMPERATURE); return (rawT / pow(2, 16)) * 165 - 40; } float ClosedCube_HDC1080::readH() { return readHumidity(); } float ClosedCube_HDC1080::readHumidity() { uint16_t rawH = readData(HUMIDITY); return (rawH / pow(2, 16)) * 100; } uint16_t ClosedCube_HDC1080::readManufacturerId() { return readData(MANUFACTURER_ID); } uint16_t ClosedCube_HDC1080::readDeviceId() { return readData(DEVICE_ID); } uint16_t ClosedCube_HDC1080::readData(uint8_t pointer) { TinyWireM.beginTransmission(_address); TinyWireM.write(pointer); TinyWireM.endTransmission(); delay(9); TinyWireM.requestFrom(_address, (uint8_t)2); byte msb = TinyWireM.read(); byte lsb = TinyWireM.read(); return msb << 8 | lsb; }