Notes d'étude STM32CubeMX (5) - Utilisation de l'interface de minuterie de base

1. Introduction à la minuterie

Série STM32F1, en plus des produits basés sur Internet, un total de 8minuteries, divisées en minuteries de base, minuteries à usage général et minuterie avancée.
Minuterie de base TIM6 et TIM7une minuterie comptant seulement 16 bits, seulement la synchronisation, pas d'E / S externe.
Le temporisateur à usage général TIM2/3/4/5 est un temporisateur de 16 bits qui peut compter vers le haut / vers le bas. Il peut être chronométré, comparaison de sortie et capture d'entrée. Chaque temporisateur dispose de quatre entrées / sorties externes.
Le temporisateur avancé TIM1/8 est un temporisateur de 16 bits qui peut compter vers le haut / vers le bas. Il peut être chronométré, la comparaison de sortie, la capture d'entrée et les signaux de sortie complémentaires du moteur triphasé.

2. Nouvelle construction

1. Ouvrez le logiciel STM32CubeMX et cliquez sur "Nouveau projet"

2. Choisissez MCU et package

3. Configurez les
paramètres de l'horloge RCC, sélectionnez HSE (horloge haute vitesse externe),

sélectionnez Configuration de l'horloge pour le résonateur à cristal / céramique (oscillateur à cristal / résonateur en céramique) , configurez l'horloge système SYSCLK à 72 MHz,
modifiez la valeur de HCLK à 72, et appuyez sur Entrée. Modifiez automatiquement toutes les configurations

4. C'est
une étape très importante pour configurer le mode de débogage , sinon le
paramètre SYS du débogueur ne sera pas reconnu après le premier programme de programmation , sélectionnez Déboguer comme Serial Wire

5. Configurez les
paramètres GPIO GPIO, recherchez la broche correspondante de la lumière LED dans la figure de droite, sélectionnez GPIO_Output, le niveau de sortie bas s'allume, vous pouvez ajouter une étiquette personnalisée

Trois, minuterie de base TIM6

3.1 Configuration des paramètres

Dans la Timerssélection des TIM6paramètres, et vérifiez l' Activatedactivation

Dans Parameter Settingsdes paramètres spécifiques.

Tclk est l'horloge interne CK_INT, qui est divisée par le prédicateur APB1. Si le coefficient de prédécaleur APB1 est égal à 1, la fréquence reste inchangée, sinon la fréquence est multipliée par 2 et le coefficient de prédécaleur APB1 dans la fonction de bibliothèque est 2, c'est-à-dire PCLK1 = 36M, comme le montre la figure, l'horloge de minuterie Tclk = 36 * 2 = 72M.

Temps de dépassement de la minuterie:

Tout = 1 / (Tclk / (psc + 1)) ∗ (arr + 1)

  • Horloge Tclk: 72 MHz
  • Prescaler PSC: 71
  • Registre de rechargement automatique arr: 999

