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

Плавный переход C -> C++ под МК

6 hours ago, Arlleex said:

Можно ли какими-то средствами задать тип-подмножество целых, только не обзывать эти подмножества именами?

Т.е. я хочу что-то типа такого


enum class ePin : u32 {
  0, 1, 2, 3, 4, 5, 6, 7
};

...

void selectPin(ePin pin);

...

void main() {
  ...
  selectPin(ePin::7); // ok
  selectPin(ePin::8); // compilation error
}


Т.е. не создавая всяких _0 = 0, _1, _2... внутри enum-а (дублирование нижних подчеркиваний просто замусоривает одни и те же смысловые конструкции).

Сделай макрос с ошибочной инстанциацией по умолчанию

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


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

36 минут назад, rkit сказал:

Сделай макрос с ошибочной инстанциацией по умолчанию

Это не спасет от случайной записи программистом туда не того, что допустимо.

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


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

Вы о чем вообще? Еще раз перечитайте мой пост: я хотел именно то, что написал, не более и не менее.

Еще раз: хочу, чтобы после ввода операции расширения области видимости :: после имени enum class-а IDE выкидывала список возможных значений. Этот список на текущий момент может быть списком допустимых для идентификаторов Си/C++ наименований. А я хочу тупо числа. 0, 1, 2, 5, да какие угодно. В идеале было бы, чтобы можно было задавать даже диапазоны: этакий enum class eExample {1, 2, 5...12, 17}, и при инициализации где-нибудь в коде за счет строгой типизации программист не запишет в переменную типа eExample, например, 100500. Фишка enum class (в отличие от enum) как раз в том, чтобы заставить программиста использовать только те значения, что определены в этом enum class. Все остальное - ошибка компиляции. Однако НЕТ в Си/C++ таких конструкций (с цифрами в enum-ах). А жаль - было бы вполне удобно. Была бы крутая разновидность перечисления - подмножество.

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


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

В С++ есть шаблоны. Которые решают задачу, если не придуриваться и не ставить условие "чтобы во что бы то ни стало присутсвовало слово enum".

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


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

15.12.2021 в 15:10, jcxz сказал:

А в чём именно "негибкость"? Чего они не позволяют?

Вот я пользуюсь файлом описания регистров периферии, который поставляет мне DFP Keil-а. Для моего МК в этом файле определены #define на USART1, USART2, USART3, UART4, UART5, USART6. Соответственно, есть и определения, в состав которых входят эти же токены, например, RCC_APB2ENR_USART1EN, RCC_APB1ENR_UART4EN. Т.е. как видно буква S должна отсутствовать в токенах 4-го и 5-го UART-ов. Собственно говоря, хочу написать макрос, которому передаю лишь цифру номера UART-а (например, 1, 5, 6), а он мне возвращает правильное определение. Например, MACRO(1) должен вернуть RCC_APB2ENR_USART1EN, MACRO(5) - RCC_APB1ENR_UART5EN, MACRO(6) - RCC_APB2ENR_USART6EN и т.д. Красивого решения здесь не будет, т.к. макросы работают с токенами, и промежуточного вычисления токенов (например, Си-шных условий) не выполняется. Решение можно сделать с введением огорода вспомогательных макрос-объектов и макрос-функций. Вот она, не гибкость.

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


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

8 minutes ago, Arlleex said:

 Вот она, не гибкость.

Это вы еще не добрались до аппаратных различий между казалось бы одинаковыми блоками. Особенно между камнями одного семейства...

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


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

1 минуту назад, Forger сказал:

Это вы еще не добрались до аппаратных различий между казалось бы одинаковыми блоками. Особенно между камнями одного семейства...

Это я понимаю, но конкретно для моей реализации все эти UART-ы одинаковые.

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


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

22 minutes ago, Arlleex said:

но конкретно для моей реализации все эти UART-ы одинаковые

Но аппаратно USART3 и UART4 разные - один поддерживает синхронный обмен, а второй - нет. Это как минимум.

Если это не учитывать, то вообще какой тогда смысл в подобных самодельных либах?

Пишем "по старинке" в регистры, используя штатный жирный h-дефайн от производителя. И не заморачиваемся )

 

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

Да, без макросов, но моя первостепенная цель - в простоте применения библиотеки, а НЕ в простоте ее реализации (отладил и внутрь вряд ли больше полезу).

Вот, например, настройка порта для работы с внешним GPS модулем:

Spoiler

