субота, 7 травня 2022 р.

MTB: PSoC4 CY8CKIT-149 дослідимо аналогові блоки ADC та LPCOMP за допомоги PDL

 Передмова

Продовжимо знайомство з мікроконтролерами Cypress, а нині це Infineon, та засобом розробки ModusToolBoxIDE. В цій статті пропоную розібратись, як працювати з аналоговими блоками, такими як ADC та LPCOMP. Програмувати будемо в засобі розробки MTB_IDE, та драйверами PDL. В попередній статті "Встановлення ModusToolBox та перший проект на CY8CKIT-149 PSoC® 4100S Plus Prototyping Kit" розглянули як встановити MTB_IDE, та створили перший проект. Використовувати будемо все той же ж  KIT Prototyping CY8CKIT-149 PSoC4, але все що буде в цій статті, буде підходити до всіх девайсів які підтримуються MTB та драйверами PDL.

Створення проекту

Запускаємо ModuToolBoxIDE, та створюємо новий проект як на малюнку, на швидкій панелі тиснемо 1:

Створення нового проекту

Далі потрібно обрати мікроконтролер або плату розробника, в цьому випадку це KIT Prototyping CY8CKIT-149 PSoC4, у вас це може бути інший чип. Головне, щоб він підтримувався та мав на борту ADC та LPCOMP:

Вибір цілі

  1. Обираємо серію МК - PSoC4
  2. Обираємо плату з цієї серії
  3. Можна переглянути документацію по цій платі
  4. Тиснемо "Next"
Створювач проектів "Project Creator" запропонує обрати шаблон проекту з прикладом до якоїсь периферії, або пустий проект. Обираємо пустий. Нам потрібно з нуля розібратись зі всіма налаштуваннями і ньюансами.
Обираємо шаблон для проекту
  1. Обираєом шаблон пустого проекту
  2. Називаємо свій проект за смаком і своїми вподобаннями
Після того, як проект створено і він з'явився в "Project explorer", пропоную розгорнути дерево проекту і відкрити головний файл "main.c":
Відкриваємо файл main.c
Залишилось всього трішки - дописати демо-код. Чим і займемось. Але перед тим відкриємо зі швидкої панелі "device-configurator", та налаштуємо периферію, як слід, для наших потреб.
Панель швидкого доступу
  1. Побудувати проект
  2. Очистити проект
  3. Прошити МК і перейти в режим відладки
  4. Залити прошивку і запустити її
  5. Відкрити утіліту "Library Manager" 
  6. Відкрити утіліту "Device Configurator"
Саме зараз нас цікавить "Device Configurator". Запусимо його і оберемо ADC:
Обираємо ADC
  1. Периферія
  2. Аналогова периферія
  3. Програмований аналог PASS
  4. 12-bit SAR ADC
Далі перейдемо до налаштувань самого ADC:
Налаштування ADC частина 1

Налаштування ADC частина 2
  1. Оберемо опірну напругу для порівняння, таку як 3.3 Вольта
  2. Поки обиремо 2 канали, далі можливо додамо ще каналів
  3. Джерело тактування оберемо один з вільних дільників
  4. Назначемо вільний та доступний пін для 0 канала
  5. Назначемо вільний та доступний пін для 1 канала
Тиснемо "CTRL + S" та зберігаємо файл налаштувань. Ще можна переглянути що за код згенерувався для налаштування ADC і двох каналів:
Попередній перегляд коду налаштувань
  1. Перейдемо до вкладки "Code Preview"
  2. Переглянемо, сам код, який генерується в проект
Зберемо такий макет для дослідження, та напишемо демо-код:
Схема макету для дослідження ADC

Демо-код ADC

Перед тим як писати демо-код, додамо бібліотеку retarget до проекту за допомоги "Library manager", для того щоб друкувати повідомлення серіал термінал по UART:

Додаємо бібліотеку retarget
  1. Тиснемо вкладку "бібліотека"
  2. Обираємо периферію
  3. Та позначаємо необхідну бібліотеку
  4. Оновлюємо перелік бібліотек для проекту
В початковий код додаємо бібліотеку ""cy_retarget_io.h"":
#include "cy_pdl.h"
#include "cybsp.h"
#include "cy_retarget_io.h"
В головну функцію main додаємо ініціалізацію бібліотеки retarget для друку повідомлень в термінал:
result = cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX, CY_RETARGET_IO_BAUDRATE);

if (result != CY_RSLT_SUCCESS)
{
	CY_ASSERT(0);
}
І на загал, код в файлі main.c буде виглядати таким чином:
#include "cy_pdl.h"
#include "cybsp.h"
#include "cy_retarget_io.h"

