Notas de estudo STM32CubeMX (5) - uso de interface de cronômetro básico

1. Introdução ao cronômetro

Série STM32F1, além de produtos baseados na Internet, um total de 8temporizadores, divididos em temporizadores básicos, temporizadores de uso geral e temporizador avançado.
Temporizador básico TIM6 e TIM7um temporizador que conta apenas 16 bits, apenas o tempo, sem E / S externo.
O temporizador de uso geral TIM2/3/4/5 é um temporizador de 16 bits que pode fazer uma contagem crescente / regressiva. Ele pode ser temporizado, comparação de saída e captura de entrada. Cada temporizador tem quatro E / S externos.
O temporizador avançado TIM1/8 é um temporizador de 16 bits que pode fazer uma contagem crescente / regressiva. Ele pode ser temporizado, comparação de saída, captura de entrada e sinais de saída complementares do motor trifásico. Cada temporizador tem 8 E / S externos.

2. Nova construção

1. Abra o software STM32CubeMX e clique em "Novo Projeto"

2. Escolha MCU e pacote

3. Defina as
configurações de RCC do relógio , selecione HSE (relógio externo de alta velocidade),

selecione Configuração de relógio para Ressonador de cristal / cerâmica (oscilador de cristal / ressonador de cerâmica) , configure o relógio do sistema SYSCLK para 72MHz,
modifique o valor de HCLK para 72, e pressione Enter. Modifique automaticamente todas as configurações

4. É
uma etapa muito importante para configurar o modo de depuração , caso contrário, a
configuração do depurador SYS não será reconhecida após o primeiro programa de programação , selecione Depurar como fio serial

5. Defina as configurações de GPIO
GPIO, encontre o pino correspondente da luz LED na figura à direita, selecione GPIO_Output, a saída de luzes de baixo nível acende, você pode adicionar um rótulo personalizado

Três, temporizador básico TIM6

3.1 Configuração de parâmetros

Na Timersseleção de TIM6configurações, e verifique a Activatedativação

Em Parameter Settingsparâmetros específicos.

Tclk é o relógio interno CK_INT, que é dividido pelo prescaler APB1. Se o coeficiente do prescaler APB1 for igual a 1, a frequência permanece inalterada, caso contrário, a frequência é multiplicada por 2 e o coeficiente do prescaler APB1 na função de biblioteca é 2, isto é PCLK1 = 36M, conforme mostrado na figura, o relógio temporizador Tclk = 36 * 2 = 72M.

Tempo de estouro do temporizador:

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

  • Relógio temporizador Tclk: 72 MHz
  • Prescaler psc: 71
  • Registro de recarga automática chega: 999

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

  • Prescaler (número do prescaler do relógio): 72-1 则驱动计数器的时钟 CK_CNT = CK_INT(即72MHz)/(71+1) = 1MHz
  • Modo de contador: Up (modo de contagem crescente) 基本定时器只能是向上计数
  • Período do contador (valor de recarga automática): 1000-1 则定时时间 1/CK_CLK*(999+1) = 1ms
  • auto-reload-preload (recarga automática): Habilitar (habilitar) `
  • Parâmetros TRGO (saída de disparo): não habilitado 在定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换)

3.2 configuração NVIC

Habilitar interrupção do cronômetro

3.3 Gerar código

Nomes de item de caminho e

seleção de entrada de item de um ambiente de desenvolvimento de aplicativo IDE MDK-ARM V5

cada periférico gera um ’.c/.h’arquivo separado
não gancho: Todo o código de inicialização é gerado em main.c
verificado: arquivo de código de inicialização gerado no periférico associado. Por exemplo, o código de inicialização GPIO é gerado em gpio.c.
Clique em GERAR CÓDIGO para gerar o código

3.4 Modificar a função de retorno de chamada de interrupção

Abra o stm32f1xx_it.carquivo de rotina de serviço de interrupção, encontre a função de serviço de TIM6_IRQHandler()
interrupção de rotina de serviço de interrupção TIM6, que é chamada de manipulador de interrupção de timerHAL_TIM_IRQHandler()

Abra o stm32f1xx_hal_tim.carquivo, encontre o protótipo do manipulador de interrupção do temporizador HAL_TIM_IRQHandler(), sua função principal é determinar que tipo de evento a interrupção do temporizador é gerada, limpar o sinalizador de interrupção e, em seguida, chamar a função de retorno de chamada de interrupção HAL_TIM_PeriodElapsedCallback().

/ * NOTA: Esta função não deve ser modificada, quando o retorno de chamada é necessário,
o HAL_GPIO_EXTI_Callback pode ser implementado no arquivo do usuário
* /
Esta função não deve ser alterada, se você precisar usar a função de retorno de chamada, reimplemente a função no arquivo do usuário.

HAL_TIM_PeriodElapsedCallback()De acordo com o prompt oficial, devemos definir a função novamente, que __weaké um sinalizador enfraquecido. A função com esta é uma função enfraquecida, ou seja, você pode escrever uma função com exatamente o mesmo nome e parâmetros em outro lugar, e o compilador irá ignorar esta função. Em vez disso UNUSED(htim), execute a função que você escreveu; e , esta é uma definição à prova de erros. Quando o número do cronômetro passado não faz nenhum processamento, o compilador não reporta um aviso. Na verdade, não precisamos mais nos preocupar com a função de serviço de interrupção quando estamos desenvolvendo. Só precisamos encontrar a função de retorno de chamada de interrupção e reescrevê-la. Há outro aspecto muito conveniente dessa função de retorno de chamada que não está refletido aqui. Quando várias interrupções são habilitadas, STM32CubeMX irá organizar automaticamente as funções de serviço de várias interrupções juntas e chamar uma função de retorno de chamada, ou seja, não importa quantas interrupções, nós só precisamos reescrever uma função de retorno de chamada e julgar o número do temporizador de entrada.

Então, nós apenas stm32f1xx_it.cadicionamos o final deste documentoHAL_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 Adicionar função de início do cronômetro

Agora entre na função principal e adicione a função timer antes do loop while HAL_TIM_Base_Start_IT(). O htim6 passado aqui é a estrutura logo após o timer ter sido inicializado.

/**
  * @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 */
}

Agora, o fenômeno experimental é que a luz do LED muda uma vez a cada 1 segundo

3.6 Biblioteca HAL e comparação de código de biblioteca padrão

STM32CubeMX usa o código gerado pela biblioteca 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); 

Use o código de biblioteca padrão 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();Correspondência BASIC_TIM_Config();BASIC_TIM_NVIC_Config();
HAL_TIM_Base_Init(&htim6)correspondência TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure)
HAL_TIM_Base_Start_IT(&htim6);correspondênciaRCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

Quatro, questões que precisam de atenção

Código de usuário a ser adicionado USER CODE BEGIN Ne USER CODE END Nentre, caso contrário, na próxima utilização após STM32CubeMX regenerar o código, ele será excluído.


Escrito por Leung em 14 de janeiro de 2021

• Referência: Tutorial 3 da série STM32CubeMX : Tutorial de
    combate real do cronômetro básico STM32CubeMX (4) - Cronômetro básico (ainda iluminando)
    "Embedded-STM32 Development Guide" Parte 2 Noções básicas - Capítulo 4 Timer (Biblioteca HAL)

Acho que você gosta

Origin blog.csdn.net/qq_36347513/article/details/112599813
Recomendado
Clasificación