YASMEN GUMAMA
Published © GPL3+

RTOS Monitor: AHT20, LCD & RGB Control

"I used FreeRTOS to make the RT-Spark multitask: reading temp, updating the LCD, and controlling LEDs simultaneously. "

IntermediateFull instructions providedOver 8 days7
RTOS Monitor: AHT20, LCD & RGB Control

Things used in this project

Hardware components

STM32F7 Series
STMicroelectronics STM32F7 Series
×1
Rotary potentiometer (generic)
Rotary potentiometer (generic)
×1
RGB Diffused Common Cathode
RGB Diffused Common Cathode
×1
Resistor 220 ohm
Resistor 220 ohm
×3
Breadboard (generic)
Breadboard (generic)
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

STM32CUBEPROG
STMicroelectronics STM32CUBEPROG

Story

Read more

Code

main.c

C/C++
Main Code of the Project
/* USER CODE BEGIN Header */
/**
 * Laboratory Activity 5: RTOS on STM32 (RT-Spark)
 * YASMEN L. GUMAMA
 * ------------------------------------------------
 * - Uses FSMC Bank 3 (hsram3)
 * - FSMC Write Operation ENABLED
 * - Manual Override for PF2/PA7 Conflicts
 */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "drv_lcd.h"
#include "drv_aht21.h"
#include <stdlib.h>
#include <math.h>

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim3;
SRAM_HandleTypeDef hsram3; // Corrected to Bank 3

/* RTOS Handles */
osThreadId_t TempTaskHandle;
osThreadId_t RGBTaskHandle;
osThreadId_t CounterTaskHandle;
osThreadId_t BlinkTaskHandle;
osMutexId_t lcdMutexHandle;

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
static void MX_FSMC_Init(void);
static void MX_TIM1_Init(void);
static void MX_TIM3_Init(void);
void Kill_Onboard_Hardware(void); // Safety Function

/* Task Functions */
void StartTempTask(void *argument);
void StartRGBTask(void *argument);
void StartCounterTask(void *argument);
void StartBlinkTask(void *argument);

/* --- HELPER FUNCTIONS --- */
void LCD_DrawRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
    LCD_DrawPixel(x1, y1, color); LCD_DrawPixel(x2, y2, color);
    for (uint16_t x = x1; x <= x2; x++) { LCD_DrawPixel(x, y1, color); LCD_DrawPixel(x, y2, color); }
    for (uint16_t y = y1; y <= y2; y++) { LCD_DrawPixel(x1, y, color); LCD_DrawPixel(x2, y, color); }
}

void LCD_ShowNum(uint16_t x, uint16_t y, int num, uint16_t color, uint16_t back_color) {
    char buf[12]; int i = 0;
    if (num == 0) { LCD_ShowChar(x, y, '0', color, back_color); return; }
    if (num < 0) { LCD_ShowChar(x, y, '-', color, back_color); x += 8; num = -num; }
    while (num > 0) { buf[i++] = (num % 10) + '0'; num /= 10; }
    while (--i >= 0) { LCD_ShowChar(x, y, buf[i], color, back_color); x += 8; }
}

void LCD_ShowFloatManual(uint16_t x, uint16_t y, float val, uint16_t color, uint16_t back_color) {
    int intPart = (int)val;
    LCD_ShowNum(x, y, intPart, color, back_color);
    int offset = 8 + (intPart<0?8:0) + (abs(intPart)>9?8:0) + (abs(intPart)>99?8:0);
    LCD_ShowChar(x + offset, y, '.', color, back_color);
    int decPart = (int)((val - (float)intPart) * 100.0f);
    if (decPart < 0) decPart = -decPart;
    if (decPart < 10) { LCD_ShowChar(x+offset+8, y, '0', color, back_color); LCD_ShowNum(x+offset+16, y, decPart, color, back_color); }
    else { LCD_ShowNum(x+offset+8, y, decPart, color, back_color); }
}

#define FILTER_SIZE 10
float Filter_Value(float new_val, float *buffer, uint8_t *index, uint8_t *count) {
    if (new_val > 100.0f || new_val < -40.0f) {
        if (*count > 0) { float sum=0; for(int i=0; i<*count; i++) sum+=buffer[i]; return sum / *count; }
        return new_val;
    }
    buffer[*index] = new_val;
    *index = (*index + 1) % FILTER_SIZE;
    if (*count < FILTER_SIZE) (*count)++;
    float sum = 0; for (uint8_t i = 0; i < *count; i++) sum += buffer[i];
    return sum / (float)(*count);
}