int main(void)
{
    cy_rslt_t result;
    uint16_t resADC[2];

    /* Initialize the device and board peripherals */
    result = cybsp_init() ;

    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(0);
    }

    result = cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX, CY_RETARGET_IO_BAUDRATE);

    if (result != CY_RSLT_SUCCESS)
    {
		CY_ASSERT(0);
    }

    /* Enable global interrupts */
    __enable_irq();

    printf("\x1b[2J\x1b[;H");
    printf("*------------------------------*\r\n");
    printf("* Demo ADC and LPCOMP started! *\r\n");
    printf("*------------------------------*\r\n");

    result = Cy_SAR_Init(SAR0, &pass_0_sar_0_config);

    if (result != CY_RSLT_SUCCESS)
    {
	CY_ASSERT(0);
    }

    Cy_SAR_Enable(SAR0);

    for (;;)
    {
    	Cy_SAR_StartConvert(SAR0, CY_SAR_START_CONVERT_SINGLE_SHOT);
    	Cy_SAR_IsEndConversion(SAR0, CY_SAR_WAIT_FOR_RESULT);
    	resADC[0] = Cy_SAR_GetResult16(SAR0, 0);
    	resADC[1] = Cy_SAR_GetResult16(SAR0, 1);
    	printf("ADC Result Channel 0 = %d mV   \r\n", Cy_SAR_CountsTo_mVolts(SAR0, 0, resADC[0]));
    	printf("ADC Result Channel 1 = %d mV   \r\n", Cy_SAR_CountsTo_mVolts(SAR0, 1, resADC[1]));
    	printf("\x1b[1F");
    	printf("\x1b[1F");
    	Cy_SysLib_Delay(1000);
    }
}
Заливаємо, запускаємо, відкриваємо термінал і спостерігаємо щось подібне:
Результат
Крутимо потенциометри і спостерігаємо за результатом.

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


Вмикаємо і налаштовуємо LPCOMP

LPCOMP (Low Power Comparator) - компаратор з низьким споживанням. Відкриємо знову "device configurator" та увімкнемо і налаштуємо один з компараторів:
Вмикаємо LPCOMP
Та налаштовуємо як на цьому скріншоті:
Налаштування LPCOMP

  1. Конфігуруємо вихід компаратора як "Direct" коли напруга на позитивному вході менша за ту, що на негативному вході, то на виході 0, і навпаки, коли на позитивному вході напруга перевищила поріг, що є на негативному вході, то на виході буде 1. 
  2. Живлення і швидкість порівняння обираємо "Normal power/Fast".
  3. Виникає переривання в обох випадках, як по фронту, так і по спаду.
  4. Обираємо шпильку для позитивного входу.
  5. Обираємо шпильку для негативного входу.
  6. Обираємо шпильку для виходу.
  7. Обиремо, саме той пін для виходу, де є під'єднаний світлодіод, щоб наочно бачити результат порівняння вхідних напруг. Так як світлодіод під'єднаний постійно до 3.3В, то світитиме, коли на виході буде 0, а як на виході компаратора з'явиться 1 то світлодіод згасне.
Вибір шпильки для виходу компаратора

Зберігаємо зміни "CTRL+S" додаємо з'єднання компаратора і дільників.

Схема під'єднання

Додамо дротів з дільників на вхід компаратора

Демо-код для компаратора

#include "cy_pdl.h"
#include "cybsp.h"
#include "cy_retarget_io.h"
#include "stdbool.h"

volatile bool flagLPComp = false;
cy_stc_lpcomp_context_t lpcomp_0_context;

static void lpcomp_interrupt_handler(void);

