Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Неожиданный результат
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему
alux
Долго не мог выявить, почему цифровой датчик давления выдает неверный результат.
Код
//Первый случай;
resultat = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK);

//Второй случай:
HiTemp=i2c_read(ACK);        
LowTemp=i2c_read(ACK);
resultat = ((unsigned int)HiTemp<<8) | LowTemp;

Во втором случае дает другой (правильный) результат. Почему?
Сергей Борщ
Цитата(alux @ Jan 19 2008, 16:37) *
Во втором случае дает другой (правильный) результат. Почему?
Потому что стандарт языка С не определяет порядок вычисления подвыражений.
Какой из i2c_read() будет вызван первым зависит в общем случае от положения звезд на небе. Ваша функция обладает побочными эффектами, поэтому вызывать ее дважды в одном выражении не рекомендуется во избежание таких неожиданностей.
alux
Я поражен меткости и, главное, оперативности Ваших ответов. Спасибо, Сергей.
Baser
Цитата(alux @ Jan 19 2008, 16:37) *
Долго не мог выявить, почему цифровой датчик давления выдает неверный результат.
...
Во втором случае дает другой (правильный) результат. Почему?

Заглядывание в листинг при непонятной работе программы частенько помогает очень быстро найти ошибку. Особенно если кажется, что все написано правильно. smile.gif
Aesthete Animus
Цитата(Сергей Борщ @ Jan 19 2008, 17:45) *
Потому что стандарт языка С не определяет порядок вычисления подвыражений.

Что значит, стандарт не определяет? Вот строчка из "K&R C": "Все бинарные операции и условная операция (прим. Перевод.: условная операция группируется справа налево; это изменение внесено в язык в 1978 г.) группируются слева направо"

Это следут проверить таким образом:
Код
resultat = ((HiTemp = (unsigned int)i2c_read(ACK))<<8) | (LowTemp = i2c_read(ACK));

printf("High byte 0x%02X\n", HiTemp);
printf("Low byte 0x%02X\n", LowTemp);
rezident
Я не такой большой специалист в Си и могу ошибаться, но ИМХО компилятор может "оптимизировать" вызов функции и вместо двух вызовов формировать один, а все бинарные операции проводить с одним полученным результатом. Как уже заметили, просмотр листинга может прояснить этот нюанс.
alux
Цитата(rezident @ Jan 20 2008, 16:23) *
...компилятор может "оптимизировать" вызов функции и вместо двух вызовов формировать один, а все бинарные операции проводить с одним полученным результатом. Как уже заметили, просмотр листинга может прояснить этот нюанс.

Вот что выдал листинг:
Код
     49          Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK);
   \   00000024   ....               RCALL   ?Subroutine0
   \                     ??CrossCallReturnLabel_0:
   \   00000026   2F80               MOV     R24, R16
   \   00000028   E090               LDI     R25, 0
     50                i2c_stop();
   \   0000002A   ........           CALL    i2c_stop

Как говорят у нас, на Украине, rezident "має рацію" smile.gif
Сергей Борщ
Цитата(Aesthete Animus @ Jan 20 2008, 14:17) *
Вот строчка из "K&R C": "Все бинарные операции и условная операция (прим. Перевод.: условная операция группируется справа налево; это изменение внесено в язык в 1978 г.) группируются слева направо"
Если под бинарными понимаются && и || - определен. Для остальных (кроме ?: и оператора "запятая") - нет. Вот цитата из стандарта языка (INTERNATIONAL STANDARD ISO/IEC 9899, Programming languages — C, Second edition 1999-12-01):
Цитата
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.
Это дает гораздо больше свободы оптимизатору.


