Начну с простого
[uquote="Reflector",url="/forum/viewtopic.php?p=3562363#p3562363"][uquote="Satarych",url="/forum/viewtopic.php?p=3562348#p3562348"]
Код: Выделить всё
// Функция изменяет состояние ножки на противоположное
void Stm32Gpio::tgl() const
{
m_gpio->ODR ^= m_pinMask;
}
[/uquote]Распространенная ошибка. В регистр читается значение ODR, потом вызывается прерывание или переключается задача и там что-то пишется в тот же порт, по возвращению в ODR сохраняется старое значение с измененным битом...[/uquote] Да, согласен. Мигрировало из интернетных примеров, когда только осваивал STM'ки. По факту никогда не натыкался на подобное только потому, что обычно использовал отдельные set() и clr(). Посему и не замечал, но да, поправить нужно.
[uquote="Reflector",url="/forum/viewtopic.php?p=3562363#p3562363"]Я тебе уже в старой теме писал, что класс с приватными полями или конструкторами не является POD и такие объекты нельзя копировать к конструкторе. Чтоб было понятнее, в структуре фиксированный порядок полей, но если в классе есть публичные и приватные поля, то внутри них порядок фиксированный, но сами эти группы могут идти в том порядке, какой компилятор сочтет более подходящим. Это во-первых, а во-вторых, и я тоже об этом уже писал, если хочешь добиться максимальной эффективности, то нужно использовать шаблоны, тогда будут одни константы которые, естественно, попадут во флеш, если не напрямую в регистры.[/uquote] Не спорю насчёт эффективности, в этом отношении вариант с шаблонами определённо лучше. Минус пара тактов на чтение из переменных, а то и вообще минус вызов функции, если оптимизатор её встроит, это всё понятно, но вопрос не в этом. Для реализации этого варианта не обязательно сводить класс к POD. В частности, я хочу использовать виртуальные функции (зачем - отдельная тема), а это добавляет в класс указатель на таблицу виртуальных функций __vptr, тоесть класс с виртуальными функциями автоматически не будет являться POD. Итого - каждый объект за зря будет занимать по 4 байта в оперативке.
Как я писал выше, воспользовался советом andryblack
[uquote="andryblack",url="/forum/viewtopic.php?p=3497586#p3497586"]Конструктор не пустой, следовательно его нужно вызвать, следовательно экземпляр нельзя разместить в rom, он размещается в ram и генерируется вызов конструктора на этапе инициализации. Замените присвоение в конструкторе на список инициализации.[/uquote]и это решило проблему в Keil'е, его компилятор сразу размещал константные экземпляры класса во Flash, используя значения, предоставленные в списке инициализации. Если бы не этот факт, забил бы. А т.к. компилятор Keil'а умеет так делать, значит технических препятствий нет. Значит ключ к решению проблемы кроется в gcc компиляторе, либо он так делать не умеет, либо просто я не знаю как его об этом попросить.
Насчёт шаблонов. Я пробовал их использовать, но, видимо, до конца в них не разобрался.
Спойлер
Код: Выделить всё
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
class Stm32Gpio
{
public:
void init(Stm32GpioMode gpioMode) const; // Функция настройки (инициализации) пинов GPIO
virtual void set() const; // Функция включения ножки на лог "1"
virtual void clr() const; // Функция выключения ножки на лог "0"
virtual void tgl() const; // Функция изменяет состояние ножки на противоположное
virtual bool get() const; // Функция возвращает текущее состояние ножек
};
// Функция настройки (инициализации) ножек порта GPIO
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
void Stm32Gpio <m_gpio, m_pinMask>::init(Stm32GpioMode gpioMode) const
{
// Включаем тактирование порта
//using namespace stm32Rcc_Ns;
//coreRcc.enableHardwareClock (m_gpio);
// !NOTE! Временная затычка, пока не починю верхние 2 строки
if (m_gpio == GPIOA) RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
if (m_gpio == GPIOB) RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
if (m_gpio == GPIOC) RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
if (m_gpio == GPIOD) RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
if (m_gpio == GPIOE) RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;
//if (m_gpio == GPIOF) RCC->APB2ENR |= RCC_APB2ENR_IOPFEN;
//if (m_gpio == GPIOG) RCC->APB2ENR |= RCC_APB2ENR_IOPGEN;
// 5-й бит аргумента gpioMode содержит исходное состояние ножек
// Если там лог "1" - инициализируем на лог "1"
if((uint32_t)gpioMode & 0x10)
m_gpio->BSRR = m_pinMask;
// Иначе, если лог "0" - инициализируем на лог "0"
else
m_gpio->BRR = m_pinMask;
// Стираем 5-й бит в аргументе pinMode
gpioMode = (Stm32GpioMode)((uint32_t)gpioMode & 0xF);
// Перебираем первые 8 ножек
for(uint8_t pinNum = 0 ; pinNum < 8 ; ++pinNum)
{
// Если нужно настроить данную ножку
if(m_pinMask & (0x1 << pinNum))
{
// Настройки каждой ножки занимают по 4 бита в регистре
// Преобразуем номер ножки в необходимое смещение в регистре
// Зачищаем редактируемую часть регистра
m_gpio->CRL &= ~(0xF << (pinNum * 4));
// Выставляем нужный режим работы ножки
m_gpio->CRL |= ((uint32_t)gpioMode << (pinNum * 4));
}
}
// Перебираем последние 8 ножек
for(uint8_t pinNum = 0 ; pinNum < 8 ; ++pinNum)
{
// Если нужно настроить данную ножку
if(m_pinMask & (0x1 << (pinNum + 8)))
{
// Настройки каждой ножки занимают по 4 бита в регистре
// Преобразуем номер ножки в необходимое смещение в регистре
// Зачищаем редактируемую часть регистра
m_gpio->CRH &= ~(0xF << (pinNum * 4));
// Выставляем нужный режим работы пина
m_gpio->CRH |= ((uint32_t)gpioMode << (pinNum * 4));
}
}
}
// Функция включения ножки на лог "1"
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
void Stm32Gpio <m_gpio, m_pinMask>::set() const
{
m_gpio->BSRR = m_pinMask;
}
// Функция выключения ножки на лог "0"
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
void Stm32Gpio <m_gpio, m_pinMask>::clr() const
{
m_gpio->BRR = m_pinMask;
}
// Функция изменяет состояние ножки на противоположное
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
void Stm32Gpio <m_gpio, m_pinMask>::tgl() const
{
m_gpio->ODR ^= m_pinMask;
}
// Функция возвращает текущее состояние ножек
template <GPIO_TypeDef *m_gpio, uint16_t m_pinMask>
bool Stm32Gpio <m_gpio, m_pinMask>::Stm32Gpio::get() const
{
return m_gpio->IDR & m_pinMask;
}
Keil это всё компилировал, но контроллер по факту вываливался в Hard Fault. Gcc выдавал ошибку следующего содержания, когда я пытался создать объекты
Код: Выделить всё
Stm32Gpio <GPIOD, 1<<2> led1;
'(GPIO_TypeDef*)((1073741824 + 65536) + 5120)' is not a valid template argument for 'GPIO_TypeDef*' because it is not the address of a variable
Благодаря вашему примеру, кажется понял что именно было не так. Видимо, пользовательский тип не может быть non-type параметром. Нужно мне побольше почитать про non-type параметры.