即 Tout = 1/(72MHz/(71+1))∗(999+1) = 1ms

  • Prescaler (numéro de prescaler d'horloge): 72-1 则驱动计数器的时钟 CK_CNT = CK_INT(即72MHz)/(71+1) = 1MHz
  • Mode compteur: Up (mode de comptage) 基本定时器只能是向上计数
  • Période du compteur (valeur de rechargement automatique): 1000-1 则定时时间 1/CK_CLK*(999+1) = 1ms
  • auto-reload-preload (rechargement automatique): Activer (activer) `
  • Paramètres TRGO (sortie de déclenchement): non activé 在定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换)

3.2 Configuration NVIC

Activer l'interruption du minuteur

3.3 Générer du code

Noms des éléments de chemin et

sélection des entrées des éléments d'un environnement de développement d'application IDE MDK-ARM V5

chaque périphérique génère un ’.c/.h’fichier séparé
non hook: tout le code d'initialisation est généré dans main.c
vérifié: fichier de code d'initialisation généré dans le périphérique associé. Par exemple, le code d'initialisation GPIO est généré dans gpio.c.
Cliquez sur GÉNÉRER LE CODE pour générer du code

3.4 Modifier la fonction de rappel d'interruption

Ouvrez le stm32f1xx_it.cfichier de routine de service d'interruption, recherchez la fonction de service d' TIM6_IRQHandler()
interruption de routine de service d'interruption TIM6 qui est appelée le gestionnaire d'interruption de minuterieHAL_TIM_IRQHandler()

Ouvrez le stm32f1xx_hal_tim.cfichier, trouvez le prototype du gestionnaire d'interruption de minuterie HAL_TIM_IRQHandler(), son rôle principal est de déterminer quel type d'événement quelle interruption de minuterie est générée, effacez l'indicateur d'interruption, puis appelez la fonction de rappel d'interruption HAL_TIM_PeriodElapsedCallback().

/ * REMARQUE: Cette fonction ne doit pas être modifiée, lorsque le rappel est nécessaire,
le HAL_GPIO_EXTI_Callback peut être implémenté dans le fichier utilisateur
* /
Cette fonction ne doit pas être modifiée, si vous devez utiliser la fonction de rappel, veuillez réimplémenter la fonction dans le fichier utilisateur.

HAL_TIM_PeriodElapsedCallback()Selon l'invite officielle, nous devrions redéfinir la fonction, qui __weakest un indicateur affaibli. La fonction avec ceci est une fonction affaiblie, c'est-à-dire que vous pouvez écrire une fonction avec exactement le même nom et les mêmes paramètres ailleurs, et le compilateur ignorera cette fonction. À la place UNUSED(htim), exécutez la fonction que vous avez écrite; et , il s'agit d'une définition à l'épreuve des erreurs. Lorsque le numéro de minuterie transmis n'effectue aucun traitement, le compilateur ne signale pas d'avertissement. En fait, nous n'avons plus besoin de nous soucier de la fonction de service d'interruption lorsque nous développons. Nous devons seulement trouver la fonction de rappel d'interruption et la réécrire. Il y a un autre aspect très pratique de cette fonction de rappel qui n'est pas reflété ici. Lorsque plusieurs interruptions sont activées, STM32CubeMX organise automatiquement les fonctions de service de plusieurs interruptions ensemble et appelle une fonction de rappel, c'est-à-dire que quel que soit le nombre d'interruptions, il suffit de réécrire une fonction de rappel et de juger le numéro de minuterie entrant.

Ensuite, nous stm32f1xx_it.cajoutons simplement le bas de ce documentHAL_TIM_PeriodElapsedCallback()

/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    
    
    static uint32_t time = 0;
	if(htim->Instance == TIM6)  // 定时器6基地址
	{
    
    
        // 自定义应用程序
        time++;           // 每1ms进来1次
        if(time == 1000)  // 每1秒LED灯翻转一次
        {
    
    
            HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
            time = 0;
        }
	}
}
/* USER CODE END 1 */

3.5 Ajouter la fonction de démarrage de la minuterie

Entrez maintenant dans la fonction principale et ajoutez la fonction timer avant la boucle while HAL_TIM_Base_Start_IT(). Le htim6 passé ici est la structure juste après l'initialisation du timer.

/**
  * @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_TIM6_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim6);  
  /* USER CODE END 2 */

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

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

Maintenant, le phénomène expérimental est que la lumière LED se retourne une fois toutes les 1 seconde

3.6 Comparaison de la bibliothèque HAL et du code de la bibliothèque standard

STM32CubeMX utilise le code généré par la bibliothèque HAL:

/**
  * @brief TIM6 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM6_Init(void)
{
    
    

  /* USER CODE BEGIN TIM6_Init 0 */

  /* USER CODE END TIM6_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {
    
    0};

  /* USER CODE BEGIN TIM6_Init 1 */

  /* USER CODE END TIM6_Init 1 */
  htim6.Instance = TIM6;
  htim6.Init.Prescaler = 72-1;
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim6.Init.Period = 1000-1;
  htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  /* USER CODE BEGIN TIM6_Init 2 */

  /* USER CODE END TIM6_Init 2 */
}

/**
  * @brief This function handles TIM6 global interrupt.
  */
void TIM6_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN TIM6_IRQn 0 */

  /* USER CODE END TIM6_IRQn 0 */
  HAL_TIM_IRQHandler(&htim6);
  /* USER CODE BEGIN TIM6_IRQn 1 */

  /* USER CODE END TIM6_IRQn 1 */
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    
    
    static uint32_t time = 0;
    if(htim->Instance == TIM6)
    {
    
    
        // 自定义应用程序
        time++;
        if(time == 1000)
        {
    
    
            HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
            time = 0;
        }
    }
}

HAL_TIM_Base_Start_IT(&htim6); 

Utilisez le code de bibliothèque standard STM32:

/**
 @brief 定时器中断配置(使用TIM6基本定时器)
 @param 无
 @return 无
*/
void BASIC_TIM_Config(void)
{
    
    
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	/*
	可以从上图看出基本定时器和通用定时器使用APB1总线,
	高级定时器使用APB2总线。
	*/
	// 开启定时器时钟,即内部时钟 CK_INT=72M
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

	/*
	预分频将输入时钟频率按1~65536之间的值任意分频,分频值决定了计数频率。
	计数值为计数的个数,当计数寄存器的值达到计数值时,产生溢出,发生中断。
	如系统时钟为72MHz,预分频 TIM_Prescaler = 71,
	计数值 TIM_Period = 1000,
	则 TIM_Period * (TIM_Prescaler + 1) / 72000000 = 0.001,
	即每1ms产生一次中断。
	*/
	// 自动重装载寄存器周的值(计数值)
	TIM_TimeBaseStructure.TIM_Period = 1000;
 
	// 累计 TIM_Period 个频率后产生一个更新或者中断
	// 时钟预分频数为 71,
	// 则驱动计数器的时钟 CK_CNT = CK_INT / (71+1)=1M
	TIM_TimeBaseStructure.TIM_Prescaler = 71;

	// 时钟分频因子 ,基本定时器没有,不用管
	//TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;

	// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
	//TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;

	// 重复计数器的值,基本定时器没有,不用管
	//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;

	/*
	完成时基设置
	*/
	// 初始化定时器
	TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);

	/*
	为了避免在设置时进入中断,这里需要清除中断标志位。
	如果是向上计数模式(基本定时器采用向上计数),
	则采用函数 TIM_ClearFlag(TIM6, TIM_FLAG_Update),
	清除向上溢出中断标志。
	*/
	// 清除计数器中断标志位
	TIM_ClearFlag(TIM6, TIM_FLAG_Update);

	// 使能计数器
	TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);

	// 开启计数器
	TIM_Cmd(TIM6, ENABLE);

	// 暂时关闭定时器的时钟,等待使用
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, DISABLE);
}

/**
 @brief NVIC初始化(使用TIM6基本定时器)
 @param 无
 @return 无
*/
void BASIC_TIM_NVIC_Config(void)
{
    
    
	NVIC_InitTypeDef NVIC_InitStructure;
	// 设置中断组为 0
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
	// 设置中断来源
	NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
	// 设置主优先级为 0
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	// 设置抢占优先级为 3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

#define BASIC_TIM_IRQHandler TIM6_IRQHandler
// 1ms发生一次中断,time 记录中断次数
uint16_t time;

void BASIC_TIM_IRQHandler(void)
{
    
    
	if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
	{
    
    
		time++;
		TIM_ClearITPendingBit(TIM6, TIM_FLAG_Update);
	}
}

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

MX_TIM6_Init();Correspondance BASIC_TIM_Config();BASIC_TIM_NVIC_Config();
HAL_TIM_Base_Init(&htim6)correspondance TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure)
HAL_TIM_Base_Start_IT(&htim6);correspondanceRCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

Quatre, les questions nécessitant une attention

Code utilisateur à ajouter USER CODE BEGIN Net USER CODE END Nentre, sinon la prochaine utilisation après que STM32CubeMX régénère le code, il sera supprimé.


Écrit par Leung le 14 janvier 2021

• Référence: Tutoriel de la série STM32CubeMX 3: Minuterie de base
    Tutoriel de combat réel STM32CubeMX (4) - Minuterie de base (éclairage fixe)
    "Guide de développement Embedded-STM32" Partie 2 Notions de base - Chapitre 4 Minuterie (bibliothèque HAL)

Je suppose que tu aimes

Origine blog.csdn.net/qq_36347513/article/details/112599813
conseillé
Classement