Registro de aprendizagem STM32F4XX: captura de entrada do temporizador

Prefácio

Use a placa principal da Tecnologia Anti-Ke STM32F407VET6 M1, oscilador de cristal de relógio principal (HSE) 8M integrado, oscilador de cristal externo de baixa velocidade (LSE) de 32,768 kHz. Contém um LED do usuário e um botão do usuário.

alvo da missão

Use a captura de entrada do temporizador para medir o tempo de alto nível da onda quadrada.
O grupo de alunos não tem um gerador de sinal em casa durante as férias e só pode usar um temporizador para gerar uma onda quadrada para medição.
Sim, eu me testo.

processo de design

Primeiro, gere uma onda PWM. Aqui eu escolho o temporizador avançado 1, que é TIM1, para configuração.
O TIM1 é montado no barramento de alta velocidade APB2. Eu uso a biblioteca padrão para configurar o relógio do sistema para 168MHZ e a frequência do barramento também é 168MHZ.
(O padrão da biblioteca padrão é configurar o HSE como a fonte do relógio, e a configuração padrão da biblioteca padrão na minha placa não é compatível, então configurei o HSI como o relógio do sistema sozinho. O código específico está em meu blog anterior, aqui é o Portal: Link: STM32F4XX Registro de aprendizagem: o temporizador interrompe a onda PWM analógica para obter luz de respiração .)
Insira a descrição da imagem aqui

Código de inicialização TIM1

O pino multiplexado PA8 é o
Insira a descrição da imagem aqui
código pin do mapeamento do pino do canal 1 .

GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);

Observe aqui que o pin clock de stm32f4 é AHB1 bus

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

Vejo outros códigos na Internet, parece que o pino do chip F1 não está neste ônibus. Se você não prestar atenção ao digitar o código, pode ignorá-lo facilmente (não me pergunte como eu sei, eu acabou de mudar os parâmetros antes, e o sistema não. Irá relatar um erro)

O temporizador é inicializado para comparação de saída, modo de onda PWM 1. O efeito deste modo pode ser encontrado na folha de dados.
·
·
·
·
·
Esqueça, acho que você não pode verificar, vou postar. [Cabeça de cachorro] O
Insira a descrição da imagem aqui
período do cronômetro está definido para 100ms, que é 10HZ, e o
cálculo é 168 000 000/16800/1000 = 10HZ.
168M primeiro obtém uma frequência de 1 000 0 Hz por meio de 16800 múltiplos
e então o contador irá transbordar a cada 1000 contagens.

o de cima

É o que aprendi quando era iniciante, mas ainda estou um pouco confuso sobre como calcular o período. Mais tarde, aprendi sobre isso depois de muita prática.

O cronômetro precisa funcionar definitivamente precisa de um relógio, e esse relógio é dividido pelo relógio do sistema.
Então, para que serve o relógio temporizador? Isso pode exigir um pouco de conhecimento sobre eletricidade.


Então, a maneira mais fácil de entender é, visto que o valor da contagem do cronômetro muda automaticamente, qual é o intervalo entre cada mudança? ? ? ? ? ? ? ? ? ? ? ?

Esse é, naturalmente, o período do relógio do temporizador, que é a frequência obtida pela divisão do relógio do sistema.
Esse entendimento é mais fácil de calcular.


A frequência dividida pelo relógio principal é de 1 000 0Hz convertido em um período, que é 100 us, ou seja, a cada 100 us o valor do temporizador muda em 1.
Em seguida, o cronômetro transborda a cada 1000 contagens.
Existem 1000 100us 1000 00us = 100ms = 0,1s


Isso é muito mais fácil de entender.
Então é o processo normal de configuração, que não é descrito aqui.

   TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
   TIM_OCInitTypeDef  TIM_OCInitStructure;
   NVIC_InitTypeDef NVIC_InitStructure;
   GPIO_InitTypeDef  GPIO_InitStructure;
   
  /* 1. 使能时钟 */
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
   
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;				 
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
   GPIO_InitStructure.GPIO_OType= GPIO_OType_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;		
   GPIO_Init(GPIOA,&GPIO_InitStructure);
   
   /* 2. 配置定时器参数 */
   TIM_TimeBaseStructure.TIM_Prescaler         = 16800 - 1;              /* 定时器时钟分频系数 */   
   TIM_TimeBaseStructure.TIM_Period            = 1000 - 1;               /* 定时器重装载值 */              
   TIM_TimeBaseStructure.TIM_ClockDivision     = TIM_CKD_DIV1;     
   TIM_TimeBaseStructure.TIM_CounterMode       = TIM_CounterMode_Up;     /* 计数器模式 */
   TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;                      /* 重复计数值 */
   TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); 

   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;					//设置每次进入中断为电平翻转模式
   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;		//输出开
   TIM_OCInitStructure.TIM_Pulse = 550;											//设置最初CCR为0,这样一配置完就进去中断服务程序
   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;			//设置最开始的电平为高电平
   TIM_OC1Init(TIM1, &TIM_OCInitStructure);									//载入寄存器	
   
   


   TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  //CH1预装载使能	 
   
   TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
   
   TIM_Cmd(TIM1, ENABLE);  //使能TIM1

