Inicie la pantalla en STM32 a través de LTDC ... en los registros

Saludos! Recientemente, un proyecto necesitaba lanzar una pantalla que tuviera una interfaz LVDS. Para implementar la tarea, se seleccionó el controlador STM32F746, porque Ya he trabajado mucho con él y tiene el módulo LTDC, que le permite trabajar directamente con la pantalla sin un controlador. En este caso, el controlador ya está implementado dentro del microcontrolador. Además, el último argumento fue que había una depuración de STM32F746-Disco en esta piedra, que tenía a mano, lo que significa que podía comenzar a trabajar en el proyecto sin esperar a que la placa, los componentes, etc.

Hoy les diré cómo ejecutar el módulo LTDC, trabajando con registros (CMSIS). A HAL y otras bibliotecas no les gusta y no usan por razones religiosas, pero esto también es de interés. Verá que generar periféricos complejos en registros es tan simple como SPI normal. Interesante? Entonces vamos!



1. Un poco sobre LTDC


Este módulo periférico es esencialmente un controlador, que generalmente se encuentra al costado de la pantalla, por ejemplo, SSD1963 y similares. Si observamos la estructura de LTDC, veremos que físicamente es un bus paralelo de 24 bits + acelerador de gráficos de hardware + matriz de datos en RAM, que de hecho es un búfer de pantalla (buffer de cuadro).



En la salida, tenemos el bus paralelo habitual, que contiene 24 bits de color (8 bits por color del modelo RGB), líneas de sincronización, una línea de encendido / apagado de pantalla y reloj de píxeles. La última, de hecho, es una señal de reloj mediante la cual los píxeles se cargan en la pantalla, es decir, si tenemos una frecuencia de 9,5 MHz, en 1 segundo podemos cargar 9,5 millones de píxeles. Esto en teoría, por supuesto, en la práctica, los números son algo más modestos debido a los tiempos y otras cosas.

Para una introducción más detallada a LTDC, le aconsejo que lea algunos documentos:

  1. Una visión general de las capacidades de LTDC en F4, en nuestro F7 todo es igual
  2. Nota de aplicación 4861. "Controlador de pantalla LCD-TFT (LTDC) en MCU STM32"

2. ¿Qué necesitamos hacer?


Los microcontroladores ST han ganado popularidad por una buena razón, el requisito más importante para cualquier componente electrónico es la documentación, y todo está bien. El sitio es ciertamente terrible, pero dejaré enlaces a toda la documentación. El fabricante nos salva del tormento y la invención de la bicicleta, por lo tanto, en la página 520 en el manual de referencia RM0385, se dan pasos en blanco y negro, lo que debemos hacer:



De hecho, no tiene que hacer la mitad de lo descrito: no es necesario iniciarlo o ya está configurado de forma predeterminada. Para el inicio mínimo, que nos permite dibujar píxeles, mostrar imágenes, gráficos, texto, etc., es suficiente hacer lo siguiente:

  • Habilitar el marcado LTDC
  • Configure el sistema de reloj y la frecuencia de salida de datos (reloj de píxeles)
  • Configure los puertos de E / S (GPIO) para trabajar con LTDC
  • Configurar horarios para nuestro modelo de pantalla
  • Ajusta la polaridad de las señales. Ya hecho por defecto
  • Especifique el color de fondo de la pantalla. Todavía no lo veremos, puedes dejarlo "a cero
  • Establecer el tamaño real del área visible de la pantalla para una capa específica
  • Seleccione el formato de color: ARGB8888, RGB 888, RGB565, etc.
  • Especifique la dirección de la matriz que actuará como un búfer de trama
  • Indique la cantidad de datos en una línea (longitud en ancho)
  • Indique el número de líneas (altura de visualización)
  • Incluya la capa con la que estamos trabajando
  • Habilitar módulo LTDC

Miedo Y tenía miedo, pero resultó funcionar durante 20 minutos con todos los procedimientos. Hay una tarea, el plan está planeado y solo queda cumplirlo.

3. Configuración del sistema de reloj


El primer elemento que necesitamos para enviar una señal de reloj al módulo LTDC, esto se hace escribiendo en el registro RCC:

