1

Тема: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Можливо пишу не в ту вітку, прошу модераторів перенести тему, якщо це потрібно.

Друзі, є декілька плат зі світлодіодними лінійками що з'єднуються одна в одну. Таких плат може бути від одної до 5 а може і більше. Платами керує МК STM32. На кожній платі 32 світлодіоди якими управляють 4 мікросхеми 74HC595. Проблема полягає в тому, що я "не дуже програміст" а ще, що розробник плати контролера чомусь почепив ці мікросхеми мимо апаратного SPI. Тобто дригати ноги управління треба програмно. Де що я вже реалізував. А саме засвітити один світлодіод із будь якої кількості плат.

#define u32                 uint32_t
#define INDRES_LOW         HAL_GPIO_WritePin_LOW(IndRes_GPIO_Port, IndRes_Pin);
#define INDRES_HIGH        HAL_GPIO_WritePin_HIGH(IndRes_GPIO_Port, IndRes_Pin);
#define INDLATCH_LOW    HAL_GPIO_WritePin_LOW(GPIOB, IndLatch_Pin);
#define INDLATCH_HIGH    HAL_GPIO_WritePin_HIGH(GPIOB, IndLatch_Pin);
#define INDDATA_LOW        HAL_GPIO_WritePin_LOW(GPIOB, IndData_Pin);
#define INDDATA_HIGH    HAL_GPIO_WritePin_HIGH(GPIOB, IndData_Pin);
#define INDCLK_LOW        HAL_GPIO_WritePin_LOW(GPIOB, IndClk_Pin);
#define INDCLK_HIGH        HAL_GPIO_WritePin_HIGH(GPIOB, IndClk_Pin);
#define IndikatorReset    INDRES_LOW;INDRES_HIGH;INDLATCH_HIGH;INDLATCH_LOW;

u32 cnt = 0, kilkIndPlat = 2;

while (1)
    {
                // подальший код прокручується декілька раз в секунду
                // тому лишнє вирізав щоб не мішало
           
                cnt++;
        if (cnt > kilkIndPlat * 32)
        {
            cnt = 1;
        }
        
                 indOut(cnt); // Засвітка світлодіода
    }



void indOut(u32 numberLed)// функція виводу одного світлодіода
{
    IndikatorReset;
    for (int i = 0; i < (kilkIndPlat * 32) + 1; i++)
    {
        INDCLK_HIGH;
        if (i != numberLed - 1)
        {
            INDDATA_LOW;
        }
        else
        {
            INDDATA_HIGH;
        }

        INDCLK_LOW;

    }

    INDLATCH_HIGH;
    INDLATCH_LOW;
}

Тут питання скоріше програмного характеру.
Як засвітити декілька світлодіодів?
А як засвітити якийсь світлодіод не погасивши той що раніше засвічував?

Я хотів би створити функцію що виглядала би приблизно так

void output(bool state, u32 numPlt, u32 numLed)
{
     // а тут фіг лого знає що написатию... ((
}

Я пробував безліч варіантів, та то все марно, отримую якусь фігню. Дякую що дочитали

Подякували: 0xDADA11C71

2 Востаннє редагувалося koala (18.06.2021 13:49:27)

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Я нічого не знаю про ці мікросхеми, але у функції indOut, очевидно, виконуються такі операції:
IndikatorReset; //схоже, ініціалізація - підготовка для виставлення стану

далі для кожного LEDу:
INDCLK_HIGH;
INDDATA_LOW; або INDDATA_HIGH; - залежно від номеру; схоже, перше відповідає за вимикання, а друге - за вмикання
INDCLK_LOW;

і один раз
INDLATCH_HIGH;
INDLATCH_LOW; - схоже, це дає команду "виконати" попереднє виставлення.

Тобто у цьому зразку коду обов'язково виставляються усі LEDи для того, щоб засвітити лише один. Що вам треба - то це змінити умову перемикання. Замість

        if (i != numberLed - 1)

потрібна інша умова - яка перевірятиме, чи має горіти i-й LED (у загальному списку).
Спробуйте так:

