реклама на сайте
подробности

 
 
2 страниц V  < 1 2  
Reply to this topicStart new topic
> Неожиданный результат, Прочитать два байта
Сергей Борщ
сообщение Jan 21 2008, 23:13
Сообщение #16


Гуру
******

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



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


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
singlskv
сообщение Jan 22 2008, 08:50
Сообщение #17


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



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

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

Вопрос:
Могу ли я написать у себя в программе
А = rand() + rand(); ?
или компилятор может вызвать rand только один раз ?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 22 2008, 11:57
Сообщение #18


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
singlskv
сообщение Jan 22 2008, 12:44
Сообщение #19


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



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

Автор, выложите более полный листинг.
Go to the top of the page
 
+Quote Post
alux
сообщение Jan 22 2008, 13:31
Сообщение #20


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Цитата(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 бит, затем сложить с прочитанным младшим байтом.
Прикрепленные файлы
Прикрепленный файл  asdx.rar ( 1.38 килобайт ) Кол-во скачиваний: 29
Прикрепленный файл  asdx_2.rar ( 1.72 килобайт ) Кол-во скачиваний: 27
 
Go to the top of the page
 
+Quote Post
singlskv
сообщение Jan 22 2008, 17:53
Сообщение #21


дятел
*****

Группа: Свой
Сообщений: 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 здесь просто не должно быть...
Go to the top of the page
 
+Quote Post
alux
сообщение Jan 22 2008, 19:47
Сообщение #22


Знающий
****

Группа: Свой
Сообщений: 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) ?
Go to the top of the page
 
+Quote Post
ReAl
сообщение Jan 22 2008, 21:17
Сообщение #23


Нечётный пользователь.
******

Группа: Свой
Сообщений: 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


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
alux
сообщение Jan 23 2008, 07:13
Сообщение #24


Знающий
****

Группа: Свой
Сообщений: 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
- это скорее для красоты. Ведь ИЛИ с нулем ничего не изменяет.
Go to the top of the page
 
+Quote Post
ReAl
сообщение Jan 23 2008, 14:49
Сообщение #25


Нечётный пользователь.
******

Группа: Свой
Сообщений: 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()
О-о-о-о!!!
Давно не видел грабелек со столь любовно отполированной ручкой 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() )

Но всё равно в данном случае как минимум одна временная переменная нужна, см. мой предыдущий пост. Иначе рано или поздно будет радостная встреча с каким-то компилятором, который сделает "неожиданный", но допустимый стандартом порядок вызова функций.


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
alux
сообщение Jan 23 2008, 15:10
Сообщение #26


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Цитата(ReAl @ Jan 23 2008, 18:49) *
в предыдущих примерах было ACK в обеих строках.

это я сделал специально для чистоты эксперимента smile.gif
ReAl - ну Вы просто гигант мысли smile.gif. Вам огромный a14.gif
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 23 2008, 15:30
Сообщение #27


Гуру
******

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



Цитата(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;
}
Кажется так может быть эффективнее. А может и нет. Надо смотреть листинг.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
ReAl
сообщение Jan 24 2008, 08:04
Сообщение #28


Нечётный пользователь.
******

Группа: Свой
Сообщений: 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.


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post

2 страниц V  < 1 2
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 27th July 2025 - 15:12
Рейтинг@Mail.ru


Страница сгенерированна за 0.015 секунд с 7
ELECTRONIX ©2004-2016