tdse-tf_3-02

Controlador PID para derretidor de miel con control local via botones y remoto via WiFi
Index Commits Files Refs Submodules README
commit be246fae835fbc466411657718e46e3a1a981138
parent 7958154a958799fd3a9b44b1cae99ea260276b57
Author: Martin Kloeckner <mjkloeckner@gmail.com>
Date:   Sat, 28 Mar 2026 01:46:18 -0300

Usar DMA para manipular el display de manera asincrónica

Diffstat:
Mfirmware/Core/Inc/stm32f1xx_it.h | 3+++
Mfirmware/Core/Src/main.c | 4++++
Mfirmware/Core/Src/stm32f1xx_hal_msp.c | 31+++++++++++++++++++++++++++++++
Mfirmware/Core/Src/stm32f1xx_it.c | 44++++++++++++++++++++++++++++++++++++++++++++
Mfirmware/app/inc/display_i2c.h | 7+++++++
Mfirmware/app/src/display_i2c.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mfirmware/app/src/task_menu.c | 9+++++----
Mfirmware/app/src/task_menu_interface.c | 2++
Mfirmware/tdse-tf_3-02.ioc | 15++++++++++++++-
9 files changed, 269 insertions(+), 23 deletions(-)
diff --git a/firmware/Core/Inc/stm32f1xx_it.h b/firmware/Core/Inc/stm32f1xx_it.h
@@ -57,6 +57,9 @@ void PendSV_Handler(void);
 void SysTick_Handler(void);
 void DMA1_Channel4_IRQHandler(void);
 void DMA1_Channel5_IRQHandler(void);
+void DMA1_Channel6_IRQHandler(void);
+void I2C1_EV_IRQHandler(void);
+void I2C1_ER_IRQHandler(void);
 void USART1_IRQHandler(void);
 void EXTI15_10_IRQHandler(void);
 /* USER CODE BEGIN EFP */
diff --git a/firmware/Core/Src/main.c b/firmware/Core/Src/main.c
@@ -45,6 +45,7 @@
 
 /* Private variables ---------------------------------------------------------*/
 I2C_HandleTypeDef hi2c1;
+DMA_HandleTypeDef hdma_i2c1_tx;
 
 RTC_HandleTypeDef hrtc;
 
@@ -332,6 +333,9 @@ static void MX_DMA_Init(void)
   /* DMA1_Channel5_IRQn interrupt configuration */
   HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
   HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
+  /* DMA1_Channel6_IRQn interrupt configuration */
+  HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
+  HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
 
 }
 
diff --git a/firmware/Core/Src/stm32f1xx_hal_msp.c b/firmware/Core/Src/stm32f1xx_hal_msp.c
@@ -23,6 +23,8 @@
 /* USER CODE BEGIN Includes */
 
 /* USER CODE END Includes */
+extern DMA_HandleTypeDef hdma_i2c1_tx;
+
 extern DMA_HandleTypeDef hdma_usart1_rx;
 
 extern DMA_HandleTypeDef hdma_usart1_tx;
@@ -113,6 +115,29 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
 
     /* Peripheral clock enable */
     __HAL_RCC_I2C1_CLK_ENABLE();
+
+    /* I2C1 DMA Init */
+    /* I2C1_TX Init */
+    hdma_i2c1_tx.Instance = DMA1_Channel6;
+    hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
+    hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
+    hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;
+    hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
+    hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
+    hdma_i2c1_tx.Init.Mode = DMA_NORMAL;
+    hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_LOW;
+    if (HAL_DMA_Init(&hdma_i2c1_tx) != HAL_OK)
+    {
+      Error_Handler();
+    }
+
+    __HAL_LINKDMA(hi2c,hdmatx,hdma_i2c1_tx);
+
+    /* I2C1 interrupt Init */
+    HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0);
+    HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
+    HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 0);
+    HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
     /* USER CODE BEGIN I2C1_MspInit 1 */
 
     /* USER CODE END I2C1_MspInit 1 */
@@ -145,6 +170,12 @@ void HAL_I2C_MspDeInit(I2C_HandleTypeDef* hi2c)
 
     HAL_GPIO_DeInit(GPIOB, GPIO_PIN_9);
 
