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

 
 
5 страниц V   1 2 3 > »   
Reply to this topicStart new topic
> Поиск по массиву в compile time на С
amaora
сообщение May 3 2018, 11:54
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Есть массив регистров, ссылки на переменные с именами и некоторыми параметрами, для удобного доступа по разным интерфейсам и конфигурирования устройства. Допустим вот так объявленный,

Код
typedef struct {

    const char        *sym;

    const char        *fmt;
    int            mode;

    union {

        float        *f;
        int        *i;
    }
    link;

    void            (* proc) (const void *reg, void *lval, const void *rval);
}
reg_t;

#define REG_E(link, extra, unit, fmt, mode, proc)    { #link extra "\0" unit, fmt, mode, \
                            { (void *) &link }, (void *) proc }

#define REG(link, unit, fmt, mode, proc)    REG_E(link, "", unit, fmt, mode, proc)

const reg_t        regfile[] = {

    REG(hal.USART_baud_rate,        "",    "%i",    REG_CONFIG, NULL),
    REG(hal.PWM_freq_hz,            "Hz",    "%i",    REG_CONFIG, NULL),
    REG(hal.PWM_dead_time_ns,        "ns",    "%i",    REG_CONFIG, NULL),
    REG(hal.ADC_reference_voltage,        "V",    "%3f",    REG_CONFIG, NULL),
    REG(hal.ADC_current_shunt_resistance,    "Ohm",    "%4e",    REG_CONFIG, NULL),
...
    REG(pm.probe_speed_ramp,    "rad/s",    "%2f",    REG_CONFIG, NULL),
    REG_E(pm.probe_speed_ramp, "_rpm",    "rpm",    "%2f",    REG_NORMAL, &reg_proc_rpm),
...


Регистры адресуются по именам или по номерам. Имена достаточно постоянны, а номера могут изменяться в следующих версиях. В этом проблема, нельзя в коде просто взять ссылку на регистр по номеру regfile[n], номер может измениться. Искать по имени в compile time слишком сложно, средствами C и препроцессора не обойтись. По адресам переменных нельзя, они могут повторятся. Какие еще варианты?

Сейчас думаю, генерировать из объявления массив макросов вида #define REG_hal_USART_baud_rate 0, но здесь тоже много проблем, надо как-то "спасать" недопустимые символы (точка и др.).

Да можно обратится к переменным напрямую, но в некоторых случаях надо например сделать вывод значений, а в этом массиве регистров уже есть информация о том как это сделать для данного параметра. Не хочется дублировать одно и тоже в разным местах.

Спасибо.


Go to the top of the page
 
+Quote Post
Kabdim
сообщение May 3 2018, 12:09
Сообщение #2


Знающий
****

Группа: Свой
Сообщений: 558
Регистрация: 26-11-14
Из: Зеленоград
Пользователь №: 83 842



А зачем так сделано? Выглядит как кусок для внешнего скриптового языка. Но судя по описанию это не так.
Go to the top of the page
 
+Quote Post
Forger
сообщение May 3 2018, 12:15
Сообщение #3


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Масло-масляное.
Ваш некий реестр (я так понял по названию - константный, т.е compile-time), тогда все это можно сделать через некий h-файл, где через #define описать все параметры проекта.
Тогда нет никакого смысла создавать некий глобальный константный массив-реестр, ведь индексам этого массива все равно придется дать осмысленные имена, т.е. плодить те самые #define.

В своих проектах я использую такой файл и называю StaticSettings.h
Всем значениям даны понятные имена, чтобы в коде не фигурировали всякие "магические числа".
Править параметры проекта в одном файле проще, чем рыться по всему проекту в поисках нужного параметра.

Пример содержимого GlobalSettings.h (Keil), (все "лишнее" вырезал, чтобы не плодить новые вопросы)
Код
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Project:        STM32L Bootloader
// Description:    Project settings
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Use Configuration Wizard in Context Menu
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// ================================================= SYSTEM ====================================================
//    <h>System

//     <o>Core Frequency [MHz]
//        <24=> 24 MHz
#define CORE_FREQUENCY_MHZ                        (24)
#define CORE_FREQUENCY_HZ                            (CORE_FREQUENCY_MHZ * 1000000UL)

//     <o>HSE Quartz Frequency [MHz]
//        <12=> 12 MHz <8=> 8 MHz
#define HSE_FREQUENCY_MHZ                            (12)

//    <o>Interrupts Stack size [bytes] <128-65536>
//    <i> Defines the stack size for all interrupts (incl. nested)
//    <i> Minimum: 128
#define INTERRUPTS_STACK_SIZE_BYTES                    (512)

//    </h>

// ============================================== APPLICATION ==================================================
//    <h>Application

//    <s.32>Application Name
#define APPLICATION_NAME                            "******"

//    <s.32>CPU Chip Name
#define CPU_CHIP_NAME                                "STM32L151RCT6A"

//    <h>Bootloader version:
//    <o>Major
#define BOOTLOADER_VERSION_MAJOR                    (1)

//    <o>Minor
#define BOOTLOADER_VERSION_MINOR                    (0)

//    <o>Build
#define BOOTLOADER_VERSION_BUILD                    (1)

//    </h>

//    <h>Hardware version:
//    <o>Major
#define HARDWARE_VERSION_MAJOR                        (1)

//    <o>Minor
#define HARDWARE_VERSION_MINOR                        (2)

//    </h>

//    </h>


.........


Вот так выглядит доступ к этим настройкам этого h-файла в Keil:

Прикрепленное изображение





зы. А вот для динамических (сохраняемых настроек), которые могут изменяться в процессе работы приложения, я использую отдельный модуль Settings, который дает интерфейс к доступу к ряду параметров.
Причем, не реализуется просто доступ по некому абстрактному индексу в неком мифическом массиве, а по названию, т. е. через соотв. функции, чтобы ибежать непреднамеренно порчи "чужих" параметров - "реестр" нужно защищать, это вам не винда wink.gif


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
amaora
сообщение May 3 2018, 13:16
Сообщение #4


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Параметры не константные, все разные, есть настройки сохраняемые во флеш, есть текущие параметры работы которые можно только прочитать, и другие типы регистров (виртуальные, для доступа к одним и тем же данным разными способами). Такое описание позволяет предоставить доступ ко всем параметрам, через командный интерфейс по USART, через CAN и тд. Модуль сохранения настроек во флеш тоже пользуется готовым списком.

Основной код работающий с этими параметрами использует свои структуры (вот на них и ссылаются регистры) и не знает про массив regfile. Но в некоторых местах хочется попользоваться описанием регистров, для форматированного вывода с указанием единиц измерения например. Вот вопрос как получить ссылку на нужный регистр. Чтобы и в run time не делать много работы (ведь регистр там будет нужен всегда один и тот же), и в то же время не поломать ссылку когда измениться порядок/состав массива regfile.

То есть сейчас у меня в коде вот так.

Код
printf("R %4e (Ohm)" EOL, &pm.const_R);


А хочется вот так.

Код
reg_print_fmt(REG_pm_const_R);

Go to the top of the page
 
+Quote Post
Kabdim
сообщение May 3 2018, 13:40
Сообщение #5


Знающий
****

Группа: Свой
Сообщений: 558
Регистрация: 26-11-14
Из: Зеленоград
Пользователь №: 83 842



А точка в ваших определениях регистров всегда будет одна? Хотя наверное можно сделать и с несколькими.

Нбросал пример на Х макросах:
Код
#define REG_LIST                                    \
    X(hal, USART_baud_rate,        "",    "%i",    REG_CONFIG, NULL) \
    X(hal, PWM_freq_hz,            "Hz",    "%i",    REG_CONFIG, NULL) \
    X(hal, PWM_dead_time_ns,        "ns",    "%i",    REG_CONFIG, NULL) \
    X(hal, ADC_reference_voltage,        "V",    "%3f",    REG_CONFIG, NULL) \
    X(hal, ADC_current_shunt_resistance,    "Ohm",    "%4e",    REG_CONFIG, NULL) \
...
    X(pm, probe_speed_ramp,    "rad/s",    "%2f",    REG_CONFIG, NULL) \
    X(pm, probe_speed_ramp, "_rpm",    "rpm",    "%2f",    REG_NORMAL, &reg_proc_rpm) \
    
#define X(a, b, c, d, e, f) REG((a ## . ## b), (c), (d), (e), (f)),
const reg_t        regfile[] = {
    REG_LIST
};
#undef X

#define X(a, b, c, d, e, f) REG_ ## a ## _ ## b,
typedef enum {
    REG_LIST
} regfile_enum;
#undef X


Могут быть шероховатости, возможно где-то не будет компилироваться из-за лишней запятой, писал по памяти.
Go to the top of the page
 
+Quote Post
x736C
сообщение May 3 2018, 14:01
Сообщение #6


Профессионал
*****

Группа: Участник
Сообщений: 1 273
Регистрация: 3-03-06
Пользователь №: 14 942



Цитата(Forger @ May 3 2018, 15:15) *
зы. А вот для динамических (сохраняемых настроек), которые могут изменяться в процессе работы приложения, я использую отдельный модуль Settings, который дает интерфейс к доступу к ряду параметров.
Причем, не реализуется просто доступ по некому абстрактному индексу в неком мифическом массиве, а по названию, т. е. через соотв. функции, чтобы ибежать непреднамеренно порчи "чужих" параметров - "реестр" нужно защищать, это вам не винда wink.gif

Как примерно хотя бы выглядит модуль Settings, не могли бы поделиться? И как обеспечивается защита?

И еще вопрос вдогонку.
Допустим, в StaticSettings у меня есть настройка #define FW_REV "v.1.1".
Где-то должен быть, например, const char *rev = FW_REV
Где вы обычно такую информацию храните? в main.c или какой-то отдельный файл, в котором организуется хранение системных констант в памяти?
Прошу прощения за нубские вопросы.
Go to the top of the page
 
+Quote Post
Forger
сообщение May 3 2018, 14:42
Сообщение #7


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(x736C @ May 3 2018, 17:01) *
Как примерно хотя бы выглядит модуль Settings, не могли бы поделиться?

Он написан на плюсах и представляет собой набор методов для доступа к нужным параметром.
Список методов в разных проектах различный, но основной принцип неизменный.
В конструкторе - чтение сохраненных параметров. Если они "битые" (проверяется контрольная сумма), то восстанавливаются параметры по-умолчанию.
Сами рабочие параметры хранятся в ОЗУ, при сохранении они переписываются в энергонезависимую память через отдельную функцию/метод отложенно или сразу при изменении параметра (зависит от проекта).
Пишутся только те параметры, которые изменились. Разумеется, пересчитывается контрольная сумма и тоже переписывается.

Цитата
И как обеспечивается защита?

Один метод = один параметр. (в терминологии С++ "метод", в терминологии С - функция).
Внутри реализации конкретного метода проверяются входные данные.
Более сложные случае реализуются несколько иначе.

Цитата
И еще вопрос вдогонку.
Допустим, в StaticSettings у меня есть настройка #define FW_REV "v.1.1".
Где-то должен быть, например, const char *rev = FW_REV

Сами параметры хранятся естественно в виде одной большой структуры (struct), т.е. никаких элементов некого мифического массива.
Доступ к полям этой структуры ИСКЛЮЧИТЕЛЬНО через методы (функции).

В StaticSettings я объявляю рабочие характеристики проекта, которые в процессе работы никогда не меняются, а также даю значения параметрам по-умолчанию (default) для модуля Settings.

Т.е. на голом С я бы создал в Settings.c ОДИН экземпляр структуры всех параметров (в ОЗУ) с квалификатором static, разумеется.
В Settings.h объявил бы кучу функций с ОСМЫСЛЕННЫМИ именами.

Цитата
Где вы обычно такую информацию храните?
Это не принципиально, но чаще всего в EEPROM памяти, если нет, то во FLASH.

Цитата
в main.c или какой-то отдельный файл, в котором организуется хранение системных констант в памяти?

Нет, все это сделано внутри файла Settings.cpp

Почему так делаю? Да потому что принципиально не использую глобальные переменные. От слова вообще, поэтому подобные данные хранятся внутри класса Setting.

Все остальные части (у меня модули) проекта понятия не имеют о существовании некого Setting (равно как и других модулей), т.е. Settings.hpp инклудится только в одном месте и виден только классу Application.
А для доступа к методам класса Settings используются "делегаты", а Application лишь связывает модули вместе, точнее связывает их делегаты.
Впрочем, это уже совсем другая история и к этой теме не имеет отношения.

зы. Я описал чисто свой способ реализации этой задачи - мне ТАК проще. Поэтому прошу не разводить на базе моих выкладок очередной холивар на тему C vs C++.


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
amaora
сообщение May 5 2018, 08:15
Сообщение #8


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Функции доступа я тоже мог бы написать, но в этом нет смысла, доступ я получаю напрямую к переменным (pm.const_R). А регистр мне нужно найти для того, чтобы вытащить дополнительную информацию об этой переменной, формат вывода и единицы измерения.

Пока только вот такое решение вижу, это поиск по строке в run time. Недостатки, медленно, некрасиво, и ошибки (опечатка в строке) буду выявлены только при выполнении этого кода.

Код
reg_print_fmt(reg_search_by_name("pm.const_R"));


Цитата(Kabdim @ May 3 2018, 16:40) *
А точка в ваших определениях регистров всегда будет одна? Хотя наверное можно сделать и с несколькими.
Могут быть шероховатости, возможно где-то не будет компилироваться из-за лишней запятой, писал по памяти.


Точка там не от того, что так захотелось, это же поля структуры. Бывают еще и скобки от индексации массивов. Но суть понятна, объявить список отдельным макросом и развернуть его два раза.

Сообщение отредактировал amaora - May 5 2018, 08:16
Go to the top of the page
 
+Quote Post
jcxz
сообщение May 5 2018, 09:33
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Forger @ May 3 2018, 15:15) *
В своих проектах я использую такой файл и называю StaticSettings.h
Править параметры проекта в одном файле проще, чем рыться по всему проекту в поисках нужного параметра.

