pub:silab4

Reloj en tiempo real STM32 DISCO-L476VG

Plataforma de evaluación HW para STM32L4

Placa de desarrollo STM32 Discovery kit //32L476GDISCOVERY// con MCU STM32L476VG

  • Depurador/programador en PCB ST-LINK/V2-1 con conector SWD
  • Alimentación (4x opciones):
    • ST-LINK/V2-1
    • Conector USB FS
    • Externa 5 V
    • Batería CR2032
  • 2x LEDs de usuario: LD4 (rojo), LD5 (verde)
  • 2x botón pulsador: USER y RESET
  • 1x puerto USB OTG FS con conector micro-AB, capacidad de re-enumeración USB y 3x interfaces USB:
    • VCP (Virtual Com port)
    • Almacenamiento masivo (USB Disk drive) para programación por arrastre
    • Depuración (Debug port)
  • 1x Joystick de 4x direcciones y selección central
  • SAI Audio DAC, estéreo, con conector Jack de salida
  • Micrófono digital MEMS
  • Acelerómetro y magnetómetro MEMS
  • Giróscopo MEMS
  • 128-Mbit memoria Quad-SPI Flash
  • Amperímetro de corriente del MCU con 4 rangos y auto calibración

  • ARM®32-bit Cortex®-M4 CPU con FPU y acelerador ART
  • 80 MHz frecuencia máxima de CPU
  • VDD de 1.71V a 3.6 V
  • 1 MB Flash
  • 128 KB SRAM
  • 114x GPIOs con capacidad de interrupción externa
  • 3x 12-bit ADCs con 16 canales
  • 2x 12-bit DAC
  • 3x USART
  • 2x UART
  • 1x LPUART
  • 2x SAI
  • 3x I2C
  • 3x SPI
  • 1x Quad SPI
  • 7x temporizadores (Timers) de propósito general, 2x básicos y 2x avanzados
  • 2x temporizadores de bajo consumo (low-power)
  • 2x temporizadores tipo Watchdog
  • 1x CAN
  • USB OTG FS
  • SDMMC
  • SWPMI
  • LCD COM x SEG
  • RTC
  • TRNG (generador de aleatorios por HW)
  • 21x sensores capacitivos
  • 2x comparadores analógicos
  • 2x amplificadores operacionales

Un reloj de tiempo real (RTC) mantiene el tiempo en curso de forma precisa en sistemas de computación tradicional incluyendo sistemas integrados, p.ej. relojes con cronómetro, con alarmas, agendas electrónicas, programadores de electrodomésticos, etc.

La gran mayoría de SoCs Cortex-M de STM32 como el STM32L476VGT6 incorporan un RTC con:

  • Calendario con hora y fecha
  • Alarma
  • Unidad de rearme periódico (periodic wakeup) desde modos LP.
  • Detección de manipulaciones no autorizadas (tamper detection)
  • Registro de marcas de tiempo (timestamp)
  • Calibración suave para relojes de cuarzo
  • Auto ajuste con reloj de referencia local o remoto

Configuración de la fuente de reloj

Tres posibles fuentes de reloj para obtener la frecuencia de 1 Hz: LSE (32.768 KHz), LSI RC (32 KHz), HSE (4-48 MHz)

Calendario

  • Formato binario y BCD (para salida a LCD)
  • Cambio automático de meses (28-29-30-31) y años bisiestos
  • RTC_SSR: campo sub-second, valor del PSC, no programable

Alarma

Dos alarmas programables: A y B

A continuación se ven los campos programables de la alarma A:

Los campos de máscara (MSKx) son 4 bits para habilitar/deshabilitar los campos de los registros por los que se rige la alarma, en el caso de la alarma A los campos de RTC_ALRMAR; y adicionalmente 1 bit de máscara (MASK_SS) para la alarma de máxima precisión sobre RTC_ALRMASSR.

Adicionalmente hay una salida externa RTC_ALARM conectada a la unidad de alarma RTC A o B para disparar una acción externa, o conectada a la unidad de reame (RTC wakeup) para despertar/rearmar un dispositivo externo.

Marcas de tiempo (Time-stamp)

Los posibles eventos de disparo:

  • Detección de flanco en RTC_TS I/O
  • Detección de evento no autorizado (tamper) en cualquier RTC_TAMP I/Os
  • Un cambio de alimentación a VBAT cuando se apaga la fuente principal.

Desarrollo SW: programación del RTC

El objetivo del tutorial es la configuración del reloj RTC, incluyendo su puesta al día y hora en curso, así como la programación de alarmas.

