Blink con interrupt (TIM16, interrupt)

I timer possono essere utilizzati per generare interrupt con un intervallo di tempo prefissato. Quando il timer genera un interrupt la CPU interrompe il programma in esecuzione ed esegue un sottoprogramma, chiamato routine di interrupt. Terminata la routine d'interrupt, la CPU torna al programma precedentemente interrotto e ne prosegue l'esecuzione.

Più in generale i timer possono essere utilizzati per attivare una varietà di interrupt. Per vedere quali sono è possibile per esempio vedere la sezione 72.2.9 del documento di riferimento API HAL/LL (UM1884 reperibile all'indirizzo http://www.st.com/content/ccc/resource/technical/document/user_manual/63/a8/8f/e3/ca/a1/4c/84/DM00173145.pdf/files/DM00173145.pdf/jcr:content/translations/en.DM00173145.pdf) per un elenco di possibili callback di interrupt supportati dalle librerie HAL.

L'interrupt utilizzato in questa applicazione è il più semplice: quando il timer raggiunge il suo valore massimo, tornerà a 0 e attiverà un interrupt. Per ottenere ciò, imposteremo i prescaler di TIM16 come indicato nell'esperienza Blink con timer (TIM16, non-blocking mode) in modo che si aggiorni con una frequenza di  scatti a una frequenza di 10 kHz.

In primo luogo sarà necessario impostare i prescaler del clock


Quindi imposteremo il Prescaler (PSC) di TIM16 a 999 (scriveremo "1000 - 1" per ricordare che un valore di prescaler di 999 significa effettivamente utilizzare un divisore di clock di 1000) e il valore del conteggio Counter Period a 4999 (nuovamente, si scriverà "5000 - 1" perché il rollover da 4999 a 0 conta come un ulteriore aggiornamento e quindi gli aggiornamenti prima di generare l'interrupt saranno 5000).

A questo punto è necessario attivare le interruzioni. Fare clic sulla scheda NVIC Settings e abilitare TIM1 update interrupt and TIM16 global interrupt setting. Quando avviene un'interruzione, in inglese si dice "the interrupt fire".

Salvare per generare il codice e aprire main.c e aggiungere le righe di codice evidenziate in grassetto tra le 2 sezioni:

  • /* USER CODE BEGIN 2 */ e /* USER CODE END 2 */
  • /* USER CODE BEGIN 4 */ e /* USER CODE END 4 */

/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim16;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM16_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM16_Init();
/* USER CODE BEGIN 2 */

HAL_TIM_Base_Start_IT(&htim16); /* CODICE DA AGGIUNGERE */

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

/** Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 10;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV16;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}

/**
* @brief TIM16 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM16_Init(void)
{

/* USER CODE BEGIN TIM16_Init 0 */

/* USER CODE END TIM16_Init 0 */

/* USER CODE BEGIN TIM16_Init 1 */

/* USER CODE END TIM16_Init 1 */
htim16.Instance = TIM16;
htim16.Init.Prescaler = 1000-1;
htim16.Init.CounterMode = TIM_COUNTERMODE_UP;
htim16.Init.Period = 5000-1;
htim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim16.Init.RepetitionCounter = 0;
htim16.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim16) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM16_Init 2 */

/* USER CODE END TIM16_Init 2 */

}

/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{

/* USER CODE BEGIN USART2_Init 0 */

/* USER CODE END USART2_Init 0 */

/* USER CODE BEGIN USART2_Init 1 */

/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */

/* USER CODE END USART2_Init 2 */

}

/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();

/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, SMPS_EN_Pin|SMPS_V1_Pin|SMPS_SW_Pin, GPIO_PIN_RESET);

/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin, GPIO_PIN_RESET);

/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

/*Configure GPIO pins : SMPS_EN_Pin SMPS_V1_Pin SMPS_SW_Pin */
GPIO_InitStruct.Pin = SMPS_EN_Pin|SMPS_V1_Pin|SMPS_SW_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/*Configure GPIO pin : SMPS_PG_Pin */
GPIO_InitStruct.Pin = SMPS_PG_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(SMPS_PG_GPIO_Port, &GPIO_InitStruct);

/*Configure GPIO pin : LD4_Pin */
GPIO_InitStruct.Pin = LD4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LD4_GPIO_Port, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) /* CODICE DA AGGIUNGERE*/
{ /* CODICE DA AGGIUNGERE */
if (htim == &htim16 ) /* CODICE DA AGGIUNGERE */
{ /* CODICE DA AGGIUNGERE */
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13); /* CODICE DA AGGIUNGERE */
} /* CODICE DA AGGIUNGERE */
} /* CODICE DA AGGIUNGERE */


/* USER CODE END 4 */

/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Una volta completato l'inserimento, compilare e caricare il codice nella scheda Nucleo come si è fatto precedentemente (per esempio nell'esercizio blink (blocking mode) ).

Le linee di codice aggiunte sono ora:

  • HAL_TIM_Base_Start_IT(&htim16);
che fa partire il conteggio del timer attivando gli interrupt (HAL_TIM_Base_Start invece fa solo partire il timer senza attivare le interruzioni)
  • Il blocco
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)   
    {                                                           
        if (htim == &htim16 )                                
        {                                                      
                     /* istruzioni */
        }                                                     
    } 

che è un gestore di interruzioni. Le librerie HAL avviano la routine di servizio delle interruzioni principale (ISR) quando si verifica l'interruzione nel microcontrollore. Ad un certo punto dell'ISR, accoresi che l'interruzione è stata generata da un timer, le librerie HAL chiameranno HAL_TIM_PeriodElapsedCallback(), per cui è necessario indicare cosa deve fare il codice in questo frangente. In caso contrario l'interruzione non produrrà alcun effetto.

Nel nostro caso nella definizione è necessario controllare che il puntatore (htim) passato dalla routinr di gestione delle interruzioni sia quello del TIM16 (e non sia un altro timer che ha generato l'interruzione).

Si noti che qualora ci siano più timer attivi che generano interruzioni, è necessario realizzare una routine di callback per ognuno di essi ed è per questo motivo che controlliamo il puntatore (handle) dell'istanza del timer (htim) per distinguere tra i possibili timer.
  • HAL_GPIO_TogglePin (GPIOB, GPIO_PIN_13);
che cambia lo stato del led.

Si noti come un utilizzo corretto delle interruzioni permetta di scrivere codice con un numero limitato di istruzioni.


Fonti:

Parte del contenuto della lezione deriva dall'articolo di Shawn Hymel all'indirizzo: https://www.digikey.com/en/maker/projects/getting-started-with-stm32-timers-and-timer-interrupts/d08e6493cefa486fb1e79c43c0b08cc6.

Ultime modifiche: sabato, 18 settembre 2021, 17:42