Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Прошу совета по индексам массивов
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
king2
Есть такой вот код:

Код
    171                usb_buffer[usb_txtail++]=0x05;
   \   00000094   9160....           LDS     R22, usb_txtail
   \   00000098   2F56               MOV     R21, R22
   \   0000009A   9553               INC     R21
   \   0000009C   9350....           STS     usb_txtail, R21
   \   000000A0   E055               LDI     R21, 5
   \   000000A2   E070               LDI     R23, 0
   \   000000A4   01FB               MOVW    R31:R30, R23:R22
   \   000000A6   ....               SUBI    R30, LOW((-(usb_buffer) & 0xFFFF))
   \   000000A8   ....               SBCI    R31, (-(usb_buffer) & 0xFFFF) >> 8
   \   000000AA   8350               ST      Z, R21


Изначально предполагалось, что так как usb_txtail unsigned char, то оно досчитает до 255 и начнет снова с нуля без всяких танцев с бубнами. А фиг...

Если после этого вставить строчку:
Код
if (usb_txtail == 255) usb_txtail = 0;

то все работает на ура. Если этой строчки нет - досчитав до 255, начинается какая-то свистопляска, оно выводит содержимое буфера еще раз...

Есть какие-нить идеи на эту тему? smile.gif
aaarrr
Цитата(king2 @ Nov 6 2006, 06:02) *
Если после этого вставить строчку:
Код
if (usb_txtail == 255) usb_txtail = 0;

Если добавить эту строку, то элемент с индексом 255 никогда не будет записан.
Осталось разобраться, почему эта запись все ломает. Может быть, буфер случайно объявлен как usb_buffer[255]?
_Bill
Цитата(king2 @ Nov 6 2006, 06:02) *
Есть такой вот код:

Изначально предполагалось, что так как usb_txtail unsigned char, то оно досчитает до 255 и начнет снова с нуля без всяких танцев с бубнами. А фиг...

Если после этого вставить строчку:
Код
if (usb_txtail == 255) usb_txtail = 0;

то все работает на ура. Если этой строчки нет - досчитав до 255, начинается какая-то свистопляска, оно выводит содержимое буфера еще раз...

Есть какие-нить идеи на эту тему? smile.gif

Есть два варианта:
1. usb_buffer[(unsigned char)(usb_txtail++)]=0x05;
2. usb_buffer[usb_txtail++ % 255]=0x05;
aaarrr
Цитата
Есть два варианта:

А Вас не смущает то, что результат работы этих двух вариантов будет разным?

Проблема не в типе char, с которым компилятор обошелся совершенно корректно, а, скорее всего, в размере буфера.
rezident
А тип самого буфера наверное unsigned int? А попробуйте-ка разбить код на две операции
Код
usb_buffer[usb_txtail]=0x05;
usb_txtail++;

и сообщите что получается.
prottoss
Скомпилил вот такой код:
Код
UCHAR bfr[300];
UCHAR indx;

void main(void)
{
   indx = 0;
   for(UINT i = 0; i < 300; i++)
       bfr[indx++] = 5;
}
Так вот, после значения 255 переменная indx принимает значение НОЛЬ. Ищите косяки в программе. НИКОГДА, если переменная объявлена как CHAR, компилятор не переделает ее в INT, LONG, LONLONG или FLOAT, если вы прямо ему об этом не укажите...
rezident
Цитата(prottoss @ Nov 6 2006, 17:00) *
НИКОГДА, если переменная объявлена как CHAR, компилятор не переделает ее в INT, LONG, LONLONG или FLOAT, если вы прямо ему об этом не укажите...

"Никогда не говори ''никогда''" smile.gif А про приведение типов в языке СИ вы знаете? И что бывает в случае арифметических операций с числами разной разрядности, надеюсь тоже в курсе?
Цитата из K&R (переводная, из библиотеки Мошкова). Глава 1.6. Массивы
Цитата
По определению перед проведением арифметических опера-
ций, вовлекающих переменные типа CHAR и INT, все они преоб-
разуются к типу INT, TAK что в арифметических выражениях пе-
ременные типа CHAR по существу идентичны переменным типа
INT.

Еще раз предлагаю инкремент индекса вынести как отдельную операцию. У меня нет IAR for AVR, чтобы проверить получившийся результат на ассемблере.
aaarrr
Цитата(rezident @ Nov 6 2006, 15:57) *
Еще раз предлагаю инкремент индекса вынести как отдельную операцию. У меня нет IAR for AVR, чтобы проверить получившийся результат на ассемблере.