/*перед циклом*/
uint32_t led_map[32*kilkIndPlat];
for(u32 i=0;i<32*kilkIndPlat;++i)
  led_map[i] = 0;
/*цикл*/
while (1)
    {
        /*тут виставляєте led_map як вам треба*/
        ...

        cnt++;
        if (cnt > kilkIndPlat * 32)
        {
            cnt = 1;
        }
        
        indOut(led_map, 32*kilkIndPlat); // Засвітка світлодіодів
    }

void setled(bool state, u32 numPlt, u32 numLed) //готує для виставлення LED-ів
{
    led_map[32*numPlt+numLed] = state;
}
void indOut(uint8_t leds[], u32 size) //виводить led-и
{
    IndikatorReset;
    for (int i = 0; i < size; i++)
    {
        INDCLK_HIGH;
        
        if (leds[i])
        {
            INDDATA_HIGH;
        }
        else
        {
            INDDATA_LOW;
        }
        INDCLK_LOW;
    }

    INDLATCH_HIGH;
    INDLATCH_LOW;
}

Ну і в циклі тепер треба буде виставити кілька LED-ів перед викликом indOut.

Подякували: Si4karuk1

3

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Гм. А чому цикл від 0 до  kilkIndPlat * 32 включно? Цього я не розумію. Скільки LED-ів на двох платах?

Подякували: Si4karuk1

4

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Наліпив слідуючий г..код. За допомогою цієї функції, я можу виводити 32х бітне число в любу плату, та коли виводжу число, скажімо в третю плату то світлодіоди вмикаються замітно повільніше ніж в першу. Це зумовлено послідовним прогоном кожного біта в гирлянду мікросхем. Як би так зробити, щоб заганяти кожен раз, скажімо 160 біт (32 * 5 плат), а виводити тільки потрібні? Тобто навіть коли підключені тільки дві плати, то все одно проганяти 160 біт. Так це буде повільніше але з однаковою швидкістю.

Прихований текст
void indOut(u32 numPLT, u32 data)
{
    IndikatorReset; // зкинути всі мікросхеми

    for (int i = 0; i < (numPLT * 32) + 1; i++)
    {
        INDCLK_HIGH; // клоки
        if ((data & 0x80000000) == 0x00)
        {
            INDDATA_LOW; // дані
        }
        else
        {
            INDDATA_HIGH;
        }

        INDCLK_LOW;
        data = (data << 1);
    }
    INDLATCH_HIGH;// вивід
    INDLATCH_LOW;
}

5

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

koala написав:

Скільки LED-ів на двох платах?

На кожній платі 32 світлодіоди

6 Востаннє редагувалося koala (18.06.2021 16:01:29)

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Ще раз: в оригінальному коді готується завжди kilkIndPlat * 32 + 1 значень перед виведенням; а у вас numPLT * 32 + 1. Як ви основний цикл змінили?
І чому там +1? Куди останній виклик іде? Чи перший? Таки перший, там з cnt=1 починається.

Подякували: Si4karuk1

7

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

koala написав:

Ще раз: в оригінальному коді готується завжди kilkIndPlat * 32 + 1 значень перед виведенням; а у вас numPLT * 32 + 1. Як ви основний цикл змінили?
І чому там +1? Куди останній виклик іде? Чи перший? Таки перший, там з cnt=1 починається.

Там просто підперто цим +1 неправильне формування тактового сигналу зсуву для 74HC595.
Цей регістр тактується фронтом сигналу, а в циклі робиться спочатку HIGH потім LOW.
Тому й виходить, що на початку цикду засувається якийсь хвіст з даних (0 як біт за останнім бітом даних з data), а потім для того, щоб засунути останній біт, треба дати ще один імпульс.
Треба поміняти місцями INDCLK_HIGH та INDCLK_LOW і зробити правильний цикл від 0 до < numPLT * 32

Подякували: 0xDADA11C7, koala, leofun01, Si4karuk4