RCC->APB2ENR |= RCC_APB2ENR_LTDCEN; 

A continuación, debe configurar la frecuencia de reloj del cuarzo externo (HSE) a una frecuencia de 216 MHz, es decir, al máximo. El primer paso es encender la fuente del reloj desde el resonador de cuarzo y esperar la bandera de listo:

 RCC->CR |= RCC_CR_HSEON; while (!(RCC->CR & RCC_CR_HSERDY)); 

Ahora configure el retraso para la memoria flash del controlador, como ella no sabe trabajar en la frecuencia central. Su valor, como el resto de los datos, se toma del manual de referencia:

 FLASH->ACR |= FLASH_ACR_LATENCY_5WS; 

Ahora, para obtener la frecuencia deseada, dividiré 25 MHz de la entrada a 25 y obtendré 1 MHz. Luego, solo en PLL multiplico por 432, porque en el futuro hay un divisor de frecuencia con un valor mínimo de / 2 y debe aplicarle el doble de frecuencia. Después de eso, conectamos la entrada PLL a nuestro resonador de cuarzo (HSE):

 RCC->PLLCFGR |= RCC_PLLCFGR_PLLM_0 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLM_4; RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_4 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_8; RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC; 

Ahora habilite PLL y espere la bandera de listo:

 RCC->CR |= RCC_CR_PLLON; while((RCC->CR & RCC_CR_PLLRDY) == 0){} 

Asignamos la salida de nuestro PLL como la fuente de la frecuencia del sistema y esperamos la bandera de listo:

 RCC->CFGR |= RCC_CFGR_SW_PLL; while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_1) {} 

Esto finaliza la configuración general del reloj y pasamos a configurar la frecuencia del reloj (PLLSAI) para nuestra pantalla (reloj de píxeles). La señal para PLLSAI según la hoja de datos se toma después del divisor / 25, es decir, en la entrada tenemos 1 MHz. Necesitamos obtener una frecuencia de aproximadamente 9.5 MHz, para esto multiplicamos la frecuencia de 1 MHz por 192, y luego usando dos divisores por 5 y 4 obtenemos el valor deseado PLLSAI = 1 MHz * 192/5/4 = 9.6 MHz:

 RCC->PLLSAICFGR |= RCC_PLLSAICFGR_PLLSAIN_6 | RCC_PLLSAICFGR_PLLSAIN_7; RCC->PLLSAICFGR |= RCC_PLLSAICFGR_PLLSAIR_0 | RCC_PLLSAICFGR_PLLSAIR_2; RCC->DCKCFGR1 |= RCC_DCKCFGR1_PLLSAIDIVR_0; RCC->DCKCFGR1 &= ~RCC_DCKCFGR1_PLLSAIDIVR_1; 

Como paso final, habilitamos PLLSAI para la pantalla y esperamos el indicador de listo para trabajar:

 RCC->CR |= RCC_CR_PLLSAION; while ((RCC->CR & RCC_CR_PLLSAIRDY) == 0) {} 

Esto completa la configuración básica del sistema de reloj, para no olvidar y no sufrir, habilitemos la sincronización en todos los puertos de entrada / salida (GPIO). No tenemos batería, al menos para la depuración, por lo que no guardamos:

 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOJEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOKEN; 

4. Configuración de puertos de E / S (GPIO)


Configurar gpio es muy simple: tenemos todas las patas del bus LTDC para configurar como salida alternativa y a una frecuencia alta. Para hacer esto, en el manual de referencia en la página 201 tenemos este consejo:



La tabla indica qué bits en los registros debe establecer para obtener la configuración necesaria. Vale la pena señalar que todas las llaves están deshabilitadas. ¿Dónde buscar qué función alternativa incluir? Y para esto, vaya a la página 76 en la hoja de datos de nuestro controlador y mire la siguiente tabla:



