Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить

Re: Вопросы по С/С++ (СИ)

Чт май 14, 2020 23:55:40

NStorm писал(а):И зачем? Зачем под это тратить память, когда размер строки во флэше всё-равно неизменен всегда?


ну надо было передать циклу в качестве значения. Или не надо?

Код:
for (i = 0; i < (strlegn-1); i ++)
{}

Re: Вопросы по С/С++ (СИ)

Пт май 15, 2020 00:35:02

Внезапно, но точно также:
Код:
for (i = 0; i < (MY_STRING_LEN-1); i ++)


PS: Я всё-еще точно также не вижу смысл заводить отдельные макросы чисто под sizeof().
Код:
for (i = 0; i < (sizeof(lcd_data)-1); i ++)

Даст тот же самый эффект, при это сразу видно размер какой строки берется.

Re: Вопросы по С/С++ (СИ)

Пт май 15, 2020 01:06:04

Понял, спасибо.
А еще такой глупенький вопрос. Я могу функцию main объявить просто main() и это будет работать (я проверил). При этом не уточняя чем она является (void, int, и т.п). Какой тип она имеет, если это явно не указано?

Re: Вопросы по С/С++ (СИ)

Пт май 15, 2020 07:01:24

А еще такой глупенький вопрос.
Это точно!

Я могу функцию main объявить просто main() и это будет работать (я проверил).
Зачем? Ведь для каждого действия должна быть мотивировка.

Какой тип она имеет, если это явно не указано?
int

Re: Вопросы по С/С++ (СИ)

Пт май 15, 2020 07:39:43

Shuspano писал(а):Какой тип она имеет, если это явно не указано?
в микроконтроллерном программировании практически всегда совершенно все равно, какой тип у функции main, поскольку эта функция никуда результат своей работы не возвращает - некуда просто.

но в Си вообще все, о чем не сказано прямо, имеет тип int

Добавлено after 4 minutes 46 seconds:
Shuspano писал(а):ну надо было передать циклу в качестве значения. Или не надо?
во многих случаях именно не надо.

концепция строк в Си подразумевает, что все они заканчиваются нулем, поэтому можно в цикле перебирать символы строки от начала, пока не дойдешь до нуля. т.е. заранее знать размер строки нет необходимости во многих случаях. а когда это необходимо (для резервирования места в памяти) то для статических переменных лучше применять sizeof, а для динамических - функцию strlen.

Добавлено after 4 minutes 46 seconds:
традиционно вот так работают со строкой посимвольно:
Код:
void my_func(char *s){
   while(*s){
      // тут обрабатываем очередной символ *s
      s++; // и берем следующий символ
   }
}

для строки в памяти программ надо делать так
Код:
void my_func(const __flash char *s){
   while(*s){
      // тут обрабатываем очередной символ *s
      s++; // и берем следующий символ
   }
}
а если сделать вот так, то будет все равно, где размещена строка (в ОЗУ или FLASH):
Код:
void my_func(const __memx char *s){
   while(*s){
      // тут обрабатываем очередной символ *s
      s++; // и берем следующий символ
   }
}

Re: Вопросы по С/С++ (СИ)

Пт май 15, 2020 11:12:22

Спасибо за разъяснения.
Но вот возник вопрос. строка с нулем на конце не может же вмещать 0x00 как данные? Иначе когда этот ноль попадет в сравнивающую подпрограмму, он будет расценен как окончание?

Re: Вопросы по С/С++ (СИ)

Пт май 15, 2020 12:22:19

Может. Но, да, \0 будет расценен как конец строки. Это актуально для стандартных функций для текстовых строк. В своих обработчиках вы вольны делать сами как вам надо. Включая тот вариант ,как вы делали ранее - заранее "прошагивая" весь массив (кроме последнего \0) через for. Пример ARV выше остановится на нуле, т.к. соб-но цикл while там идет пока символ не 0. Аналогично делают и всякие стандартные строковые функции, вроде strcmp. Ну как бы считается, что строка - это именно строка и \0 она не может содержать в середине. Если у вас произвольный набор данных, который может этот ноль содержать, то да, надо еще длину отдельно хранить, если строка не константа. Для констант остается sizeof().