/**
  * @brief  The application entry point.
  */
int main(void)
{
  HAL_Init();
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_FSMC_Init(); // Initializes hsram3
  MX_TIM1_Init();
  MX_TIM3_Init();

  /* 1. SAFETY: OVERRIDE ONBOARD CONFLICTS */
  Kill_Onboard_Hardware();
  HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
  /* 2. START PWM CHANNELS */
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // Red
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2); // Green
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3); // Blue

  /* 3. DRIVER INIT */
  LCD_Init();
  LCD_Clear(WHITE);
  LCD_DrawRect(5, 5, 235, 40, BLUE);
  LCD_ShowString(40, 15, "RT-Spark FreeRTOS", BLUE, WHITE);
  LCD_ShowString(20, 60, "Temp:", BLACK, WHITE);
  LCD_ShowString(20, 80, "Hum: ", BLACK, WHITE);
  LCD_ShowString(20, 120, "Counter:", BLACK, WHITE);
  LCD_ShowString(20, 160, "LED Bright:", BLACK, WHITE);
  AHT21_Init();

  /* 4. RTOS INIT */
  osKernelInitialize();

  const osMutexAttr_t lcdMutex_attr = { .name = "LCDMutex" };
  lcdMutexHandle = osMutexNew(&lcdMutex_attr);

  const osThreadAttr_t tempTask_attr = { .name = "TempTask", .stack_size = 1024, .priority = (osPriority_t) osPriorityNormal };
  TempTaskHandle = osThreadNew(StartTempTask, NULL, &tempTask_attr);

  const osThreadAttr_t rgbTask_attr = { .name = "RGBTask", .stack_size = 512, .priority = (osPriority_t) osPriorityNormal };
  RGBTaskHandle = osThreadNew(StartRGBTask, NULL, &rgbTask_attr);

  const osThreadAttr_t countTask_attr = { .name = "CountTask", .stack_size = 256, .priority = (osPriority_t) osPriorityNormal };
  CounterTaskHandle = osThreadNew(StartCounterTask, NULL, &countTask_attr);

  const osThreadAttr_t blinkTask_attr = { .name = "BlinkTask", .stack_size = 128, .priority = (osPriority_t) osPriorityLow };
  BlinkTaskHandle = osThreadNew(StartBlinkTask, NULL, &blinkTask_attr);

  osKernelStart();

  while (1) { }
}

/* --- HARDWARE INIT FUNCTIONS --- */

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}

static void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  HAL_ADC_Init(&hadc1);
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}

static void MX_TIM1_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 15;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 999;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  HAL_TIM_PWM_Init(&htim1);
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2);
  HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3);
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig);
  HAL_TIM_MspPostInit(&htim1);
}

static void MX_TIM3_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 15;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 999;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  HAL_TIM_PWM_Init(&htim3);
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
  HAL_TIM_MspPostInit(&htim3);
}

static void MX_FSMC_Init(void)
{
  FSMC_NORSRAM_TimingTypeDef Timing = {0};
  hsram3.Instance = FSMC_NORSRAM_DEVICE;
  hsram3.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
  hsram3.Init.NSBank = FSMC_NORSRAM_BANK3;
  hsram3.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
  hsram3.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
  hsram3.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
  hsram3.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
  hsram3.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
  hsram3.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
  hsram3.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;

  /* !!!! CORRECTED: WRITE ENABLED !!!! */
  hsram3.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;

  hsram3.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
  hsram3.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
  hsram3.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
  hsram3.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
  hsram3.Init.PageSize = FSMC_PAGE_SIZE_NONE;
  Timing.AddressSetupTime = 15;
  Timing.AddressHoldTime = 15;
  Timing.DataSetupTime = 255;
  Timing.BusTurnAroundDuration = 15;
  Timing.CLKDivision = 16;
  Timing.DataLatency = 17;
  Timing.AccessMode = FSMC_ACCESS_MODE_A;
  HAL_SRAM_Init(&hsram3, &Timing, NULL);
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /* 1. Configure Initial Output Levels */
  // LCD Reset (Active Low) - Start HIGH (Run)
  HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET);

  // LED_Y (Onboard LED) - Start LOW (Off)
  HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_RESET);

  // AHT21 Sensor Pins (I2C) - Start HIGH (Idle)
  HAL_GPIO_WritePin(GPIOE, AHT_SCL_Pin|AHT_SDA_Pin, GPIO_PIN_SET);

  /* 2. Configure Pin Modes */

  /* Configure LCD_RST (PD3) and LED_Y (PF11) as Output Push-Pull */
  GPIO_InitStruct.Pin = LCD_RST_Pin|LED_Y_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOF, &GPIO_InitStruct); // Note: Ensure LED_Y is on GPIOF, LCD_RST might be GPIOD

  /* Configure LCD_RST explicitly on PD3 if defined there */
  // Check your board: If LCD_RST is on PD3, use this block:
  GPIO_InitStruct.Pin = LCD_RST_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LCD_RST_GPIO_Port, &GPIO_InitStruct);

  /* Configure PA7 (Initially Output, but Safety function overrides later) */
  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* Configure AHT21 Pins (PE0=SCL, PE1=SDA) as Output Open-Drain */
  GPIO_InitStruct.Pin = AHT_SCL_Pin|AHT_SDA_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // Open Drain is required for I2C!
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}