Como puede ver, la lógica de la tabla es la más simple: encontramos la función que necesitamos, en nuestro caso LTDC B0, luego miramos en qué GPIO está (PE4, por ejemplo) y en la parte superior vemos el número de la función alternativa que usaremos para configurar (AF14 aquí). Para configurar nuestra salida como una salida push-pull con una función alternativa, LTDC B0, necesitamos escribir el siguiente código:

 GPIOE->MODER &= ~GPIO_MODER_MODER4; GPIOE->MODER |= GPIO_MODER_MODER4_1; GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4_1; GPIOE->AFR[0] &= ~GPIO_AFRL_AFRL4_0; GPIOE->AFR[0] |= GPIO_AFRL_AFRL4_1 | GPIO_AFRL_AFRL4_2 | GPIO_AFRL_AFRL4_3; 

Di un ejemplo para el pin PE4, que corresponde al pin B0 en el bus LTDC, es decir, es un bit azul de color cero. Para todas las demás conclusiones, la configuración es idéntica, solo 2 conclusiones merecen especial atención, una de las listas incluye una pantalla y la otra su luz de fondo. Están configurados como una salida push-pull normal, que todos usan para parpadear un LED. La configuración se ve así:

 GPIOK->MODER &= ~GPIO_MODER_MODER3; GPIOK->MODER |= GPIO_MODER_MODER3_0; 

Esta configuración es para la salida PK3, que enciende y apaga nuestra luz de fondo. Por cierto, también puede EMPUJARLO para ajustar suavemente el brillo. Para PI12, que incluye una pantalla (DISP), todo es igual. La velocidad en estos 2 pines es baja por defecto, porque no se requieren algunas acciones de alta frecuencia de ellos.

Puede ver todos los demás puertos de E / S en la placa de circuito de la placa de depuración, o en el diagrama de circuito de su propio dispositivo.

5. Tiempos y sus configuraciones


Los tiempos desde un punto de vista físico son retrasos comunes. Creo que ha observado repetidamente varias perversiones del tipo de retraso (1) cuando miró ejemplos de código en pantallas con controladores SPI / I2C similares a ILI9341. Allí, se necesita un retraso para que el controlador, por ejemplo, tenga tiempo de aceptar el comando, ejecutarlo y luego hacer algo con los datos. En el caso de LTDC, todo es casi igual, solo que no haremos muletas y, por qué no, nuestro microcontrolador puede configurar los tiempos necesarios en el hardware. ¿Por qué se necesitan en una pantalla donde no hay controlador? Sí, es elemental que después de llenar la primera línea de píxeles, vaya a la siguiente línea y regrese a su comienzo. Esto se debe a la tecnología de producción de pantallas y, por lo tanto, cada modelo de pantalla específico tiene sus propios tiempos.

Para averiguar qué valores necesitamos, vaya al sitio web de ST y mire el diagrama de la placa de depuración STM32F746-Disco . Allí podemos ver que la pantalla es RK043FN48H-CT672B y la documentación está disponible, por ejemplo, aquí . Estamos más interesados ​​en la tabla en la página 13 en la sección 7.3.1:



Estos son nuestros valores que necesitaremos al configurar. También en la documentación hay mucho más que es interesante, por ejemplo, diagramas de señales en el bus, etc., que podría necesitar si, por ejemplo, desea elevar la pantalla a FPGA o CPLD.

Ve a la configuración. En primer lugar, para no mantener estos valores en mi cabeza, los organizaré en forma de definiciones:

 #define DISPLAY_HSYNC ((uint16_t)30) #define DISPLAY_HBP ((uint16_t)13) #define DISPLAY_HFP ((uint16_t)32) #define DISPLAY_VSYNC ((uint16_t)10) #define DISPLAY_VBP ((uint16_t)2) #define DISPLAY_VFP ((uint16_t)2) 

Hay una característica interesante. Timing Pulse Width , que se llama DISPLAY_HSYNC , tiene un valor en la tabla solo para la frecuencia de reloj de píxeles de 5 MHz, pero para 9 y 12 MHz no lo es. Este tiempo debe seleccionarse para su pantalla, obtuve este valor de 30, cuando en los ejemplos de ST era diferente. Al principio, si tiene un error con su configuración, la imagen se desplazará hacia la izquierda o hacia la derecha. Si está a la derecha, disminuimos el tiempo; si a la izquierda, lo aumentamos. De hecho, afecta el origen de la zona visible, que veremos más adelante. Solo tenga en cuenta que la siguiente imagen de la página 24 de nuestro AN4861 ayudará a comprender todo este párrafo:



Una pequeña abstracción es conveniente aquí. Tenemos 2 zonas de visualización: visibles y generales. La zona visible tiene dimensiones con una resolución declarada de 480 por 272 píxeles, y la zona total es el visible + nuestros tiempos, de los cuales hay 3 en cada lado. También vale la pena entender (esto ya no es una abstracción) que una marca del sistema es 1 píxel, por lo que el área total es 480 píxeles + HSYNC + HBP + HFP.

También vale la pena darse cuenta de que cuantos menos tiempos, mejor: la pantalla se actualizará más rápido y la velocidad de fotogramas aumentará ligeramente. Por lo tanto, después de la primera ejecución, experimente con los tiempos y reduzca lo más posible mientras mantiene la estabilidad.

Para establecer los tiempos, hice una pequeña "hoja de trucos" para el futuro dentro del proyecto, también lo ayudará a comprender qué figura específica y dónde escribirla:

 /* *************************** Timings for TFT display********************************** * * HSW = (DISPLAY_HSYNC - 1) * VSH = (DISPLAY_VSYNC - 1) * AHBP = (DISPLAY_HSYNC + DISPLAY_HBP - 1) * AVBP = (DISPLAY_VSYNC + DISPLAY_VBP - 1) * AAW = (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1) * AAH = (DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP - 1) * TOTALW = (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP + DISPLAY_VFP - 1) * TOTALH = (DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP + DISPLAY_HFP - 1) * */ 

¿De dónde viene esta "hoja de trucos" ... Primero, viste una "fórmula" similar un par de párrafos antes. En segundo lugar, vaya a la página 56 de nuestro AN4861:



Es cierto, espero que haya entendido el significado físico de los tiempos antes de la aparición de esta hoja de trucos y estoy seguro de que usted mismo podría haberlo compilado. No tiene nada de complicado, y las imágenes de RM y AN ayudan a comprender visualmente el efecto de los tiempos en el proceso de formación de imágenes.

Ahora es el momento de escribir un código que configure estos tiempos. En la "hoja de trucos" se indican los bits del registro en el que escribir, por ejemplo, TOTALH, y después de que el signo sea igual a la fórmula que le da a la salida un cierto número. Ok? Luego escribimos:

 LTDC->SSCR |= ((DISPLAY_HSYNC - 1) << 16 | (DISPLAY_VSYNC - 1)); LTDC->BPCR |= ((DISPLAY_HSYNC+DISPLAY_HBP-1) << 16 | (DISPLAY_VSYNC+DISPLAY_VBP-1)); LTDC->AWCR |= ((DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP - 1) << 16 | (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1)); LTDC->TWCR |= ((DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP + DISPLAY_HFP -1)<< 16 |(DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP + DISPLAY_VFP - 1)); 

¡Y eso es todo con los tiempos! En esta sección, solo puede configurar el color de fondo. Lo tengo negro por defecto, por lo que está escrito en cero. Si desea cambiar el color de la capa de fondo (fondo), puede escribir igualmente cualquier valor, por ejemplo, 0xFFFFFFFF y llenar todo con blanco:

 LTDC->BCCR = 0; 

Hay una ilustración maravillosa en el manual de referencia , que demuestra claramente que en realidad tenemos 3 capas: fondo, capa 1 y capa 2. La capa de fondo está "castrada" y solo puede rellenarse con un color específico, pero también puede ser increíblemente útil en la implementación futuro diseño de GUI. Además, esta ilustración demuestra claramente la prioridad de las capas, lo que significa que veremos el color de relleno en el fondo solo cuando las capas restantes estén vacías o transparentes.

Como ejemplo, mostraré una de las páginas del proyecto donde, durante la implementación de la plantilla, el fondo se llenó con un color y el controlador no volvió a dibujar toda la página, sino solo sectores individuales, lo que permitió recibir aproximadamente 50-60 fps para muchas otras tareas:



6. La parte final de la configuración de LTDC


