Re: STM32, Atolic TrueStudio, CubeMX
Колись давно шановний VitekSVM, свого часу, дуже допоміг з цим меню розібратись, хоч на той час в мене так і не вийшло.
VitekSVM написав:#define MENU_ITEM_READ_POINTER(Addr) (void*)pgm_read_word(Addr)
замінити на
#define MENU_ITEM_READ_POINTER(Addr) (void*)(Addr)
Щось тут не так.
З першим рядком для AVR все добре. pgm_read_word приймає адресу об'єкта, але повертає uint16_t. Ми у цьому макросі знаємо, що читаємо вказівник, тому приводимо отриманий uint16_t до (void*).
А от з другим рядком біда. Макрос приймає таку саму адресу об'єкта у пам'яті, але замість прочитати значення просто приводить цю адресу до (void*).
Відповідно тут
void (*SelectCallback)(void) = MENU_ITEM_READ_POINTER(&CurrentMenuItem->SelectCallback); if (SelectCallback) SelectCallback();
Відбувається «виклик» не по адресі функції, а по адресі даних, того місця, де у структурах меню розміщено адресу функції. Тобто далі виконується випадковий код.
І тому треба саме так
#define MENU_ITEM_READ_POINTER(Addr) *(Addr)
Тут макрос отримує адресу і звертається по ній. Оскільки ми знаємо, що там у комірці лежить теж адреса, ніякі приведення не потрібні. Зрештою, якби писалося відразу для мікроконтролера з єдиним простором адрес, то і макрос не був би потрібен. То для AVR через необхідність звертання до флеша іншими командами довелося загорнути у макрос, щоб не виписувати кожен раз pgm_read_word, що не таке зрозуміле при наступних читаннях коду, як самодокументоване ім'я макроса.
Хтось в курсі чому так відбувається? Ну або чим запис (void*)(Addr) відрізняється від *(Addr)?
Якщо викласти сказане вище коротко, то (void*)(Addr) — приведення параметра Addr до типу void*, а друге — зчитування того, що лежить у комірці з адресою Addr