+    /* I2C1 DMA DeInit */
+    HAL_DMA_DeInit(hi2c->hdmatx);
+
+    /* I2C1 interrupt DeInit */
+    HAL_NVIC_DisableIRQ(I2C1_EV_IRQn);
+    HAL_NVIC_DisableIRQ(I2C1_ER_IRQn);
     /* USER CODE BEGIN I2C1_MspDeInit 1 */
 
     /* USER CODE END I2C1_MspDeInit 1 */
diff --git a/firmware/Core/Src/stm32f1xx_it.c b/firmware/Core/Src/stm32f1xx_it.c
@@ -55,6 +55,8 @@
 /* USER CODE END 0 */
 
 /* External variables --------------------------------------------------------*/
+extern DMA_HandleTypeDef hdma_i2c1_tx;
+extern I2C_HandleTypeDef hi2c1;
 extern DMA_HandleTypeDef hdma_usart1_rx;
 extern DMA_HandleTypeDef hdma_usart1_tx;
 extern UART_HandleTypeDef huart1;
@@ -231,6 +233,48 @@ void DMA1_Channel5_IRQHandler(void)
 }
 
 /**
+  * @brief This function handles DMA1 channel6 global interrupt.
+  */
+void DMA1_Channel6_IRQHandler(void)
+{
+  /* USER CODE BEGIN DMA1_Channel6_IRQn 0 */
+
+  /* USER CODE END DMA1_Channel6_IRQn 0 */
+  HAL_DMA_IRQHandler(&hdma_i2c1_tx);
+  /* USER CODE BEGIN DMA1_Channel6_IRQn 1 */
+
+  /* USER CODE END DMA1_Channel6_IRQn 1 */
+}
+
+/**
+  * @brief This function handles I2C1 event interrupt.
+  */
+void I2C1_EV_IRQHandler(void)
+{
+  /* USER CODE BEGIN I2C1_EV_IRQn 0 */
+
+  /* USER CODE END I2C1_EV_IRQn 0 */
+  HAL_I2C_EV_IRQHandler(&hi2c1);
+  /* USER CODE BEGIN I2C1_EV_IRQn 1 */
+
+  /* USER CODE END I2C1_EV_IRQn 1 */
+}
+
+/**
+  * @brief This function handles I2C1 error interrupt.
+  */
+void I2C1_ER_IRQHandler(void)
+{
+  /* USER CODE BEGIN I2C1_ER_IRQn 0 */
+
+  /* USER CODE END I2C1_ER_IRQn 0 */
+  HAL_I2C_ER_IRQHandler(&hi2c1);
+  /* USER CODE BEGIN I2C1_ER_IRQn 1 */
+
+  /* USER CODE END I2C1_ER_IRQn 1 */
+}
+
+/**
   * @brief This function handles USART1 global interrupt.
   */
 void USART1_IRQHandler(void)
diff --git a/firmware/app/inc/display_i2c.h b/firmware/app/inc/display_i2c.h
@@ -8,6 +8,7 @@
 #define _DISPLAY_I2C_H_
 
 #include <stdint.h>
+#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -18,6 +19,12 @@ void display_i2c_set_cursor_pos(uint8_t x, uint8_t y);
 void display_i2c_write_string(const char *str);
 void display_i2c_clear(void);
 