Re: Вопросы по С/С++ (СИ)

Пт май 15, 2020 12:35:01

NStorm, самое веселое начинается в юникоде. Если в UTF-8 нулевой байт спокойно можно расценивать, как конец строки, то в UTF-16 это совсем не так.

Re: Вопросы по С/С++ (СИ)

Пт май 15, 2020 15:46:19

ПростоНуб писал(а):самое веселое начинается в юникоде
я так понимаю, в юникоде не надо умничать, а надо пользоваться библиотечными функциями/классами поддержки юникода, и веселье будет отменено.

Re: Вопросы по С/С++ (СИ)

Пт май 15, 2020 20:31:01

ARV, когда оперативки гигабайт - так и делаю. А на МК тащить библиотеку поддержки UTF-16 - себе дороже. Только локали способны весь флеш занять. Аккуратно, ручками, только то, что надо или вообще самостоятельно. Не так уж часто приходится на МК по NTFS на флешке лазить или через ESP8266 SOAP/REST на UTF-16 парсить.
С UTF-8, как я уже писал выше, проще.

Re: Вопросы по С/С++ (СИ)

Чт июл 30, 2020 22:16:04

Братцы помогите разобраться с EEPROM и EEMEM-ами.
Железо - Мега 128, дисплей с тачем и остальная периферия. В EEPROM надо хранить разного рода настройки, кольцевой список данных, ну и сам указатель на текущую "ячейку" этих данных. Общение с EEPROM будет из разных си файлов проекта. Поэтому хотелось бы как-то оптимизировать, каталогизировать это дело. Начал с настроек тач панели.
Схимичил файлы "eeprom.h"
Код:
#ifndef EEPROM_H_
#define EEPROM_H_

#define EEPROM_TOUCH_ADDRESS   0

/*   Настройки тач панели дисплея   */
float   touch_factor_x;
float   touch_factor_y;
int      touch_term_x;
int      touch_term_y;

#endif /* EEPROM_H_ */
и "eeprom.с"
Код:
#include <avr/eeprom.h>
#include "config.h"

/*   Настройки тач панели дисплея   */
float EEMEM touch_factor_x;
float EEMEM touch_factor_y;
int EEMEM touch_term_x;
int EEMEM touch_term_y;/**/

Приинклюдил к файлу "touch.c". Так вот код
Код:
float factor_x, factor_y;
int term_x, term_y;
...
...
//uint16_t addr = EEPROM_TOUCH_ADDRESS;
   //factor_x = eeprom_read_float((float *) addr);
   //addr += sizeof(factor_x);
   //factor_y = eeprom_read_float((float *) addr);
   //addr += sizeof(factor_y);
   //term_x = eeprom_read_word((uint16_t *) addr);
   //addr += sizeof(term_x);
   //term_y = eeprom_read_word((uint16_t *) addr);

factor_x = eeprom_read_float((float *) &touch_factor_x);
   factor_y = eeprom_read_float((float *) &touch_factor_y);
   term_x = eeprom_read_word((uint16_t *) &touch_term_x);
   term_y = eeprom_read_word((uint16_t *) &touch_term_y);
   
   if(term_x == -1 && term_y == -1)   touch_calibrated = 0;
странно работает. Тач панель не отрабатывает нажатия. И стирание EEPROM, при прошивке, не дает никакого эффекта. Как будто калибровка корректная. Прежний вариант кода(закомментированный) с "абсолютной" адресацией работает отменно. Тач отрабатывает, как надо и стирание памяти запускает автоматическую калибровку. Не пойму, где ошибка?

Re: Вопросы по С/С++ (СИ)

Чт июл 30, 2020 22:53:49