Цитата(alux @ Jan 20 2008, 17:01) *
Вот что выдал листинг:
Мне пришло в голову, что во избежание подобных недоразумений функцию надо бы объявить как volatile. В этом случае:
1) Компилятор был бы обязан вызвать ее дважды.
2) IAR выдал бы предупреждение о неопределенности при использовании более одной volatile в выражении. GCC такое предупреждение не выдает, это ему минус. Хотя когда в IAR появилась эта проверка - многих предупреждение ставило в тупик. Вот, видимо, наглядный пример, когда это предупреждение обосновано.
Aesthete Animus
Цитата(Сергей Борщ @ Jan 20 2008, 21:59) *
Если под бинарными понимаются && и || - определен. Для остальных (кроме ?: и оператора "запятая") - нет. Вот цитата из стандарта языка (INTERNATIONAL STANDARD ISO/IEC 9899, Programming languages — C, Second edition 1999-12-01)...

Вот оно как... Не дадите ссылочку на первоисточник - надо бы почитать для просвещения smile.gif
Сергей Борщ
Цитата(Aesthete Animus @ Jan 20 2008, 21:43) *
Не дадите ссылочку на первоисточник - надо бы почитать для просвещения smile.gif
Да запросто: Google -> ISO/IEC 9899, вторая ссылка дает pdf
singlskv
Цитата(Сергей Борщ @ Jan 20 2008, 21:59) *
Мне пришло в голову, что во избежание подобных недоразумений функцию надо бы объявить как volatile. В этом случае:
1) Компилятор был бы обязан вызвать ее дважды.
Даже без volatile компилятор обязан был вызвать эту функцию дважды, т.к. внутри
этой функции есть работа с volatile переменными, например с регистрами TWI...

P.S. Нда... , чего-то у IAR совсем плохо c volatile..., уже 2 баги за последние пару дней...
Dog Pawlowa
Цитата(singlskv @ Jan 21 2008, 02:07) *
Даже без volatile компилятор обязан был вызвать эту функцию дважды, т.к. внутри
этой функции есть работа с volatile переменными, например с регистрами TWI...

P.S. Нда... , чего-то у IAR совсем плохо c volatile..., уже 2 баги за последние пару дней...


Ничего он не обязан.
Кто писал программу - Вы или компилятор? smile.gif
Все свойства функции - это типы параметров и адрес вызова, а требовать от компилятора анализа свойств используемых переменных внутри функции - эта попытка взвалить на него исправление Ваших ошибок.
singlskv
Цитата(Dog Pawlowa @ Jan 21 2008, 10:39) *
Ничего он не обязан.
Кто писал программу - Вы или компилятор? smile.gif
Все свойства функции - это типы параметров и адрес вызова, а требовать от компилятора анализа свойств используемых переменных внутри функции - эта попытка взвалить на него исправление Ваших ошибок.
Атлична, раскажите мне пожалуйста, исходя из каких правил описанных в стандарте С
компилятор имеет право обработать эти две ситуации по разному:
Код
int NegPort()
{
  PORTB~=0xFF;
  return 1;
}

.......
  a = NegPort() + NegPort();

и
Код
a = NegPort();
a+=NegPort();


по Вашей логике в первом случае он имеет право вызвать NegPort() один раз а во втором
обязян вызвать 2 раза...
и почему во втором случае он не вызовет так же один раз ?

О порядке вызова функций при сложении речи не идет...

P.S. Пример конечно сильно упрощен, но смысл я надеюсь понятен...

P.P.S А так же в догонку очень хотелось бы понять, имеет ли право на жизнь
следующая запись в языке С:
Z = X * K1 * rand() + Y * K2 * rand();
или я обязан разделить ее на 2 подвыражения для того чтобы не получить одинаковый
результат на выходе rand().... ???
Сергей Борщ
Цитата(singlskv @ Jan 21 2008, 22:54) *
по Вашей логике в первом случае он имеет право вызвать NegPort() один раз а во втором обязян вызвать 2 раза... и почему во втором случае он не вызовет так же один раз ?
Видимо потому, что между вызовами во втором случае стоит ";", т.е. sequence point, на момент которой все побочные эффекты должны выполниться. Побочным эффектом в данном случае является запись в PORTB. Если я все правильно понимаю.
singlskv
Цитата(Сергей Борщ @ Jan 22 2008, 00:10) *
Побочным эффектом в данном случае является запись в PORTB. Если я все правильно понимаю.