#include <USART.hpp>
  ....

	class Thread : public OS::Thread<kStackSize>
	{
		private:
      		void serialPortIrqVector();
      ....
       		OS::Semaphore semaphoreByteRx;
            stm32f4::SerialPortUSART6 serialPort;
            stm32f4::Pin<PC6> pinTX;
            stm32f4::Pin<PC7> pinRX;
.....

void GPSLib::Thread::initialize()
{
....
  serialPort.setPinTx(pinTX);
  serialPort.setPinRx(pinRX);
  
  auto vector = DELEGATE(&Thread::serialPortIrqVector, this);
  serialPort.initialize();
  serialPort.installVector(vector);
  serialPort.irq.rxNotEmpty.enable();
  serialPort.setModeAsynchronous();
  serialPort.setStopBits1_0();
  serialPort.setParityNone();
  serialPort.setFrameSizeTo8bits();
  serialPort.setOversamplingModeTo16bits();
  serialPort.setBaudRate(9600);
  serialPort.run();
....
  
  
void GPSLib::Thread::serialPortIrqVector()
{
	// Overrun error
	if (serialPort.isOverrunError())
	{
      ....
        
	if (serialPort.irq.rxNotEmpty.isPending())
	{
		rxByte = serialPort.read();
		semaphoreByteRx.signal();
	}
...
      

 

 

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


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

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

Вот я пользуюсь файлом описания регистров периферии, который поставляет мне DFP Keil-а. Для моего МК в этом файле определены #define на USART1, USART2, USART3, UART4, UART5, USART6. Соответственно, есть и определения, в состав которых входят эти же токены, например, RCC_APB2ENR_USART1EN, RCC_APB1ENR_UART4EN. Т.е. как видно буква S должна отсутствовать в токенах 4-го и 5-го UART-ов.

По-моему Вы всё усложняете. Я просто написал все свои определения, где везде поставил UART:

//UART
typedef struct {
  __IO u32 SR;
  __IO u32 DR;
  __IO u32 BRR;
  __IO u32 CR[3];
  __IO u32 GTPR;
} HwRegsUART;
...
EXTERN          HWREG_MAP(UART)       UART1    @ 0x40011000;
EXTERN          HWREG_MAP(UART)       UART2    @ 0x40004400;
EXTERN          HWREG_MAP(UART)       UART3    @ 0x40004800;
EXTERN          HWREG_MAP(UART)       UART4    @ 0x40004C00;
EXTERN          HWREG_MAP(UART)       UART5    @ 0x40005000;
EXTERN          HWREG_MAP(UART)       UART6    @ 0x40011400;
EXTERN          HWREG_MAP(UART)       UART7    @ 0x40007800;
EXTERN          HWREG_MAP(UART)       UART8    @ 0x40007C00;
...
#define RCC_APB_UART2         0x11
#define RCC_APB_UART3         0x12
#define RCC_APB_UART4         0x13
#define RCC_APB_UART5         0x14
...

Это требует всего несколько минут.

Зачем тратить время на ерунду?  Сэкономленное время лучше потратить на что-то более полезное - на практическую задачу, чтобы её оптимальнее реализовать.

56 минут назад, Forger сказал:

Но аппаратно USART3 и UART4 разные - один поддерживает синхронный обмен, а второй - нет. Это как минимум.

 

Если известно, что сейчас нужен только асинхронный режим и в будущем с 99% вероятностью будет нужен только он - зачем эти сложности?

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


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

7 minutes ago, jcxz said:

Если известно, что сейчас нужен только асинхронный режим и в будущем с 99% вероятностью будет нужен только он - зачем эти сложности?

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

Сразу реализовывать все изыски - это по-моему "мартышкин труд" или уже совсем нечем заняться.

 

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


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

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

Да, без макросов, но моя первостепенная...

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

В общем, решение я нашел, в целом, кратко опишу реализацию чуть ниже. В прошлый раз я пришел к идее, чтобы в конструктор объекта приемного FIFO передавать некий параметр, на основе которого из статической таблицы будут браться все аппаратно-зависимые данные для настройки периферии. Т.е. класс выглядит примерно так

class cUARTDMARx {
  public:
    enum class eHwCfg : u32 {
      UART1_PA10_DMA2STR2 = 0, UART1_PA10_DMA2STR5,
      UART1_PB7_DMA2STR2,      UART1_PB7_DMA2STR5,
      UART2_PA3_DMA1STR5,      UART2_PD6_DMA1STR5,
      UART3_PB11_DMA1STR1,     UART3_PC11_DMA1STR1,
      UART3_PD9_DMA1STR1,
      UART4_PA1_DMA1STR2,      UART4_PC11_DMA1STR2,
      UART5_PD2_DMA1STR0,
      UART6_PC7_DMA2STR1,      UART6_PC7_DMA2STR2,
      UART6_PG9_DMA2STR1,      UART6_PG9_DMA2STR2
      };
    ...
    
    cUARTDMARx(eHwCfg hwCfg);
};

а таблица периферийных зависимостей описана пока что вот так (это уже в .cpp-файле)

Скрытый текст

static const struct {
  struct {
    u32    pclkEnPos;
    GPIO_TypeDef *hw;
    u8    pinNum : 4;
    u8    altNum : 4;
  }gpio;
  
  struct {
    volatile u32  *pclkEnReg;
             u32   pclkEnPos;
    USART_TypeDef *hw;
  }uart;
  
  struct {
    u32 pclkEnPos;
    u8  dmaNum : 2;
    u8  strNum : 3;
    u8  reqNum : 3;
  }dma;
}HwCfgTbl[] = {
#define SEL(n) static_cast<u32>(cUARTDMARx::eHwCfg::n)
  [SEL(UART1_PA10_DMA2STR2)] = {{RCC_AHB1ENR_GPIOAEN, GPIOA, 10u, 7u},
                                {&RCC->APB2ENR, RCC_APB2ENR_USART1EN, USART1},
                                {RCC_AHB1ENR_DMA2EN, 2u, 2u, 4u}},
  [SEL(UART1_PA10_DMA2STR5)] = {{RCC_AHB1ENR_GPIOAEN, GPIOA, 10u, 7u},
                                {&RCC->APB2ENR, RCC_APB2ENR_USART1EN, USART1},
                                {RCC_AHB1ENR_DMA2EN, 2u, 5u, 4u}},
  [SEL(UART1_PB7_DMA2STR2)]  = {{RCC_AHB1ENR_GPIOBEN, GPIOB, 7u, 7u},
                                {&RCC->APB2ENR, RCC_APB2ENR_USART1EN, USART1},
                                {RCC_AHB1ENR_DMA2EN, 2u, 2u, 4u}},
  [SEL(UART1_PB7_DMA2STR5)]  = {{RCC_AHB1ENR_GPIOBEN, GPIOB, 7u, 7u},
                                {&RCC->APB2ENR, RCC_APB2ENR_USART1EN, USART1},
                                {RCC_AHB1ENR_DMA2EN, 2u, 5u, 4u}},
  [SEL(UART2_PA3_DMA1STR5)]  = {{RCC_AHB1ENR_GPIOAEN, GPIOA, 3u, 7u},
                                {&RCC->APB1ENR, RCC_APB1ENR_USART2EN, USART2},
                                {RCC_AHB1ENR_DMA1EN, 1u, 5u, 4u}},
  [SEL(UART2_PD6_DMA1STR5)]  = {{RCC_AHB1ENR_GPIODEN, GPIOD, 6u, 7u},
                                {&RCC->APB1ENR, RCC_APB1ENR_USART2EN, USART2},
                                {RCC_AHB1ENR_DMA1EN, 1u, 5u, 4u}},
  [SEL(UART3_PB11_DMA1STR1)] = {{RCC_AHB1ENR_GPIOBEN, GPIOB, 11u, 7u},
                                {&RCC->APB1ENR, RCC_APB1ENR_USART3EN, USART3},
                                {RCC_AHB1ENR_DMA1EN, 1u, 1u, 4u}},
  [SEL(UART3_PC11_DMA1STR1)] = {{RCC_AHB1ENR_GPIOCEN, GPIOC, 11u, 7u},
                                {&RCC->APB1ENR, RCC_APB1ENR_USART3EN, USART3},
                                {RCC_AHB1ENR_DMA1EN, 1u, 1u, 4u}},
  [SEL(UART3_PD9_DMA1STR1)]  = {{RCC_AHB1ENR_GPIODEN, GPIOD, 9u, 7u},
                                {&RCC->APB1ENR, RCC_APB1ENR_USART3EN, USART3},
                                {RCC_AHB1ENR_DMA1EN, 1u, 1u, 4u}},
  [SEL(UART4_PA1_DMA1STR2)]  = {{RCC_AHB1ENR_GPIOAEN, GPIOA, 1u, 8u},
                                {&RCC->APB1ENR, RCC_APB1ENR_UART4EN, UART4},
                                {RCC_AHB1ENR_DMA1EN, 1u, 2u, 4u}},
  [SEL(UART4_PC11_DMA1STR2)] = {{RCC_AHB1ENR_GPIOCEN, GPIOC, 11u, 8u},
                                {&RCC->APB1ENR, RCC_APB1ENR_UART4EN, UART4},
                                {RCC_AHB1ENR_DMA1EN, 1u, 2u, 4u}},
  [SEL(UART5_PD2_DMA1STR0)]  = {{RCC_AHB1ENR_GPIODEN, GPIOD, 2u, 8u},
                                {&RCC->APB1ENR, RCC_APB1ENR_UART5EN, UART5},
                                {RCC_AHB1ENR_DMA1EN, 1u, 0u, 4u}},
  [SEL(UART6_PC7_DMA2STR1)]  = {{RCC_AHB1ENR_GPIOCEN, GPIOC, 7u, 8u},
                                {&RCC->APB2ENR, RCC_APB2ENR_USART6EN, USART6},
                                {RCC_AHB1ENR_DMA2EN, 2u, 1u, 5u}},
  [SEL(UART6_PC7_DMA2STR2)]  = {{RCC_AHB1ENR_GPIOCEN, GPIOC, 7u, 8u},
                                {&RCC->APB2ENR, RCC_APB2ENR_USART6EN, USART6},
                                {RCC_AHB1ENR_DMA2EN, 2u, 2u, 5u}},
  [SEL(UART6_PG9_DMA2STR1)]  = {{RCC_AHB1ENR_GPIOGEN, GPIOG, 9u, 8u},
                                {&RCC->APB2ENR, RCC_APB2ENR_USART6EN, USART6},
                                {RCC_AHB1ENR_DMA2EN, 2u, 1u, 5u}},
  [SEL(UART6_PG9_DMA2STR2)]  = {{RCC_AHB1ENR_GPIOGEN, GPIOG, 9u, 8u},
                                {&RCC->APB2ENR, RCC_APB2ENR_USART6EN, USART6},
                                {RCC_AHB1ENR_DMA2EN, 2u, 2u, 5u}}
#undef SEL
};


Как видно, инициализация таблицы имеет регулярную структуру, поэтому можно сконструировать некий обобщенный макрос инициализации единичного элемента. Именно здесь была проблема UART/USART (попутно показываю, как победил). Теперь эта инициализация выглядит так

struct {
...
}HwCfgTbl[] = {
#define INIT(n, gpio, pin, alt, apb, uart, dma, str, req)                                   \
          [static_cast<u32>(cUARTDMARx::eHwCfg::n)] = {                                     \
            {RCC_AHB1ENR_GPIO##gpio##EN, GPIO##gpio, pin, alt},                             \
            {&RCC->APB##apb##ENR, concat4(RCC_APB##apb##ENR_, S(uart), uart, EN), U(uart)}, \
            {RCC_AHB1ENR_DMA##dma##EN, dma, str, req}}
#define S(n) concat3(U, concat2(selarg, n)(S, S, S, , , S), ART)
#define U(n) concat2(S(n), n)
  INIT(UART1_PA10_DMA2STR2, A, 10, 7, 2, 1, 2, 2, 4),
  INIT(UART1_PA10_DMA2STR5, A, 10, 7, 2, 1, 2, 5, 4),
  INIT(UART1_PB7_DMA2STR2,  B,  7, 7, 2, 1, 2, 2, 4),
  INIT(UART1_PB7_DMA2STR5,  B,  7, 7, 2, 1, 2, 5, 4),
  INIT(UART2_PA3_DMA1STR5,  A,  3, 7, 1, 2, 1, 5, 4),
  INIT(UART2_PD6_DMA1STR5,  D,  6, 7, 1, 2, 1, 5, 4),
  INIT(UART3_PB11_DMA1STR1, B, 11, 7, 1, 3, 1, 1, 4),
  INIT(UART3_PC11_DMA1STR1, C, 11, 7, 1, 3, 1, 1, 4),
  INIT(UART3_PD9_DMA1STR1,  D,  9, 7, 1, 3, 1, 1, 4),
  INIT(UART4_PA1_DMA1STR2,  A,  1, 8, 1, 4, 1, 2, 4),
  INIT(UART4_PC11_DMA1STR2, C, 11, 8, 1, 4, 1, 2, 4),
  INIT(UART5_PD2_DMA1STR0,  D,  2, 8, 1, 5, 1, 0, 4),
  INIT(UART6_PC7_DMA2STR1,  C,  7, 8, 2, 6, 2, 1, 5),
  INIT(UART6_PC7_DMA2STR2,  C,  7, 8, 2, 6, 2, 2, 5),
  INIT(UART6_PG9_DMA2STR1,  G,  9, 8, 2, 6, 2, 1, 5),
  INIT(UART6_PG9_DMA2STR2,  G,  9, 8, 2, 6, 2, 2, 5)
#undef INIT
#undef S
#undef U
};


concat(), selargx() - это у меня в отдельном файле, который всегда подключен

Скрытый текст

#define selarg1(...)                          _selarg1(__VA_ARGS__)
#define selarg2(...)                          _selarg2(__VA_ARGS__)
#define selarg3(...)                          _selarg3(__VA_ARGS__)
#define selarg4(...)                          _selarg4(__VA_ARGS__)
#define selarg5(...)                          _selarg5(__VA_ARGS__)
#define selarg6(...)                          _selarg6(__VA_ARGS__)
#define selarg7(...)                          _selarg7(__VA_ARGS__)
#define selarg8(...)                          _selarg8(__VA_ARGS__)

#define concat2(a, b)                         _concat2(a, b)
#define concat3(a, b, c)                      _concat3(a, b, c)
#define concat4(a, b, c, d)                   _concat4(a, b, c, d)
#define concat5(a, b, c, d, e)                _concat5(a, b, c, d, e)
#define concat6(a, b, c, d, e, f)             _concat6(a, b, c, d, e, f)
#define concat7(a, b, c, d, e, f, g)          _concat7(a, b, c, d, e, f, g)
#define concat8(a, b, c, d, e, f, g, h)       _concat8(a, b, c, d, e, f, g, h)


#define _selarg1(a, ...)                      a
#define _selarg2(a, b, ...)                   b
#define _selarg3(a, b, c, ...)                c
#define _selarg4(a, b, c, d, ...)             d
#define _selarg5(a, b, c, d, e, ...)          e
#define _selarg6(a, b, c, d, e, f, ...)       f
#define _selarg7(a, b, c, d, e, f, g, ...)    g
#define _selarg8(a, b, c, d, e, f, g, h, ...) h

#define _concat2(a, b)                        a##b
#define _concat3(a, b, c)                     a##b##c
#define _concat4(a, b, c, d)                  a##b##c##d
#define _concat5(a, b, c, d, e)               a##b##c##d##e
#define _concat6(a, b, c, d, e, f)            a##b##c##d##e##f
#define _concat7(a, b, c, d, e, f, g)         a##b##c##d##e##f##g
#define _concat8(a, b, c, d, e, f, g, h)      a##b##c##d##e##f##g##h


Ну, более менее еще пока, жить можно.

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


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

8 minutes ago, Arlleex said:

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

Для этого в C++ есть шаблоны.

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


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

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

Для этого в C++ есть шаблоны.

Понял, значит буду к ним подбираться ближе.

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


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

16.12.2021 в 14:48, Forger сказал:

auto vector = DELEGATE(&Thread::serialPortIrqVector, this);

Хорошо, когда вектор прерывания периферии выделен только для нее. Но бывают вектора, которые делят между собой несколько периферийных блоков. Например, TIM6_DAC_IRQHandler. Как здесь поможет делегат? А еще, бывает, одна и та же периферия оккупирует несколько векторов, например, TIM8_CC_IRQHandler, TIM8_TRG_COM_TIM14_IRQHandler, TIM8_UP_TIM13_IRQHandler, TIM8_BRK_TIM12_IRQHandler... А еще, бывает, например, один таймер может использоваться для обслуживания драйверов нескольких вообще не связанных друг с другом механизмов. Грубо говоря, есть вектор TIM2_IRQHandler. И есть N объектов класса nsFIFO::cUARTDMARx, которым нужен аппаратный таймер для отсчитывания 1мс интервалов ("стандартный" механизм DMA + TIM в STM32 для приема неизвестного количества байтов в непрерывном режиме по UART-у). Разве делегатом можно "привязаться" к одному аппаратному вектору в нескольких инстанциях? Т.е. какое здесь решение наиболее изящно? У меня, пока что, кроме как создать публичные методы svcOnDMAIrq() и svcOnTmrIrq(), дергать их из обработчика соответствующего вектора прерывания DMA и таймера, идей нет. Но здесь нужно всегда руками "добавлять" обработку конкретного экземпляра класса в соответствующих векторах. Может, я чего-то не знаю?:blush:

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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