Код приведен в первом посте.
rezident
Вот еще в Википедии отыскал существенное (по-моему) замечание/комментарий, подходящее как раз для данного случая.
Цитата
Note that for addition and subtraction of integers and pointers, the value of the pointer is not adjusted by the integer amount, but is adjusted by the amount multiplied by the size (in bytes) of the type to which the pointer refers. One pointer may also be subtracted from another, provided they point to elements of the same array (or the position just beyond the end of the array). If you have a pointer that points to an element of an array, the index of the element is the result when the array name is subtracted from the pointer. Here's an example.

Раздел 1.4 Pointers and Arrays.
IgorKossak
Цитата(king2 @ Nov 6 2006, 05:02) *
А фиг...

Что под этим подразумевается? В отладчике смотрели?
Ведь судя по приведенному коду всё должно быть нормально.
Или это в результате вставки того пресловутого условия?

Приведите сгенерированный код с условием и без него.
aaarrr
Цитата(rezident @ Nov 6 2006, 16:51) *
Вот еще в Википедии отыскал существенное (по-моему) замечание/комментарий, подходящее как раз для данного случая.

А по-моему, все Ваши замечания ни разу не подходят к рассматриваемому случаю angry.gif
rezident
Цитата(aaarrr @ Nov 6 2006, 19:56) *
Цитата(rezident @ Nov 6 2006, 16:51) *

Вот еще в Википедии отыскал существенное (по-моему) замечание/комментарий, подходящее как раз для данного случая.

А по-моему, все Ваши замечания ни разу не подходят к рассматриваемому случаю angry.gif

ОК smile.gif Если резюмировать, то мои намеки были на то, что хорошим тоном является явная проверка на выход за границы массива. Вместо того, чтобы надеяться на "благоразумность" компилятора и размерность переменных им же определенных.
aaarrr
Цитата(rezident @ Nov 6 2006, 18:02) *
ОК smile.gif Если резюмировать, то мои намеки были на то, что хорошим тоном является явная проверка на выход за границы массива. Вместо того, чтобы надеяться на "благоразумность" компилятора и размерность переменных им же определенных.

Полностью согласен. Впрочем, автор топика в результате получил от компилятора в точности то, о чем просил smile.gif
prottoss
Цитата(rezident @ Nov 6 2006, 19:57) *
Цитата(prottoss @ Nov 6 2006, 17:00) *

НИКОГДА, если переменная объявлена как CHAR, компилятор не переделает ее в INT, LONG, LONLONG или FLOAT, если вы прямо ему об этом не укажите...

"Никогда не говори ''никогда''" smile.gif А про приведение типов в языке СИ вы знаете? И что бывает в случае арифметических операций с числами разной разрядности, надеюсь тоже в курсе?
Вы читайте, не то что выделенно большими буквами, а все предложение)

Когда мы приводим один тип к другому, мы УКАЗЫВАЕМ компилятору про тип, и, соответсвенно, разрядность переменой, тип которой мы приводим. Когда мы выполняем арифметические операции над числами разной разрядности, мы то же, неявно УКАЗЫВАЕМ компилятору на разрядность операции...

А если компилятор начнет предполагать, что после инкремента какой то переменной может произойти переполнение разрядной сетки, то все переменные, в конечном итоге, у него превратятся в LONGLONGLONGLONGLONG.....................

Для того, что бы проверить код, не обязательно иметь ИАР Си, возьмите от Борланда или Мелкософта...



Цитата(rezident @ Nov 6 2006, 19:57) *
Цитата из K&R (переводная, из библиотеки Мошкова). Глава 1.6. Массивы
Цитата
По определению перед проведением арифметических опера-
ций, вовлекающих переменные типа CHAR и INT, все они преоб-
разуются к типу INT, TAK что в арифметических выражениях пе-
ременные типа CHAR по существу идентичны переменным типа
INT.

Еще раз предлагаю инкремент индекса вынести как отдельную операцию. У меня нет IAR for AVR, чтобы проверить получившийся результат на ассемблере.
К сожалению, не читал данное произведение...