я всегда все настройки собираю в структуру, потом эту структуру целиком пишу/читаю в/из EEPROM при помощи eeprom_read_block или eeprom_update_block.

Re: Вопросы по С/С++ (СИ)

Чт июл 30, 2020 23:16:28

ARV, Проблема в том, что настройки потребуются "кусками" в непредсказуемые периоды времени. И обновляться тоже кусочно будут. Да еще и данные как-то надо писать/читать. Сначала массив данных будет расти до определенного предела, а потом старые данные будут затираться свежими по кругу. Получается, всю эту кучу надо будет перезаписывать, при малейшем изменении? Коли так, то буду признателен, если поделитесь примером кода применительным к ситуации. И "eeprom_update_block" обновит только изменившиеся данные или отрефрешит весь объем, занятый структурой?

Re: Вопросы по С/С++ (СИ)

Пт июл 31, 2020 07:33:36

eeprom_update_* сначало побайтово читают eeprom и если новые данные такие же - то не пишет, пропуская этот байт.

А у вас размер данных всегда динамический? Т.е. расти может до неопределенных размеров. Что будете делать, когда он помещаться перестанет? Лучше всё-таки имхо сразу установить максимальный размер и писать в EEPROM блоками по кольцу, вместе с настройками. Для уменьшения износа EEPROM настройки т.е. тоже лучше "двигать".

Re: Вопросы по С/С++ (СИ)

Пт июл 31, 2020 07:57:42

MOHCTEP писал(а):настройки потребуются "кусками" в непредсказуемые периоды времени
после того, как все настройки считаны в ОЗУ в структуру config, вы в любой момент можете обращаться к её куску config.someone - в чем проблема?
MOHCTEP писал(а):обновляться тоже кусочно будут
на здоровье: в той же структуре обновляйте config.someone когда угодно и сколько угодно раз!
MOHCTEP писал(а):всю эту кучу надо будет перезаписывать, при малейшем изменении?
зачем?! только в критические моменты: при (перед) выключении питания или в моменты простоя (раз в 1...100 секунд, например). для чего после каждого чиха обновлять настройки?!
MOHCTEP писал(а):И "eeprom_update_block" обновит только изменившиеся данные
да, сверит все и обновит только те байты, которые изменились. потому она и update

Re: Вопросы по С/С++ (СИ)

Пт июл 31, 2020 10:20:40

EEMEM не очень подходит для кольцевых буферов. Если уж делаете кольцевой, то делайте "абсолютную" адресацию, как и делали. Только действительно объедините все ваши значения в структуру и всё. Даже разного размера можно делать, только текущий размер тоже записывать в EEPROM надо будет. У меня с фиксированным размером по кольцевому буферу как-то так обычно настройки сохраняются/загружаются:
Спойлер
Код:
#define EEPROM_SIZE 512
#define EEPROM_ENTRY_SIZE (sizeof(settings_t) + sizeof(state_t))
#define EEPROM_ENTRIES (EEPROM_SIZE / EEPROM_ENTRY_SIZE)

typedef struct {
    uint32_t seq;
    // state_t goes here in actual EEPROM
    uint16_t crc;
} settings_t;

void load_settings() {
    uint8_t *ptr = 0; // byte pointer in EEPROM
    uint32_t cur_seq = 0;
    state_t state_buf;
   
    cli();

    while (queue_num < EEPROM_ENTRIES) {
        settings.seq = eeprom_read_dword((void *) ptr);
        if (settings.seq <= cur_seq || settings.seq == 0xFFFFFFFF) {
            settings.seq = cur_seq;
            break;
        }
        cur_seq = settings.seq;
        ptr += sizeof (settings.seq);
        eeprom_read_block(&state_buf, (void *) ptr, sizeof (state_buf));
        ptr += sizeof (state_buf);
        settings.crc = eeprom_read_word((void *) ptr);
        ptr += sizeof (settings.crc);

        if (settings.crc != crc16((void *) &state_buf, sizeof (state_buf))) {
            LOG("CRC incorrect\r\n");
            break;
        }
        // Since we are here, settings are valid, CRC are good
        state = state_buf;
        queue_num++;
    }
    sei();
}