8 Востаннє редагувалося ReAl (18.06.2021 23:49:37)

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Si4karuk написав:

Наліпив слідуючий г..код. За допомогою цієї функції, я можу виводити 32х бітне число в любу плату, та коли виводжу число, скажімо в третю плату то світлодіоди вмикаються замітно повільніше ніж в першу. Це зумовлено послідовним прогоном кожного біта в гирлянду мікросхем. Як би так зробити, щоб заганяти кожен раз, скажімо 160 біт (32 * 5 плат), а виводити тільки потрібні? Тобто навіть коли підключені тільки дві плати, то все одно проганяти 160 біт. Так це буде повільніше але з однаковою швидкістю.

• Виводити кожен раз всі. Зробити буфер-образ «екрану», приблизно як пропонував koala, але не витрачаючи зайві байти RAM, у мікроконтролерах це не прийнято :-)
• «Помітно повільніше» — щось занадто … Оптимізація хоч якась увімкнена? Бо навіть HAL не повинен був аж так уповільнити.

Отже, має бути щось таке

/*перед циклом*/
uint32_t led_buffer[board_count];

void clear_buffer(void)
{
    for (unsigned board = 0; i < board_count; ++board)
        led_buffer[board] = 0;
}

/* pixel_n -- номер піксела у всій лінійці плат 0 .. (32*board_count - 1) */
void set_pixel(unsigned pixel_n, bool state)
{
    uint32_t *pword = &led_map[pixel_n / 32];
    uint32_t mask = 1UL << (pixel_n % 32);

    if (state)
        *pword |= mask;
    else
        *pword &= ~mask;
}

/* Зсув даних для однієї плати */
void shift_one_board(uint32_t data)
{
    for (unsigned bit = 0; bit  < 32; ++bit)  {
        INDCLK_LOW;
        
        if (data & 0x80000000)
            INDDATA_HIGH;
        else
            INDDATA_LOW;

        INDCLK_HIGH;
        data <<= 1;
    }
}

/* 
 * Оновлюємо всю лінійку плат
 * Схему не бачив, але судячи з усього, починати треба зі старшої плати.
 */
void refresh_screen(void)
{
    INDLATCH_LOW;    // storage reg теж по фронту, технічно він і не latch

    for (unsigned board = 0; board < board_count; ++board)
        shift_one_board(led_buffer[borad_count - 1 - i]);

    INDLATCH_HIGH;
}
Подякували: koala, leofun01, Si4karuk3

9 Востаннє редагувалося ReAl (18.06.2021 23:50:17)

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Тепер щодо швидкості. Про оптимізацію вже спитав.

Про швидкий ногодриг засобами HAL слід забути. Там на кожен біт виходить три виклики функцій HAL, які ще й якщо з вимкненою оптимізацією скомпілюються, буде зовсім повільно.
Можна працювати з регістрами напряму, щось в дусі (IndClk_Pin це ж бітова маска? я в HAL не вмію).

#define INDCLK_LOW        do { GPIOB->BRR = IndClk_Pin; } while(0)
#define INDCLK_HIGH       do { GPIOB->BSRR = IndClk_Pin; } while(0)

і аналогічно для інших.
За відсутності навичок роботи з регістрами напряму конфігурувати простіше через HAL.

Подякували: koala, leofun01, Si4karuk3

10

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Si4karuk написав:

Друзі, є декілька плат зі світлодіодними лінійками що з'єднуються одна в одну. Таких плат може бути від одної до 5 а може і більше.

