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
firmware-nucleo/App/Src/display_i2c.c (9488B)
   1 /*
   2  * Copyright (c) 2026 Martin Javier Klöckner <mklockner@fi.uba.ar>
   3  *
   4  * See file `LICENSE` for full details
   5  */
   6 
   7 #include "main.h"
   8 #include "display_i2c.h"
   9 #include <stdbool.h>
  10 
  11 // Demo includes
  12 #include "logger.h"
  13 #include "dwt.h"
  14 #include "systick.h"
  15 
  16 #define DISPLAY_I2C_ADDRESS         0x27
  17 #define DISPLAY_ASYNC_QUEUE_LEN     256
  18 #define DISPLAY_ASYNC_CLEAR_DEL_MS  2ul
  19 #define DISPLAY_TICK_CNT_INI_MS     0ul
  20 #define DISPLAY_TICK_DMA_TIMEOUT_MS 100ul
  21 
  22 #define DISPLAY_INIT_SET_8BIT 0x03
  23 #define DISPLAY_INIT_SET_4BIT   0x02
  24 
  25 #define DISPLAY_CMD_CLEAR_DISPLAY   0x01
  26 #define DISPLAY_CMD_SET_CURSOR_HOME 0x02
  27 #define DISPLAY_CMD_ENTRY_MODE_SET  0x04
  28 #define DISPLAY_CMD_DISPLAY_CONTROL 0x08
  29 #define DISPLAY_CMD_SHIFT_CONTROL   0x10
  30 #define DISPLAY_CMD_FUNCTION_SET    0x20
  31 #define DISPLAY_CGRAM_ADDR          0x40
  32 #define DISPLAY_DDRAM_ADDR          0x80
  33 
  34 #define DISPLAY_CMD_FUNCTION_SET_8BITS    0x10
  35 #define DISPLAY_CMD_FUNCTION_SET_4BITS    0x00
  36 #define DISPLAY_CMD_FUNCTION_SET_5x8DOTS  0x00
  37 #define DISPLAY_CMD_FUNCTION_SET_2LINES   0x08
  38 #define DISPLAY_CMD_FUNCTION_SET_1LINE    0x00
  39 #define DISPLAY_CMD_FUNCTION_SET_5x10DOTS 0x04
  40 
  41 #define DISPLAY_CMD_ENTRY_MODE_SET_INCREMENT_CURSOR 0x02
  42 #define DISPLAY_CMD_ENTRY_MODE_SET_DECREMENT_CURSOR 0x00
  43 #define DISPLAY_CMD_ENTRY_MODE_SET_SHIFT            0x01
  44 #define DISPLAY_CMD_ENTRY_MODE_SET_NO_SHIFT         0x00
  45 
  46 #define DISPLAY_CMD_DISPLAY_CONTROL_DISPLAY_ON   0x04
  47 #define DISPLAY_CMD_DISPLAY_CONTROL_DISPLAY_OFF  0x00
  48 #define DISPLAY_CMD_DISPLAY_CONTROL_CURSOR_ON    0x02
  49 #define DISPLAY_CMD_DISPLAY_CONTROL_CURSOR_OFF   0x00
  50 #define DISPLAY_CMD_DISPLAY_CONTROL_BLINK_ON     0x01
  51 #define DISPLAY_CMD_DISPLAY_CONTROL_BLINK_OFF    0x00
  52 
  53 #define DISPLAY_LINE1_FIRST_CHAR_ADDR 0
  54 #define DISPLAY_LINE2_FIRST_CHAR_ADDR 64
  55 #define DISPLAY_LINE3_FIRST_CHAR_ADDR 20
  56 #define DISPLAY_LINE4_FIRST_CHAR_ADDR 84
  57 
  58 // Private variables
  59 typedef enum {
  60     DISPLAY_CMD,
  61     DISPLAY_CHAR,
  62     DISPLAY_DELAY
  63 } display_data_type_e;
  64 
  65 typedef struct {
  66     display_data_type_e type;
  67     uint8_t data;
  68 } display_data_t;
  69 
  70 typedef struct {
  71     uint8_t head;
  72     uint8_t tail;
  73     uint8_t count;
  74     display_data_t queue[DISPLAY_ASYNC_QUEUE_LEN];
  75 } display_async_queue_t;
  76 
  77 static bool b_display_i2c_dma_done;
  78 static uint32_t g_display_i2c_tick, g_display_i2c_dma_tick;
  79 static uint8_t async_buffer[4];
  80 static display_async_queue_t display_async_queue;
  81 static display_data_t curr_tx_data;
  82 
  83 // Private functions declaration
  84 static void display_i2c_send_cmd(uint8_t cmd);
  85 static void display_i2c_send_data(uint8_t data);
  86 static void display_i2c_async_write(display_data_t data);
  87 static void display_i2c_async_enqueue(display_data_t data);
  88 static display_data_t display_i2c_async_dequeue(void);
  89 void display_i2c_recover(void);
  90 uint8_t display_i2c_is_stuck(void);
  91 
  92 // Public functions definition
  93 void display_i2c_init(void)
  94 {
  95     HAL_Delay(50);
  96 
  97     display_i2c_send_cmd(DISPLAY_INIT_SET_8BIT);
  98     HAL_Delay(5);
  99 
 100     display_i2c_send_cmd(DISPLAY_INIT_SET_8BIT);
 101     HAL_Delay(1);
 102 
 103     display_i2c_send_cmd(DISPLAY_INIT_SET_8BIT);
 104     HAL_Delay(1);
 105 
 106     display_i2c_send_cmd(DISPLAY_INIT_SET_4BIT);
 107     HAL_Delay(1);
 108 
 109     display_i2c_send_cmd(DISPLAY_CMD_FUNCTION_SET |
 110                          DISPLAY_CMD_FUNCTION_SET_4BITS |
 111                          DISPLAY_CMD_FUNCTION_SET_2LINES |
 112                          DISPLAY_CMD_FUNCTION_SET_5x8DOTS);
 113     HAL_Delay(1);
 114 
 115     display_i2c_send_cmd(DISPLAY_CMD_DISPLAY_CONTROL |
 116                          DISPLAY_CMD_DISPLAY_CONTROL_DISPLAY_OFF);
 117     HAL_Delay(1);
 118 
 119     display_i2c_send_cmd(DISPLAY_CMD_CLEAR_DISPLAY);
 120     HAL_Delay(1);
 121 
 122     display_i2c_send_cmd(DISPLAY_CMD_ENTRY_MODE_SET |
 123                          DISPLAY_CMD_ENTRY_MODE_SET_INCREMENT_CURSOR |
 124                          DISPLAY_CMD_ENTRY_MODE_SET_NO_SHIFT);
 125     HAL_Delay(1);
 126 
 127     display_i2c_send_cmd(DISPLAY_CMD_DISPLAY_CONTROL |
 128                          DISPLAY_CMD_DISPLAY_CONTROL_DISPLAY_ON |
 129                          DISPLAY_CMD_DISPLAY_CONTROL_CURSOR_OFF |
 130                          DISPLAY_CMD_DISPLAY_CONTROL_BLINK_OFF);
 131     HAL_Delay(1);
 132 
 133     b_display_i2c_dma_done = true;
 134     g_display_i2c_tick = DISPLAY_TICK_CNT_INI_MS;
 135     g_display_i2c_dma_tick = DISPLAY_TICK_CNT_INI_MS;
 136 
 137     display_async_queue.head = 0;
 138     display_async_queue.tail = 0;
 139     display_async_queue.count = 0;
 140 }
 141 
 142 void display_i2c_set_cursor_pos(uint8_t x, uint8_t y)
 143 {
 144     switch (y)
 145     {
 146         case 0:
 147             display_i2c_send_cmd(DISPLAY_DDRAM_ADDR |
 148                     (DISPLAY_LINE1_FIRST_CHAR_ADDR + x));
 149         break;
 150         case 1:
 151             display_i2c_send_cmd(DISPLAY_DDRAM_ADDR |
 152                     (DISPLAY_LINE2_FIRST_CHAR_ADDR + x));
 153         break;
 154     }
 155 }
 156 
 157 void display_i2c_clear(void)
 158 {
 159     display_i2c_send_cmd(DISPLAY_CMD_CLEAR_DISPLAY);
 160     HAL_Delay(1);
 161 }
 162 
 163 void display_i2c_write_string(const char *str)
 164 {
 165     while (*str)
 166     {
 167         display_i2c_send_data(*str++);
 168     }
 169 }
 170 
 171 void display_i2c_async_set_cursor_pos(uint8_t x, uint8_t y)
 172 {
 173     uint8_t cmd;
 174 
 175     switch (y)
 176     {
 177         case 0:
 178             cmd = (DISPLAY_DDRAM_ADDR | (DISPLAY_LINE1_FIRST_CHAR_ADDR + x));
 179         break;
 180         case 1:
 181             cmd = (DISPLAY_DDRAM_ADDR | (DISPLAY_LINE2_FIRST_CHAR_ADDR + x));
 182         break;
 183         default: break;
 184     }
 185 
 186     display_i2c_async_enqueue((display_data_t){DISPLAY_CMD, cmd});
 187 }
 188 
 189 void display_i2c_async_clear(void)
 190 {
 191     display_i2c_async_enqueue((display_data_t){DISPLAY_CMD, DISPLAY_CMD_CLEAR_DISPLAY});
 192     display_i2c_async_enqueue((display_data_t){DISPLAY_DELAY, 0});
 193 }
 194 
 195 void display_i2c_async_write_string(const char *str)
 196 {
 197     while (*str)
 198     {
 199         display_i2c_async_enqueue((display_data_t){DISPLAY_CHAR, *str++});
 200     }
 201 }
 202 
 203 bool display_i2c_async_done(void)
 204 {
 205     return b_display_i2c_dma_done;
 206 }
 207 
 208 void display_i2c_async_tick(void)
 209 {
 210     if (0 < g_display_i2c_tick)
 211     {
 212         g_display_i2c_tick--;
 213     }
 214     else
 215     {
 216         if ((display_async_queue.count != 0) && b_display_i2c_dma_done)
 217         {
 218             curr_tx_data = display_i2c_async_dequeue();
 219             display_i2c_async_write(curr_tx_data);
 220         }
 221     }
 222 
 223     if (false == b_display_i2c_dma_done)
 224     {
 225         if (0 < g_display_i2c_dma_tick)
 226         {
 227             g_display_i2c_dma_tick--;
 228         }
 229         else
 230         {
 231             // DMA timeout. Assume error. Try to recover i2c peripheral and
 232             // send again last sent byte.
 233             //
 234             // TODO: report the error in some way if there are repeated losses.
 235 
 236             if (display_i2c_is_stuck())
 237             {
 238                 display_i2c_recover();
 239             }
 240 
 241             display_i2c_async_write(curr_tx_data);
 242         }
 243     }
 244 }
 245 
 246 // Private functions definitions
 247 static void display_i2c_send_cmd(uint8_t cmd)
 248 {
 249     uint8_t buffer[4], data_u, data_l;
 250 
 251     data_u = (cmd & 0xF0);
 252     data_l = ((cmd << 4) & 0xF0);
 253 
 254     buffer[0] = data_u | 0x0C;
 255     buffer[1] = data_u | 0x08;
 256     buffer[2] = data_l | 0x0C;
 257     buffer[3] = data_l | 0x08;
 258 
 259     HAL_I2C_Master_Transmit(&hi2c1, DISPLAY_I2C_ADDRESS<<1, buffer, 4, 100);
 260 }
 261 
 262 static void display_i2c_send_data(uint8_t data)
 263 {
 264     uint8_t data_u, data_l;
 265     uint8_t data_t[4];
 266 
 267     data_u = (data & 0xF0);
 268     data_l = ((data << 4) & 0XF0);
 269 
 270     data_t[0] = data_u | 0x0D;
 271     data_t[1] = data_u | 0x09;
 272     data_t[2] = data_l | 0x0D;
 273     data_t[3] = data_l | 0x09;
 274 
 275     HAL_I2C_Master_Transmit(&hi2c1, DISPLAY_I2C_ADDRESS<<1, (uint8_t *)data_t, 4, 100);
 276 }
 277 
 278 static void display_i2c_async_write(display_data_t data)
 279 {
 280     uint8_t data_u, data_l;
 281     memset(async_buffer, 0, sizeof(async_buffer));
 282 
 283     data_u = (data.data & 0xF0);
 284     data_l = ((data.data << 4) & 0xF0);
 285 
 286     switch (data.type)
 287     {
 288         case DISPLAY_CMD:
 289             async_buffer[0] = data_u | 0x0C;
 290             async_buffer[1] = data_u | 0x08;
 291             async_buffer[2] = data_l | 0x0C;
 292             async_buffer[3] = data_l | 0x08;
 293         break;
 294 
 295         case DISPLAY_CHAR:
 296             async_buffer[0] = data_u | 0x0D;
 297             async_buffer[1] = data_u | 0x09;
 298             async_buffer[2] = data_l | 0x0D;
 299             async_buffer[3] = data_l | 0x09;
 300         break;
 301 
 302         case DISPLAY_DELAY:
 303             g_display_i2c_tick = DISPLAY_ASYNC_CLEAR_DEL_MS;
 304         break;
 305 
 306         default: break;
 307     }
 308 
 309     if (DISPLAY_DELAY != data.type)
 310     {
 311         HAL_I2C_Master_Transmit_DMA(&hi2c1, DISPLAY_I2C_ADDRESS<<1, async_buffer, 4);
 312         b_display_i2c_dma_done = false;
 313         g_display_i2c_dma_tick = DISPLAY_TICK_DMA_TIMEOUT_MS;
 314     }
 315 }
 316 
 317 void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
 318 {
 319     b_display_i2c_dma_done = true;
 320 }
 321 
 322 static void display_i2c_async_enqueue(display_data_t data)
 323 {
 324     display_async_queue.count++;
 325     display_async_queue.queue[display_async_queue.head++] = data;
 326 }
 327 
 328 static display_data_t display_i2c_async_dequeue(void)
 329 {
 330     display_async_queue.count--;
 331     return display_async_queue.queue[display_async_queue.tail++];
 332 }
 333 
 334 uint8_t display_i2c_is_stuck(void)
 335 {
 336     if ((hi2c1.Instance->SR1 & I2C_SR1_AF) ||
 337         (hi2c1.Instance->SR2 & I2C_SR2_BUSY) ||
 338         (DMA1_Channel6->CCR & DMA_CCR_EN) ||
 339         (hi2c1.State != HAL_I2C_STATE_READY))
 340     {
 341         return 1;
 342     }
 343 
 344     return 0;
 345 }
 346 
 347 
 348 void display_i2c_recover(void)
 349 {
 350     HAL_DMA_Abort(hi2c1.hdmatx);
 351 
 352     HAL_I2C_DeInit(&hi2c1);
 353 
 354     __HAL_RCC_I2C1_FORCE_RESET();
 355     __HAL_RCC_I2C1_RELEASE_RESET();
 356 
 357     __HAL_UNLOCK(&hi2c1);
 358     hi2c1.State = HAL_I2C_STATE_READY;
 359 
 360     systick_delay_us(200);
 361 
 362     HAL_I2C_Init(&hi2c1);
 363 }