Registro de aprendizaje STM32F4XX: captura de entrada del temporizador

Prefacio

Utilice la placa base de la tecnología Anti-Ke STM32F407VET6 M1, oscilador de cristal de reloj principal (HSE) de 8M integrado, oscilador de cristal externo de baja velocidad (LSE) de 32,768 kHz. Contiene un LED de usuario y un botón de usuario.

objetivo de la misión

Utilice la captura de entrada del temporizador para medir el tiempo de alto nivel de la onda cuadrada.
La fiesta de estudiantes no tiene un generador de señal en casa durante las vacaciones, y solo puede usar un temporizador para generar una onda cuadrada para la medición.
Sí, me pongo a prueba.

proceso de diseño

Primero, genere una onda PWM, aquí elijo el temporizador avanzado 1, que es TIM1, para la configuración.
TIM1 está montado en el bus de alta velocidad APB2. Utilizo la biblioteca estándar para configurar el reloj del sistema a 168MHZ, y la frecuencia del bus también es de 168MHZ.
(El valor predeterminado de la biblioteca estándar es configurar HSE como fuente de reloj, y la configuración predeterminada de la biblioteca estándar en mi placa no es compatible, por lo que configuré HSI como el reloj del sistema por mí mismo. El código específico está en mi blog anterior, aquí es Portal: Enlace: STM32F4XX Registro de aprendizaje: El temporizador interrumpe la onda PWM analógica para lograr la luz de respiración .)
Inserte la descripción de la imagen aquí

Código de inicialización TIM1

El pin multiplexado PA8 es el
Inserte la descripción de la imagen aquí
código pin del mapeo de pin del canal 1 .

GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);

Tenga en cuenta aquí que el reloj de pines de stm32f4 es el bus AHB1

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

Veo otros códigos en Internet, parece que el reloj del pin del chip F1 no está en este bus. Si no prestas atención al escribir el código, puedes ignorarlo fácilmente (no me preguntes cómo lo sé, acaba de cambiar los parámetros antes, y el sistema no. Informará un error)

El temporizador se inicializa para la comparación de salida, modo de onda PWM 1. El efecto de este modo se puede encontrar en la hoja de datos.
·
·
·
·
·
Olvídalo, creo que es posible que no lo revises, lo publicaré. [Cabeza de perro] El
Inserte la descripción de la imagen aquí
período del temporizador está configurado en 100 ms, que es 10 HZ, y el
cálculo es 168 000 000/16800/1000 = 10HZ.
168M primero obtiene una frecuencia de 1000 0HZ a través de 16800 múltiplos
y luego el contador se desbordará cada 1000 conteos.

lo anterior

Es lo que aprendí cuando era principiante, pero todavía estoy un poco confundido acerca de cómo calcular el período. Más tarde, lo supe después de mucha práctica.

El temporizador debe funcionar definitivamente necesita un reloj, y este reloj está dividido por el reloj del sistema.
Entonces, ¿para qué sirve el reloj temporizador? Esto puede requerir un poco de conocimiento sobre electricidad.


Entonces, la forma más fácil de entender es, dado que el valor del conteo del temporizador cambiará automáticamente, ¿cuál es el intervalo entre cada cambio? ? ? ? ? ? ? ? ? ? ? ?

Naturalmente, ese es el período de reloj del temporizador, que es la frecuencia obtenida dividiendo el reloj del sistema.
Esta comprensión es más fácil de calcular.


La frecuencia dividida por el reloj principal es 1000 0HZ convertida en un período, que es 100us, en otras palabras, cada 100us, el valor del temporizador cambia en 1.
Luego, el temporizador se desborda cada 1000 recuentos.
Hay 1000 100us 1000 00us = 100ms = 0.1s


Esto es mucho más fácil de entender.
Luego es el proceso de configuración normal, que no se describe aquí.

   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 inicialización TIM5

A continuación, utilizo el temporizador universal TIM5 para la captura de entrada.
Según el manual de datos, TIM5 está montado en el bus APB1. Cuando la frecuencia del sistema está configurada en 168MHZ, la frecuencia del bus es 84MHZ.
Es decir, al temporizador se le puede asignar un reloj de 84MHZ desde el sistema. Pero el temporizador ciertamente no puede funcionar con un reloj tan alto, por lo que primero debe dividirse. Para facilitar el cálculo, elijo 84 divisiones de frecuencia. Entonces, la frecuencia de conteo del temporizador es 1000 000MHZ.
Al mismo tiempo, establezca un valor de recuento de 1000 para el contador. Es decir, el valor de recuento cambia 1000 veces para generar un evento de desbordamiento.
Por lo tanto, la frecuencia del tiempo de desbordamiento es 1000 HZ, que es 1 ms una vez.