La configuración de LTDC se divide en 2 secciones: la primera es común para todo el módulo LTDC y está ubicada en el grupo de registro LTDC , y la segunda está configurada en una de dos capas y está en el grupo LTDC_Layer1 y LTDC_Layer2 .

Realizamos la configuración general en el párrafo anterior, que incluye la configuración de los tiempos, la capa de fondo. Ahora pasamos a configurar las capas y nuestra lista requiere el tamaño real de la zona visible de la capa, que se describe en forma de 4 coordenadas (x0, y0, x1, y2), que nos permiten obtener las dimensiones del rectángulo. El tamaño de la capa visible puede ser menor que la resolución de la pantalla, nadie se molesta en hacer que el tamaño de la capa sea 100 por 100 píxeles. Para ajustar el tamaño de la zona visible, escriba el siguiente código:

 LTDC_Layer2->WHPCR |= (((DISPLAY_WIDTH + DISPLAY_HBP + DISPLAY_HSYNC - 1) << 16) | (DISPLAY_HBP + DISPLAY_HSYNC)); LTDC_Layer2->WVPCR |= (((DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1) << 16) |(DISPLAY_VSYNC + DISPLAY_VBP)); 

Como puede ver, todo es igual que con los tiempos. Los puntos de partida (x0, y0) de la zona visible consisten en la suma de dos tiempos: HSYNC + HBP y VSYNC + VBP. Para calcular las coordenadas del punto final (x1, y1), el ancho y la altura en píxeles simplemente se agregan a los datos del valor.

Ahora necesita configurar el formato de los datos recibidos. La calidad máxima se obtiene cuando se utiliza el formato ARGB8888, pero al mismo tiempo obtenemos la cantidad máxima de memoria ocupada. Un píxel ocupa 32 bits o 4 bytes, lo que significa que toda la pantalla ocupa 4 * 480 * 272 = 522,240 bytes, es decir, la mitad de la memoria flash de nuestro controlador no es el más débil. No tenga miedo: la conexión de SDRAM externa y memoria Flash a través de QSPI resuelve problemas de memoria y no hay restricciones en este formato, nos alegramos de buena calidad. Si desea ahorrar espacio o su pantalla no admite el formato de 24 bits, se utilizan modelos más adecuados para esto, por ejemplo, RGB565. Un formato muy popular tanto para pantallas como para cámaras, y lo más importante al usarlo, 1 píxel toma solo 5 + 6 + 5 = 16 bits o 2 bytes. En consecuencia, la cantidad de memoria ocupada por la capa será 2 veces menor. Por defecto, el controlador ya tiene configurado el formato ARGB8888 y tiene este aspecto:

 LTDC_Layer2->PFCR = 0; 

Si necesita un formato diferente al ARGB8888, vaya a las páginas 533 y 534 del manual de referencia y seleccione el formato deseado de la lista a continuación:



Ahora cree una matriz y pase su dirección a LTDC, se convertirá en un búfer de trama y será un "reflejo" de nuestra capa. Por ejemplo, debe llenar el primer píxel en la primera fila con color blanco, para esto solo necesita escribir el valor de color (0xFFFFFFFF) en el primer elemento de esta matriz. ¿Necesita llenar el primer píxel en la segunda fila? Luego también escribimos el valor de color en el elemento con el número (480 + 1). 480 - haga un salto de línea, luego agregue el número en la línea que necesitamos.

Esta configuración se ve así:

 #define DISPLAY_WIDTH ((uint16_t)480) #define DISPLAY_HEIGHT ((uint16_t)272) const uint32_t imageLayer2[DISPLAY_WIDTH * DISPLAY_HEIGHT]; LTDC_Layer2->CFBAR = (uint32_t)imageLayer2; 

En el buen sentido, después de configurar LTDC, también debe configurar SDRAM para eliminar el modificador const y obtener el búfer de trama en la RAM, porque La propia RAM de MK no es suficiente incluso para una capa con 4 bytes. Aunque esto no está de más probar la configuración correcta de los periféricos.

