|
Неожиданный результат, Прочитать два байта |
|
|
|
Jan 22 2008, 08:50
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Сергей Борщ @ Jan 22 2008, 02:13)  Ведь тело функции может находиться и в другой единице компиляции. Откуда в таком случае компилятор узнает, что вы в ней обращаетесь к volatille-объектам? Я полагаю, что для получения правильного поведения функция должна быть описана как int NegPort() voltile {}. Хорошо, есть функция rand() которая определена в другой единице компиляции, в стандартной библиотеке. Определена она так int rand(void) , т.е. без всяких volatile. Вопрос: Могу ли я написать у себя в программе А = rand() + rand(); ? или компилятор может вызвать rand только один раз ?
|
|
|
|
|
Jan 22 2008, 11:57
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(singlskv @ Jan 22 2008, 10:50)  Вопрос: Могу ли я написать у себя в программе А = rand() + rand(); ? или компилятор может вызвать rand только один раз ? Проконсультировался с ReAl - в С функция не может иметь атрибута volatile, это я с плюсами попутал: Цитата Но в любом случае функция "по определению" может иметь побочные эффекты (как та же getchar() - её побочный эффект - изменение некоего файлового об'екта, аппаратного окружения, ...) и компилятор вообще говоря не имеет права заменить два вызова функции одним. Ну, за исключением работы оптимизатора, "видящего" тело функции и решившего, что это можно сделать - inline-функции в С99 или компиляция всей программы как одного файла ( --whole-program *.c для gcc ). В gcc можно дать атрибут функции - "результат зависит только от переданных аргументов, больше никуда функция не смотрит и ничего снаружи не меняет", тогда несколько её вызовов с одинаковыми аргументами оптимизатор заменяет на один. Так что тут вы правы - компилятор не может заменить два i2c_read() или NegPort() на один. Признаю, вводил в заблуждение. Это заставило вновь внимательно посмотреть листинг из поста №7 - и что мы видим? Мы не видим там одного вызова i2c_read() - мы видим, что вызов(ы) i2c_read() происходят в подпрограмме ?Subroutine0, содержимое которой нам не показали. Видимо подобная конструкция используется в файле не один раз и компилятор посчитал оптимальным вынести ее в отдельную подпрограмму. Ставлю себе большую двойку за невнимательность. Так что вопрос - ошибся тут компилятор или нет остается открытым до обнародования листинга ?Subroutine0
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 22 2008, 12:44
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Сергей Борщ @ Jan 22 2008, 14:57)  Это заставило вновь внимательно посмотреть листинг из поста №7 - и что мы видим? Мы не видим там одного вызова i2c_read() - мы видим, что вызов(ы) i2c_read() происходят в подпрограмме ?Subroutine0, содержимое которой нам не показали. Видимо подобная конструкция используется в файле не один раз и компилятор посчитал оптимальным вынести ее в отдельную подпрограмму. Ставлю себе большую двойку за невнимательность. Так что вопрос - ошибся тут компилятор или нет остается открытым до обнародования листинга ?Subroutine0 Возможно Вы правы насчет объединения двух вызовов в отдельную подпраграмму, только тогда очень странно выглядит LDI R25,0 после подпрограммы... Автор, выложите более полный листинг.
|
|
|
|
|
Jan 22 2008, 13:31
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(singlskv @ Jan 22 2008, 16:44)  Автор, выложите более полный листинг. Пожалуйста. Не подозревал, что простой, на первый взгляд, вопрос вызовет такое бурное обсуждение  Добавлено
Прошу прощения, но теперь в данном lst функция i2c_read(ACK) вызывается дважды. Хотя готов поклясться, что в посте №7 функция вызывалась один раз. Попытаюсь воспроизвести ту ситуацию еще раз. Листинги я не сохранил, к сожалению. Еще раз добавлено
Вот , восстановил (asdx-2). Возможно я был не внимательный, когда говорил, что функция вызывается один раз. Кстати, вопросик по ходу: как лучше писать Код Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK); или Accumulator = ((unsigned int)i2c_read(ACK)<<8) + i2c_read(ACK); В последний раз добавлено 
Прокоментируйте, пожалуйста следующее: Код 49 Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK); \ 00000022 ........ CALL i2c_readAck \ 00000026 ........ CALL i2c_readAck \ 0000002A 2F80 MOV R24, R16 \ 0000002C E090 LDI R25, 0 50 i2c_stop(); \ 0000002E ........ CALL i2c_stop Возможно, здесь нарушен порядок вызовов функций: первым должен читаться старший байт, затем сдвинуться на 8 бит, затем сложить с прочитанным младшим байтом.
Прикрепленные файлы
asdx.rar ( 1.38 килобайт )
Кол-во скачиваний: 29
asdx_2.rar ( 1.72 килобайт )
Кол-во скачиваний: 27
|
|
|
|
|
Jan 22 2008, 17:53
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(alux @ Jan 22 2008, 16:31)  Прошу прощения, но теперь в данном lst функция i2c_read(ACK) вызывается дважды. Хотя готов поклясться, что в посте №7 функция вызывалась один раз. Попытаюсь воспроизвести ту ситуацию еще раз. Листинги я не сохранил, к сожалению.
Еще раз добавлено Вот , восстановил (asdx-2). Возможно я был не внимательный, когда говорил, что функция вызывается один раз. К сожалению Вы востановили не до конца, перед обсуждаемыми строками у Вас еще стоят: 44 HiTemp=i2c_read(ACK); 45 LowTemp=i2c_read(NACK); которые и мешают отработать оптимизатору так же как и было в самом первом листинге... Цитата Кстати, вопросик по ходу: как лучше писать Код Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK); или Accumulator = ((unsigned int)i2c_read(ACK)<<8) + i2c_read(ACK); ИМХО, в данном случае пофиг, но в общем и в целом лучше в таких случаях пользоваться |, так и вероятность ошибок меньше и скорость работы может быть выше, все зависит от конкретного компилятора. Цитата Прокоментируйте, пожалуйста следующее: Код 49 Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK); \ 00000022 ........ CALL i2c_readAck \ 00000026 ........ CALL i2c_readAck \ 0000002A 2F80 MOV R24, R16 \ 0000002C E090 LDI R25, 0 50 i2c_stop(); \ 0000002E ........ CALL i2c_stop Возможно, здесь нарушен порядок вызовов функций: первым должен читаться старший байт, затем сдвинуться на 8 бит, затем сложить с прочитанным младшим байтом. Порядок то здесь наверняка нарушен(наверное мы чуть запутали Вас нашим обсуждением), в Вашем случае все равно нужно делить эти вызовы на два разных оператора, те первый ответ данный Сергей Борщ был абсолютно правильный. Тока вот все равно какая то фигня творится, LDI R25,0 здесь просто не должно быть...
|
|
|
|
|
Jan 22 2008, 19:47
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Код 49 Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK); \ 00000022 ........ CALL i2c_readAck \ 00000026 ........ CALL i2c_readAck \ 0000002A 2F80 MOV R24, R16 \ 0000002C E090 LDI R25, 0 50 i2c_stop(); \ 0000002E ........ CALL i2c_stop Этот листинг формируется, если перед этим убрать вызовы функций: Код // HiTemp=i2c_read(ACK); // LowTemp=i2c_read(NACK); Как узнать, в какой регистр возвращает первый и второй CALL i2c_readAck ? Мне кажется, что в результате младший и старший байты перепутаны местами. MOV R24, R16 LDI R25, 0 это есть: ((unsigned int)i2c_read(ACK)<<8) ?
|
|
|
|
|
Jan 22 2008, 21:17
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(alux @ Jan 22 2008, 15:31)  Кстати, вопросик по ходу: как лучше писать Код Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK); или Accumulator = ((unsigned int)i2c_read(ACK)<<8) + i2c_read(ACK); Никак. Сергей Борщ с самого начала дал правильный ответ. Порядок вычисления подвыражений неопределён для всего, кроме . Точка. Это не имеет никакого отношения к порядку группирования операций слева направо или наоборот. Цитата ISO/IEC 9899:1999 (E) 6.5 Expressions 3 The grouping of operators and operands is indicated by the syntax. Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified. Сразу, чтобы не было иллюзий по поводу function call () ( "скобок вызова функции", круглые скобки выполняют роль cast operator, parentheses punctuator и function-call operator, в данном случае об этом) Цитата 6.5.2.2 Function calls 10 The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call. Ну так вот, независимо от порядка группирования операторов op (за исключением перечисленных выше операторов) в следующем выражении Код f1() op f2() op f3() порядок вызова функций неопределён. Могут вычисляться в порядке группирования, могут быть вызваны все три в противоположном порядке, а потом будут произведены вычисления. Отдано на откуп реализации. "вероятнее всего" будут вызываться в порядке группирования, но я бы не стал закладываться. Поэтому правильный вариант либо как указал Сергей, либо Код /* так */ temp = i2c_read(ACK); Accumulator = ((unsigned int)temp << 8) | i2c_read(ACK); /* или так */ Accumulator = ((unsigned int)i2c_read(ACK)<<8); Accumulator |= i2c_read(ACK); А вот в этих местах что лучше писать - как правило, лучше ИЛИ. Цитата(alux @ Jan 22 2008, 21:47)  Код \ 00000022 ........ CALL i2c_readAck \ 00000026 ........ CALL i2c_readAck \ 0000002A 2F80 MOV R24, R16 \ 0000002C E090 LDI R25, 0 Как узнать, в какой регистр возвращает первый и второй CALL i2c_readAck ? В один и тот же. В регистр, принятый в компиляторе для возврата байтовых значений из функций. Просто результат первого вызова по какой-то причине игнорируется вообще (но вызов сделан, ибо компилятор лишён информации о том, имеет ли данная функция побочные эффекты и поэтому обязан сделать вызов). А результат второго вызова из R16 приводится к беззнаковому целому в паре R25:R24 Почему так - совершенно непонятно. Покажите (без ассемблера, С-шный исходник) - объявления функций i2c_* и тело этой функции, в коде которой бардак выходит. У IAR-а случайно нет какого-то ключика/галочки - сделать int 8-битовым? Очень похоже, что ((unsigned int)i2c_read(ACK) << 8) считается равным 0
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 23 2008, 07:13
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(ReAl @ Jan 23 2008, 01:17)  У IAR-а случайно нет какого-то ключика/галочки - сделать int 8-битовым? Очень похоже, что ((unsigned int)i2c_read(ACK) << 8) считается равным 0 Код // --------i2c.h---------------- #define ACK 1 // continue receiving #define NACK 0 // end of receiving extern unsigned char i2c_readAck(void); extern unsigned char i2c_readNack(void); extern unsigned char i2c_read(unsigned char ack); #define i2c_read(ack) (ack) ? i2c_readAck() : i2c_readNack()
// --------i2c.c---------------- unsigned char i2c_readAck(void) { TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); while(!(TWCR & (1<<TWINT)));
return TWDR; }
//--------------------------------------------------------------------------- unsigned char i2c_readNack(void) { TWCR = (1<<TWINT)|(1<<TWEN); while(!(TWCR & (1<<TWINT))); return TWDR; } На счет галочек... Не нашел такого, чтоб "сделать int 8-битовым". Единственное, что нашел,- в закладке C/C++ Compiler->Language->Language conformance выбрано Allow IAR extantion; Plain char is -> unsigned; Enable multybyte support -> оставлено по-умолчанию (пустым). Код //############################################################################# // Измерение атмосферного давления //_____________________________________________________________________________ void AbsPressureMeasure(void) { unsigned char ret, SampleCounter=0, HiTemp, LowTemp; unsigned long Accumulator=0; ret = i2c_start(ASDX_ADR|W); if(ret) { // failed to issue start condition, possibly no device found i2c_stop(); } else // читаем ASDX-DO: { while(SampleCounter != NMAX) { i2c_rep_start(ASDX_ADR|R); HiTemp=i2c_read(ACK); LowTemp=i2c_read(NACK); Accumulator += ((unsigned int)HiTemp<<8) | LowTemp; SampleCounter++; } i2c_stop();
absPressure = (((((double)Accumulator/NMAX)-ASDX_OFFSET)*15.)/ASDX_SPAN)*mm_Hg; // Атмосферное давление, мм. рт. ст. // printf_P("\n\r Абсолютное давление : %.3f", absPressure); } } Вот пример вызова. Все ли здесь правильно? i2c_stop(); - правильно или вынести в общий цикл. Напоролся на такую проблему. Хоть и написано что данный сенсор "The ASDX family uses I2C compatible protocol" , но он не читает последовательность байтов. Т.е. перед каждой парой байт необходимо делать RESTART. Обязательно ли после приема пары байтов делать STOP? Или оставить как есть. Код ASDX_ADR|W - это скорее для красоты. Ведь ИЛИ с нулем ничего не изменяет.
|
|
|
|
|
Jan 23 2008, 14:49
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Я немного цитаты перетасую Цитата(alux @ Jan 23 2008, 09:13)  На счет галочек... Не нашел такого, чтоб "сделать int 8-битовым". И не надо. Просто если бы она была и стояла, то это могло привести к некоторым глупостям, отдалённо напоминающим то, что было. Цитата(alux @ Jan 23 2008, 09:13)  Код HiTemp=i2c_read(ACK); LowTemp=i2c_read(NACK); Accumulator += ((unsigned int)HiTemp<<8) | LowTemp; Так для этого случая, с промежуточными переменными - вроде бы, всё было нормально? И ещё - в предыдущих примерах было ACK в обеих строках. Цитата(alux @ Jan 23 2008, 09:13)  Код #define ACK 1 // continue receiving #define NACK 0 // end of receiving ... #define i2c_read(ack) (ack) ? i2c_readAck() : i2c_readNack() О-о-о-о!!! Давно не видел грабелек со столь любовно отполированной ручкой Получил массу удовльствия  Значит так: Код // Это было написано одной строкой без промежуточных переменных Accumulator = ((unsigned)i2c_read(ACK) << 8) | i2c_read(ACK);
// это будет после препроцессора Accumulator = ((unsigned) (ACK) ? i2c_readAck() : i2c_readNack() << 8) | (AСK) ? i2c_readAck() : i2c_readNack();
// Вот так стоят "логические скобки" согласно приоритетам и группированию Accumulator = ( ((unsigned) (ACK)) ? i2c_readAck() : (i2c_readNack() << 8) ) | (AСK) /* 1 */ ? i2c_readAck() : i2c_readNack(); /* 2 */ Т.е. вся первая тернарная операция входит в условие второй. Дальше выполняется следующее Константа ACK приводится к unsigned int и проверяется на 0. Она не 0, поэтому вызывается i2c_readAck(), но ничего не сдвигается - сдвиг "прилип" к вызову в ветви "ЛОЖЬ" тернарной операции. Результат первой тернарной операции "заиливается" с ACK и проверяется на 0. Поскольку ACK не равно 0, эту битовую операцию "ИЛИ" можно и не производить, и так ясно, что будет не 0. Итого от строки /* 1 */ остаётся только вызов i2c_readAck() (нельзя убрать, несмотря на то, что результат не используется) и к началу строки /* 2 */ мы получаем условие "ИСТИНА", в результате чего вызывается ещё раз i2c_readAck() и уже её результат приводится к unsigned, что и было в итоговом коде. Настоятельно рекомендую если и пользоваться макросами - выражениями, то в скобки брать не только аргументы, но и весь макрос как целое. Код #define i2c_read(ack) ( (ack) ? i2c_readAck() : i2c_readNack() ) Но всё равно в данном случае как минимум одна временная переменная нужна, см. мой предыдущий пост. Иначе рано или поздно будет радостная встреча с каким-то компилятором, который сделает "неожиданный", но допустимый стандартом порядок вызова функций.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jan 23 2008, 15:30
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(alux @ Jan 23 2008, 17:10)  ReAl огромный  Я тоже получил массу удовольствия от этого объяснения. Позвольте реабилитироваться и внести свой вклад: Код #define i2c_read(ack) i2c_read_func( (ack) ? (1<<TWINT)|(1<<TWEN)|(1<<TWEA) : (1<<TWINT)|(1<<TWEN) )
// --------i2c.c---------------- unsigned char i2c_read_func(uint8_t control) { TWCR = control; while(!(TWCR & (1<<TWINT)));
return TWDR; } Кажется так может быть эффективнее. А может и нет. Надо смотреть листинг.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 24 2008, 08:04
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(Сергей Борщ @ Jan 23 2008, 17:30)  Позвольте реабилитироваться и внести свой вклад: Код #define i2c_read(ack) i2c_read_func( (ack) ? (1<<TWINT)|(1<<TWEN)|(1<<TWEA) : (1<<TWINT)|(1<<TWEN) ) ... Кажется так может быть эффективнее. А может и нет. Надо смотреть листинг. Ну у меня вообще не так, у меня read сразу с указателем и длиной, а внутри цикла что-то в духе Код while( len--) { ... temp = TWCR_MODE | (1<<TWEA); // TWCR_MODE задефайнено на то, что надо if( len == 0 ) /* на последнем проходе тут уже 0 */ temp = TWCR_MODE; TWCR = temp; ... Всё равно по одному байтику практически никогда не читается, а если вдруг надо - то не так и страшно вызвать с указателем и длиной 1.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|