void save_settings(eSaveMode savemode) {
    cli();

    settings.crc = crc16((void *) &state, sizeof (state)); // Calculate crc16 of state struct

    // Save data to EEPROM
    settings.seq++;
    if (queue_num >= EEPROM_ENTRIES) // Reset queue_num once we reach end of EEPROM
        queue_num = 0;
    uint8_t *ptr = (void *)(queue_num * EEPROM_ENTRY_SIZE); // Get location of next segment in EEPROM
   
   
    eeprom_write_dword((void *) ptr, settings.seq);
    ptr += sizeof (settings.seq);
    eeprom_update_block((void *)&state, (void *) ptr, sizeof (state));
    ptr += sizeof (state);
    eeprom_write_word((void *) ptr, settings.crc);

    sei();
}



Ну у меня тут небольшой костыль, две структуры. Т.к. настоящие "настройки" в структуре state, а структура settings - там лишь порядковый номер и CRC. Я тут обрезал, у меня еще кое что там было, не важно в данном случае. Смысл в том, что EEPROM по блокам по размеру обоих структур разбита и по кольцу пишется-читается с порядковым номером и CRC.

Re: Вопросы по С/С++ (СИ)

Пт июл 31, 2020 12:48:34

имхо, проблема "кольцевых буферов" в EEPROM для сохранения настроек надумана: настройки - это не та информация, которая обновляется часто. скажем, показания одометра обновляются часто, а настройки?! без малейших ухищрений ресурса в 100000 перезаписей должно хватить на 27 часов ежесекундных перезаписей! какие такие настройки можно так часто перезаписывать? а если настройки ведутся 1 раз в минуту, то ресурс исчерпается не раньше, чем через 70 суток. но я не могу себе представить и этой ситуации.

Re: Вопросы по С/С++ (СИ)

Пт июл 31, 2020 13:19:58

ARV, ну в принципе да, если именно настройки, которые не часто сохраняются, то можно и без колец обойтись. ТС просто сам про кольц. буфер написал. А у меня там не совсем настройки, а состояния различные сохраняются, не раз в минуту, но может раз 10 за час меняться. Поэтому буфер сделал, чтобы хватило на годы.

Re: Вопросы по С/С++ (СИ)

Пт июл 31, 2020 13:59:53

ARV, NStorm, спасибо!
Видимо я не совсем точно описал проблему.
Настройки - их одинаковая кучка, а значит и размер в памяти всегда одинаков, меняться будут только непосредственно значения, по необходимости.
Данные. Сначала, при самом первом включении, их просто еще нет. Со временем, они появляются тоже кучкой и пишутся в EEPROM, каждая следующая порция - за предыдущей. Это необходимо, для анализа их изменения и оперативного изменения тех самых настроек. Когда допустимый объем данных заполнен, следующая порция должна запомниться на месте устаревших данных. Вот тут-то, в области данных и нужен кольцевой буфер.
Ступор у меня в том, что разнообразие настроек довольно велико и они тоже разные: от 32 битных, до бит-полей, есть и структуры и массивы, возможны и строки. Вот как весь этот винегрет увязать в структуру и подружить ее с EEPROM?

Re: Вопросы по С/С++ (СИ)

Пт июл 31, 2020 14:59:56

Ступор у меня в том, что разнообразие настроек довольно велико и они тоже разные: от 32 битных, до бит-полей, есть и структуры и массивы, возможны и строки. Вот как весь этот винегрет увязать в структуру и подружить ее с EEPROM?

Так в чем проблема? Делаете из них структуру и сохраняете, как выше писали. Структуры тоже вполне можно включать в структуры. Строки, если фиксированной максимальной длины - вообще без проблем.
Ответить