int main(void)
{
    cy_rslt_t result;
    uint16_t resADC[2];


    cy_stc_sysint_t lpcomp_interrupt_config = {
    	.intrSrc = lpcomp_0_comp_0_IRQ,
	.intrPriority = 3
    };

    /* Initialize the device and board peripherals */
    result = cybsp_init() ;

    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(0);
    }

    result = cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX, CY_RETARGET_IO_BAUDRATE);

    if (result != CY_RSLT_SUCCESS)
    {
	CY_ASSERT(0);
    }

    /* Enable global interrupts */
    __enable_irq();

    printf("\x1b[2J\x1b[;H");
    printf("*------------------------------*\r\n");
    printf("* Demo ADC and LPCOMP started! *\r\n");
    printf("*------------------------------*\r\n");

    result = Cy_SAR_Init(SAR0, &pass_0_sar_0_config);

    if (result != CY_RSLT_SUCCESS)
    {
	CY_ASSERT(0);
    }

    Cy_SAR_Enable(SAR0);

    Cy_LPComp_Init(LPCOMP, CY_LPCOMP_CHANNEL_0, &lpcomp_0_comp_0_config, &lpcomp_0_context);
    Cy_LPComp_Enable(LPCOMP, CY_LPCOMP_CHANNEL_0, &lpcomp_0_context);

    Cy_LPComp_SetInterruptMask(LPCOMP, CY_LPCOMP_CHANNEL0_INTR);
    Cy_SysInt_Init(&lpcomp_interrupt_config, &lpcomp_interrupt_handler);
    NVIC_ClearPendingIRQ(lpcomp_interrupt_config.intrSrc);
    NVIC_EnableIRQ(lpcomp_interrupt_config.intrSrc);

    for (;;)
    {
    	Cy_SAR_StartConvert(SAR0, CY_SAR_START_CONVERT_SINGLE_SHOT);
	Cy_SAR_IsEndConversion(SAR0, CY_SAR_WAIT_FOR_RESULT);
	resADC[0] = Cy_SAR_GetResult16(SAR0, 0);
	resADC[1] = Cy_SAR_GetResult16(SAR0, 1);
	printf("ADC Result Channel 0 (-input) = %d mV   \r\n", Cy_SAR_CountsTo_mVolts(SAR0, 0, resADC[0]));
	printf("ADC Result Channel 1 (+input) = %d mV   \r\n", Cy_SAR_CountsTo_mVolts(SAR0, 1, resADC[1]));
	if(flagLPComp)
	{
	    printf("Output PIN LPComp is HIGT\r\n");
	}
	else
	{
	    printf("Output PIN LPComp is LOW \r\n");
	}
	printf("\x1b[1F");
	printf("\x1b[1F");
	printf("\x1b[1F");
	Cy_SysLib_Delay(1000);
    }
}

static void lpcomp_interrupt_handler(void)
{
	if(CY_LPCOMP_CHANNEL0_INTR == Cy_LPComp_GetInterruptStatusMasked(LPCOMP))
	{
	    if(Cy_LPComp_GetCompare(LPCOMP, CY_LPCOMP_CHANNEL_0))
	    {
		flagLPComp = true;
	    }
	    else
	    {
		flagLPComp = false;
	    }
	    Cy_LPComp_ClearInterrupt(LPCOMP, CY_LPCOMP_CHANNEL0_INTR);
	    NVIC_ClearPendingIRQ(lpcomp_interrupt_IRQn);
	}
}
Крутимо потенциометром на позитивному вході компаратора, і спотерігаємо, що як напруга на позитивному вході перевищує напругу на негативному вході, то вихід компаратора встановлює високий рівень і навпаки.
Результат роботи

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



субота, 24 квітня 2021 р.

Встановлення ModusToolbox та перший проект на CY8CKIT-149 PSoC® 4100S Plus Prototyping Kit

Передмова

На фоні різкого здорожчання чипів STM32, та їх дефіциту, на часі розглянути якісь альтернативи. Звісно, тієї ж вагової категорії. Це має бути 32 розрядний MCU, та архітектури ARM. Пропоную до вашої уваги дошку розробника KIT Prototyping CY8CKIT-149 PSoC4 від компанії Cypress + Infineon. Розписувати характеристики і опис дублюючи документацію - не бачу сенсу. Можете самі перейти за наданими ланками і ознайомитись більш детально. 

CY8CKIT-149 PSoC® 4100S Plus Prototyping Kit


Мета цієї статті:

  • показати як встановити засіб розробки Eclipse ModusToolBox IDE
  • створення проекту "Hello, World" для ознайомлення з процесом розробки

Встановлення ModusToolbox

Перейдіть за ланкою, щоб завантажити ModusToolbox, та оберіть вашу операційну систему. ModusToolBox доступний для Windows, Linux, macOS. Ну звісно, це ж Eclips :)
Після завантаження, встановіть ModusToolBox. 

Встановлення без особливостей, достатньо:

  1. тиснути "Next"
  2. погодитись з ліцензійною угодою
  3. залишити шлях встановлення за замовчуванням, або вказати свій шлях (шлях не має містити кирилічні символи)
  4. обрати тип інсталяції "Default installation"
  5. кнопкою "Finish" завершити інсталяцію та запускаємо ModusToolBox
Завершення встановлення

Оберіть свій WorkSpaces, тека де будуть зберігатись ваші проекти, або залиште як є:

Обираємо шлях workspaces

Тиснемо кнопку "Launch" і перейдемо до створення першого проекту "Hello World" надрукуємо текстовий рядок в серіал порт та поблимаємо світлодіодами. А світлодіодів на цій платі розробника дуже багато :)

Створення нового проекту

Створення нового проекту
Створення нового проекту:
  1. На панелі швидкого доступу "Quick panel"
  2. Тиснемо "New Application". Запуститься утіліта "Project Creator"
  3. Обираємо свою дошку розробника "CY8CKIT-149" з чипом CY8C4147AZI-S475
  4. Тиснемо кнопку "Next"