.....и как только над проектом начинают одновременно работать >=2 программиста, начнутся проблемы с совместной модификацией этого мега-файла настроек. laughing.gif
А когда число программистов >=4 начинается вообще полная $опа.
Лучше разбивать настройки по отдельным файлам., принадлежащим разным программным модулям. Естественно - речь о локальных настройках. Глобальных много не должно быть и они должны быть в одном файле. Вобщем - настройки надо делить на группы, по функционалу и не класть всё в одну кучу. И сопровождение будет проще и модификации и использование в других проектах.
Go to the top of the page
 
+Quote Post
Forger
сообщение May 5 2018, 10:26
Сообщение #10


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(jcxz @ May 5 2018, 12:33) *
Лучше разбивать настройки по отдельным файлам., принадлежащим разным программным модулям. Естественно - речь о локальных настройках. Глобальных много не должно быть и они должны быть в одном файле. Вобщем - настройки надо делить на группы, по функционалу и не класть всё в одну кучу. И сопровождение будет проще и модификации и использование в других проектах.

Согласен, неднократно это обмозговывал, но я работаю в основном один, проекты небольшие, но много. Общая стилистика и подход в таком случае очень оправданы.
Общие константные настройки остались в одном файле, поскольку Keil умеет нормально отображать такие файлы (см. скриншот выше).
Так получает удобнее и нагляднее видеть все на одной странице экрана, все разбито по группам, "лишние" настройки пр нужде "сворачиваются".
К тому же приучил себя и многих коллег к SVN, это дисциплинирует и приучает к порядку sm.gif