конечно, и функция уже должна наследовать признак "volatile", те sequence point должны
быть при каждом вызове этой функции
Сергей Борщ
Цитата(singlskv @ Jan 21 2008, 23:16) *
конечно, и функция уже должна наследовать признак "volatile"
Кому должна? Ведь тело функции может находиться и в другой единице компиляции. Откуда в таком случае компилятор узнает, что вы в ней обращаетесь к volatille-объектам? Я полагаю, что для получения правильного поведения функция должна быть описана как int NegPort() voltile {}. "И сразу польется вода" © Тот самый Мюнхгаузен.
singlskv
Цитата(Сергей Борщ @ Jan 22 2008, 02:13) *
Ведь тело функции может находиться и в другой единице компиляции. Откуда в таком случае компилятор узнает, что вы в ней обращаетесь к volatille-объектам? Я полагаю, что для получения правильного поведения функция должна быть описана как int NegPort() voltile {}.

Хорошо, есть функция rand() которая определена в другой единице компиляции,
в стандартной библиотеке.
Определена она так int rand(void) , т.е. без всяких volatile.

Вопрос:
Могу ли я написать у себя в программе
А = rand() + rand(); ?
или компилятор может вызвать rand только один раз ?
Сергей Борщ
Цитата(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
singlskv
Цитата(Сергей Борщ @ Jan 22 2008, 14:57) *
Это заставило вновь внимательно посмотреть листинг из поста №7 - и что мы видим? Мы не видим там одного вызова i2c_read() - мы видим, что вызов(ы) i2c_read() происходят в подпрограмме ?Subroutine0, содержимое которой нам не показали. Видимо подобная конструкция используется в файле не один раз и компилятор посчитал оптимальным вынести ее в отдельную подпрограмму. Ставлю себе большую двойку за невнимательность.
Так что вопрос - ошибся тут компилятор или нет остается открытым до обнародования листинга ?Subroutine0
Возможно Вы правы насчет объединения двух вызовов в отдельную подпраграмму,
только тогда очень странно выглядит LDI R25,0 после подпрограммы...

Автор, выложите более полный листинг.
alux
Цитата(singlskv @ Jan 22 2008, 16:44) *
Автор, выложите более полный листинг.

Пожалуйста. Не подозревал, что простой, на первый взгляд, вопрос вызовет такое бурное обсуждение 05.gif


Добавлено

Прошу прощения, но теперь в данном 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);


В последний раз добавлено smile.gif

Прокоментируйте, пожалуйста следующее:
Код
     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 бит, затем сложить с прочитанным младшим байтом.
singlskv
Цитата(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 здесь просто не должно быть...
alux
Код
     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) ?
ReAl
Цитата(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
alux
Цитата(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
- это скорее для красоты. Ведь ИЛИ с нулем ничего не изменяет.
ReAl
Я немного цитаты перетасую

Цитата(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()
О-о-о-о!!!
Давно не видел грабелек со столь любовно отполированной ручкой smile.gif
Получил массу удовльствия smile.gif
Значит так:
Код
// Это было написано одной строкой без промежуточных переменных
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() )

Но всё равно в данном случае как минимум одна временная переменная нужна, см. мой предыдущий пост. Иначе рано или поздно будет радостная встреча с каким-то компилятором, который сделает "неожиданный", но допустимый стандартом порядок вызова функций.
alux
Цитата(ReAl @ Jan 23 2008, 18:49) *
в предыдущих примерах было ACK в обеих строках.

это я сделал специально для чистоты эксперимента smile.gif
ReAl - ну Вы просто гигант мысли smile.gif. Вам огромный a14.gif
Сергей Борщ
Цитата(alux @ Jan 23 2008, 17:10) *
ReAl огромный a14.gif
Я тоже получил массу удовольствия от этого объяснения.

Позвольте реабилитироваться и внести свой вклад:
Код
#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;
}
Кажется так может быть эффективнее. А может и нет. Надо смотреть листинг.
ReAl
Цитата(Сергей Борщ @ 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.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.