Обираємо приклад з репозиторію

    5. Переконаємось чи вірний шлях
    6. Оберемо приклад "Hello World"
    7. Та назвемо його "PSoC4_Hello_World"
    8. Ознайомимось з описом прикладу
    9. Підсумуємок, що створили
    10. Тиснемо кнопку "Create"

Структура проекту

Коли проект створиться, то в "Workspaces" створяться дві теки "mtb_shared" з бібліотеками, які будуть доступні всім проектам в цьому "Workspaces" та тека, власне, з самим проектом. В нашому випадку це "PSoC4_Hello_World":

Структура проекту

  1. mtb_shared - тека з бібліотеками, які доступні будь якому проекту в цьому workspaces
  2. pdl (peripheral driver library) та hal (high abstraction level) бібліотеки
  3. PSoC4_Hello_World - тека з проектом який ми створили
  4. COMPONENT_CUSTOM_DESING_MODUS - тека яка стосується утіліти Device Manager. Про це в наступних статтях.
  5. Файл main.c - тут і будемо дивитись/писати код
  6. Панель швидкого доступу. Ознайомимиось з нею нижче.

Панель швидкого доступу

Для кожного проекту в "workspaces" викликається своя панель швидкого доступу. Де можна  швидко знайти всі необхідні речі для роботи над цим проектом:

Панель швидкого доступу

  1. Побудувати і очистити проект
  2. Запустити відладку або просто залити прошивку за допомоги Jlink або KitProg3
  3. Викликати утіліти, наприклад Library Manager, Device Configurator, тощо
  4. Та швидкий доступ до документації на саму дошку розробника, всіх бібліотек, що дуже зручно

Приклад Hello World

Подвійним кліком на файлі main.c відкриємо файл на редагування, та перш за все, пропоную побудувати проект, натиснувши в панелі швидкого доступу "Quick panel" -> "Build PSoC4_Hello_World Application". Це для того щоб переконатись що з проектом все гаразд і всі файли створились як слід, та щоб всі інклюди, змінні, функції, дефайни і таке інше проіндесувались. Як приклад зібрався без помилок, тиснемо на панелі швидкого доступу "PSoC4_Hello_World program (KitProg3_MiniProg4)". Прошивка заллється до мікроконтролеру і почне працювати. В серіал порт один раз надішлеться текстове повідомлення "Hello World", та почне блимати набортний світлодіод.

Сам файл main.c виглядає так. Він, приклад, побудований на бібліотеці PDL, більш низького рівня чим HAL бібліотека.

/******************************************************************************
* File Name: main.c
*
* Description: This is the source code for the PSoC 4 MCU Hello World Example
*              for ModusToolbox.
*
* Related Document: See README.md
*
*******************************************************************************
* (c) 2020, Cypress Semiconductor Corporation. All rights reserved.
*******************************************************************************
* This software, including source code, documentation and related materials
* ("Software"), is owned by Cypress Semiconductor Corporation or one of its
* subsidiaries ("Cypress") and is protected by and subject to worldwide patent
* protection (United States and foreign), United States copyright laws and
* international treaty provisions. Therefore, you may use this Software only
* as provided in the license agreement accompanying the software package from
* which you obtained this Software ("EULA").
*
* If no EULA applies, Cypress hereby grants you a personal, non-exclusive,
* non-transferable license to copy, modify, and compile the Software source
* code solely for use in connection with Cypress's integrated circuit products.
* Any reproduction, modification, translation, compilation, or representation
* of this Software except as specified above is prohibited without the express
* written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress
* reserves the right to make changes to the Software without notice. Cypress
* does not assume any liability arising out of the application or use of the
* Software or any product or circuit described in the Software. Cypress does
* not authorize its products for use in any products where a malfunction or
* failure of the Cypress product may reasonably be expected to result in
* significant property damage, injury or death ("High Risk Product"). By
* including Cypress's product in a High Risk Product, the manufacturer of such
* system or application assumes all risk of such use and in doing so agrees to
* indemnify Cypress against all liability.
*******************************************************************************/


/*******************************************************************************
 * Include header files
 ******************************************************************************/
#include "cy_pdl.h"
#include "cybsp.h"


/*******************************************************************************
* Macros
*******************************************************************************/
#define LED_DELAY_MS              (500u)
#define CY_ASSERT_FAILED          (0u)


