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 .)
Código de inicialización TIM1
El pin multiplexado PA8 es el
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
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í.
- 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.