Primeros pasos STM32: ¿Qué son los registros? ¿Cómo trabajar con ellos?

Consideración continua de cuestiones básicas


En la lección anterior, examinamos el trabajo con operaciones de bits y números binarios, sentando así las bases para considerar un nuevo tema. En esta lección, consideraremos otra pregunta: ¿qué son los registros y cómo trabajar con ellos ?




Lista de artículos:
  1. Comenzar a estudiar STM32 o administrar la luz de forma inteligente
  2. Procedimientos iniciales STM32: operaciones de bits
  3. Primeros pasos STM32: ¿Qué son los registros? ¿Cómo trabajar con ellos?


Memoria y registros


Una de las habilidades más importantes necesarias cuando se trabaja con microcontroladores es la capacidad de interactuar con los registros. Vamos a descubrir por nosotros mismos qué es ?

En general, un registro es un tipo especial de memoria dentro de un microcontrolador que se utiliza para controlar el procesador y los periféricos. Cada registro en la arquitectura ARM es una celda de memoria y tiene una longitud de 32 bits, donde cada bit se puede representar como un pequeño interruptor con la ayuda de cuál u otro parámetro del microcontrolador se controla.

Cada uno de los registros tiene su propio número de serie: dirección. La dirección de registro se indica mediante un número de 32 bits representado en notación hexadecimal. Al escribir en la dirección de registro una cierta combinación de unos y ceros, que generalmente se presentan en hexadecimal, se realiza la configuración y el control de uno u otro nodo en MK. Recuerde que en un programa para trabajar con operaciones de bits, podríamos representar un conjunto arbitrario de unos y ceros como un número hexadecimal. En general, vale la pena señalar que hay dos tipos de registros: registros de propósito general y registros especiales. Los primeros se encuentran dentro del núcleo MK, y los segundos son parte de la memoria RAM.

También vale la pena señalar que el Manual de referencia , que descargamos en la primera lección , es un gran directorio de registros contenidos en el microcontrolador de destino, y la biblioteca CMSIS nos permite operar con nombres de registros simbólicos en lugar de direcciones numéricas. Por ejemplo, podemos acceder al registro 0x40011018 simplemente usando el nombre simbólico GPIOC_BSSR . Consideraremos ejemplos específicos de configuración durante el análisis de nuestro programa desde la primera lección .

Entonces, por lo general, la estructura del registro se describe en forma de una pequeña tabla que indica:

  1. Registrar nombres y descripciones de su finalidad.
  2. Registrar direcciones o compensar desde la dirección base
  3. Valores predeterminados después de restablecer
  4. Tipo de acceso a las celdas de registro (lectura, escritura, lectura / escritura)
  5. Valores y descripciones de los parámetros de los bits grabados.

Veamos un ejemplo de trabajo con registros en una situación específica para tener una idea general de los principios de ajuste del microcontrolador.

Analizando el código de la primera lección


Entonces, recordemos el problema que resolvimos en la primera lección usando un código de ejemplo listo: necesitábamos escribir un programa que asegurara la inclusión alternativa de dos LED en la placa Discovery (tal vez no dos si tienes una versión diferente de la placa Discovery) con un intervalo de tiempo .

Echemos otro vistazo al código del programa que utilizamos para hacer que nuestro MK salte con dos patas en las que se encuentran nuestros LED:

Código main.c
/*      */ #include "stm32f0xx.h" /*    */ int main(void) { /*     GPIO */ RCC->AHBENR |= RCC_AHBENR_GPIOCEN; /*     PC8  PC9  Output*/ GPIOC ->MODER = 0x50000; /*  Output type   Push-Pull */ GPIOC->OTYPER = 0; /*      Low */ GPIOC->OSPEEDR = 0; while(1) { /*   PC8,  PC9 */ GPIOC->ODR = 0x100; for (int i=0; i<500000; i++){} //   /*   PC9,  PC8 */ GPIOC->ODR = 0x200; for (int i=0; i<500000; i++){} //   } } 


En primer lugar, cuando se trabaja con STM32, incluso para una tarea tan simple como encender y apagar el LED, primero debemos responder una serie de preguntas:

  1. ¿Dónde están conectados nuestros LED? ¿Cuál es la conclusión del microcontrolador?
  2. ¿Cómo habilitar la sincronización en el puerto GPIO deseado?
  3. ¿Cómo configurar los pines del puerto GPIO que necesitamos para poder encender el LED?
  4. ¿Cómo encender y apagar el LED?

Les responderemos en orden.

¿Dónde están conectados nuestros LED? ¿Cuál es la conclusión del microcontrolador?


Para ver dónde está ubicado en la placa Discovery, y en particular, los LED que necesitamos, debe abrir el archivo Esquema, ya sea el que descargamos del sitio web de ST o directamente de Keil:


Al abrir Schematic, veremos un diagrama de todo lo que está en el tablero: un diagrama ST-Link, un enlace de toda la periferia y mucho más. Por el momento, estamos interesados ​​en dos LED, estamos buscando su designación:


