Перейти к содержанию
    

Нетиповой вариативный шаблон класса. Помогите написать.

И все-таки... Чем не угодила реализация на обычных макросах?

Полагаю, только тем, что нужно каким-то образом отслеживать различные модули GPIO...

ИМХО, так загоняться не стоит. Сегодня какой-нибудь дисплей развели на ножки, которые были свободны после разводки всего остального на плате, завтра - приоритет отдают уже самому экрану и вся шина данных ложится строго в один порт целиком... Уже можно оптимизнуть:smile:

Ну, вот, например.

 

Разводим клей...

#define __WIRE_GPIO(g, p)  GPIO##g
#define __WIRE_PIN(g, p)   GPIO_Pin_##p
#define __WIRE_SRC(g, p)   p
#define __WIRE(g, p)       __WIRE_GPIO(g, p), __WIRE_PIN(g, p)
#define _WIRE_GPIO(w)      __WIRE_GPIO(w)
#define _WIRE_PIN(w)       __WIRE_PIN(w)
#define _WIRE_SRC(w)       __WIRE_SRC(w)
#define _WIRE(w)           __WIRE(w)
#define WIRE_GPIO(w)       _WIRE_GPIO(WIRE_##w)
#define WIRE_PIN(w)        _WIRE_PIN(WIRE_##w)
#define WIRE_SRC(w)        _WIRE_SRC(WIRE_##w)
#define WIRE(w)            _WIRE(WIRE_##w)

 

Согласно схеме платы смотрим, куда чего подключено...

...
#define WIRE_DIN1          B,  0
#define WIRE_DIN2          B,  1
#define WIRE_DIN3          B,  2
#define WIRE_DIN4          B,  3
#define WIRE_DIN5          B,  4
#define WIRE_DIN6          B,  5
#define WIRE_DIN7          B,  6
#define WIRE_DIN8          B,  7
#define WIRE_DIN9          B,  8
#define WIRE_DIN10         B,  9
#define WIRE_DIN11         B, 10
#define WIRE_DIN12         B, 11
#define WIRE_DIN13         B, 12
#define WIRE_DIN14         B, 13
#define WIRE_DIN15         B, 14
#define WIRE_DIN16         B, 15
#define GPIODIN            GPIOB

#define WIRE_DOUT1         F,  7
#define WIRE_DOUT2         F,  6
#define WIRE_DOUT3         A, 12
#define WIRE_DOUT4         A, 11
#define WIRE_DOUT5         A,  8
#define WIRE_DOUT6         C,  9
#define WIRE_DOUT7         C,  8
#define WIRE_DOUT8         C,  7
#define GPIODOUT12         GPIOF
#define GPIODOUT35         GPIOA
#define GPIODOUT68         GPIOC

#define WIRE_LED           A, 15
...

 

Видим, что все 16 линий цифровых входов подключены тупо на весь порт B. Примем это во внимание далее.

А вот с цифровыми выходами несколько сложнее - часть висит на порте A, часть на C, еще немного на F. Ок.

Линия, куда подключен светодиод, не имеет макроса GPIO, потому что это одна линия и мы этот GPIO узнаем из макроса WIRE_GPIO...

 

Инициализация...

static void DIOInit(void)
{
  GPIO_InitTypeDef GPIOCfg;
  GPIOCfg.GPIO_Mode = GPIO_Mode_IN;
  GPIOCfg.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIOCfg.GPIO_Pin  = WIRE_PIN(DIN1)  | WIRE_PIN(DIN2)  |
                      WIRE_PIN(DIN3)  | WIRE_PIN(DIN4)  |
                      WIRE_PIN(DIN5)  | WIRE_PIN(DIN6)  |
                      WIRE_PIN(DIN7)  | WIRE_PIN(DIN8)  |
                      WIRE_PIN(DIN9)  | WIRE_PIN(DIN10) |
                      WIRE_PIN(DIN11) | WIRE_PIN(DIN12) |
                      WIRE_PIN(DIN13) | WIRE_PIN(DIN14) |
                      WIRE_PIN(DIN15) | WIRE_PIN(DIN16);
  GPIO_Init(GPIODIN, &GPIOCfg);
  
  GPIOCfg.GPIO_Mode  = GPIO_Mode_OUT;
  GPIOCfg.GPIO_OType = GPIO_OType_PP;
  GPIOCfg.GPIO_Speed = GPIO_Speed_50MHz;
  GPIOCfg.GPIO_Pin   = WIRE_PIN(DOUT1) | WIRE_PIN(DOUT2);
  GPIO_Init(GPIODOUT12, &GPIOCfg);
  GPIOCfg.GPIO_Pin   = WIRE_PIN(DOUT3) | WIRE_PIN(DOUT4) |
                                         WIRE_PIN(DOUT5);
  GPIO_Init(GPIODOUT35, &GPIOCfg);
  GPIOCfg.GPIO_Pin   = WIRE_PIN(DOUT6) | WIRE_PIN(DOUT7) |
                                         WIRE_PIN(DOUT8);
  GPIO_Init(GPIODOUT68, &GPIOCfg);
}

