1

(56 відповідей, залишених у Системне програмування)

@taburyak
мені при роботі з масивами вказівників набагато більше подобається інший варіант запису.

Menu_Item_t const * PageStart[]

однозначно дає зрозуміти що це масив вказівників на константі структури, а от з варіантом запису

Menu_Item_t const ** PageStart

можна вже наплужити)) так само я роблю і передачі масива вказівників у функцію:

static void TableMenuWrite(Menu_Item_t const * page[])

і тоді всередині функції всі виклики стають

while (page[i]->Index != 0) {
    table.addRow(page[i]->Index, page[i]->Text, page[i]->Value);
    i++;
} 

ну і ще, наступні три записи еквівалентні

page[i]->Index
(*(page + i))->Index
(**(page + i)).Index

використання синтаксису масивів при цьому позбавляє від проблеми приорітетів операцій * . -> і купи скобок :)
та при цьому інформація про розмір масиву при передачі у функцію всеодно не зберігається

2

(56 відповідей, залишених у Системне програмування)

@taburyak в сі інформація про розмір масиву зберігається тільки в тому ж "контексті" що і об'явлений сам масив (тобто sizeof(arr) поверне розмір масиву в байтах). Якщо масив передати в функцію то передається вказівник (копіюється за значенням в локальну змінну аргумент) і всередині функції  губиться інформація про розмір масиву, там sizeof(arr) поверне розмір вказівника - 4 байта. Тому в сі при передачі масиву в функцію зазвичай використовуbють два аргументи - вказівник та кількість елементів, як написали вище. Щодо NULL як на мене можуть бути нюанси, це підходить тільки для масиву вказівників, інакше NULL, що визначений як "(void *) 0" можна сплутати з порожнім полем, бо у автора масив структур.

3

(196 відповідей, залишених у Системне програмування)

@taburyak я таки розібрався! :)

Справа в тому що екран повертає дані в форматі 888. Тобто для зчитування одного пікселю треба зробити два рази Read по 16 біт в результаті отримаємо два шматка: [R1G1][B1R2]. З яких забираємо три байти кольору, і потім ще треба привести до формату 565 (у мене для цього є окрема функція)

Ввідні дані: весь екран залитий кольором 0x1234, один піксель залив 0x4321.
Ось тут зробив послідовно десять разів LCD_RD_Data() в масив temp. (нульова ячейка - dummy read)
https://i.ibb.co/X2Snph4/Capture.png
як бачиш паттерн повторюється 10 44 a4 (це у 888 що відповідає 0x1234 у форматі 565) - фоновий колір. А наш пацієнт це 40 64 08 (це у 888 форматі відповідає 0x4321 у форматі 565) - колір замальованого пікселя.

Функція що робить перетворення 888 -> 565:

/**
 * \brief Calculate 16Bit-RGB
 *
 * \param r    Red
 * \param g    Green
 * \param b    Blue
 *
 * \return uint16_t    16Bit-RGB
 */
uint16_t LCD_Color565(uint8_t r, uint8_t g, uint8_t b) {
    return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}

Плюс не потрібно виставляти ціле вікно для отримання пікселю, достатньо виставити лише лівий верхній кут. Ось так виглядає функція ReadPixel:

/**
 * \brief Reads a point from the specified coordinates
 *
 * \param x        x-Coordinate
 * \param y        y-Coordinate
 *
 * \return uint16_t     Color
 */
uint16_t LCD_ReadPixel(int16_t x, int16_t y) {
    uint16_t temp[3];
    // Clip
    if ((x < 0) || (y < 0) || (x >= TFTWIDTH) || (y >= TFTHEIGHT))
        return;
    LCD_WR_REG(ILI9341_COLADDRSET);
    LCD_WR_Data(x >> 8);
    LCD_WR_Data(x & 0xFF);

    LCD_WR_REG(ILI9341_PAGEADDRSET);
    LCD_WR_Data(y >> 8);
    LCD_WR_Data(y & 0xFF);

    LCD_WR_REG(ILI9341_MEMORYREAD);
    temp[0] = LCD_RD_Data(); // dummy read
    temp[1] = LCD_RD_Data();
    temp[2] = LCD_RD_Data();
    
    return LCD_Color565((temp[1] >> 8) & 0xFF, temp[1] & 0xFF, (temp[2] >> 8) & 0xFF);
}

Ось так у мене виглядає DrawPixel - ставлю тільки x,y (без другої точки кінця вікна):

/**
 * \brief Draws a point at the specified coordinates
 *
 * \param x        x-Coordinate
 * \param y        y-Coordinate
 * \param color    Color
 *
 * \return void
 */
