Показ дописів із міткою shift registry. Показати всі дописи
Показ дописів із міткою shift registry. Показати всі дописи

пʼятницю, 8 січня 2021 р.

Запускаємо FreeRTOS на ESP32. Створення задач. Видалення задач. Призупинення виконання задачі і її відновлення. Семафори MUTEX - справжня бійня за периферію

 Передмова


Оволодіння вмінням працювати з FreeRTOS надає великі переваги в проектах на мікроконтролерах. Двох-ядерний  мікроконтролер ESP32 дозволяє з легкістю спробувати цю операційну систему для мікроконтролерів. Складемо макет та напишемо невеличкий приклад коду. Де будемо надсилати дані для різних пристроїв по одному SPI з різних задач, використаємо семафор, створимо декілька задач, призупинимо/відновимо роботу задачі, видалимо задачу з пам'яті.

Приклад

Складіть макет схеми як на малюнку. Створімо новий проект в Platformio, дамо проекту назву "ESP32_HC595_MAX7219_FreeRTOS". Для роботи з драйвером "max7219" завантажте цю бібліотеку, та додайте її до свого проекту, який щойно створили. Тепер можемо додати демонстраційний код:

#include <Arduino.h>
#include <SPI.h>
#include "max7219.h"

#define CS_HC595    5
#define CS_MAX7219  15
#define KEY_PIN     0
#define LED_BLUE    2

#define CS_HC595_SET()  digitalWrite(CS_HC595, LOW)
#define CS_HC595_RESET()  digitalWrite(CS_HC595, HIGH)

void Task0(void* parameters);
void Task1(void* parameters);
void Task2(void* parameters);
void Task3(void* parameters);
void Task4(void* parameters);
void Task5(void* parameters);

bool keyFlag = false;
bool task4Active = true;

TaskHandle_t Task2Handle = NULL;

SemaphoreHandle_t mutexSPI;
SemaphoreHandle_t mutexSerial;

Max7219 max7219 = Max7219(CS_MAX7219, 8, 9);

void setup() 
{
  Serial.begin(115200);
  Serial.println("Serial is OK!");
  SPI.begin(18, 21, 23);
  max7219.Begin();

  mutexSPI = xSemaphoreCreateMutex();
  mutexSerial = xSemaphoreCreateMutex();

  xTaskCreate(Task0, "Task0", 2048, NULL, 1, NULL);
  xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL, 0);
  xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 1, &Task2Handle, 1);
  xTaskCreate(Task3, "Task3", 2048, NULL, 2, NULL);
  xTaskCreate(Task4, "Task4", 2048, NULL, 1, NULL);
  xTaskCreate(Task5, "Task5", 2048, NULL, 1, NULL);
}

void loop() 
{
  
}

void Task0(void* parameters)
{
  xSemaphoreTake(mutexSerial, portMAX_DELAY);
  Serial.printf("[%8lu] Run Task0 only once: %d\r\n", millis(), xPortGetCoreID());
  xSemaphoreGive(mutexSerial);

  vTaskDelete(NULL);
}

void Task1(void* parameters)
{
  //TODO: код що виконується раз при запускі задачі
  pinMode(CS_HC595, OUTPUT);
  CS_HC595_RESET();
  
  while (1)
  {
    //TODO: код що виконується безкінечно
    for (size_t i = 0; i < 8; i++)
    {
      xSemaphoreTake(mutexSerial, portMAX_DELAY);
      Serial.printf("[%8lu] Run Task1 on Core: %d\r\n", millis(), xPortGetCoreID());
      xSemaphoreGive(mutexSerial);
      
      xSemaphoreTake(mutexSPI, portMAX_DELAY);
      CS_HC595_SET();
      SPI.transfer(1 << i);
      CS_HC595_RESET();
      xSemaphoreGive(mutexSPI);

      vTaskDelay(500 / portTICK_PERIOD_MS);
    }
  }
}

void Task2(void* parameters)
{
  while (1)
  {
    max7219.Clean();
    max7219.DecodeOn();

    xSemaphoreTake(mutexSerial, portMAX_DELAY);
    Serial.printf("[%8lu] Run Task2 on Core: %d\r\n", millis(), xPortGetCoreID());
    xSemaphoreGive(mutexSerial);
    
    xSemaphoreTake(mutexSPI, portMAX_DELAY);
    max7219.PrintNtos(8, millis(), 8);
    xSemaphoreGive(mutexSPI);

    vTaskDelay(973 / portTICK_PERIOD_MS);
  }  
}

void Task3(void* parameters)
{
  pinMode(KEY_PIN, INPUT);

  while (1)
  {
    if(!digitalRead(KEY_PIN) && !keyFlag)
    {
      keyFlag = true;

      xSemaphoreTake(mutexSerial, portMAX_DELAY);
      Serial.printf("[%8lu] KEY is pressed!\r\n", millis());
      xSemaphoreGive(mutexSerial);      
    }

    if (digitalRead(KEY_PIN) && keyFlag)
    {
      keyFlag = false;

      xSemaphoreTake(mutexSerial, portMAX_DELAY);
      Serial.printf("[%8lu] KEY is unpressed!\r\n", millis());
      xSemaphoreGive(mutexSerial);

      if (task4Active)
      {
        task4Active = false;
        vTaskSuspend(Task2Handle);
      }
      else
      {
        task4Active = true;        
        vTaskResume(Task2Handle);
      }           
    }

    vTaskDelay(50 / portTICK_PERIOD_MS);
  }
  
}

void Task4(void* parameters)
{ 
  uint8_t brightness[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 
                           0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 };
  
  while (1)
  {
      for (size_t i = 0; i < sizeof(brightness) / sizeof(brightness[0]); i++)
      {
        xSemaphoreTake(mutexSPI, portMAX_DELAY);
        max7219.SetIntensivity(brightness[i]);
        xSemaphoreGive(mutexSPI);

        vTaskDelay(150 / portTICK_PERIOD_MS); 
      }            
  }  
}

void Task5(void* parameters)
{
  ledcSetup(0, 5000, 8);
  ledcAttachPin(LED_BLUE, 0);

  uint8_t n = 0;
  bool direction = false;

  while (1)
  {  
    if (!direction && n == 255)    
    {
      direction = true;
    }
    
    if (direction && n == 0)
    {
      direction = false;
    }
    
    ledcWrite(0, n);

    if (!direction)
    {
      n++;
    }
    else
    {
      n--;
    }
    
    vTaskDelay(10 / portTICK_PERIOD_MS); 
  }
}

Компілюємо, заливаємо, відкриваємо Serial-monitor, спостерігаємо за роботою.

Файли

Файли прикладів можна завантажити з репозиторію GitHub

Відео-посібник