/*******************************************************************************
* Function Name: main
********************************************************************************
* Summary:
*  System entrance point. This function performs
*  - initial setup of device
*  - configure the SCB block as UART interface
*  - prints out "Hello World" via UART interface
*  - Blinks an LED under firmware control at 1 Hz
*
* Parameters:
*  none
*
* Return:
*  int
*
*******************************************************************************/
int main(void)
{
    cy_rslt_t result;
    cy_stc_scb_uart_context_t CYBSP_UART_context;

    /* Initialize the device and board peripherals */
    result = cybsp_init();

    /* Board init failed. Stop program execution */
    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(CY_ASSERT_FAILED);
    }

    /* Configure and enable the UART peripheral */
    Cy_SCB_UART_Init(CYBSP_UART_HW, &CYBSP_UART_config, &CYBSP_UART_context);
    Cy_SCB_UART_Enable(CYBSP_UART_HW);

    /* Enable global interrupts */
    __enable_irq();

    /* Send a string over serial terminal */
        Cy_SCB_UART_PutString(CYBSP_UART_HW, "Hello world\r\n");

    for(;;)
    {
        /* Toggle the user LED state */
        Cy_GPIO_Inv(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN);

        /* Wait for 0.5 seconds */
        Cy_SysLib_Delay(LED_DELAY_MS);
    }
}

/* [] END OF FILE */

Пропоную ознайомитись з задіяними в цьому прикладі функціями та структурами. Як побудоване налаштування пінів і периферії. Спробувати внести в приклад свої зміни. Ознайомитись з результатами, своїх внесків в код, шляхом заливки оновленої прошивки.

Ще я підготував відео в підтримку до цієї статті, де покажу як встановити "ModusToolBox", створю пустий проект, та напишу більш цікавий приклад мікроконтролерного "Hello World", за допомоги, вже, HAL бібліотек.

Відео версія статті





пʼятниця, 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

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



середа, 16 грудня 2020 р.

Blynk: підключення до точки WiFi та серверу Blynk, очікування WiFi точки, перепідключення до WiFi та серверу Blynk, робота основного коду без блокування при очікування з'єднання для ESP32/ESP8266

Передмова

Помітив, що багато початківців, які хочуть створити для себе IoT пристрій на базі WiFi мікроконтролера ESP8266 або ESP32 з системою Blynk, стикаються з такою проблемою як:

  • не виконання коду, поки пристрій не підключиться до точки WiFi;
  • відсутность спроб відновити з'єднання у разі втрати зв'язку з мережею;
  • блокування коду при втраті зв'язку з сервером Blynk.
Як не вирішити ці проблеми пристрій і залишиться поробкою вихідного дня для спробувати. Якийсь путній і завершений пристрій створити з таким підходом неможливо. Бібліотека Blynk потужна і універсальна бібліотека, до якої входять і інші бібліотеки, такі як робота з таймерами, датою, керування підключеннями, і багато іншого. Але! Без власного, продуманого коду створити завершений пристрій, який можна вже і виводити на ринок, або хоча б, щоб він був зручний для власного користування, для дому, для сім'ї - не побудувати. Так що пропоную все ж таки попрацювати над власним кодом, який працює з мережею, та усуває недолік блокування власного коду у разі втрати з'єднання з сервером Blynk. Та досить балачок, давайте розпочнемо, бо насправді не все так складно.

Проблематика

Ми можемо взяти за основу будь який приклад Blynk і вже навколо нього "наростити" свій функціонал. Але це буде не зовсім вірно. Тому пишемо в першу чергу код, який буде, наш модуль WiFi, завжди тримати на зв'язку з точкою WiFi. Та при втраті підключення до Blynk зробити логіку, яка дозволить не блокувати основний код, та періодично намагатись під'єднатись до серверу Blynk.

Поширений приклад для підключення до WiFi нам пропонують в такому вигляді:

#include <ESP8266WiFi.h>