И хотя инкремент индекса, в приципе тоже арифметическая операция, ИМХО никогда любой нормальный компилятор не преобразует индекс при инкременте в переменную бОльшей разрядности... Может быть только, если этот компилятор написан Вами)
_Sam_
Я вообще не понял в чём проблема!

Цитата
если после этого поставить строчку if (usb_txtail == 255) usb_txtail = 0;

Получится
Код
usb_buffer[usb_txtail++]=0x05;
if (usb_txtail == 255) usb_txtail = 0;

судя по асм. листингу usb_txtail однобайтовая переменная, а значит добавленная строка вообще не влияет на результат, либо её добавляют не после "этого", а перед "этим"!

Ну да ладно читаем дальше.
Цитата
начинается какая-то свистопляска, оно выводит содержимое буфера еще раз...

А "оно" и должно выводить ещё раз! Индекс то обнулился! biggrin.gif


Цитата
И хотя инкремент индекса, в приципе тоже арифметическая операция

Было сказано про арифметические операции в которых участвуют переменные разных типовwink.gif
prottoss
Цитата(_Sam_ @ Nov 8 2006, 15:06) *
Цитата
начинается какая-то свистопляска, оно выводит содержимое буфера еще раз...

А "оно" и должно выводить ещё раз! Индекс то обнулился! biggrin.gif
Цитата
И хотя инкремент индекса, в приципе тоже арифметическая операция

Было сказано про арифметические операции в которых участвуют переменные разных типов wink.gif
Да, я про последнюю строчку автора совсем забыл...))) Ему надо еще (учиться & * 3), как завещал великий Ленин biggrin.gif
ARIM
Цитата(king2 @ Nov 6 2006, 06:02) *
начинается какая-то свистопляска, оно выводит содержимое буфера еще раз...

Есть какие-нить идеи на эту тему? smile.gif

какая свистопляска? куда выводит?
судя по коду на асме происходит циклическое заполнение массива по адресу usb_buffer+usb_txtail.
sz36
Цитата(rezident @ Nov 6 2006, 15:57) *
Цитата(prottoss @ Nov 6 2006, 17:00) *

НИКОГДА, если переменная объявлена как CHAR, компилятор не переделает ее в INT, LONG, LONLONG или FLOAT, если вы прямо ему об этом не укажите...

"Никогда не говори ''никогда''" smile.gif

Именно так.

Цитата(rezident @ Nov 6 2006, 15:57) *
Еще раз предлагаю инкремент индекса вынести как отдельную операцию. У меня нет IAR for AVR, чтобы проверить получившийся результат на ассемблере.

У меня IAR есть, и по эти граблям я уже ходил в точно такой же ситуации - записи в кольцевой буфер. Индекс массива у IAR - int, даже если он объявлен как char[256]. В данном случае нужно писать так (если usb_txtail объявлен как char):

usb_txtail++;
usb_buffer[usb_txtail]=0x05;

или так:
usb_buffer[(usb_txtail++) & 0xff]=0x05;
prottoss
Цитата(sz36 @ Nov 10 2006, 06:58) *
Цитата(rezident @ Nov 6 2006, 15:57) *

Цитата(prottoss @ Nov 6 2006, 17:00) *

НИКОГДА, если переменная объявлена как CHAR, компилятор не переделает ее в INT, LONG, LONLONG или FLOAT, если вы прямо ему об этом не укажите...

"Никогда не говори ''никогда''" smile.gif

Именно так.

Цитата(rezident @ Nov 6 2006, 15:57) *
Еще раз предлагаю инкремент индекса вынести как отдельную операцию. У меня нет IAR for AVR, чтобы проверить получившийся результат на ассемблере.

У меня IAR есть, и по эти граблям я уже ходил в точно такой же ситуации - записи в кольцевой буфер. Индекс массива у IAR - int, даже если он объявлен как char[256]. В данном случае нужно писать так (если usb_txtail объявлен как char):

usb_txtail++;
usb_buffer[usb_txtail]=0x05;

или так:
usb_buffer[(usb_txtail++) & 0xff]=0x05;

Бред какой то...Вы первый пост читали? Если нет, то я Вам настоятельно рекомендую...
king2
Отвечаю сразу всем.
Если вынести инкремент в отдельную операцию, получается вот что:

Код
     84             usb_txtail++;
   \   00000026   ....               LDI     R30, LOW(usb_txtail)
   \   00000028   ....               LDI     R31, (usb_txtail) >> 8
   \   0000002A   8100               LD      R16, Z
   \   0000002C   9503               INC     R16
   \   0000002E   8300               ST      Z, R16
     85             usb_buffer[usb_txtail]=0x05;
   \   00000030   E005               LDI     R16, 5
   \   00000032   9120....           LDS     R18, usb_txtail
   \   00000036   E030               LDI     R19, 0
   \   00000038   01F9               MOVW    R31:R30, R19:R18
   \   0000003A   ....               SUBI    R30, LOW((-(usb_buffer) & 0xFFFF))
   \   0000003C   ....               SBCI    R31, (-(usb_buffer) & 0xFFFF) >> 8
   \   0000003E   8300               ST      Z, R16


От (unsigned char) перед инкрементом или от & 0xFF ничего не меняется.

"ОНО" не должно выводить буфер еще раз, потому что вывод написан вот так:

Код
       while (usb_txhead != usb_txtail) {
           Usb_write_byte(usb_buffer[usb_txhead++]);
       }


По идее, переход от 255 к нулю не должен ничем отличаться от перехода с 10 на 11, например.
А фиг..
Даже если заполнять буфер просто инкрементом, то оно потихонечку досчитывает до 255, потом быстро выводит буфер еще раз (уже заполненный) и продолжает.

Объявления переменных:
Код
volatile unsigned char usb_buffer[256];
volatile unsigned char usb_txhead;
volatile unsigned char usb_txtail;
aaarrr
Цитата(king2 @ Nov 13 2006, 17:26) *
"ОНО" не должно выводить буфер еще раз, потому что вывод написан вот так...
...Даже если заполнять буфер просто инкрементом, то оно потихонечку досчитывает до 255, потом быстро выводит буфер еще раз (уже заполненный) и продолжает.

То есть выходит, что проблема имеет место при выводе данных, а не при вводе?
king2
Цитата(aaarrr @ Nov 13 2006, 17:57) *
Цитата(king2 @ Nov 13 2006, 17:26) *

"ОНО" не должно выводить буфер еще раз, потому что вывод написан вот так...
...Даже если заполнять буфер просто инкрементом, то оно потихонечку досчитывает до 255, потом быстро выводит буфер еще раз (уже заполненный) и продолжает.

То есть выходит, что проблема имеет место при выводе данных, а не при вводе?

Получается, что так. Фишка в том, что блоки и ввода и вывода одинаковые, индекс вывода "догоняет" индекс ввода, пока они не станут равны. И тот и другой только увеличивается, так что по идее, если индекс ввода поменялся 255->0, то и индекс вывода должен поменяться так же (и вывести байтик).
А оно так не происходит, вместо этого оно каким-то образом проскакивает через проверку равенства индексов и выводит по-новой весь буфер.. sad.gif
prottoss
Цитата(king2 @ Nov 13 2006, 22:38) *
А оно так не происходит, вместо этого оно каким-то образом проскакивает через проверку равенства индексов и выводит по-новой весь буфер.. sad.gif
Люди! так и не возьму в толк что ВАМ не понятного? ЕСЛИ и индекс обьявлен, как 8-разрядная переменная, то, естественно, какой бы ни был длины массив, хоть 160 гбайт,выше границы 256 элементов массива, ВЫ не выйдите ни когда) Компилятор будет тупо увеличивать разрядность индекса, дополнением его до нужной разрядности дополнением нулей ПРИ ВЫЧИСЛЕНИИ АБСОЛЮТНОГО АДРЕСА ЭЛЕМЕНТА МАССИВА, НО при выходе индекса (при его инкременте-декременте) за пределы объявленной разрядности, индекс будет урезан до той разрядности, которую ВЫ указали компилятору. В крайнем случае, если компилятор посчитает, что есть НЕОДНОЗНАЧНОСТЬ, он (компилятор) предупредит ВАС соответсвующим предупреждением. Ну.
aaarrr
А как при условии равенства индексов определить, содержит буфер 0 или 256 байт?

Usb_write_byte() - это, случайно, не макрос?