А вот привязку пинов в новых проектах уже разнес по соотв. модулям. Это выходит проще (пример):
Код
class PowerController : public Module
{
............

private:

............

    class Thread : public OS::Thread<PowerController, POWER_CONTROLLER_MODULE_STACK_SIZE>
    {
    public:

        inline void powerEnable() { pinPowerEnable.setToHigh(); }
        inline void powerDisable() { pinPowerEnable.setToLow(); }
............
        
    private:

        virtual void initialize();
        virtual void body();

    private:

        Hardware::DigitalOutputPin<PA15>     pinPowerEnable;
        Hardware::DigitalOutputPin<PC13>     pinEnable12V;
        Hardware::DigitalOutputPin<PC6>     pinSlaveEnable;
    
............
    
    } thread;

};


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
jcxz
сообщение May 5 2018, 11:02
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Forger @ May 5 2018, 13:26) *
К тому же приучил себя и многих коллег к SVN, это дисциплинирует и приучает к порядку sm.gif

SVN хорошо, когда каждый работает над своей группой файлов и общих файлов (модифицируемых разными людьми) как можно меньше.
Потому, что например когда Петя пишет/отлаживает модуль SPI-FLASH и по ходу работы правит ключи ему принадлежащие, в этот момент Вася закончил отладку CAN и фиксит его на SVN (со своей версией общих файлов). А Петя уже поменял ряд констант в общем файле и не факт, что окончательно (а может у него вообще несколько вариантов драйвера с разными алгоритмами работы (ногодрыг/прерывания/DMA/безвестная_либа_надыбанная_в_нете). И он тестит все эти варианты. И теперь ему ещё нужно накатить все изменения с SVN на все эти варианты (каждый со своими изменениями в общих файлах!).
А рядом с Васей сидит Коля, который пишет прикладной уровень рабочего протокола обмена (тоже внеся свои изменения в общие файлы). Т.е. - писал, пока его не прервал, Александр Иванович из ГИП-ов, прибежавший в мыле от Заказчика, когда у того вылез очередной Колин баг и система (с нашими девайсами) встала колом. И Коля бросает текущую задачу и ищет баг. И после того как он смастырит костыль, фиксящий данный баг, ему надо вернуться к прерванной задаче, не забыв накатить все изменения общих файлов и разрешив все коллизии накопившиеся за это время из-за своих, Петиных и Васиных commit-ов в SVN. А ведь кроме Александра Ивановича есть и другие ГИП-ы, возможно более авторитетные, в связи с чем уровень рекурсии Коли может быть существенным.
Вобщем - получается куча лишней пустой работы. laughing.gif
Go to the top of the page
 
+Quote Post
Forger
сообщение May 5 2018, 14:50
Сообщение #12


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(jcxz @ May 5 2018, 14:02) *
Вобщем - получается куча лишней пустой работы. laughing.gif

Такая ситуация лишь означает:
- не самое рациональное использование SVN как инструмента,
- недостаточно грамотное проектирование проекта
- неудачное разделение областей ответственности.

Но это все решаемо. Обычно, по ходу работ отлаживается этот механизм. Было бы желание wink.gif


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
amaora
сообщение Aug 11 2018, 18:54
Сообщение #13


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Вот так сделал wink.gif

Код
cat regfile.c | sed -n 's/^[^#]*\(REG_DEF_\?E\?(.\+)\).*$/\U\1/p' | sed 's/REG_DEF(\([^, ]\+\) *,.\+)/ID_\1,/;s/REG_DEF_E(\([^, ]\+\) *,[^,]*\"\([^, ]\+\)\".\+)/ID_\1\2,/;s/\.\|\[\|\]/_/g' > regdefs.h

...

enum {
#include "regdefs.h"
};

...

        reg_print_fmt(&regfile[ID_PM_CONST_LD], 1);
        reg_print_fmt(&regfile[ID_PM_CONST_LQ], 1);


То есть генерирую список констант по исходнику с объявлением массива, и делаю из этого enum.
Go to the top of the page
 
+Quote Post
Arlleex
сообщение Aug 12 2018, 07:00
Сообщение #14


Местный
***

Группа: Участник
Сообщений: 492
Регистрация: 12-11-11
Пользователь №: 68 264



Цитата(amaora @ Aug 11 2018, 21:54) *
Вот так сделал wink.gif

Код
cat regfile.c | sed -n 's/^[^#]*\(REG_DEF_\?E\?(.\+)\).*$/\U\1/p' | sed 's/REG_DEF(\([^, ]\+\) *,.\+)/ID_\1,/;s/REG_DEF_E(\([^, ]\+\) *,[^,]*\"\([^, ]\+\)\".\+)/ID_\1\2,/;s/\.\|\[\|\]/_/g' > regdefs.h

...

enum {
#include "regdefs.h"
};

...

        reg_print_fmt(&regfile[ID_PM_CONST_LD], 1);
        reg_print_fmt(&regfile[ID_PM_CONST_LQ], 1);


То есть генерирую список констант по исходнику с объявлением массива, и делаю из этого enum.

Что это за (очень мягко говоря) фигня??? Вы добиваетесь монопольного владения проектом и быть незаменимым сотрудником? Тогда для этого есть куча других способов сломать проект и мозги его поддерживающих в будущем wacko.gif
Go to the top of the page
 
+Quote Post
amaora
сообщение Aug 12 2018, 16:52
Сообщение #15


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Ну, здесь я обычно не задаю вопросов по своей основной работе, это пока всего лишь проект который делаю один в свободное время. Но все таки у меня нет цели написать непонятный код, наоборот я пытаюсь уменьшить его сложность. А подобные задачи у меня возникали уже не раз, и ничего лучше генерации кода сторонними средствами (sed в данном случае) я не нашел. Иначе получится код в котором внося некоторое изменение нужно подправить еще вот здесь и вот там, и следить чтобы каждая часть соответствовала другой.
Go to the top of the page
 
+Quote Post

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

 


RSS Текстовая версия Сейчас: 16th April 2024 - 08:55
Рейтинг@Mail.ru


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