void LCD_DrawPixel(int16_t x, int16_t y, uint16_t color) {
    // Clip
    if ((x < 0) || (y < 0) || (x >= TFTWIDTH) || (y >= TFTHEIGHT))
        return;
    LCD_WR_REG(ILI9341_COLADDRSET);
    LCD_WR_Data(x >> 8);
    LCD_WR_Data(x & 0xFF);

    LCD_WR_REG(ILI9341_PAGEADDRSET);
    LCD_WR_Data(y >> 8);
    LCD_WR_Data(y & 0xFF);

    LCD_WR_REG(ILI9341_MEMORYWRITE);
    LCD_WR_Data(color);
}

І пам'ятаєш казав у тебе правильно працює але невірно зроблена функція SetWindow - переплутані місцями x1, x2, y1, y2 і в аргументах і всередині. Потрібно передавати

LCD_SetAddrWindow (x1, y1, x2, y2)

І тоді в CASET писати х1/х2, а в PASET - y1/y2. А у тебе навпаки :)

Сама функція:

/**
 * \brief Sets window address
 *
 * \param x1         Left top window x-coordinate
 * \param y1         Left top window y-coordinate
 * \param x2         Rigth bottom window x-coordinate
 * \param y2         Rigth bottom window y-coordinate
 *
 * \return void
 */
void LCD_SetAddrWindow(int x1, int y1, int x2, int y2) {
    LCD_WR_REG(ILI9341_COLADDRSET);
    LCD_WR_Data(x1 >> 8);
    LCD_WR_Data(x1 & 0xFF);
    LCD_WR_Data(x2 >> 8);
    LCD_WR_Data(x2 & 0xFF);
    LCD_WR_REG(ILI9341_PAGEADDRSET);
    LCD_WR_Data(y1 >> 8);
    LCD_WR_Data(y1 & 0xFF);
    LCD_WR_Data(y2 >> 8);
    LCD_WR_Data(y2 & 0xFF);
    LCD_WR_REG(ILI9341_MEMORYWRITE);
}

4

(196 відповідей, залишених у Системне програмування)

знайшов в чому проблема. створив новий проект за своєю задумкою, але вставив шмат вашого коду колбека. переривання спрацьовує, а колбек не викликається. подивився уважніше, а там в імені функції помилка (велика літера у CallBack, а треба Callback)

HAL_TIM_IC_CaptureCallback()

А не ругається бо є weak реалізація в надрах HAL ;)

p.s. все одно в мене вже все запрацювало - поділюсь одразу всім проектом (там як я і спершу описував direct rising + indirect falling, без reset)  https://www.dropbox.com/s/r30zrfdrq5jae … h.zip?dl=0

5

(196 відповідей, залишених у Системне програмування)

taburyak вітаю ;)

по-перше найцікавіша частина ініціалізації таймера залишилась поза зором - функцію HAL_TIM_IC_MspInit() в студію :)
а переривань не відбувається, бо мабуть в налаштуваннях NVIC не включене (розмасковане) перерирвання TIM2 Global Interrupt.

UPD: або проблема в тому що тригер налаштований на falling

sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;

і по ньому з одного боку скидається таймер, а по-друге виконується захват сигналу (який в цей момент нуль). Результат захвату треба забирати не з регістру CNT а регістру каналу, відповідно CCR1.

по-друге можу підказати як міряти простіше: можна налаштувати два канали на одну ніжку (наприклад channel 1 в режимі direct mode з трігером по rising, channel 2 - indirect mode з трігером по falling). тоді обробник переривання буде наступного вигляду:

HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
uint32_t pulse;

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
    static uint32_t rising;
    if (htim->Instance == TIM2) {
        if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
            rising = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
        } else if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) {
            pulse = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2) - rising;
        }
    }
}

трігер тоді взагалі не потрібен, а якщо зробити період таймеру максимальним (2^32-1) то тоді і переповнення буде автоматично працювати через unsigned математику.

або залишити так само два канали direct + indirect з різнимим тригерами. і по TI1 ресетити таймер як у вас, а по TI2 робити семпл:

HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
uint32_t pulse;

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
    if (htim->Instance == TIM2) {
        } else if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) {
            pulse = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
        }
    }
}

6

(196 відповідей, залишених у Системне програмування)

вітаю всіх :)
як на мене, то багатострокові макроси все ж таки краще огортати в конструкцію без крапкокоми

do {
    /* smth */
} while(0)

інакше, як вище вказав ReAl будуть проблеми у випадках коли не ставлять дужки ітд..
краще перестрахуватись заздалегідь
це чудово описано отут на stackoverflow