Массив структур и указявки.

Тема в разделе "Микроконтроллеры AVR", создана пользователем parovoZZ, 13 фев 2019 в 01:19.

  1. parovoZZ

    parovoZZ Гуру

    Вопрос №1
    Имеется такая, мать ее, структура
    Код (C++):
    struct Settings_t            
    {
        uint8_t   data1;              
        uint8_t    data2;              
        uint8_t    data3;          
    };
    Массив:
    Код (C++):
     struct    Settings_t    Settings[Count];
    Теперь я хочу заполнить все поля всех структур значениями. Разумеется, что перебирать все индексы массива и поля структуры - это дичайший брееееед. Есть же указявки! Хорошо. Читаем еееепром:
    Код (C++):
    void Read_settings(void)
    {
        uint8_t    i = 0;
        uint8_t    address;
        uint8_t*    pt;

        pt = (uint8_t*)Settings;

        address = (uint8_t*)&Settings[Count] - (uint8_t*)Settings;

        while (i <= address)
        {
            *pt++ = EEPROM_read((uint16_t)i);

            i++;
        }

    i = Count;
    while (i--)
    {
        Change_Setting[i] = 1;
    }

    }
    Вопрос-то вот в чем. Вообще не отдупляю, как избавиться от лишнего pt? С pt всё мне логично и понятно. Ну и метод вычисления длины массива - так говнокодит кто? Имя переменной address не совсем логичный, но так сложилось исторически.

    Вопрос №2 Есть ещё массив с флагами изменения полей структуры
    Код (C++):
    uint8_t Change_Setting[Count];
    Как вот это цикл
    Код (C++):
    i = Count;
    while (i--)
    {
        Change_Setting[i] = 1;
    }
    вставить в этот мотоцикл:
    Код (C++):
        while (i <= address)
        {
            *pt++ = EEPROM_read((uint16_t)i);

            i++;
        }
    разумеется, без всяких делений и прочей мудозвонной мутаты.

    В самой структуре мне этот флаг вообще не нужен. Это заметка с упреждением =)
     
  2. DIYMan

    DIYMan Гуру

    1. Дабы не огрести анального огораживания от выравнивания, делаешь:

    Код (C++):
    #pragma pack(push,1)
    struct Settings_t
    {
        uint8_t   data1;          
        uint8_t    data2;          
        uint8_t    data3;      
    };
    #pragma pack(pop)
    Это выровняет структуру по границе 1 байт, и сделает код более переносимым.

    2. Создаёшь массив структур:

    Код (C++):
    Settings_t  myArray[100];
    3. Легко получаешь кол-во элементов в массиве, на этапе компиляции:

    Код (C++):

    const size_t arrayCount = sizeof(myArray)/sizeof(myArray[0]);
     
    4. Если читаешь побайтово из EEPROM в структуру, то без кастинга - никак, это про твой pt.

    5. Если ты забиваешь массив значениями по умолчанию (на примере твоего Change_Setting[ i ]=1) - то не проще ли применить банальный memset?
     
    parovoZZ и Tomasina нравится это.
  3. Asper Daffy

    Asper Daffy Гик

    А как у тебя Count задан?
     
  4. parovoZZ

    parovoZZ Гуру

    атмел студио разгребает, что такое #pragma ? Честно говоря, не понимаю, почему компиляторы сдвигают данные. Даже на 64 битах у нас же адресуется 1 байт, а не все 8?

    Кстати, да. Тупанул на ночь глядя. Это я скопипастил побыренькому из кода для вычисления смещения адреса для записи в еееепром. Логичнее сделать так
    Код (C++):
    define Count_array   Count * sizeof(myArray[0])
     
    Так эта функция просто верстает взад значение регистра. И всё равно короче не записать?

    memset вытянет на ровном месте 200 байт не нужного мне кода. Перебрать все значения по коду меньше. Скорость выполнения не сильно интересует в данном случае.

    Число задано на этапе компиляции. Либо через макрос, либо в makefile. Это не автомассив.
     
  5. AlexU

    AlexU Гуру

    Вот это вот:
    Код (C++):
    uint8_t    i = 0;
        uint8_t    address;
        uint8_t*    pt;

        pt = (uint8_t*)Settings;

        address = (uint8_t*)&Settings[Count] - (uint8_t*)Settings;

        while (i <= address)
        {
            *pt++ = EEPROM_read((uint16_t)i);

            i++;
        }
    решается одной строчкой типа:
    Код (C++):
    eeprom_read_block(Settings, EESettings, sizeof(Settings));
    где EESettings -- это память выделенная в EEPROM для хранения массива структур.
    А вот это:
    Код (C++):
    i = Count;
    while (i--)
    {
        Change_Setting[i] = 1;
    }
     
    строкой типа:
    Код (C++):
    memset(Change_Setting, 1, Count);
    Вот откуда это вот взялось в твоей голове. Как четырнадцать байт кода могли превратиться в 200 байт?
     
  6. DIYMan

    DIYMan Гуру

    Обзязательно.
    Потому что это, как минимум, platform specific.
    Да ну? Там унутре тот же цикл, не будет там 200 байт лишнего кода, по сравнению с твоим циклом.
     
  7. DetSimen

    DetSimen Гуру

    memset написан даже не на ассемблере, а в чистых, чесных машинных кодах, раскинутых по перфокартам, он мало флэша жрёть. :)
     
  8. parovoZZ

    parovoZZ Гуру

    от библиотечных функций решено сразу отказаться ввиду их монструозности.
     
    Последнее редактирование: 13 фев 2019 в 13:17
  9. parovoZZ

    parovoZZ Гуру

    Раз вы так все уверенно говорите, это значит я с malloc попутал. Но вечером проверю.
    \
    Так я то хочу цикл заполнения массива единичкой приткнуть в верхний цикл. Да вот беда - полей в структуре 3. А как выделить из индекса i число, кратное трем, и простыми методами - в голову не приходит никак.
     
    Последнее редактирование: 13 фев 2019 в 23:40
  10. AlexU

    AlexU Гуру

    Ага... И придумать свой ну совсем не монструозный "лисапед".
    Так понимаю 'EEPROM_read' это "копипаст" из доки. В этом случае твой "оптимальный" код (вместе с вызовом функции) весит 60 байт.
    А абсолютно не оптимальная библиотечная 'eeprom_read_block' (так же вместе с вызовом) всего 46 байт.
    Оптимизатор, млин....
    Сам-то хоть понимаешь, что создание тем с детскими вопросами по поводу языков C и C++ и заявления о монструозности библиотечных функций, написанных профессионалами, -- вещи не совместимые.

    Вот код memset, который выдаёт avr-gcc версии 5.4.0 (данная версия даёт не самый оптимальный по размеру код):
    Код (Text):

    94a:    80 e2           ldi    r24, 0x20    ; 32
    94c:    e4 ed           ldi    r30, 0xD4    ; 212
    94e:    f1 e0           ldi    r31, 0x01    ; 1
    950:    df 01           movw    r26, r30
    952:    1d 92           st    X+, r1
    954:    8a 95           dec    r24
    956:    e9 f7           brne    .-6          ; 0x952 <loop+0x32>
     
    DIYMan, DetSimen и Asper Daffy нравится это.
  11. DIYMan

    DIYMan Гуру

    Куда уж проще-то:
    Код (C++):
    uint8_t idx = i % 3;
    Не? Или я чего не понял? Тебе надо именно чётко кратное трём? Или, независимо от итератора цикла - иметь индексы 0, 1, 2? Если второе - пример выше. Если первое, то, поскольку оперируем целочисленной арифметикой, то число, кратное трём, на любом проходе цикла получается делением итератора цикла на 3 :)
     
    parovoZZ нравится это.
  12. parovoZZ

    parovoZZ Гуру

    Не беги впереди паровоза ... он под откос летит!
     
  13. parovoZZ

    parovoZZ Гуру

    я имел ввиду за приемлемое количество тактов. 2, 4, ну 6. Не больше))
     
  14. Asper Daffy

    Asper Daffy Гик

    Не, Дим, ты не понял, человек высокого полёта! Для него мемсет - монструально, а уж остаток от деления .... за такое надо расстреливать через повешение!
     
    Andrey12, DIYMan и DetSimen нравится это.
  15. parovoZZ

    parovoZZ Гуру

    Да. Но в свете сообщений уважаемого @AlexU , второй вопрос может потерять актуальность.
     
  16. AlexU

    AlexU Гуру

    Да зачем что-то придумывать, если почти всё придумано до нас. В состав компилятора avr-gcc (точнее в состав его стандартных библиотек) входит библиотека для работы с EEPROM -- заголовочный файл <avr/eeprom.h>.
    В этой библиотеке все необходимые функции для работы с EEPROM есть.
    Код (C++):
    const size_t count = Count * sizeof(Settings[0]);
    зачем вот это лишнее вычисление размера путём перемножения, когда размер в байтах (необходимый для чтения из EEPROM) можно вычислить простым sizeof:
    Код (C++):
    eeprom_read_block(Settings, 0, sizeof(Settings));
    Далее, указываешь вручную адрес в EEPROM -- 0. Т.е. нечем больше заняться как самому распределять память EEPROM по переменным и следить за правильным её использованием? Может пусть этим займётся компилятор, а ты чем-нибудь более полезным?
    И ещё, нулевую ячейку EEPROM лучше не использовать -- данные повреждаются. Стоит отметить, что это не всегда происходит, но дефект признан производителем (правда тогда ещё это был Atmel).
    Код (C++):
    uint8_t idx = i % 3;
    У AVR'ок не аппаратной поддержки деления (по крайней мере у распространнённых типа ATmega328). Поэтому данный код развернётся в немалую портянку.
    Есть такая штука как -- avr-objdump. И вообще в состав компиляторов семейства GCC для разных платформ входит такая штука -- objdump -- дизассемблер.
    В Arduino IDE (если память не изменяет) и в Eclipse с плагином AVR-Eclipse эта утилита (avr-objdump) используется по-умолчанию -- после компиляции создаётся файл <project_name>.lss, который содержит дизассемблированный код (на ассемблере) для скомпилированной прошивки. В частности выше приводил пример такого кода (для функции memset). По содержимому этого файла можно оценить, что там компилятор накомпилил. Правда нужен определённый навык, а то оптимизатор работает довольно-таки хорошо...

    UPD: objdump работает с ELF, а не с HEX. Но, тот же avr-gcc (Atmel Studio) всё компилирует в ELF. А уже потом из него HEX достает.
     
  17. ИгорьК

    ИгорьК Давно здесь Команда форума

    Это флуд? Или гост? Я удалил ваше сообщение. Все.
     
  18. parovoZZ

    parovoZZ Гуру

    Ну я свои написал. Зачем - ну, вопрос не уместен. Как я показывал ранее - счет получился 0:16 байт в мою пользу.

    Я не до конца понимаю поведение этого sizeof. Где-то он вернет размерность uint8_t, где-то количество байт. В любом случае, данное выражение вычисляется компилятором, а не в недрах МК.

    У младших умножитель не присутствует. Но есть в AtXmega (про тиньки от них не знаю).