+void display_i2c_async_set_cursor_pos(uint8_t x, uint8_t y);
+void display_i2c_async_write_string(const char *str);
+void display_i2c_async_clear(void);
+void display_i2c_async_tick(void);
+bool display_i2c_async_done(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/firmware/app/src/display_i2c.c b/firmware/app/src/display_i2c.c
@@ -13,7 +13,10 @@
 #include "dwt.h"
 #include "systick.h"
 
-#define DISPLAY_I2C_ADDRESS 0x27
+#define DISPLAY_I2C_ADDRESS        0x27
+#define DISPLAY_ASYNC_QUEUE_LEN    256
+#define DISPLAY_ASYNC_CLEAR_DEL_MS 1ul
+#define DISPLAY_TICK_CNT_INI       0ul
 
 #define DISPLAY_CMD_CLEAR_DISPLAY   0b00000001
 #define DISPLAY_CMD_SET_CURSOR_HOME 0b00000010
@@ -39,15 +42,42 @@
 #define DISPLAY_CMD_DISPLAY_CONTROL_BLINK_ON    0b00000001
 #define DISPLAY_CMD_DISPLAY_CONTROL_BLINK_OFF   0b00000000
 
-#define DISPLAY_LINE1_FIRST_CHARACTER_ADDRESS 0
-#define DISPLAY_LINE2_FIRST_CHARACTER_ADDRESS 64
-#define DISPLAY_LINE3_FIRST_CHARACTER_ADDRESS 20
-#define DISPLAY_LINE4_FIRST_CHARACTER_ADDRESS 84
+#define DISPLAY_LINE1_FIRST_CHAR_ADDR 0
+#define DISPLAY_LINE2_FIRST_CHAR_ADDR 64
+#define DISPLAY_LINE3_FIRST_CHAR_ADDR 20
+#define DISPLAY_LINE4_FIRST_CHAR_ADDR 84
+
+// Private variables
+static bool b_display_i2c_dma_done;
+static uint32_t g_display_i2c_tick;
+static uint8_t async_buffer[4];
+
+typedef enum {
+    DISPLAY_CMD,
+    DISPLAY_CHAR,
+    DISPLAY_DELAY
+} display_data_type_e;
+
+typedef struct {
+    display_data_type_e type;
+    uint8_t data;
+} display_data;
+
+static struct {
+    uint8_t head;
+    uint8_t tail;
+    uint8_t count;
+    display_data queue[DISPLAY_ASYNC_QUEUE_LEN];
+} display_async_queue;
 
 // Private functions declaration
 void display_i2c_send_cmd(uint8_t cmd);
 void display_i2c_send_data(uint8_t data);
 
+void display_i2c_async_write(display_data data);
+void display_i2c_async_enqueue(display_data data);
+display_data display_i2c_async_dequeue(void);
+
 // Public functions definition
 void display_i2c_init(void)
 {
@@ -94,6 +124,13 @@ void display_i2c_init(void)
                          DISPLAY_CMD_DISPLAY_CONTROL_CURSOR_OFF |
                          DISPLAY_CMD_DISPLAY_CONTROL_BLINK_OFF);
     HAL_Delay(1);
+
+    b_display_i2c_dma_done = true;
+    g_display_i2c_tick = DISPLAY_TICK_CNT_INI;
+
+    display_async_queue.head = 0;
+    display_async_queue.tail = 0;
+    display_async_queue.count = 0;
 }
 
 void display_i2c_set_cursor_pos(uint8_t x, uint8_t y)
@@ -102,11 +139,11 @@ void display_i2c_set_cursor_pos(uint8_t x, uint8_t y)
     {
         case 0:
             display_i2c_send_cmd(DISPLAY_DDRAM_ADDR |
-                    (DISPLAY_LINE1_FIRST_CHARACTER_ADDRESS + x));
+                    (DISPLAY_LINE1_FIRST_CHAR_ADDR + x));
         break;
         case 1:
             display_i2c_send_cmd(DISPLAY_DDRAM_ADDR |
-                    (DISPLAY_LINE2_FIRST_CHARACTER_ADDRESS + x));
+                    (DISPLAY_LINE2_FIRST_CHAR_ADDR + x));
         break;
     }
 }
@@ -125,23 +162,72 @@ void display_i2c_write_string(const char *str)
     }
 }
 