А вообще, rezident правильно написал по поводу контроля за состоянием указателя.
king2
Посылательный код:
Код
    //! writes byte in FIFO for current endpoint
    #define Usb_write_byte(byte)                      (UEDATX  =   (U8)byte)
    //! tests if endpoint write allowed
    #define Is_usb_write_enabled()                   (UEINTX&(1<<RWAL))

    412                 while (Is_usb_write_enabled() && (usb_txhead != usb_txtail)) {
                                                                                   ^
Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
   \                     ??loop1:
   \   0000028E   910000E8           LDS     R16, 232
   \   00000292   2F10               MOV     R17, R16
   \   00000294   FF15               SBRS    R17, 5
   \   00000296   C014               RJMP    ??exit
   \   00000298   9100....           LDS     R16, usb_txhead
   \   0000029C   9110....           LDS     R17, usb_txtail
   \   000002A0   1701               CP      R16, R17
   \   000002A2   F071               BREQ    ??exit
    413                     Usb_write_byte(usb_buffer[usb_txhead++]);
   \   000002A4   9100....           LDS     R16, usb_txhead
   \   000002A8   2F10               MOV     R17, R16
   \   000002AA   9513               INC     R17
   \   000002AC   9310....           STS     usb_txhead, R17
   \   000002B0   E010               LDI     R17, 0
   \   000002B2   01F8               MOVW    R31:R30, R17:R16
   \   000002B4   ....               SUBI    R30, LOW((-(usb_buffer) & 0xFFFF))
   \   000002B6   ....               SBCI    R31, (-(usb_buffer) & 0xFFFF) >> 8
   \   000002B8   8100               LD      R16, Z
   \   000002BA   930000F1           STS     241, R16
   \   000002BE   CFE7               RJMP    ??loop1
    415                 }


Вопрос нифига не в том, почему индекс становится 16-битным - тут-то понятно почему, потому что он становится индексом не массива, а памяти, а это как раз unsigned int smile.gif

Вопрос в том, почему при переходе через 0 оно посылает еще один раз весь буфер.

Отличать 0 от 256 символов в буфере не надо, предполагается, что такой ситуации не возникает, а если возникает, то и фиг с ней. В данном примере ее реально не возникает, всегда реальных данных меньше чем 64 байт.
_Sam_
Цитата
Вопрос в том, почему при переходе через 0 оно посылает еще один раз весь буфер.

потому что при переходе через ноль (usb_txhead != usb_txtail)

условие
Код
(usb_txhead != usb_txtail)
вы приводили почти во всех примерах, а как меняется usb_txtail вы не показывали.
king2
Цитата(_Sam_ @ Nov 14 2006, 09:48) *
Цитата
Вопрос в том, почему при переходе через 0 оно посылает еще один раз весь буфер.

потому что при переходе через ноль (usb_txhead != usb_txtail)

условие
Код
(usb_txhead != usb_txtail)
вы приводили почти во всех примерах, а как меняется usb_txtail вы не показывали.

Здесь я написал как оно из буфера выводится (инкремент usb_txtail), в первом посте написал как в буфер запихивается (инкремент usb_txhead).

Не понимаю, почему условие НЕРАВЕНСТВА должно по другому работать, если одна из сравниваемых переменных равна нулю.
_Sam_
Могли бы и целиком код привести. И указать в этом целом куске что конкретно не работает. excl.gif

Вот попытался как-то обобщить то что вы писали.
Код
#include "stdio.h"

volatile unsigned char usb_buffer[256];
volatile unsigned char usb_txhead = 1;   // начинаем вывод с первого элемента
volatile unsigned char usb_txtail = 255;  // чтобы при выполнении цикла usb_txtail == 0

int main( void ){
  usb_buffer[usb_txtail++]=0x05;
  while (usb_txhead != usb_txtail) {                // Is_usb_write_enabled() я убрал, т.к. не знаю что такое    UEDATX и UEINTX
           printf("%i - %i\n",usb_buffer[usb_txhead++],usb_txhead); // функцию Usb_write_byte заменил на printf
  }
  return 0;
}


У меня всё работает и буфер выводится один раз несмотря на переполнение индекса!
Компилятор и отладчик использовал IAR. Формат printf - medium.
king2
Итак, проблема найдена, вопрос закрыт.

Дело было в программе на windows, в которую лился поток данных, задержка на обработку добавления в визуальный элемент была ТОЧНО на 4 буфера при постоянной передаче, поэтому создавалось впечатление, что не то скипается, не то повторно передается 256 байт. На самом деле виндовая программа не давала принимать данные точно на размер одного "кольца" по буферу. Если буфер сделать на один байт меньше, эффект был не так очевиден и я просто его не заметил.

Спасибо большое всем за помощь!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.