Como podemos ver, nuestros LED están conectados al puerto GPIOC en 8 y 9 pines.

¿Cómo habilitar la sincronización en el puerto GPIO deseado?


En general, cualquier trabajo con periféricos en microcontroladores STM32 se reduce a una secuencia estándar de acciones:

  1. Habilitación del reloj del correspondiente módulo periférico. Esto se realiza a través del registro RCC aplicando una señal de reloj directamente desde el bus en el que se encuentra este módulo. Por defecto, el reloj de todos los periféricos está deshabilitado para minimizar el consumo de energía.
  2. Configuración a través de registros de control, cambiando los parámetros específicos de un dispositivo periférico específico
  3. Inicio directo y uso de los resultados de operación del módulo

Es decir, para comenzar, necesitamos ejecutar el reloj en el puerto GPIOC. Esto se hace directamente accediendo al registro RCC responsable de marcar todo y todo y encender la señal del reloj desde el bus al que está conectado nuestro puerto GPIO.

Atencion La pregunta sobre el sistema de reloj, su configuración y uso se discutirá en detalle en un artículo separado.

Encuentre a qué bus está conectado nuestro puerto GPIOC en la Hoja de datos de nuestro MK en la sección Asignación de memoria en la Tabla 16. Direcciones de límite de registro periférico STM32F051xx.


Como ya notó, el autobús que necesitamos se conoce como AHB2. Para familiarizarse con el registro en el que se activa el marcado al puerto GPIO en el bus AHB que necesitamos, debe ir a la sección correspondiente en el Manual de referencia. Por el nombre de los registros, podemos determinar el que necesitamos:


Llegamos a este punto y vemos nuestro registro de 32 bits, su dirección de desplazamiento, valor predeterminado, la forma de acceder al registro y enumerar de qué es responsable cada bit en el registro.


Observamos la tabla y vemos algo que recuerda la opción de habilitar la sincronización en los puertos GPIO. Vaya a la descripción y encuentre la opción que necesitamos:


En consecuencia, si establecemos 19 bits en el valor "1", esto garantizará que el reloj esté habilitado en el puerto I / OC, es decir, en nuestro GPIOC. Además, necesitamos habilitar por separado un bit del grupo, sin afectar el resto porque No debemos interferir o cambiar otras configuraciones innecesariamente.

Según los materiales de la última lección, sabemos que para establecer un bit específico, debe usar la operación lógica "OR" para agregar el valor actual del registro con una máscara que contiene esos bits que deben activarse. Por ejemplo, agreguemos el valor predeterminado del registro RCC-> AHBENR, es decir 0x14 y el número 0x80000 habilitan el reloj GPIOC configurando 19 bits:



¿Cómo podemos hacer esto desde un programa? Todo es bastante simple. En este caso, tenemos dos opciones:

  1. Escribir directamente en el registro el valor numérico del registro directamente a través de su dirección.
  2. Configuración usando la biblioteca CMSIS

No hay problemas particulares directamente al escribir un valor en el registro, pero hay un par de inconvenientes importantes. En primer lugar, dicho código se vuelve ilegible y, en segundo lugar, no podemos determinar de inmediato a qué registro se refiere esta o aquella dirección en la memoria.

Es decir, podríamos direccionar las direcciones de los registros directamente en la dirección y escribir esto:

 __IO uint32_t * register_address = (uint32_t *) 0x40021014U; //      *(__IO uint32_t *)register_address |= 0x80000; //  19     

La segunda opción me parece la más atractiva, porque La biblioteca CMSIS está organizada de tal manera que se puede acceder al registro utilizando solo su nombre. Durante el preprocesamiento del texto del programa, el preprocesador sustituirá automáticamente todos los valores digitales de la dirección de registro antes de la compilación. Miremos esta pregunta con más detalle.

Sugiero abrir nuestro proyecto, lo que hicimos en la primera lección, o descargar el previamente preparado desde aquí y eliminar todo el contenido del programa, dejando solo el archivo de encabezado conectado, la función main () y las instrucciones para activar el reloj (lo necesitaremos para un análisis detallado del código).

Nuestro código se verá así:

 /*      */ #include "stm32f0xx.h" /*    */ int main(void) { /*     GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN; } 

Profundicemos en la biblioteca CMSIS para familiarizarse.

Para ir rápidamente al lugar donde se declara esta o aquella constante o variable, se implementa una función conveniente en Keil. Haga clic derecho en la constante que necesitamos, por ejemplo, en RCC:


Y somos transportados a las profundidades de la biblioteca CMSIS, en la que veremos que todos los registros disponibles para el control programático tienen la forma de estructuras TypeDef, incluido nuestro RCC:


Al haber fallado de manera similar en RCC_TypeDef, veremos una estructura en la que se describen todos los campos de nuestro registro:


En consecuencia, podemos acceder de forma segura al registro que necesitamos con un registro del formulario PERIPH_MODULE-> REGISTER y asignarle un valor específico.

Además de la designación mnemónica de registros, también hay designaciones de bits específicos. Si no declaramos el parámetro RCC_AHBENR_GPIOCEN de nuestro programa, también veremos la declaración de todos los parámetros:


Por lo tanto, al usar la biblioteca CMSIS, obtenemos un registro conciso y legible del parámetro que necesitamos en el registro, a través de la instalación de la cual comenzamos el cronometraje en el puerto que necesitamos:

 /*     GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN; 

Como tarea: determinar, utilizando las capacidades de Keil, cómo se obtuvo la dirección del registro RCC-> AHBENR como 0x40021014.

¿Cómo configurar los pines GPIO que necesitamos para que podamos encender el LED?


Entonces, sabemos que los LED que necesitamos están conectados al puerto GPIOC a los pines PC8 y PC9. Necesitamos configurarlos de tal manera que el LED se ilumine. Me gustaría hacer una reserva de inmediato para que veamos los puertos GPIO con más detalle en otro artículo y aquí nos concentraremos específicamente en trabajar con registros.

En primer lugar, necesitamos poner los pines PC8 y PC9 en modo de Salida. Se pueden dejar otros parámetros de puerto por defecto. Vaya al Manual de referencia en la sección 9. E / S de propósito general (GPIO) y abra el elemento responsable del modo de funcionamiento de los pines del puerto GPIO y compruebe que el registro MODER es responsable de este parámetro:


A juzgar por la descripción, para establecer los pines PC8 y PC9 en modo Salida, debemos escribir 01 en los campos correspondientes del registro GPIOC.

Esto se puede hacer a través de la instalación directa utilizando valores numéricos:

  1. Formamos un número para escribir:

  2. Asignamos este valor a nuestro registro:

     /*     PC8  PC9  Output*/ GPIOC->MODER |= 0x50000; 


O mediante el uso de definiciones de la biblioteca:

 /*     GPIO */ GPIOC->MODER |= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0; 

Después de estas instrucciones, nuestros pines PC8 y PC9 pasarán al modo de Salida.

¿Cómo encender el LED?


Si prestamos atención a la lista de registros disponibles para controlar el puerto GPIO, podemos ver el registro ODR:


Cada uno de los bits correspondientes es responsable de uno de los pines del puerto. Puedes ver su estructura a continuación:


Para proporcionar un cambio alternativo de los estados del LED, es necesario activar / desactivar 8 y 9 bits con un cierto intervalo de tiempo. Es decir, asigne alternativamente el valor 0x100 y 0x200 al registro.

Podemos hacer esto asignando directamente valores al registro:

 GPIOC->ODR = 0x100; //  PC8,  PC9 GPIOC->ODR = 0x200; //  PC9,  PC8 

A través del uso de definiciones de la biblioteca podemos:

 GPIOC->ODR = GPIO_ODR_8; //  PC8,  PC9 GPIOC->ODR = GPIO_ODR_9; //  PC9,  PC8 

Pero dado que el microcontrolador funciona muy rápido, no notaremos el cambio en el estado de los LED y visualmente parecerá que ambos se iluminan constantemente. Para que realmente parpadeen alternativamente, introduciremos un retraso artificial en forma de un ciclo que llevará a MK a cálculos inútiles por un tiempo. El siguiente código resultará:

 /*   PC8,  PC9 */ GPIOC->ODR = GPIO_ODR_8; for (int i=0; i<500000; i++){} //   /*   PC9,  PC8 */ GPIOC->ODR = GPIO_ODR_9; for (int i=0; i<500000; i++){} //   

Con esto, podemos terminar el conocimiento inicial de los registros y los métodos para trabajar con ellos.

Verificando los resultados de nuestro código


Una pequeña adición agradable al final del artículo: Keil tiene una excelente herramienta de depuración con la que podemos ejecutar nuestro programa paso a paso y ver el estado actual de cualquier bloque periférico. Para hacer esto, después de descargar el firmware después de la compilación, podemos hacer clic en el botón Iniciar sesión de depuración:


El espacio de trabajo de Keil cambiará al modo de depuración. Podemos controlar el progreso del programa usando estos botones:


Y hay otra función conveniente para trabajar con periféricos en modo de depuración, le permite ver el estado actual de los registros y cambiar su estado con un simple clic del mouse.

Para usarlo, debe ir a la unidad periférica correspondiente y se abrirá una ventana a la derecha con los registros y su valor.


Si hace clic en uno de los elementos de este menú, verá la dirección del registro y su breve descripción. También puede ver la descripción de cada parámetro de registro individual:


Intente ejecutar el programa de forma independiente paso a paso, encienda / apague los LED que no usan el programa, sino que usan este modo de operación con el microcontrolador. El alcance de la imaginación es enorme. También intente jugar con la duración de los retrasos, realice el parpadeo simultáneo de ambos LED. En general, ¡experimenta! )

¡Nos vemos en los siguientes artículos!
Lista de artículos:
  1. Comenzar a estudiar STM32 o administrar la luz de forma inteligente
  2. Procedimientos iniciales STM32: operaciones de bits
  3. Primeros pasos STM32: ¿Qué son los registros? ¿Cómo trabajar con ellos?

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


All Articles