static void LEDInit(void)
{
  GPIO_InitTypeDef GPIOCfg;
  GPIOCfg.GPIO_Mode  = GPIO_Mode_OUT;
  GPIOCfg.GPIO_OType = GPIO_OType_PP;
  GPIOCfg.GPIO_Speed = GPIO_Speed_50MHz;
  GPIOCfg.GPIO_Pin   = WIRE_PIN(LED);
  GPIO_Init(WIRE_GPIO(LED), &GPIOCfg);
}

 

Допустим, надо считать цифровые входы: я знаю, что они все на PORTB и вращать/переставлять ничего не надо. Значит можно тупо вот так вот сделать...

u32 hw_GetDIn(void)
{
  return GPIODIN->IDR;
}

 

Допустим, надо переключить светодиод...

void hw_LEDTgl(void)
{
  WIRE_GPIO(LED)->ODR ^= WIRE_PIN(LED);
}

 

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

#define abs(a, b)       ((a) > (b) ? (a) - (b) : (b) - (a))
#define WOMAKE(s, w, p) (((p) > (w) ? (s) >> abs(w, p) :          \
                                      (s) << abs(w, p)) & 1 << (w)) // делает для проверяемого бита в позиции p источника s
                                                                    // сдвиг влево или вправо в зависимости от положения w самой линии в порте

// Out[7:0] - упакованные дискретные выходы
// W[]      - битовый выхлоп, который надо записать в разные порты
static void WMakeDOut(u8 Out, u32 W[3])
{
  W[0] = WOMAKE(Out, WIRE_SRC(DOUT1), 0) |
         WOMAKE(Out, WIRE_SRC(DOUT2), 1);
  W[1] = WOMAKE(Out, WIRE_SRC(DOUT3), 2) |
         WOMAKE(Out, WIRE_SRC(DOUT4), 3) |
         WOMAKE(Out, WIRE_SRC(DOUT5), 4);
  W[2] = WOMAKE(Out, WIRE_SRC(DOUT6), 5) |
         WOMAKE(Out, WIRE_SRC(DOUT7), 6) |
         WOMAKE(Out, WIRE_SRC(DOUT8), 7);
}

// установка дискретных выходов
// Out[7:0] - упакованное представление этих выходов
void hw_SetDOut(u8 Out)
{
  u32 W[3]; WMakeDOut(Out, W);
  GPIODOUT12->BSRR = W[0];
  GPIODOUT35->BSRR = W[1];
  GPIODOUT68->BSRR = W[2];
}

// сброс дискретных выходов
// Out[7:0] - упакованное представление этих выходов
void hw_RstDOut(u8 Out)
{
  u32 W[3]; WMakeDOut(Out, W);
  GPIODOUT12->BSRR = W[0] << 16;
  GPIODOUT35->BSRR = W[1] << 16;
  GPIODOUT68->BSRR = W[2] << 16;
}

#undef abs
#undef WOMAKE

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, Arlleex сказал:

GPIO_InitTypeDef GPIOCfg;

Противно забивать память, ОЗУ ли, ПЗУ, хламом, используемым однократно.
У меня было на макросах. Чисто обращения к регистрам GPIO. Сейчас хочу то же, но на шаблонных функциях. Опыт пригодится в более сложных задачах. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 часа назад, Arlleex сказал:

GPIO_Init(GPIODIN, &GPIOCfg);

Вот она, неоптимальность. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

GPIOA->BSRR = 0xFF'0000 | (GPIOA->IDR & 0xFF);

Вот из комментариев к статье. Это что, группы цифр в числе могут разделяться ' ? 

Точно!
https://ru.stackoverflow.com/questions/438281/Разделители-групп-разрядов

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, ViKo сказал:

Вот она, неоптимальность. 

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

В данном проекте время старта мне не сильно важно.

Уж если совсем глубоко оптимизировать, то никаких SPL.

К тому же основной акцент был сделан на реализацию вывода данных в порты.

 

Вот пример. Управляем дискретными выходами.

Хост отправляет команды управления этими выходами в виде упакованных 8 бит

typedef union
{
  struct
  {
    u8 o1 : 1;
    u8 o2 : 1;
    u8 o3 : 1;
    u8 o4 : 1;
    u8 o5 : 1;
    u8 o6 : 1;
    u8 o7 : 1;
    u8 o8 : 1;
  };
  u8 out;
}uDOut;

 

МК принимает эти биты и сразу отправляет функции распидаливания этого всего в соответствующие GPIO

hw_SetDOut(DOut.out);

 

Как оптимизировать вывод? А тем более для общего случая - раскиданные биты по всему GPIO?

image.thumb.png.be800958947547aebdb9bc2828d93c51.png

 

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

Но, естественно, нужно принимать во внимание, что эти биты могут управлять не одним портом, а могут быть разбросаны по нескольким GPIO (еще более общий случай). Я как раз приводил именно такой пример.