+void display_i2c_async_set_cursor_pos(uint8_t x, uint8_t y)
+{
+    uint8_t cmd;
+
+    switch (y)
+    {
+        case 0:
+            cmd = (DISPLAY_DDRAM_ADDR | (DISPLAY_LINE1_FIRST_CHAR_ADDR + x));
+        break;
+        case 1:
+            cmd = (DISPLAY_DDRAM_ADDR | (DISPLAY_LINE2_FIRST_CHAR_ADDR + x));
+        break;
+        default: break;
+    }
+
+    display_i2c_async_enqueue((display_data){DISPLAY_CMD, cmd});
+}
+
+void display_i2c_async_clear(void)
+{
+    display_i2c_async_enqueue((display_data){DISPLAY_CMD, DISPLAY_CMD_CLEAR_DISPLAY});
+    display_i2c_async_enqueue((display_data){DISPLAY_DELAY, 0});
+}
+
+void display_i2c_async_write_string(const char *str)
+{
+    while (*str)
+    {
+        display_i2c_async_enqueue((display_data){DISPLAY_CHAR, *str++});
+    }
+}
+
+bool display_i2c_async_done(void)
+{
+    return b_display_i2c_dma_done;
+}
+
+void display_i2c_async_tick(void)
+{
+    if (0 == g_display_i2c_tick)
+    {
+        if ((display_async_queue.count != 0) && b_display_i2c_dma_done)
+        {
+            display_i2c_async_write(display_i2c_async_dequeue());
+        } 
+    }
+    else
+    {
+        g_display_i2c_tick--;
+    }
+}
+
 // Private functions definitions
 void display_i2c_send_cmd(uint8_t cmd)
 {
-    uint8_t data_t[4], data_u, data_l;
-
-    data_u = (cmd & 0xF0);           // extract upper 4 bits
-    data_l = ((cmd << 4) & 0xF0);    // extract lower 4 bits
+    uint8_t buffer[4], data_u, data_l;
 
-    // send upper 4 bits with enable pulse
-    data_t[0] = data_u | 0x0C;   // EN=1, RS=0  -> bxxxx1100
-    data_t[1] = data_u | 0x08;   // EN=0, RS=0  -> bxxxx1000
+    data_u = (cmd & 0xF0);
+    data_l = ((cmd << 4) & 0xF0);
 
-    // send lower 4 bits with enable pulse
-    data_t[2] = data_l | 0x0C;   // EN=1, RS=0  -> bxxxx1100
-    data_t[3] = data_l | 0x08;   // EN=0, RS=0  -> bxxxx1000
+    buffer[0] = data_u | 0x0C;
+    buffer[1] = data_u | 0x08;
+    buffer[2] = data_l | 0x0C;
+    buffer[3] = data_l | 0x08;
 
-    HAL_I2C_Master_Transmit(&hi2c1, DISPLAY_I2C_ADDRESS<<1, (uint8_t*)data_t, 4, 100);
+    HAL_I2C_Master_Transmit(&hi2c1, DISPLAY_I2C_ADDRESS<<1, (uint8_t*)buffer, 4, 100);
 }
 
 void display_i2c_send_data(uint8_t data)
@@ -159,3 +245,58 @@ void display_i2c_send_data(uint8_t data)
 
     HAL_I2C_Master_Transmit(&hi2c1, DISPLAY_I2C_ADDRESS<<1, (uint8_t *)data_t, 4, 100);
 }