До речі, про «від … до …»
Можна, якщо потрібно, зробити таку річ — вихід останньої плати подати назад на вхід мікроконтролера.
При ініціалізації модуля зробити таке
• reset всім регістрам, скрізь 0.
• Записувати у регістри зсуву 1 допоки на вході не з'явиться 1 (строб перезапису у регістр зберігання не видавати, щоб не блимнути світлодіодами). Рахувати такти.
• Знову reset.
Таким чином можна дізнатися кількість підключених плат і наступний вивід робити під таку кількість плат для оптимізації часу.
Залежно від організації проекту або обмежити led_buffer[] «достатньо великим» числом (наприклад, 10 плат) і встановити це як обмеження для використання, або після з'ясування кількості плат отримувати пам'ять по malloc.
board_count тоді стає змінною, а не константою.
Якщо led_buffer[] зробити типу uint8_t і відповідно змінити всі інші функції, board_count перейменувати у hc595_count чи щось таке, то цей модуль працюватиме хоч з тими готовими платами по чотири регістри, хоч з якоюсь спеціально зробленою системою з сімома.

11

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

А, ще дрібна порада щодо стилю: макроси INDCLK_LOW і інші щось роблять, тобто це за дією функції (бувають і інші, які, наприклад, визначають стандартні змінні, налаштовують компілятор тощо). Тоді краще їх оформлювати як макроси з порожнім списком параметрів:

#define INDCLK_LOW()        do { GPIOB->BRR = IndClk_Pin; } while(0)
...
INDCLK_LOW();

Тепер видно, що це якась дія.
Якщо цікаво, що то за do-while - то це стандартний прийом у макросах, щоб, з одного боку, екранувати дію макросу фігурними дужками, а з іншого, змусити компілятор вимагати крапку з комою після виклику. Так, C - мова збочена.

Подякували: ReAl, Si4karuk2

12

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

koala написав:

А, ще дрібна порада щодо стилю.

Так, звісно. Я утримався від чисто стильових порад, бо й так багато написав.
Від do { ... } while (0) у цьому місці втриматися не міг, бо це вже не просто стиль, а гігієна :)

Подякували: 0xDADA11C71

13

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Де що займався іншими справами, тому не відповідав.
Дякую за допомогу та настанову.
Переписав ногодриг

Прихований текст
#define INDRES_LOW()         do {GPIO_WritePin_LOW(IndRes_GPIO_Port, IndRes_Pin);} while(0)
#define INDRES_HIGH()        do {GPIO_WritePin_HIGH(IndRes_GPIO_Port, IndRes_Pin);} while(0)
#define INDLATCH_LOW()        do {GPIO_WritePin_LOW(GPIOB, IndLatch_Pin);} while(0)
#define INDLATCH_HIGH()        do {GPIO_WritePin_HIGH(GPIOB, IndLatch_Pin);} while(0)
#define INDDATA_LOW()        do {GPIO_WritePin_LOW(GPIOB, IndData_Pin);} while(0)
#define INDDATA_HIGH()        do {GPIO_WritePin_HIGH(GPIOB, IndData_Pin);} while(0)
#define INDCLK_LOW()        do {GPIO_WritePin_LOW(GPIOB, IndClk_Pin);} while(0)
#define INDCLK_HIGH()        do {GPIO_WritePin_HIGH(GPIOB, IndClk_Pin);} while(0)
#define IndikatorReset()    do {INDRES_LOW();INDRES_HIGH();INDLATCH_HIGH();INDLATCH_LOW();} while(0)
#define Latch()                do {INDLATCH_HIGH();INDLATCH_LOW();} while(0)

void GPIO_WritePin_LOW(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  GPIOx->BRR = (uint32_t)GPIO_Pin;
  //GPIOx->ODR &= ~(uint32_t)GPIO_Pin;
}

void GPIO_WritePin_HIGH(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  GPIOx->BSRR = (uint32_t)GPIO_Pin;
  //GPIOx->ODR |= (uint32_t)GPIO_Pin;
}

Зараз я рахую кількість плат таким чином. Перед загальним циклом резетую всі мікросхеми, не вмикаючи строб виводжу одиницю на INDDATA і просто рахую INDCLK. Коли дорахував,визначаю кількість плат та виходжу з циклу. Я надіюсь, це так має виглядати? До речі у відладчику бачу реальну кількість, от же працює.