Ну а дальнейшая оптимизация, ИМХО, только ручками: высматривать парные биты, например, как на рисунке выше биты 4 и 3 OUT одним сдвигом ставятся в положения 8 и 7 бит регистра BSRR, менять положения бит в uDOut и т.д.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 минут назад, Arlleex сказал:

Уж если совсем глубоко оптимизировать, то никаких SPL.

К тому же основной акцент был сделан на реализацию вывода данных в порты.

Именно, никаких либ.
Я такого акцента не делал. Я говорил и говорю, мне нужно предельно оптимально по размеру, оно и по скорости выйдет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А Вы смотрели фреймвррк xpcc? Там они сильно в мета закинулись, порты описывать. Не катит их решение?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 часов назад, Arlleex сказал:

Сегодня какой-нибудь дисплей развели на ножки, которые были свободны

Весь низкоуровневый вывод дисплея реализован в одной функции. При смене разводки переписать ее - плевое дело и совсем не напрягает. А вот плодить под каждый проц, порт, бит портянки шаблонов, в которых черт ногу сломит, я как-то пока не созрел. А кроме как в дисплеях мне параллельная шина и не требовалась никогда, все сигналы для ногодрыга однобитовые. Вот такое у меня пока что мнение.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, AlexRayne сказал:

А Вы смотрели фреймвррк xpcc? Там они сильно в мета закинулись, порты описывать. Не катит их решение?

Спасибо. Не успеваю кидаться во все стороны, и еще читать 3 книги по С++ одновременно. :prankster2:

Вот как придумал проверять, что в порте все 16 пинов имеют разные номера. Pxx - структуры, описывающие свойства пинов.

/*  Check that all Pin numbers are different  */
	static_assert(
		(1 << P00.bitn | 1 << P01.bitn | 1 << P02.bitn | 1 << P03.bitn |
		1 << P04.bitn | 1 << P05.bitn | 1 << P06.bitn | 1 << P07.bitn |
		1 << P08.bitn | 1 << P09.bitn | 1 << P10.bitn | 1 << P11.bitn |
		1 << P12.bitn | 1 << P13.bitn | 1 << P14.bitn | 1 << P15.bitn) == 0xFFFF, 
		"Pin numbers aren't different");

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Сергей Борщ сказал:

Весь низкоуровневый вывод дисплея реализован в одной функции. При смене разводки переписать ее - плевое дело и совсем не напрягает. А вот плодить под каждый проц, порт, бит портянки шаблонов, в которых черт ногу сломит, я как-то пока не созрел. А кроме как в дисплеях мне параллельная шина и не требовалась никогда, все сигналы для ногодрыга однобитовые. Вот такое у меня пока что мнение. 

В целом согласен. Я набросал шинку в качестве упражнения в современных плюсах (кто-то здесь на форуме попросил). Применил для проверки в одном проекте, где используется ЖКИ на 8-битной шине. И с тех пор не вспоминал.

Да и ViKo, насколько я понял, нужна не сама шина, а удобная начальная инициализация групп ножек порта.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 минуты назад, AHTOXA сказал:

Да и ViKo, насколько я понял, нужна не сама шина, а удобная начальная инициализация групп ножек порта.

Спасибо, вы показали дорогу в шаблоны со статическими функциями. Мне нужна инициализация, безусловно. Причем, не только начальная. По ходу программы требуется переинициализировать режимы. И манипулировать небольшими группами битов. Одновременно не обязательно, зато делается коротко и красиво. А для параллельных шин использую FSMC.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, Сергей Борщ сказал:

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

Понял. У меня сейчас просто ситуация противоположная - есть линейка девайсов примерно схожего функционала. Контроллер там один, а разведено по-разному.

Поэтому в целом светодиоды, дискретные входы/выходы и интерфейсы гуляют из порта в порт. Вот и решил сделать прослойку.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 26.12.2019 в 17:32, ViKo сказал:

/* Check that all Pin numbers are different */ static_assert( (1 << P00.bitn | 1 << P01.bitn | 1 << P02.bitn | 1 << P03.bitn | 1 << P04.bitn | 1 << P05.bitn | 1 << P06.bitn | 1 << P07.bitn | 1 << P08.bitn | 1 << P09.bitn | 1 << P10.bitn | 1 << P11.bitn | 1 << P12.bitn | 1 << P13.bitn | 1 << P14.bitn | 1 << P15.bitn) == 0xFFFF, "Pin numbers aren't different");

Не совсем понял, что вы хотите, но в вашем случае можно сделать так:

template<auto& ...args>

struct Pack
{
static constexpr bool unique = (((1 << args.btn) | ...) == 0xFF);
} ;

using PortLits = Pack<P00, P01,P02,P03,P04,P05,P06,P07> ;

static_assert(PortLits::unique, "Беда, пины не разные") ;
 

https://gcc.godbolt.org/z/9CdBsR

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А sizeof... здесь задействовать можно? Чтобы определить, с каким числом сравнивать? 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...