+
+void display_i2c_async_write(display_data data)
+{
+    uint8_t data_u, data_l;
+    memset(async_buffer, 0, sizeof(async_buffer));
+
+    data_u = (data.data & 0xF0);
+    data_l = ((data.data << 4) & 0xF0);
+
+    switch (data.type)
+    {
+        case DISPLAY_CMD:
+            async_buffer[0] = data_u | 0x0C;
+            async_buffer[1] = data_u | 0x08;
+            async_buffer[2] = data_l | 0x0C;
+            async_buffer[3] = data_l | 0x08;
+        break;
+
+        case DISPLAY_CHAR:
+            async_buffer[0] = data_u | 0x0D;
+            async_buffer[1] = data_u | 0x09;
+            async_buffer[2] = data_l | 0x0D;
+            async_buffer[3] = data_l | 0x09;
+        break;
+
+        case DISPLAY_DELAY:
+            g_display_i2c_tick = DISPLAY_ASYNC_CLEAR_DEL_MS;
+        break;
+
+        default: break;
+    }
+
+    if (DISPLAY_DELAY != data.type)
+    {
+        HAL_I2C_Master_Transmit_DMA(&hi2c1, DISPLAY_I2C_ADDRESS<<1, async_buffer, 4);
+        b_display_i2c_dma_done = false;
+    }
+}
+
+void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
+{
+    b_display_i2c_dma_done = true;
+}
+
+void display_i2c_async_enqueue(display_data data)
+{
+    display_async_queue.count++;
+    display_async_queue.queue[display_async_queue.head++] = data;
+}
+
+display_data display_i2c_async_dequeue(void)
+{
+    display_async_queue.count--;
+    return display_async_queue.queue[display_async_queue.tail++];
+}
diff --git a/firmware/app/src/task_menu.c b/firmware/app/src/task_menu.c
@@ -81,6 +81,7 @@ void task_menu_update(void *parameters)
 
     while (b_time_update_required)
     {
+        display_i2c_async_tick();
         task_menu_statechart();
 
         __asm("CPSID i");
@@ -151,11 +152,11 @@ void task_menu_statechart(void)
             if (g_display_update)
             {
                 g_display_update = false;
-                display_i2c_clear();
+                display_i2c_async_clear();
                 memset(menu_row_str, '\0', sizeof(menu_row_str));
 
                 snprintf(menu_row_str, 20, "Idle");
-                display_i2c_write_string(menu_row_str);
+                display_i2c_async_write_string(menu_row_str);
             }
 
             if (true == p_task_menu_dta->event_flag)
@@ -187,14 +188,14 @@ void task_menu_statechart(void)
             if (g_display_update)
             {
                 g_display_update = false;
-                display_i2c_clear();
+                display_i2c_async_clear();
                 memset(menu_row_str, '\0', sizeof(menu_row_str));
 
                 HAL_RTC_GetTime(&hrtc, &s_time, RTC_FORMAT_BIN);
                 HAL_RTC_GetDate(&hrtc, &s_date, RTC_FORMAT_BIN);
                 sprintf(menu_row_str, "%02d:%02d:%02d",
                         s_time.Hours, s_time.Minutes, s_time.Seconds);
-                display_i2c_write_string(menu_row_str);
+                display_i2c_async_write_string(menu_row_str);
             }
 
             if (true == p_task_menu_dta->event_flag)
diff --git a/firmware/app/src/task_menu_interface.c b/firmware/app/src/task_menu_interface.c
@@ -40,7 +40,9 @@ void init_queue_event_task_menu(void)
     task_menu_event_queue.count = 0;
 
     for (i = 0; i < MAX_EVENTS; i++)
+    {
         task_menu_event_queue.queue[i] = EVENT_UNDEFINED;
+    }
 }
 
 void put_event_task_menu(task_menu_ev_t event)
diff --git a/firmware/tdse-tf_3-02.ioc b/firmware/tdse-tf_3-02.ioc
@@ -2,9 +2,19 @@
 CAD.formats=
 CAD.pinconfig=
 CAD.provider=
+Dma.I2C1_TX.2.Direction=DMA_MEMORY_TO_PERIPH
+Dma.I2C1_TX.2.Instance=DMA1_Channel6
+Dma.I2C1_TX.2.MemDataAlignment=DMA_MDATAALIGN_BYTE
+Dma.I2C1_TX.2.MemInc=DMA_MINC_ENABLE
+Dma.I2C1_TX.2.Mode=DMA_NORMAL
+Dma.I2C1_TX.2.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
+Dma.I2C1_TX.2.PeriphInc=DMA_PINC_DISABLE
+Dma.I2C1_TX.2.Priority=DMA_PRIORITY_LOW
+Dma.I2C1_TX.2.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
 Dma.Request0=USART1_RX
 Dma.Request1=USART1_TX
-Dma.RequestsNb=2
+Dma.Request2=I2C1_TX
+Dma.RequestsNb=3
 Dma.USART1_RX.0.Direction=DMA_PERIPH_TO_MEMORY
 Dma.USART1_RX.0.Instance=DMA1_Channel5
 Dma.USART1_RX.0.MemDataAlignment=DMA_MDATAALIGN_BYTE
@@ -68,10 +78,13 @@ MxDb.Version=DB.6.0.150
 NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
 NVIC.DMA1_Channel4_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true
 NVIC.DMA1_Channel5_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true
+NVIC.DMA1_Channel6_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true
 NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
 NVIC.EXTI15_10_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
 NVIC.ForceEnableDMAVector=true
 NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.I2C1_ER_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
+NVIC.I2C1_EV_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
 NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
 NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
 NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false