void setup()
{
  Serial.begin(115200);
  Serial.println();

  WiFi.begin("network-name", "pass-to-network");

  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {}

В цьому прикладі далі циклу while (WiFi.status() != WL_CONNECTED), поки не буде з'єднання з точкою WiFi, програма не виконається. Нам потрібно, щоб спроби приєднатись до точки WiFi були постійними, а код в циклі loop() виконувався та контролював стан з'єднання з точкою WiFi. 

А поширений приклад для підключення до Blynk нам пропонують в такому вигляді:

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "YourAuthToken";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "YourNetworkName";
char pass[] = "YourPassword";

void setup()
{
  // Debug console
  Serial.begin(9600);

  Blynk.begin(auth, ssid, pass);
  // You can also specify server:
  //Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 80);
  //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080);
}

void loop()
{
  Blynk.run();
  // You can inject your own code or combine it with other sketches.
  // Check other examples on how to communicate with Blynk. Remember
  // to avoid delay() function!
}

В цьому випадку, при відсутності WiFi ваш пристрій так і буде "мертвою залізякою", а у разі наявності WiFi і втрати зв'язку з сервером Blynk, ваш код блокуватиметься функцією Blynk.run() яка викликається з циклу loop() і користуватись пристроєм буде не можливо. 

Приклад рішення для ESP8266

Я пропоную такий варіант коду для підтримки зв'язку з точкою WiFi та сервером Blynk без блокування основного коду:

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "BlynkSimpleEsp8266.h"

const char* ssid        = "MY_SSID";
const char* password    = "MY_PASS";
const char* blynkToken  = "MY_TOKEN";

bool isWiFiConnected = false;
int  numTimerReconnect = 0;

WiFiEventHandler gotIpEventHandler;
WiFiEventHandler disconnectedEventHandler;
BlynkTimer timer;

void WiFiStationConnected(const WiFiEventStationModeGotIP& event);
void WiFiStationDisconnected(const WiFiEventStationModeDisconnected& event);
void ReconnectBlynk(void);
void BlynkRun(void);

void setup() 
{
  Serial.begin(115200);
  
  gotIpEventHandler = WiFi.onStationModeGotIP(&WiFiStationConnected);
  disconnectedEventHandler = WiFi.onStationModeDisconnected(&WiFiStationDisconnected);

  WiFi.begin(ssid, password);

  Blynk.config(blynkToken);

  if(Blynk.connect())
  {
    Serial.printf("[%8lu] setup: Blynk connected\r\n", millis());
  }
  else
  {
    Serial.printf("[%8lu] setup: Blynk no connected\r\n", millis());
  }
  
  Serial.printf("[%8lu] Setup: Start timer reconnected\r\n", millis());
  numTimerReconnect = timer.setInterval(60000, ReconnectBlynk);
}

void loop() 
{
  BlynkRun();  
  timer.run();
}

void WiFiStationConnected(const WiFiEventStationModeGotIP& event)
{
  isWiFiConnected = true;
  Serial.printf("[%8lu] Interrupt: Connected to AP, Ip: ", millis());
  Serial.println(WiFi.localIP());
}

void WiFiStationDisconnected(const WiFiEventStationModeDisconnected& event)
{
  isWiFiConnected = false;
  Serial.printf("[%8lu] Interrupt: Disconnected to AP!\r\n", millis());
}

void BlynkRun(void)
{
  if (isWiFiConnected)
  {
    if(Blynk.connected())
    {
      if (timer.isEnabled(numTimerReconnect))
      {
        timer.disable(numTimerReconnect);
        Serial.printf("[%8lu] BlynkRun: Stop timer reconnected\r\n", millis());
      }

      Blynk.run();
    }
    else
    {
      if (!timer.isEnabled(numTimerReconnect))
      {
        timer.enable(numTimerReconnect);
        Serial.printf("[%8lu] BlynkRun: Start timer reconnected\r\n", millis());
      }      
    }
  }
}

void ReconnectBlynk(void)
{
  if (!Blynk.connected())
  {
    if (Blynk.connect())
    {				        
      Serial.printf("[%8lu] ReconnectBlynk: Blynk reconnected\r\n", millis());
    }
    else
    {
      Serial.printf("[%8lu] ReconnectBlynk: Blynk not reconnected\r\n", millis());
    }
  }
  else
  {
    Serial.printf("[%8lu] ReconnectBlynk: Blynk connected\r\n", millis());
  }
}


Приклад рішення для ESP32

Приклад для ESP32 дещо відрізняється з попереднім для ESP8266, але логіка роботи залишається такою самою:

#include <Arduino.h>
#include <WiFi.h>
#include "BlynkSimpleEsp32.h"

const char* ssid        =  "MY_SSID";
const char* password    =  "MY_PASS";
const char* blynkToken  =  "MY_TOKEN";

bool isWiFiConnected = false;
int  numTimerReconnect = 0;

BlynkTimer timer;

void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info);
void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
void ReconnectBlynk(void);
void BlynkRun(void);

void setup() 
{
  Serial.begin(115200);

  WiFi.onEvent(WiFiStationConnected, SYSTEM_EVENT_STA_GOT_IP);
  WiFi.onEvent(WiFiStationDisconnected, SYSTEM_EVENT_STA_DISCONNECTED);
  WiFi.begin(ssid, password);

  Blynk.config(blynkToken);

  if(Blynk.connect())
  {
    Serial.printf("[%8lu] setup: Blynk connected\r\n", millis());
  }
  else
  {
    Serial.printf("[%8lu] setup: Blynk no connected\r\n", millis());
  }

  Serial.printf("[%8lu] Setup: Start timer reconnected\r\n", millis());
  numTimerReconnect = timer.setInterval(60000, ReconnectBlynk);
}

void loop() 
{
  BlynkRun();
  timer.run();
}

void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info)
{
  isWiFiConnected = true;
  Serial.printf("[%8lu] Interrupt: Connected to AP, IP: ", millis());
  Serial.println(WiFi.localIP());
}

void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info)
{
  isWiFiConnected = false;
  Serial.printf("[%8lu] Interrupt: Disconnected to AP!\r\n", millis());
}