Desde ventana principal acceder a la selección de placas STBoard e iniciar un nuevo proyecto (Start Project) para la DISCO-L476VG. En la ventana emergente para la inicialización de periféricos a su modo por defecto da igual en este caso lo que contestemos, puesto que vamos a partir de un pinout totalmente vacío para evitar colisiones de cualquier periférico por defecto, interno o externo en PCB.

Desde la pestaña Pinout & Configuration, sub-pestaña Pinout, seleccionamos Clear Pinouts (Ctrl-P)

Verificamos que la interfaz de depuración está establecida a Serial Wire en la categoría SYS y el temporizador base del sistema el SysTick.

En la categoría RCC activamos el cristal del LSE (32.768 KHz) que se presupone más preciso para el uso con el RTC.

Sobre la vista Pinout view definimos únicamente los pines de los LEDs rojo (PB2) y verde (PE8), y en la categoría GPIO cambiamos las etiquetas de usuario para obtener un resultado similar al de la siguiente figura.

En la categoría RTC dentro de Timers activamos la fuente de reloj para el RTC, el calendario y la alarma A, y lo programamos para que se active la alarma A en 1 segundo al menos y comprobar así su funcionamiento.

Activamos la interrupción de la alarma que corresponde a la línea EXTI 18 en la NVIC, p.ej:

Incorporamos también una salida estándar serie vía USB CDC (VCOM), como se hizo en las prácticas anteriores, para obtener de forma textual los valores medidos.

En la configuración de reloj asignamos la frecuencia máxima (HCLK) = 80 MHz, procedente típicamente de la entrada PLLCLK vía HSI o vía HSE, si se ha activado, calculada por el encaminamiento más adecuado, y se dejará por defecto esta misma frecuencia para los periféricos y temporizadores de los buses APB1 y APB2. Par el reloj RTC se selecciona el reloj LSE que hemos activado previamente.

Generamos código y pasamos al IDE del TrueStudio donde introducimos varias rutinas de apoyo al manejo del RTC en las secciones de usuario, que nos permitirán reconfigurar y mostrar los valores de tiempo y alarma sobre los ya predefinidos en la configuración de CubeMX :

/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "usbd_cdc_if.h"
/* USER CODE END Includes */
/* USER CODE BEGIN PV */
char string[80];
volatile int i;
static uint32_t last_second = 0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_RTC_Init(void);
/* USER CODE BEGIN PFP */
static void RTC_Config(uint8_t h, uint8_t m, uint8_t s);
static void RTC_Alarm(uint8_t h, uint8_t m, uint8_t s);
static void RTC_DateShow(char* showdate);
static void RTC_TimeShow(char* showtime);
static void RTC_AlarmShow(char* showalarm);
void Toggle_Leds(void);
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
static void RTC_Config(uint8_t h, uint8_t m, uint8_t s) {
 
  RTC_DateTypeDef  sDate;
  RTC_TimeTypeDef  sTime;
 
 
  /*##-1- Configure the Date #################################################*/
  /* Set Date: Tuesday February 18th 2014 */
  sDate.Year = 0x18;
  sDate.Month = RTC_MONTH_MAY;
  sDate.Date = 0x19;
  sDate.WeekDay = RTC_WEEKDAY_SATURDAY;
 
  if(HAL_RTC_SetDate(&hrtc,&sDate,RTC_FORMAT_BCD) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }
 
  /*##-2- Configure the Time #################################################*/
  /* Set Time: 02:20:00 */
  sTime.Hours = h;//0x02;
  sTime.Minutes = m;//0x20;
  sTime.Seconds = s;//0x00;
  sTime.TimeFormat = RTC_HOURFORMAT12_AM;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE ;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
 
  if(HAL_RTC_SetTime(&hrtc,&sTime,RTC_FORMAT_BCD) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }
}
 
static void RTC_Alarm(uint8_t h, uint8_t m, uint8_t s) {
 
  RTC_AlarmTypeDef sAlarm;
  /*##-3- Configure the RTC Alarm peripheral #################################*/
  /* Set Alarm to 02:20:10
     RTC Alarm Generation: Alarm on Hours, Minutes and Seconds */
  sAlarm.Alarm = RTC_ALARM_A;
  sAlarm.AlarmDateWeekDay = RTC_WEEKDAY_MONDAY;
  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
  sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;
  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
  sAlarm.AlarmTime.TimeFormat = RTC_HOURFORMAT12_AM;
  sAlarm.AlarmTime.Hours = h;//0x02;
  sAlarm.AlarmTime.Minutes = m;//0x20;
  sAlarm.AlarmTime.Seconds = s;//0x10;
  sAlarm.AlarmTime.SubSeconds = 0x56;
 
  if(HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm,RTC_FORMAT_BCD) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }
}
 
 
/**
  * @brief  Display the current time.
  * @param  showdate : pointer to buffer
  * @retval None
  */