Código de inicialização TIM5

Em seguida, uso o temporizador universal TIM5 para a captura de entrada.
De acordo com o manual de dados, o TIM5 é montado no barramento APB1. Quando a frequência do sistema é configurada para 168MHZ, a frequência do barramento é 84MHZ.
Ou seja, o temporizador pode ser alocado a um relógio de 84MHZ do sistema. Mas o cronômetro certamente não pode funcionar com um relógio tão alto, então ele deve ser dividido primeiro. Para a conveniência do cálculo, escolho 84 divisão de frequência. Portanto, a frequência de contagem do temporizador é de 1 000 000 MHz.
Ao mesmo tempo, defina um valor de contagem de 1000 para o contador. Ou seja, o valor da contagem muda 1000 vezes para gerar um evento de estouro.
Portanto, a frequência do tempo de estouro é 1 000 HZ, que é 1 ms uma vez.


A próxima etapa é a configuração do modo de captura de entrada. Aqui eu escolho o canal 1 como canal de injeção.
O pino PA0 de remapeamento é o temporizador 5, canal 1.

  • Observe aqui, eu vi muitos códigos STM32F1 para inicializar os pinos configurados como modo de entrada. O chip F4 deve ser configurado para o modo multiplex. Aqui é um pouco diferente. Se a configuração estiver incorreta, alguns locais podem não estar disponíveis. Já o encontrei antes, mas para todos os pinos remapeados, agora o configuro no modo de multiplexação (parece que não há relógio de multiplexação no código F4).
  • Então este código é para configurar o relógio do filtro digital
    TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM5_ICInitStructure.TIM_ICFilter = 0x02;
    

Aqui TIM_ICPSC_DIV1 significa: configurar captura 1 prescaler. A captura é realizada sempre que uma borda é detectada na entrada de captura. Em outras palavras, quando uma borda ascendente é detectada, ela é julgada como uma borda ascendente. Esta frase pode ser um pouco estranha. Se você colocar de outra forma

Capture assim que uma borda ascendente for detectada. Em outras palavras, ele pode ser definido para detectar a borda ascendente duas, quatro vezes e oito vezes para capturar a configuração.

A próxima variável de membro é configurar o período de clock do filtro.O período de clock do filtro é dividido diretamente pelo temporizador, que determina diretamente o período de amostragem do filtro.A introdução na folha de dados é postada aqui.
Insira a descrição da imagem aqui

  • Pode-se ver na figura que o fator real que o temporizador considera uma mudança de sinal de borda como uma mudança real (ao invés de ruído) é que o filtro captura N vezes continuamente no período como uma borda de subida antes de ser reconhecido como uma mudança Beira.
    A captura aqui pode ser um pouco confundida com a captura do prescaler anterior. Faça esse entendimento aqui

1. A captura do filtro é usada para determinar se o sinal está mudando ao invés de tremido devido ao ruído. Quando um evento é continuamente detectado N vezes no ciclo, o evento é considerado uma borda válida, caso contrário, é uma confusão de ruído .
2. A configuração do prescaler é usada para detectar quando um evento de interrupção deve ser disparado. Ou seja, a interrupção é disparada apenas quando há N bordas crescentes.

O código de inicialização está postado abaixo. Habilite a interrupção de atualização e interrupção de captura.

TIM_ICInitTypeDef  TIM5_ICInitStructure;
 
void TIM5_Cap_Init(void)
{
    
    	 
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);	//使能TIM5时钟
 	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5);
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  //PA0 清除之前设置  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //PA0 输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;		
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);						 //PA0 下拉
	
	//初始化定时器5 TIM5	 
	TIM_TimeBaseStructure.TIM_Period = 84-1; //设定计数器自动重装值 
	TIM_TimeBaseStructure.TIM_Prescaler =1000-1; 	//预分频器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  
	//初始化TIM5输入捕获参数
	TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 	选择输入端 IC1映射到TI1上
  	TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
  	TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
  	TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
  	TIM5_ICInitStructure.TIM_ICFilter = 0x02;//IC1F=0000 配置输入滤波器 不滤波
  	TIM_ICInit(TIM5, &TIM5_ICInitStructure);
	
	//中断分组初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 
	
	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断	
	
    TIM_Cmd(TIM5,ENABLE ); 	//使能定时器5
 
}

