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:
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