El siguiente paso es la configuración del modo de captura de entrada. Aquí elijo el canal 1 como canal de inyección.
El pin de reasignación PA0 es el canal 1 del temporizador 5.

  • Tenga en cuenta aquí, he visto muchos códigos STM32F1 para inicializar los pines configurados como modo de entrada. El chip F4 debe configurarse en modo multiplex. Aquí es un poco diferente. Si la configuración es incorrecta, es posible que algunos lugares no estén disponibles. Lo he encontrado antes, pero para todos los pines reasignados, ahora lo configuro en modo multiplexado (parece que no hay reloj de multiplexación en el código F4).
  • Entonces este código es para configurar el reloj de filtro digital
    TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM5_ICInitStructure.TIM_ICFilter = 0x02;
    

Aquí TIM_ICPSC_DIV1 significa: configurar captura 1 prescaler. La captura se realiza cada vez que se detecta un borde en la entrada de captura. En otras palabras, cuando se detecta un flanco ascendente, se juzga como un flanco ascendente. Esta frase puede resultar un poco extraña. Si lo pones de otra manera

Capture una vez que se detecta un borde ascendente. En otras palabras, se puede configurar para que detecte el borde ascendente dos, cuatro y ocho veces para capturar la configuración.

La siguiente variable miembro es configurar el período de reloj para el filtro. El período de reloj del filtro se divide directamente por el temporizador, que determina directamente el período de muestreo del filtro. La introducción en la hoja de datos se publica aquí.
Inserte la descripción de la imagen aquí

  • Se puede ver en la figura que el factor real de que el temporizador considera un cambio de señal de borde como un cambio real (en lugar de ruido) es que el filtro captura N veces continuamente en el período como un borde ascendente antes de que se reconozca como un borde ascendente. borde.
    La captura aquí puede confundirse un poco con la captura previa al escalador. Haz este entendimiento aquí

1. La captura del filtro se utiliza para determinar si la señal está cambiando en lugar de fluctuar debido al ruido. Cuando un evento se detecta continuamente N veces en el ciclo, el evento se considera un borde válido; de lo contrario, es ruido desordenado .
2. La configuración del pre-escalador se usa para detectar cuándo debe activarse un evento de interrupción. Es decir, la interrupción se activa solo cuando hay N flancos ascendentes.

El código de inicialización se publica a continuación. Habilite la interrupción de la actualización y la interrupción de la 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
 
}

Función de servicio de interrupción


He encontrado un problema aquí. Primero, déjame hablar sobre mis ideas. Espero que algunos grandes puedan ver dónde está el problema y me ayuden a resolverlo.

  • Aquí originalmente planeé registrar el valor de conteo actual del temporizador, que es el valor en CNT, cuando se activa la interrupción del evento de captura. Luego calcule valores más precisos.
TIM5->CNT  //该寄存器中即为定时器当前计数值

Permítanme hablar primero sobre el método: después de detectar el borde ascendente, establezca la captura en la captura del borde descendente primero y registre el número de interrupciones que ingresan al evento de actualización del temporizador. Según el método de configuración que acaba de hacer, interrumpir durante 1 ms. Cuando se captura el flanco descendente, se registra el valor actual del contador y luego se calcula el tiempo en que la señal está en el flanco ascendente.

Publique el código a continuación (rechace la pregunta)

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

Bueno, esto se debe a que no soy bueno aprendiendo. Habla sobre el problema.

Como puede ver, en el código anterior, no implementé completamente la idea que dije, sino que solo mantuve la parte de la interrupción del tiempo de cálculo con la actualización.

Comenté el valor de recuento del temporizador de grabación y el código de cálculo relacionado.

La razón es que hay problemas extraños en esta parte durante el funcionamiento real.

Cuando llego a un punto de interrupción en la parte de asignación del código, puedo ver que el valor de la variable cambia constantemente a través de la depuración.

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

¡pero! Cuando no interrumpo esta oración, corro directamente a la última oración de la función de servicio de interrupción durante la depuración. Aunque el valor del contador cambia constantemente, el valor de la variable que registra el valor del contador no cambia y sigue siendo 42 (decimal).
Cuando llegué al punto de ruptura nuevamente, descubrí que esta variable comenzó a cambiar felizmente nuevamente (sonrisa).

Espero que algunos grandes puedan ver cuál es el problema.
Puede comentar si necesita archivos de ingeniería.

· El código anterior es efectivo

Supongo que te gusta

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