void ReconnectBlynk(void)
{
  if (!Blynk.connected())
  {
    if (Blynk.connect())
    {				        
      Serial.printf("[%8lu] ReconnectBlynk: Blynk reconnected\r\n", millis());
    }
    else
    {
      Serial.printf("[%8lu] ReconnectBlynk: Blynk not reconnected\r\n", millis());
    }
  }
  else
  {
    Serial.printf("[%8lu] ReconnectBlynk: Blynk connected\r\n", millis());
  }
}

void BlynkRun(void)
{
  if (isWiFiConnected)
  {
    if(Blynk.connected())
    {
      if (timer.isEnabled(numTimerReconnect))
      {
        timer.disable(numTimerReconnect);
        Serial.printf("[%8lu] BlynkRun: Stop timer reconnected\r\n", millis());
      }

      Blynk.run();
    }
    else
    {
      if (!timer.isEnabled(numTimerReconnect))
      {
        timer.enable(numTimerReconnect);
        Serial.printf("[%8lu] BlynkRun: Start timer reconnected\r\n", millis());
      }      
    }
  }
}

Розумію, що для початківця це доволі складний код і потребує детальних пояснень. Тому  я підготував відео з поясненням і демонстрацією роботи з кнопкою, світлодіодом і таймером. А також демонстраційний код доступний і для завантаження і для ESP8266 , і для ESP32. Приємного перегляду.

Відео з поясненням і демонстрацією



середа, 25 листопада 2020 р.

Бібліотека драйвера MAX7219 в Arduino framework

 Передмова

Бібліотека для роботи з семисегментним-восьмирозрядним модулем з драйвером на мікросхемі max7219. Дозволяє друкувати окремі цифри-символи в окремих розрядах, друкувати цілі числа, цілі числа певної ширини, числа з плаваючою комою, засвічувати окремі сегменти. Бібліотека написана в межах Arduino framework, тому має працювати на будь якому чипові який підтримує Arduino framework (але це не точно).
Модуль 7 сегментного 8 розрядного дисплею на max7219

Приклад використання

Завантажуєте бібліотеку з GitHub репозиторію. Додаєте до свого проекту файли бібліотеки. Вставляєте до головного файлу вашого проекту цей приклад:

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

// 15 - GPIO15 (CS), 8 - number of digits, 5 - intensivity (brightness)
Max7219 max7219 = Max7219(15, 8, 5);

/* ESP32 SPI
  SCK   - GPIO_18
  MISO  - GPIO_21
  MOSI  - GPIO_23
*/

void setup() 
{
  SPI.begin(18, 21, 23);
  max7219.Begin();
}

void loop() 
{
  max7219.DecodeOn();
  max7219.SetIntensivity(5);
  
  max7219.PrintNtos(max7219.DIGIT_7, 1234, 6);
  delay(1000);
  max7219.Clean();

  max7219.PrintItos(max7219.DIGIT_4, 5678);
  delay(1000);
  max7219.Clean();

  max7219.PrintFtos(max7219.DIGIT_8, -45.678f, 2);
  delay(1000);
  max7219.Clean();

  for (uint8_t i = 0; i < 8; i++)
  {
    max7219.PrintDigit(i + 1, i + 1, false);
    delay(100);
  }

  for (uint8_t i = 0x0F; i > 0x00; i--)
  {
    max7219.SetIntensivity(i);
    delay(100);
  }
  
  for (uint8_t i = 0; i < 0x0F; i++)
  {
    max7219.SetIntensivity(i);
    delay(100);
  }
  
  for (uint8_t i = 8; i > 0 ; i--)
  {
    max7219.PrintItos(i, 87654321);
    delay(200);
    max7219.Clean();  
  }
  
  max7219.Clean();
  delay(1000);
}

Відео демострація



понеділок, 5 жовтня 2020 р.

Прошивка ESP32 власною firmware за допомоги Flash Download Tools

Передмова

Коли ви створили власну прошивку для ESP32, наприклад в Platformio, чи Arduino IDE. І вам потрібно передати бінарний код для прошивки пристроїв ESP32 іншим людям, не розголошуючи сирцевий код, або прошивати серію власних пристроїв. То IDE не підходить для цих цілей. Краще і правильно користуватись фірмовою утилітою "Flash Download Tools". Як це зробити, читаємо далі в статті.

Завантаження і встановлення

Щоб завантажити утиліту, перейдіть за цією ланкою

Завантажити FLASH DOWNLOAD TOOL

Теку з утилітою потрібно витягнути з архіву і розмістити в зручне для вас місце на диску ПК. Але зауважу, шлях до утиліти не має містити кириличних літер. Лише латиницею.

Запуск утиліти:

Запуск утиліти

Обирайте "Developer Mode":

Оберіть "Developer Mode"

Оберіть тип чипу, в мене ESP32:

Вибір чипу

Тепер з'явиться головне вікно прошивальщика:

Додаємо файли і налаштування

