Библиотека от IARDUINO, что не нравится компилятору?

Для отслеживания, какой вариант компилируется был изменен кусок в iarduino_I2C_Relay_I2C.h
Добавлены #warning и sei(); cli(); - чтобы смотреть, что помещается в полностью скомпилированный файл.

Спойлер
//		Функция пакетного чтения нескольких байт данных из регистров модуля:		//
		bool	readBytes(uint8_t adr, uint8_t reg, uint8_t *data, uint8_t sum){	//	Определяем функцию пакетного чтения нескольких байт данных из регистров модуля	(аргументы: адрес_модуля, адрес_первого_регистра, указатель_на_массив, количество_байт)
				#if defined(iarduino_I2C_TW)
#warning readBytes iarduino_I2C_TW
				//	Если используется шина I2C под управлением библиотеки Wire:		//
					uint8_t										i=0;				//	Предустанавливаем переменную i в значение 0 - это ответ по умолчанию.
					Wire.beginTransmission(adr);									//	Инициируем передачу данных по шине I2C к устройству с адресом adr и битом RW=0 => запись. При этом сама передача не начнётся.
					Wire.write(reg);												//	Определяем значение первого байта (reg - адреса регистра) который будет отправлен после байта адреса. Функция write() поместит указанный байт в буфер для передачи.
sei();
sei();
sei();
					i=Wire.endTransmission(false); if(i){return	0;}					//	Выполняем инициированную ранее передачу данных (без установки состояния STOP). Функция endTransmission() возвращает: 0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка.
					if(!Wire.requestFrom( adr, sum ))	{return	i;}					//	Читаем (запрашиваем) sum байт данных от устройства с адресом adr и битом RW=1 => чтение. Функция requestFrom() возвращает количество реально принятых байтов. Так как предыдущая функция не установила состояние STOP, то состояние START данной функции будет расценено как RESTART.
					while(Wire.available() && i<sum){data[i]=Wire.read(); i++;}		//	Читаем sum принятых байт из буфера для полученных данных в массив по указателю data.
					while(Wire.available()){Wire.read();}return	i==sum;				//	Если в буфере для полученных данных есть еще принятые байты, то чистим буфер. Возвращаем true если удалось прочитать sum байт.
				#else																//
				//	Если шина управляется функциями нижнего уровня данного класса:	//
					uint8_t										i=0;				//	Предустанавливаем переменную i в значение 0 - это результат записи.
#warning RedaBytes LOW_LEVEL_FUNCTIONS
cli();
cli();
sei();
					if (			start	()		)	{		i=1;				//	Если на шине I2C установилось состояние START, то ...
					if (			sendID	(adr,0)	)	{		i=2;				//	Если модуль ответил ACK на получение адреса устройства adr с битом RW=0 (запись), то ...
					if (			setByte	(reg)	)	{		i=3;				//	Если модуль ответил ACK на получение адреса регистра i, то ...
					if (			reStart	()		)	{		i=4;				//	Если на шине I2C установилось состояние RESTART, то ...
					if (			sendID	(adr,1)	)	{		i=5;				//	Если модуль ответил ACK на получение адреса устройства adr с битом RW=1 (чтение), то ...
					while(sum>0){	*data=getByte(sum>1); 							//	Получаем по одному байту из очередного регистра за каждый проход цикла while. Прочитанный байт записываются по указателю data. Аргумент функции getByte имеет значение true на всех проходах цикла кроме последнего
									data++; sum--;									//	Увеличиваем адрес указателя data, и уменьшаем сумму прочитанных байт sum.
					#if defined(iarduino_I2C_HW)									//	Проверить корректность чтения каждого байта можно только на аппаратном уровне
					if (sum)	{	if(TWSR&0xF8!=0x50)	{		i=0;}}				//	Если после чтения очередного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x50 значит произошла ошибка при чтении
					else		{	if(TWSR&0xF8!=0x58)	{		i=0;}}				//	Если после чтения последного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x58 значит произошла ошибка при чтении
					#endif															//
					}}}}}}			stop	();			return	i==5;				//	Отправляем команду STOP и возвращаем результат успешности чтения
				#endif																//
		}																			//

Ага, ашёл, спасибо.

Запустил компиляцию - две страницы предупреждений, как всегда у iarduino :frowning:

Посмотрю, сегодня.

1 лайк

Ну, Вы всё правильно поняли, только вот с этим запутались:

Этого не нужно.

Компиляция файла iarduino_I2C_Relay.cpp происходит “в свой черёд”. Результат - объектный файл с расширением “.a”. И в нём есть скомпилированная функция readBytes.

Компиляция файла digitalWrite.ino также происходит “свой черёд”. Результат - тоже объектный файл с расширением “.a”. И в нём тоже есть скомпилированная функция readBytes (уже другая, но с точно теми же параметрами и тем же возвращаемым типом).

При этом структура объектных фалов такова, что там вызовы функций не разрешены, т.к. их адреса пока не известны. Вместо этого там есть таблица имён функций и их адресов внутри объектного файла. А вызовы этих функций ссылаются пока на таблицу.

То, что в разных объектных файлах присутствуют функции с одинаковыми именами/параметрами - не редкость. Например, так будет всегда, когда Вы объявили функцию как inline, а компилятор решил не делать её inline (типа часто вызывается) и сделал её обычной функцией. Но вот чтобы эти функции были разными - это нужно было знатно поговнокодить!

Так вот, система никак не ожидает, что они разные и считает, что использовать можно любую. Что она и делает. Какую именно - нигде не определено, каждый сборщик сам решает.

Так вот, что делает сборщик: он собирает все объектные файлы в кучу, собирает все уникальные имена функций из таблиц всех объектных файлов и разрешает все вызовы (теперь то он уже знает физические адреса функций!). А лишние (неиспользуемые) функции просто выбрасывает.

Вот, собственно, что и произошло. Не ожидая, что Ваши readBytes разные, он использовал одну, а остальные выбросил.

Не знаю, умышленно ли это сделали разработчики (зная, что возьмёт из скетча) или “так получилось”, но в любом случае говнокод знатный. iarduino ещё раз подтвердило свою репутацию.

4 лайка

Большое спасибо за доходчивое объяснение.

  1. достаточно ли будет поправить имя функции?
  2. как в таком случае находят ошибки и отлаживают код?
  3. как всё сложно однако

Вы совершенно не в теме – коды посмотрите. Там одна функция. На две она делится благодаря внешнему, по отношению к ней, говнокодингу.

Профессионалы таких ошибок не делают просто потому, что они так никогда не пишут (как электрик никогда не соединит фазу на жёлто-зелёный провод), а как отлаживаются любители я не знаю.

Разработчикам надо поменьше умничать и говнокодить, тогда проще будет.

Код я не смотрел, но из обсуждения сделал вывод, что в проекте присутствуют две одноименные функции. Соответственно, нужно понять, из какого места какую из них вызывать. После того, как это понимание будет достигнуто, можно делать именно так, как Вы указали.

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

Чтобы не было так сложно, люди давно придумали правила, которые, строго говоря, совершенно не обязательны с точки зрения работы программы, но зато здорово помогают при поиске ошибок и отладке.

Вывод неверный, а потому, дальнейшее, основанное не нём, теряет смысл.

2 лайка

и где их взять?