Luego, debe especificar el valor de la capa alfa, es decir, la transparencia de nuestra capa Layer2 , para esto escribimos un valor de 0 a 255, donde 0 es una capa completamente transparente, 255 es completamente opaca, que es 100% visible:

 LTDC_Layer2->CACR = 255; 

Según nuestro plan, ahora es necesario registrar el tamaño de nuestra área de visualización visible en bytes, para esto escribimos los valores correspondientes en los registros:

 LTDC_Layer2->CFBLR |= (((PIXEL_SIZE * DISPLAY_WIDTH) << 16) | (PIXEL_SIZE * DISPLAY_WIDTH + 3)); LTDC_Layer2->CFBLNR |= DISPLAY_HEIGHT; 

Quedan los dos últimos pasos, a saber, la inclusión de la capa 2 y el módulo periférico LTDC en sí. Para hacer esto, escriba los bits correspondientes:

 LTDC_Layer2->CR |= LTDC_LxCR_LEN; LTDC->GCR |= LTDC_GCR_LTDCEN; 

¡Esto completa la configuración de nuestro módulo y usted puede trabajar con nuestra pantalla!

7. Un poco sobre trabajar con LTDC


Todo el trabajo con la pantalla ahora se reduce a escribir datos en la matriz imageLayer2 , tiene un tamaño de 480 por 272 elementos, lo que corresponde totalmente a nuestra resolución y sugiere una verdad simple: 1 elemento de matriz = 1 píxel en la pantalla .

Como ejemplo, escribí una imagen en una matriz que convertí en LCD Image Converter , pero en realidad es poco probable que sus tareas se limiten a esto. Hay dos formas: usar una GUI preparada y escribirla usted mismo. Para tareas relativamente simples como la salida de texto, gráficos y similares, le aconsejo que escriba su propia GUI, tomará un poco de tiempo y le dará una comprensión completa de su trabajo. Cuando la tarea es grande y difícil, y no hay tiempo para desarrollar su propia GUI, le aconsejo que preste atención a las soluciones ya preparadas, por ejemplo, uGFX y similares.

Los símbolos de texto, líneas y otros elementos son inherentemente conjuntos de píxeles, por lo que para implementarlos debe implementar la lógica usted mismo, pero debe comenzar con la función más básica: "salida de píxeles". Debe tomar 3 argumentos: la coordenada a lo largo de X, la coordenada a lo largo de Y y, en consecuencia, el color en el que se pinta el píxel dado. Puede verse así:

 typedef enum ColorDisplay { RED = 0xFFFF0000, GREEN = 0xFF00FF00, BLUE = 0xFF0000FF, BLACK = 0xFF000000, WHITE = 0xFFFFFFFF } Color; void SetPixel (uint16_t setX, uint16_t setY, Color Color) { uint32_t numBuffer = ((setY - 1) * DISPLAY_WIDTH) + setX; imageLayer2[numBuffer] = Color; } 

Después de tomar las coordenadas en una función, las recalculamos en el número de la matriz que corresponde a la coordenada dada y luego escribimos el color recibido en el elemento recibido. En función de esta función, ya puede implementar funciones para mostrar geometría, texto y otras "ventajas" de la GUI. Creo que la idea es comprensible, pero la forma de darle vida depende de su criterio.

Resumen


Como puede ver, la implementación de periféricos incluso complejos en registros (CMSIS) no es una tarea difícil, solo necesita comprender cómo funciona en su interior. Por supuesto, ahora está de moda desarrollar firmware sin comprender lo que está sucediendo, pero este es un callejón sin salida si planea convertirse en ingeniero, y no ...

Si compara el código resultante con una solución en HAL o SPL, notará que el código escrito en los registros es más compacto. Agregando un par de comentarios donde lo necesite y envolviéndolo en funciones, obtenemos legibilidad al menos no peor que la de HAL / SPL, y si recuerda que el manual de referencia documenta los registros, entonces trabajar con CMSIS es más conveniente.

1) El proyecto con fuentes en TrueSTUDIO se puede descargar aquí

2) Para aquellos que se sienten más cómodos mirando GitHub

3) Descargue la utilidad para convertir imágenes en código LCD Image Converter aquí

Source: https://habr.com/ru/post/es412753/


All Articles