void countPlt()
{
    INDDATA_HIGH();
    for (int i = 1; i < 320; i++)
    {
        INDCLK_HIGH();
        INDCLK_LOW();
        if (HAL_GPIO_ReadPin(GPIOA, IndInput_Pin))
        {
            kilkIndPlat = i / 32;
            return;
        }

    }
}
Подякували: ReAl1

14

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

ReAl написав:

Оптимізація хоч якась увімкнена?

Коли вмикаю оптимізацію, нічого не працює (.

15

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

void countPlt()
{
    INDDATA_HIGH();
    for (int i = 1; i < 320; i++)
    {
        INDCLK_HIGH();
        INDCLK_LOW();
        if (HAL_GPIO_ReadPin(GPIOA, IndInput_Pin))
        {
            kilkIndPlat = i / 32;
            return;
        }

    }
}

ти сам зрозумів що ти ото понаписав?

16

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

ur_naz написав:

ти сам зрозумів що ти ото понаписав?

Увімкнути високий рівень на DATA
Дригати CKL до тих пір доки мк не побачить високий рівень на вході.
Якщо отримав 1 то кількість відправлених біт ділити на 32.
далі виліт з циклу.

Таким чином я фактично рахую кількість плат на яких по 32 діоди.

17

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Si4karuk написав:

Де що займався іншими справами, тому не відповідав.
Дякую за допомогу та настанову.
Переписав ногодриг
...
Зараз я рахую кількість плат таким чином. Перед загальним циклом резетую всі мікросхеми, не вмикаючи строб виводжу одиницю на INDDATA і просто рахую INDCLK. Коли дорахував,визначаю кількість плат та виходжу з циклу. Я надіюсь, це так має виглядати? До речі у відладчику бачу реальну кількість, от же працює.

void countPlt()
{
    INDDATA_HIGH();
    for (int i = 1; i < 320; i++)
    {
        INDCLK_HIGH();
        INDCLK_LOW();
        if (HAL_GPIO_ReadPin(GPIOA, IndInput_Pin))
        {
            kilkIndPlat = i / 32;
            return;
        }
    }
}

Загалом добре, але якщо хтось почепить 11 плат, то ця функція залишить в kilkIndPlat старе значення, за цим треба слідкувати і треба вирішити, що робити у такому випадкові (обслуговувати нормально лише перші 10, а далі хоч трава не рости, etc.). Також треба захиститися від того, що не почеплена жодна плата, але це вже апаратно, pull-up на IndInput_Pin відразу дасть одиничку і буде 0 плат.
Ну й стильове — такі функції варто відв'язувати від іншого коду, хай вона повертає кількість плат, а не пише у глобальну змінну. Після циклу повертає або 0 (відмовляємося працювати з ланцюжком більше 10 плат) або 10 (сміття на платах після 10-ї нас не цікавить).

Подякували: koala, Si4karuk2

18

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

Si4karuk написав:
ReAl написав:

Оптимізація хоч якась увімкнена?

Коли вмикаю оптимізацію, нічого не працює (.

Це погана ознака, десь щось у коді зроблено геть неправильно.

Подякували: Si4karuk1

19

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

ReAl написав:

якщо хтось почепить 11 плат
але це вже апаратно, pull-up на IndInput_Pin відразу дасть одиничку і буде 0 плат.
Ну й стильове

Плат не може бути більше 10
У мене pull-down я чекаю up( коли обрив або нема плат) то повертається 0. Далі працювати не буде )
Дякую за пораду.

20

Re: STM32 Software SPI та Сзувні регістри із засувкою 74HC595

ReAl написав:

Це погана ознака, десь щось у коді зроблено геть неправильно.

Цікава штука цей volatile
Справа в тому що у мене є такий собі системний Тік, який реалізований на таймері. Тобто якийсь код я прокручую по за цим Тіком, а якийсь в ньому. Отже як раз лінійка світлодіодів була в цьому Тіку. При будь якій оптимізації, компілятор не враховував до уваги зміни змінної Тіка, а значить код лінійки не виконувався. Я зробив її volatile, і все запрацювало.

Подякували: koala, 0xDADA11C7, leofun01, ReAl4