Interromper função de serviço


Encontrei um problema aqui. Primeiro, deixe-me falar sobre minhas idéias. Espero que alguns caras grandes possam ver onde está o problema e me ajudar a resolvê-lo.

  • Aqui, originalmente planejei registrar o valor de contagem atual do temporizador, que é o valor em CNT, quando a interrupção do evento de captura for acionada. Em seguida, calcule valores mais precisos.
TIM5->CNT  //该寄存器中即为定时器当前计数值

Deixe-me falar sobre o método primeiro: depois de detectar a borda ascendente, defina a captura para a captura da borda descendente primeiro e registre o número de interrupções que entram no evento de atualização do temporizador. De acordo com o método de configuração agora, é para registrar um interromper por 1 ms. Quando a borda descendente é capturada, o valor do contador atual é registrado e, em seguida, o tempo em que o sinal está na borda ascendente é calculado.

Poste o código abaixo (rejeite a pergunta)

u8  TIM5CH1_CAPTURE_STA=0;	//输入捕获状态		    				
float TIM5CH1_CAPTURE_VAL;	//输入捕获值
float TIM5CH1_CAPTURE_VAL_1,TIM5CH1_CAPTURE_VAL_2;
float TIM5CH1_CAPTURE_VAL_N=0;
void TIM5_IRQHandler(void)
{
    
    
	if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
	{
    
    
//		if( TIM5CH1_CAPTURE_STA == 0 )//未捕获到上升沿
//		{
    
    
//			TIM5CH1_CAPTURE_VAL_N = 0;
//		}
		if(TIM5CH1_CAPTURE_STA==1)//未捕获到上升沿
		{
    
    
			TIM5CH1_CAPTURE_VAL_N++;
//			TIM5->CNT = 0 ;
		}
	}
	if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
	{
    
    
		if(TIM5CH1_CAPTURE_STA==0)
		{
    
    
//			TIM5CH1_CAPTURE_VAL_1=TIM5->CNT;
			TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);
			TIM5->CNT = 0 ;
			TIM5CH1_CAPTURE_STA=1;
		}
		else
		{
    
    
//			TIM5CH1_CAPTURE_VAL_2=TIM5->CNT;
			TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);
			TIM5->CNT = 0 ;
			TIM5CH1_CAPTURE_VAL=TIM5CH1_CAPTURE_VAL_N;
			/*+(((float)TIM5CH1_CAPTURE_VAL_2+1000-(float)TIM5CH1_CAPTURE_VAL_1)/1000)*/
			TIM5CH1_CAPTURE_VAL_N=0;
			TIM5CH1_CAPTURE_STA=0;
		}
	}
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
}

Código de Schrodinger

Bem, isso é porque não sou bom em aprender. Fale sobre o problema.

Como você pode ver, no código acima, não implementei totalmente a ideia que falei, mas apenas mantive a parte da interrupção do tempo de cálculo com atualização.

Comentei o valor da contagem do temporizador de gravação e o código de cálculo relacionado.

A razão é que existem problemas estranhos nesta parte durante a operação real.

Quando eu alcanço um ponto de interrupção na parte de atribuição do código, posso ver que o valor da variável está mudando constantemente por meio da depuração.

/*就是这俩行代码,打断点和不打断点是两种状态。*/
		TIM5CH1_CAPTURE_VAL_1=TIM5->CNT;
		TIM5CH1_CAPTURE_VAL_2=TIM5->CNT;

mas! Quando não interrompo esta frase, vou diretamente para a última frase da função de interrupção do serviço durante a depuração. Embora o valor do contador esteja mudando constantemente, o valor da variável que registra o valor do contador não muda e permanece 42 (decimal).
Quando eu atingi o ponto de interrupção novamente, descobri que essa variável começou a mudar alegremente novamente (sorriso).

Espero que alguns caras grandes possam ver qual é o problema.
Você pode comentar se precisar de arquivos de engenharia.

· O código acima é eficaz

Acho que você gosta

Origin blog.csdn.net/YuHWEI/article/details/113195203
Recomendado
Clasificación