static void RTC_DateShow(char* showdate)
{
  RTC_DateTypeDef sDate;
  RTC_TimeTypeDef sTime;
 
  /* Get the RTC current Time */
  HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
  /* Get the RTC current Date */
  HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
  /* Display time Format : hh:mm:ss */
  sprintf(showdate,"%02d-%02d-%04d\r\n",sDate.Date, sDate.Month, 2000+sDate.Year);
  CDC_Transmit_FS((uint8_t *)showdate, strlen(showdate));
}
 
/**
  * @brief  Display the current time.
  * @param  showtime : pointer to buffer
  * @retval None
  */
static void RTC_TimeShow(char* showtime)
{
  RTC_DateTypeDef sDate;
  RTC_TimeTypeDef sTime;
 
  /* Get the RTC current Time */
  HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
  /* Get the RTC current Date */
  HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
  /* Display time Format : hh:mm:ss */
  sprintf(showtime,"%02d:%02d:%02d\r\n",sTime.Hours, sTime.Minutes, sTime.Seconds);
  CDC_Transmit_FS((uint8_t *)showtime, strlen(showtime));
}
 
/**
  * @brief  Display the next alarm time
  * @param  showalarm : pointer to buffer
  * @retval None
  */
static void RTC_AlarmShow(char* showalarm)
{
  RTC_AlarmTypeDef sAlarm;
 
  /* Get the RTC current Time */
  HAL_RTC_GetAlarm(&hrtc,&sAlarm,RTC_ALARM_A,FORMAT_BIN);
  /* Display time Format : hh:mm:ss */
  sprintf(showalarm,"Next alarm @%02d:%02d:%02d\r\n", sAlarm.AlarmTime.Hours, sAlarm.AlarmTime.Minutes, sAlarm.AlarmTime.Seconds);
  CDC_Transmit_FS((uint8_t *)showalarm, strlen(showalarm));
}
/* USER CODE END 0 */
  /* USER CODE BEGIN 2 */
  RTC_DateShow(string);
  RTC_TimeShow(string);
  RTC_AlarmShow(string);
 
  RTC_Config(0x02,0x20,0x00);
  RTC_Alarm(0x02,0x20,0x10);
 
  RTC_DateShow(string);
  RTC_TimeShow(string);
  RTC_AlarmShow(string);
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  RTC_TimeShow(string);
 
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  uint32_t current_second = HAL_GetTick();
  if (current_second - last_second > 1) {
    last_second = current_second;
    sprintf(string, "Loop: %d\r\n", i++);
    CDC_Transmit_FS((uint8_t *)string, strlen(string));
  }
  }
  /* USER CODE END 3 */
/* USER CODE BEGIN 4 */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) {
  RTC_AlarmTypeDef sAlarm;
  HAL_RTC_GetAlarm(hrtc,&sAlarm,RTC_ALARM_A,FORMAT_BIN);
  if(sAlarm.AlarmTime.Seconds>54) sAlarm.AlarmTime.Seconds=0;
  else sAlarm.AlarmTime.Seconds=sAlarm.AlarmTime.Seconds+5;
  while(HAL_RTC_SetAlarm_IT(hrtc, &sAlarm, FORMAT_BIN)!=HAL_OK) Error_Handler();
  RTC_AlarmShow(string);
  Toggle_Leds();
}
 
/**
  * @brief  Toggle LEDs
  * @param  None
  * @retval None
  */
void Toggle_Leds(void)
{
  HAL_GPIO_TogglePin(LD_R_GPIO_Port, LD_R_Pin);
  HAL_Delay(100);
  HAL_GPIO_TogglePin(LD_G_GPIO_Port, LD_G_Pin);
  HAL_Delay(100);
}
/* USER CODE END 4 */

Una captura de la salida en terminal mostraría el resultado de la prueba anterior.

A modo de propuestas se plantea:

  • la creación de un reloj con alarma con salida LCD e interfaz de entrada de Joystick que permita la puesta en hora y alarma por dígitos individuales
  • la creación de un programador de acciones en base a múltiples configuraciones de alarmas que se almacenan en una lista enlazada dinámica ordenada
  • pub/silab4.txt
  • Última modificación: 2020/09/28 11:44
  • (editor externo)