void Kill_Onboard_Hardware(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    // Force PF2 (Matrix Power) LOW
    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_2, GPIO_PIN_RESET);
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

    // Force PF12 (Red LED) HIGH (OFF)
    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_12, GPIO_PIN_SET);
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

    // Force PA7 (Matrix Data) to Input/PullDown (Safety)
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

/* --- TASKS --- */
void StartRGBTask(void *argument) {
    uint32_t adc_val;
    uint32_t last_adc_val = 9999;
    int brightness_pct;
    uint32_t pwm_val;
    for(;;) {
        HAL_ADC_Start(&hadc1);
        if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) {
            adc_val = HAL_ADC_GetValue(&hadc1);
        }
        if (adc_val < 150) adc_val = 0;
        if (abs((int)adc_val - (int)last_adc_val) > 20) {
            last_adc_val = adc_val;
            pwm_val = (adc_val * 999) / 4095;
            brightness_pct = (adc_val * 100) / 4095;

            __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwm_val);
            __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, pwm_val);
            __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, pwm_val);

            if (osMutexAcquire(lcdMutexHandle, 100) == osOK) {
                LCD_ShowString(120, 160, "   ", WHITE, WHITE);
                LCD_ShowNum(120, 160, brightness_pct, BLACK, WHITE);
                LCD_ShowChar(150, 160, '%', BLACK, WHITE);
                osMutexRelease(lcdMutexHandle);
            }
        }
        osDelay(50);
    }
}

void StartBlinkTask(void *argument) {
    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_12, GPIO_PIN_SET);
    for(;;) {
        HAL_GPIO_TogglePin(LED_Y_GPIO_Port, LED_Y_Pin);
        osDelay(200);
    }
}

void StartTempTask(void *argument) {
    float raw_temp=0, raw_hum=0, final_temp=0, final_hum=0;
    float temp_buf[FILTER_SIZE]={0}, hum_buf[FILTER_SIZE]={0};
    uint8_t t_idx=0, h_idx=0, t_cnt=0, h_cnt=0;
    for(;;) {
        if (AHT21_Read(&raw_temp, &raw_hum) == 1) {
            final_temp = Filter_Value(raw_temp, temp_buf, &t_idx, &t_cnt);
            final_hum = Filter_Value(raw_hum, hum_buf, &h_idx, &h_cnt);
            if (osMutexAcquire(lcdMutexHandle, 500) == osOK) {
                LCD_ShowString(70, 60, "      ", WHITE, WHITE);
                LCD_ShowFloatManual(70, 60, final_temp, RED, WHITE);
                LCD_ShowChar(130, 60, 'C', RED, WHITE);
                LCD_ShowString(70, 80, "      ", WHITE, WHITE);
                LCD_ShowFloatManual(70, 80, final_hum, BLUE, WHITE);
                LCD_ShowChar(130, 80, '%', BLUE, WHITE);
                osMutexRelease(lcdMutexHandle);
            }
        }
        osDelay(1000);
    }
}

void StartCounterTask(void *argument) {
    int counter = 0;
    for(;;) {
        counter++;
        if (counter > 100) counter = 0;
        if (osMutexAcquire(lcdMutexHandle, 500) == osOK) {
            LCD_ShowString(100, 120, "   ", WHITE, WHITE);
            LCD_ShowNum(100, 120, counter, BLACK, WHITE);
            osMutexRelease(lcdMutexHandle);
        }
        osDelay(500);
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim->Instance == TIM6) { HAL_IncTick(); }
}

void Error_Handler(void)
{
  __disable_irq();
  while (1) { }
}

Credits

YASMEN GUMAMA
1 project • 0 followers

Comments