Nivel: Básico
¿Qué tengo que saber para este post?
—————————————
En el post anterior estuvimos analizando las herramientas de Software y Hardware que utilizaremos en las siguientes entrada del blog para aprender a programar un Cortex-M utilizando Software Libre. Pero no hicimos un análisis de cómo es la programación o la API de libOpenCM3 (la cual se encuentra en desarrollo actualmente y no es definitiva). Lo que seguro no va a cambiar es el µControlador, es decir, el STM32F103x que lleva 10 años en el mercado recibirá quizás alguna revisión, pero en sí no cambiará el Hardware. Por lo tanto analizaremos algunas cuestiones específicas de éste, ya que sería mala idea no conocer lo que programamos, para eso usa Arduino y agarrate la cabeza cuando no sepas porqué no funciona.
Lo primero que vamos a analizar es el RCC
(Reset and Clock Control), que es el periférico/módulo (casi) principal después del CPU en estos micros. No me voy a detener mucho en el Reset, pero es bueno que sepan que hay varias fuentes de Reset, y un registro que levanta un flag dependiendo de cual fue el que lo causo (para ejecutar diferentes rutinas de inicialización). Por ejemplo, además de la (obvia) señal externa del Reset, podría haberse causado por alguno de los Watchdogs (es decir que nuestro programa se colgó en algún punto) y quizás queremos reportar o aplicar un log a esa situación.
Lo que me parece más relevante en este punto, más introductorio, es hablar del intrincado sistema de Clocks que poseen esta familia de microcontroladores. Empecemos fácil, tenemos las siguientes alternativas como fuente generadoras de clock:
SYSCLK
):Como la configuración del SYSCLK
, como se lo pueden imaginar, es bastante compleja, y consta de varias instrucciones, libOpenCM3 nos ofrece funciones intuitivas que nos permiten configurarlo con algunas alternativas (si no invocamos ninguna de estas se utiliza el HSI como fuente del SYSCLK
):
void rcc_clock_setup_in_hsi_out_64mhz (void);
void rcc_clock_setup_in_hsi_out_48mhz (void);
void rcc_clock_setup_in_hsi_out_24mhz (void);
void rcc_clock_setup_in_hse_8mhz_out_24mhz (void);
void rcc_clock_setup_in_hse_8mhz_out_72mhz (void);
void rcc_clock_setup_in_hse_12mhz_out_72mhz (void);
void rcc_clock_setup_in_hse_16mhz_out_72mhz (void);
void rcc_clock_setup_in_hse_25mhz_out_72mhz (void);
Del SYSCLK se cuelgan todos los demás periféricos, ya que este se dirije directamente al AHB (Advanced High-performance Bus), el cual se distribuye a los periféricos a través de dos APBs (Advanced Peripheral Bus) independientes, uno con una frecuencia de trabajo máxima de 36Mhz y otro de 72Mhz. A continuación listaré los periféricos:
Cada uno de los periféricos nombrados tiene su habilitación de clock manejada por los registros del RCC
, y están todos desconectados por default. Por ello tenemos la función de libOpenCM3:
void rcc_periph_clock_enable(enum rcc_periph_clken clken);
Los enum tiene el siguiente formato RCC_<PERIFÉRICO>
, así por ejemplo, si queremos habilitar el clock del GPIOC
debemos pasar como argumento el RCC_GPIOC
. Des esta forma habilitamos los clocks de cualquier periférico sin importarnos o recordar si estaba en el APB1 o APB2 (eso lo determina la función internamente).
Lo principal en cualquier µControlador es empezar a manejar las entradas y salidas, si bien hablamos un poco de esto en el post anterior, formalicemos algunas cuestiones interesantes que tiene estos dispositivos. Para empezar, este integrado funciona con 3v3 pero tiene pines que soportan 5v, como nunca recuerdo cuales son y cada vez le encuentro menos sentido a utilizar lógicas de 5v, recomiendo utilizar todo 3v3 para evitar dolores de cabeza (y quemar pines). Y saber que si necesitan algo de 5v ir a buscar el pin que más les convenga.
Tenemos entonces funciones de GPIO normales, entrada y salida, como funciones alternativas tanto en salida como entrada. Las función que nos permite esto es la siguiente:
void gpio_set_mode(uint32_t gpioport, uint8_t mode, uint8_t cnf, uint16_t gpios);
Las alternativas son las siguientes para el STM32F103Cx:
GPIOA
, GPIOB
, GPIOC
y GPIOD
GPIO_MODE_INPUT
, GPIO_MODE_OUTPUT_10_MHZ
, GPIO_MODE_OUTPUT_2_MHZ
, GPIO_MODE_OUTPUT_50_MHZ
GPIO_CNF_INPUT_ANALOG
, GPIO_CNF_INPUT_FLOAT
, GPIO_CNF_INPUT_PULL_UPDOWN
, GPIO_CNF_OUTPUT_PUSHPULL
, GPIO_CNF_OUTPUT_OPENDRAIN
, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL
, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN
GPIO0|...|GPIO15
Lo que tiene de interesante, es que nos permite darle a varios pines a la vez del mismo puerto la misma configuración separando a los gpios deseados con |
(OR). Porque debemos pensar que esta funciones son como escribir en los registros directamente pero sin aprendernos los nombres de los mismos ni cuantos son realmente. Luego tenemos las siguientes funciones para la manipulación de los puertos:
void gpio_set (uint32_t gpioport, uint16_t gpios);
void gpio_clear (uint32_t gpioport, uint16_t gpios);
void gpio_toggle (uint32_t gpioport, uint16_t gpios);
uint16_t gpio_get (uint32_t gpioport, uint16_t gpios);
Aquí solo dejo las de más alto nivel, donde podemos pasar el puerto y los pines (grupo de pines) sobre el cual efectuamos la acción. Recalco lo de «grupo de pines», ya que cuando usamos gpio_get()
nos devolverá un uint16_t
donde solo los bits (representando a los pines) que le pedimos tendran un valor significativo.
Como este micro tiene muchos periféricos, también podemos remapear algunas funciones, por ejemplo los pines de la USART1 pueden ser el PA9 y PA10 o el PB6 y PB7. Los protocolos de programación y debugger JTAG y SWD son las funciones principales de los pines, y alternativamente son GPIOs. Para poder utilizar estas funcionalidades con libOpenCM3 tenemos la función:
void gpio_primary_remap(uint32_t swjdisable, uint32_t maps);
swjdisable
es justamente para inhabilitar alguna señal de programación que no utilicemos con el SWD que se utilizan con JTAG (ya dejaré un ejemplo con eso en otro post).
Como no me gustaría dejarles solo información sin un ejemplo, les dejo un programita donde utilizaré 3 LEDs y un pulsador. Les dejo el conexionado y el main.c
.
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
int main()
{
// SYSCLK a 72Mhz
rcc_clock_setup_in_hse_8mhz_out_72mhz();
// Se habilitan los clocks de los puertos a utilizar:
rcc_periph_clock_enable(RCC_GPIOC);
rcc_periph_clock_enable(RCC_GPIOB);
// Configuramos el LED de la BluePill PC13
gpio_set_mode(
GPIOC,
GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL,
GPIO13);
gpio_set(GPIOC,GPIO13); // LED1 apagado
// Configuramos los LED2 y LED3 (PB5 y PB6)
gpio_set_mode(
GPIOB,
GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL,
GPIO5 | GPIO6);
gpio_set(GPIOB, GPIO5); // LED2 prendido
gpio_clear(GPIOB, GPIO6); // LED3 apagado
// Configuramos el pulsador PB15
gpio_set_mode(
GPIOB,
GPIO_MODE_INPUT,
GPIO_CNF_INPUT_FLOAT, // tiene pull-up externo
GPIO15);
while (1)
{
if (gpio_get(GPIOB, GPIO15) == 0) // Si el pulsador está presionado
gpio_toggle(GPIOB, GPIO6); // invertimos el LED
// Siempre se invierte el LED2
gpio_toggle(GPIOB, GPIO5);
// El PC13 está prendido cuando el PB5 está prendido
gpio_toggle(GPIOC, GPIO13);
for (uint32_t i = 0; i < 7200000; ++i)
__asm__("nop");
}
}
Entonces los LED1 y LED2 prenden alternadamente (cuando uno está prendido el otro está apagado) y el LED3 solo alterna si el pulsador está presionado.
Repo de GitLab: https://gitlab.com/tute-avalos/libopencm3-basic-gpios
Hojas de datos: https://www.st.com/resource/en/datasheet/stm32f103c8.pdf https://www.st.com/resource/en/reference_manual/cd00171190-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf
Documentación de libOpenCM3: http://libopencm3.org/docs/latest/stm32f1/html/modules.html
Nivel: Básico ¿Qué tengo que saber para este post? Diseño básico de páginas con HTML…
Nivel: Avanzado. La modificación de fuentes ATX para su utilización en laboratorio o comunicaciones es…
El pasado 23 de abril se celebró la 18° edición del FLISoL en la que…
Circuito del regulador, con los elementos de simulación. Típicamente, las motocicletas de baja cilindrada utilizan…
¿Qué puedo decir? Siempre quise tener un analizador lógico, había visto estos pequeños y baratos…
Nivel: Intermedio ¿Qué tengo que saber para este post? Entender el uso de un ADC.Programación…
Esta web usa cookies.
Ver comentarios