Треба додати 4 файли і призначити адреси розташування в пам'яті модуля:

  • 0x1000 bootloader_dio_40m.bin
  • 0x8000 partitions.bin
  • 0xe000 boot_app0.bin
  • 0x10000 firmware.bin
Крім власного бінарного файла прошивки, що ми зкомпілювали firmware.bin нам протрібні ще три файли:
  • bootloader file
  • partition table file
  • firmware/app file
І де їх взяти?

Arduino IDE

Підготовка файлів для прошивання в Arduino IDE:
Підготовка файлів
Після компіляції створіть окрему папку для всіх чотирьох файлів де вам зручно. Перший файл "firmware.bin" буде розташований в теці скетча. Можна перейти з меню Arduino IDE "Скетч -> Показати теку скетчів". Або перейти за шляхом: "C:\Users\{user}\Documents\Arduino\{назва_вашого_скетчу}. Щоб добути інші файли, в файловому провідникові, потрібно дозволити перегляд прихованих тек. І так, розташування всіх файлів:
C:\Users\"Користувач"\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\tools\partitions\boot_app0.bin
C:\Users\"Користувач"\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\tools\sdk\bin\bootloader_dio_40m.bin
C:\Users\"Користувач"\AppData\Local\Temp\arduino_build_491506м'я_вашого_скетчу.ino.bin
C:\Users\"Користувач"\AppData\Local\Temp\arduino_build_491506м'я_вашого_скетчу.ino.partitions.bin
Кладемо всі ці чотири файли до теки, яку вже заздалегідь приготували і вказуємо їх для "FLASH DOWNLOAD TOOL". Або архівуємо теку і відправляємо третій стороні для прошивання.

Адреси для цих файлів для "FLASH DOWNLOAD TOOL", як вже було зазначено вище, мають бути такі:
  • 0x1000 bootloader_dio_40m.bin
  • 0x8000 Ім'я_вашого_скетчу.ino.partitions.bin
  • 0xe000 boot_app0.bin
  • 0x10000 Ім'я_вашого_скетчу.ino.bin

Platformio

Спершу підготуємо потрібні файли. В терміналі вашого проекту виконайте цей рядок:
pio run -v -t upload

Підготовка файлів


Після роботи компілятора і прошивача створяться всі потрібні файли за таким шляхом:
0x1000 C:\Users\"Ім'я_користувача"\.platformio\packages\framework-arduinoespressif32\tools\sdk\bin\bootloader_dio_40m.bin
0xe000 C:\Users\"Ім'я_користувача"\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin
0x8000 .pio\build\esp32dev\partitions.bin
0x10000 .pio\build\esp32dev\firmware.bin 
В файловому провідникові, потрібно дозволити перегляд прихованих тек. Файл partitions.bin та firmware.bin знаходяться в прихованій теці .pio вашого поточного проекту.

Кладемо всі ці чотири файли до теки, яку вже заздалегідь приготували і вказуємо їх для "FLASH DOWNLOAD TOOL". Або архівуємо теку і відправляємо третій стороні для прошивання.

Адреси для цих файлів для "FLASH DOWNLOAD TOOL", як вже було зазначено вище, мають бути такі:
  • 0x1000 bootloader_dio_40m.bin
  • 0x8000 partitions.bin
  • 0xe000 boot_app0.bin
  • 0x10000 firmware.bin

VisualMicro for MS Visual Studio

Хто використовує розширення ARDUINO IDE FOR VISUAL STUDIO, то добути потрібні файли можна за такими шляхами, як шлях до вашого проекту і тека "Debug" або "Release", де будуть два файли "назва_проекту.bin" та "назва_проекту.partitions.bin", а ще два файли беруться там де і для Arduino IDE файли bootloader_dio_40m.bin та boot_app0.bin. Наприклад для проекту "blink1":
C:\Users\"Ім'я_користувача"\source\repos\Blink1\Blink1\Release\Blink1.ino.bin
C:\Users\"Ім'я_користувача"\source\repos\Blink1\Blink1\Release\Blink1.partitions.bin
C:\Users\"Ім'я_користувача"\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\tools\partitions\boot_app0.bin
C:\Users\"Ім'я_користувача"\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\tools\sdk\bin\bootloader_dio_40m.bin
Кладемо всі ці чотири файли до теки, яку вже заздалегідь приготували і вказуємо їх для "FLASH DOWNLOAD TOOL". Або архівуємо теку і відправляємо третій стороні для прошивання.

Адреси для цих файлів для "FLASH DOWNLOAD TOOL", як вже було зазначено вище, мають бути такі:
  • 0x1000 bootloader_dio_40m.bin
  • 0x8000 Ім'я_вашого_проекту.ino.partitions.bin
  • 0xe000 boot_app0.bin
  • 0x10000 Ім'я_вашого_проекту.ino.bin