文章目录
前言
参考资料:江协科技
参考资料:野火
stm32f10x系列(stm32f103c8t6核心板)基本定时器、通用定时器、高级控制定时器学习笔记。
stm32f10x系列定时器分为基本定时器、通用定时器、高级控制定时器三种。STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4。
一、基本定时器
基本定时器主要部分是时基单元。时基单元包含:16位计数器寄存器(TIMx_CNT) 、预分频寄存器(TIMx_PSC) 、自动重装载寄存器(TIMx_ARR)。
基本定时器的时钟源来自内部时钟(CK_INT)。通过触发控制器可以触发输出到DAC,也可以控制定时器复位、使能、计数。
注意,基本定时器和通用定时器的总线是APB1,但是APB1预分频系数为2,提供给定时器的时钟CK_INT依然是36*2 = 72MHz。
内部时钟CK_INT为72MHz,进入预分频器的CK_PSC为72MHz。预分频寄存器可以对时钟按系数为1~65536(预分频器数值为0~65535)之间进行分频。预分频器寄存器内部也是通过预分频计数器来实现分频的。
预分频寄存器具有影子寄存器(缓冲器,实际寄存器)。当在运行过程中进行分频时,通过设置寄存器中的控制位,可以立即或者在更新事件时,将预分频控制寄存器(预装载寄存器)中的分频值传入它的影子寄存器。一般默认更新事件时更新分频值。
分频后,计数器的时钟频率CK_CNT等于CK_PSC/(预分频器数值+1)。
自动重装载寄存器也具有影子寄存器。当写入重装载值时,通过设置寄存器中的控制位,可以立即或者在更新事件时,将预加载寄存器中的重装载值传入它的影子寄存器。一般默认更新事件时更新重装载值。
计数器从0开始向上累加,计数到自动重装载值时,重新从0开始计数,并产生一个计数器溢出事件。计数器溢出会产生更新事件。
当发生更新事件时,如果相应控制位使能,预分频寄存器、自动重装载寄存器各自的影子寄存器内容都会被更新。
定时器时基单元结构体
与基本定时器时基单元结构体相关成员只有预分频、计数模式、定时器周期三项。
采用中断方式定时计数方式如下。
实验现象:定时器定时1s,num自增(中断方式/非中断轮询)。
中断方式:
timer.c
#include "timer.h"
extern uint8_t num;
void Timer_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//定时器默认上电后使用内部时钟
TIM_InternalClockConfig(TIM2);
//配置定时器时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructInit;
TIM_TimeBaseStructInit.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructInit.TIM_CounterMode = TIM_CounterMode_Up;
//假设定时1s,频率1Hz
TIM_TimeBaseStructInit.TIM_Period = 10000 - 1;
TIM_TimeBaseStructInit.TIM_Prescaler = 7200 - 1;
//高级定时器TIM1 and TIM8使用TIM_RepetitionCounter
TIM_TimeBaseStructInit.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructInit);
//清除更新标志位。TIM_TimeBaseInit初始化会立即产生更新时间和更新中断
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
//使能更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//NVIC初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStruct);
//定时器使能
TIM_Cmd(TIM2,ENABLE);
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update))
{
//执行操作
num++;
//必须手动清除中断挂起位
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.H"
#include "timer.h"
/**
功能需求:定时器定时1s,中断,num自增
*/
uint8_t num;
int main()
{
Timer_Init();
OLED_Init();
OLED_ShowString(1,1,"hello,world!");
OLED_ShowString(2,1,"num:");
while(1)
{
OLED_ShowNum(2,5,num,3);
}
}
非中断轮询方式,相当于延时
timer.c
#include "timer.h"
extern uint8_t num;
void Timer_delay1s()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//定时器默认上电后使用内部时钟
TIM_InternalClockConfig(TIM2);
//配置定时器时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructInit;
TIM_TimeBaseStructInit.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructInit.TIM_CounterMode = TIM_CounterMode_Up;
//假设定时1s,频率1Hz
TIM_TimeBaseStructInit.TIM_Period = 10000 - 1;
TIM_TimeBaseStructInit.TIM_Prescaler = 7200 - 1;
//高级定时器TIM1 and TIM8使用TIM_RepetitionCounter
TIM_TimeBaseStructInit.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructInit);
//清除更新标志位。TIM_TimeBaseInit初始化会立即产生更新时间和更新中断
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
//定时器使能
TIM_Cmd(TIM2,ENABLE);
while(!TIM_GetFlagStatus(TIM2,TIM_FLAG_Update));
TIM_Cmd(TIM2,DISABLE);
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.H"
#include "timer.h"
/**
功能需求:定时器定时1s,中断,num自增
*/
uint8_t num;
int main()
{
OLED_Init();
OLED_ShowString(1,1,"hello,world!");
OLED_ShowString(2,1,"num:");
while(1)
{
Timer_delay1s();
OLED_ShowNum(2,5,num++,3);
}
}
二、通用定时器
通用定时器在基本定时器的基础上,增加了输出比较单元、输入捕获单元、编码器接口、从模式选择等。输出比较单元可以输出一定频率、占空比的PWM波形,输入捕获单元可以测量输入信号的频率、占空比等,编码器接口可以测量转速等,从模式可以使定时器与外部触发同步,主从模式搭配可以使定时器之间进行同步。
通用定时器和基本定时器一样,具有时基单元。不同的是,基本定时器只能向上计数,而通用定时器具有向上计数、向下计数、中央对齐(向上/向下计数)3种计数模式。
1.时钟选择
通用定时器的时钟源来源:
- 内部时钟(CK_INT)
- 外部时钟模式1:外部输入脚(TIx,x=1,2)
- 外部时钟模式2:外部触发输入(ETR)
- 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。硬件上高级控制定时器和通用定时器在内部连接在一起,可以实现定时器同步或级联。
内部时钟模式与基本定时器一样。默认情况下,从模式控制寄存器 TIMx_SMCR 的 SMS 位等于 000,使用内部时钟。
外部时钟模式1
当TIMx_SMCR寄存器的SMS=111时,此模式被选中。计数器可以在选定输入端的每个上升沿或下降沿计数。
下图,时钟信号来自定时器的输入通道TI2。滤波器可以对信号进行重新采样,达到降频或者去除高频干扰的目的。边沿检测器选择上升沿或者下降沿计数。最终定时器输入2(TI2FP2)成为触发输入源。
在外部时钟模式1下,触发输入源有两个:定时器输入1(TI1FP1)、定时器输入2(TI2FP2)
外部时钟模式2
当TIMx_SMCR寄存器的ECE=1 时,此模式被选中。计数器能够在外部触发ETR的每一个上升沿或下降沿计数。
下图,时钟信号来自定时器的特定输入通道ETR。ETP位选择信号的上升沿计数还是下降沿计数。由于外部触发信号ETRP的频率必须最多是CK_INT频率的1/4(来源参考手册),分频器对过高的信号频率分频。滤波器可以对信号进行重新采样,达到降频或者去除高频干扰的目的。
通用定时器中断结构图,参考江协科技课件,但是稍微改动了下(个人理解)。
实验现象: 对射式红外传感器信号连接到ETR输入通道,定时器计次。
timer.c
#include "timer.h"
extern uint8_t num;
void Timer_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//外部时钟ETR模式2配置
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
/**
//设置ECE位与选择外部时钟模式1并将TRGI连到ETRF(SMS=111和TS=111)具有相同功效
*/
//外部时钟模式2:设置ECE位
//TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0);
//外部时钟模式1+TRGI:ETRF
TIM_ETRClockMode1Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0);
//配置定时器时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructInit;
TIM_TimeBaseStructInit.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructInit.TIM_CounterMode = TIM_CounterMode_Up;
//假设定时1s,频率1Hz
TIM_TimeBaseStructInit.TIM_Period = 10 - 1;
TIM_TimeBaseStructInit.TIM_Prescaler = 1 - 1;
//高级定时器TIM1 and TIM8使用TIM_RepetitionCounter
TIM_TimeBaseStructInit.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructInit);
//清除更新标志位。TIM_TimeBaseInit初始化会立即产生更新时间和更新中断
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
//使能更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//NVIC初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStruct);
//定时器使能
TIM_Cmd(TIM2,ENABLE);
}
uint16_t TIMER_GetCount()
{
return TIM_GetCounter(TIM2);
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update))
{
//执行操作
num++;
num %= 10;
//必须手动清除中断挂起位
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "timer.h"
#include "OLED.h"
/**
功能需求:定时器外部时钟中断,定时1s,num自增
*/
uint8_t num,counter;
int main()
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"num:");
OLED_ShowString(2,1,"counter:");
while(1)
{
OLED_ShowNum(1,5,num,3);
counter = TIMER_GetCount();
OLED_ShowNum(2,9,counter,3);
}
}
2.捕获/比较通道
通用定时器的捕获/比较通道主要包含捕获的输入部分(数字滤波、多路复用和预分频器)、主电路(计数器、捕获/比较寄存器)、输出部分(比较器、输出控制)。捕获/比较寄存器(包含影子寄存器)具有输入捕获和输出比较功能。
上图左侧为捕获/比较通道的输入部分。定时器的输入通道TIx(x=1,2,3,4)经过滤波、边沿检测后,产生信号TIxFPx。其中TI1FP1、TI2FP2可以作为从模式控制器的输入触发(外部时钟模式1、编码器接口模式),最终为时基单元提供时钟。
信号TIxFPx、TIxFPy以及其他ITRx定时器产生的TRC作为输入捕获的信号源,经过预分频后产生信号ICxPS,后进入捕获/比较寄存器。
通过配置寄存器,将捕获/比较寄存器配置为输入捕获功能,根据输入信号ICxPS的上升沿或下降沿,捕获计数器的数值,获取输入信号的相关信息。
通过配置寄存器,将捕获/比较寄存器配置为输出比较功能,计数器和捕获/比较寄存器中的内容比较,根据输出模式控制器中设置的输出比较模式,得到输出参考信号OCxRef,最终将得到的输出信号OCx输出到IO口,控制IO的电平翻转生成波形。
1.输出比较单元
OC(Output Compare)单元通过比较计数器CNT与捕获/比较寄存器CCR值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形。
输出模式控制器的输出比较模式主要分为以下8种。其中,PWM1、PWM2在输出比较模式中,使用较多。
输出比较单元结构体
采用PWM1模式的结构图如下。
PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比: Duty = CCR / (ARR + 1)
PWM分辨率: Reso = 1 / (ARR + 1)
1、呼吸灯实验
实验一:呼吸灯
led.c
#include "led.h"
void led_config()
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = LED_GPIO_PIN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
//设置时钟
RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
//配置CRL
GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
}
led.h
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
#define LED_GPIO_PIN GPIO_Pin_0
#define LED_GPIO_PORT GPIOB
#define LED_GPIO_CLK RCC_APB2Periph_GPIOB
#define ON 1
#define OFF 0
#define LED(a) if(a) \
GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN); \
else \
GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN);
void led_config();
#endif
pwm.c
#include "pwm.h"
/**
功能需求:呼吸灯。PA0引脚对应CH1通道。输出PWM波形,驱动led灯
*/
void PWM_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//内部时钟配置
TIM_InternalClockConfig(TIM2);
//GPIO初始化
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//输出比较通道:推挽复用输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置定时器时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructInit;
TIM_TimeBaseStructInit.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructInit.TIM_CounterMode = TIM_CounterMode_Up;
//假设频率100Hz,周期0.01s
TIM_TimeBaseStructInit.TIM_Period = 100 - 1;
TIM_TimeBaseStructInit.TIM_Prescaler = 7200 - 1;
//高级定时器TIM1 and TIM8使用TIM_RepetitionCounter
TIM_TimeBaseStructInit.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructInit);
//输出比较单元初始化
TIM_OCInitTypeDef TIM_OC1InitStruct;
//注意:部分成员只在高级定时器使用,需要默认赋值
TIM_OCStructInit(&TIM_OC1InitStruct);
TIM_OC1InitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OC1InitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1InitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC1InitStruct.TIM_Pulse = 0;
TIM_OC1Init(TIM2,&TIM_OC1InitStruct);
//定时器使能
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetOCVal(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
main.c
#include "stm32f10x.h" // Device header
#include "pwm.h"
#include "SysTick_delay.h"
int main()
{
uint8_t index;
PWM_Init();
while(1)
{
for(index = 0;index < 10;index++)
{
PWM_SetOCVal(index * 10);
SysTick_delay_ms(500);
}
for(;index > 0;index--)
{
PWM_SetOCVal(index * 10);
SysTick_delay_ms(500);
}
}
}
2、舵机实验
实验二:PWM驱动舵机。按键按下,角度增加30°。
由于周期为20ms,自动重装载值建议选取200、2000、20000等。
#include "key.h"
#include "delay.h"
void Key_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
uint8_t Key_Num()
{
uint8_t num = 0;
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == RESET)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == RESET);
Delay_ms(20);
num = 1;
}
return num;
}
pwm.c
#include "PWM.h"
void PWM_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
TIM_InternalClockConfig(TIM2);
//引脚
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 2000 - 1; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler = 720 - 1; //PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
//输出单元
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_Pulse = 0; //CCR
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC2Init(TIM2,&TIM_OCInitStruct);
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetOcVal(uint16_t CompareVal)
{
TIM_SetCompare2(TIM2,CompareVal);
}
Servo.c
#include "Servo.h"
#include "PWM.H"
void Servo_Init()
{
PWM_Init();
}
void Servo_SetAngle(float angle)
{
//假定角度从0°到180°
PWM_SetOcVal(angle * 200 /180 + 50);
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.H"
#include "Servo.H"
#include "key.h"
#include "delay.h"
/**
功能需求:PWM驱动舵机。驱动电压为5V。按键控制角度,增加30°
*/
int main()
{
uint8_t num = 0;
float angle = 0;
OLED_Init();
OLED_ShowString(1,1,"angle:");
OLED_ShowString(2,1,"num:");
Servo_Init();
Key_Init();
while(1)
{
num = Key_Num();
OLED_ShowNum(2,5,num,3);
if(num == 1)
{
angle += 30;
if(angle > 180)
{
angle = 0;
}
}
Servo_SetAngle(angle);
OLED_ShowNum(1,7,angle,3);
}
}
3、直流电机实验
直流电机接上电源,既可以转动。但是直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作。
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向。
PWMA2接PA2,而PA2在硬件上是定时器2的通道3,需要选择定时器2,输出比较通道3。AIN1、AIN2分别接PA4、PA5。
pwm.c
#include "PWM.h"
void PWM_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
TIM_InternalClockConfig(TIM2);
//引脚
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
//PWMA接PA2
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler = 720 - 1; //PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
//输出单元
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_Pulse = 0; //CCR
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
//通道3
TIM_OC3Init(TIM2,&TIM_OCInitStruct);
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetOcVal(uint16_t CompareVal)
{
TIM_SetCompare3(TIM2,CompareVal);
}
motor.c
#include "motor.h"
#include "PWM.h"
void Motor_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
PWM_Init();
}
void Motor_SetSpeed(int8_t Speed)
{
if(Speed >= 0)
{
//PA4给高电平,PA5给低电平,速度大于0
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
PWM_SetOcVal(Speed);
}
else
{
//PA5给高电平,PA4给低电平,速度小于0
GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
PWM_SetOcVal(-Speed);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.H"
#include "motor.H"
#include "key.h"
#include "delay.h"
/**
功能需求:PWM驱动直流电机。驱动电压为5V。按键按下,速度增加20。
*/
int main()
{
uint8_t num = 0;
uint8_t Speed = 0;
OLED_Init();
OLED_ShowString(1,1,"Speed:");
Motor_Init();
Key_Init();
while(1)
{
num = Key_Num();
if(num == 1)
{
Speed += 20;
if(Speed > 100)
{
Speed = -100;
}
}
Motor_SetSpeed(Speed);
OLED_ShowNum(1,7,Speed,3);
}
}
2.输入捕获单元
IC(Input Capture)输入捕获下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数。
测量频率:假设捕获通道上升沿有效,第一次捕获时,将计数器数值清零,记录第二次捕获数值,即可算出信号的周期(频率)。
测量脉宽:假定捕获通道上升沿有效,第一次捕获时,将计数器数值清零,并设置下降沿有效,记录第二次捕获数值,即可算出信号的脉宽。
普通的输入捕获模式使用一个捕获/比较寄存器在测量脉宽时,需要切换捕获边沿的极性。
PMWI输入模式可以在不切换捕获边沿的极性时,测量信号的脉宽和频率,但是需要使用两个捕获通道。大概原理:假定捕获通道1上升沿有效,捕获通道2上升沿有效(2捕获极性与1相反)。第一次捕获时,捕获通道1、2将计数器数值清零,捕获通道2进行第二次捕获(脉宽),捕获通道1进行第三次捕获(频率)。
PWMI输入模式是输入捕获模式的特例。区别:
两个ICx信号被映射至同一个TIx输入。
这2个ICx信号为边沿有效,但是极性相反。
一个TIxFP信号被作为触发输入信号,而从模式控制器被配置成复位模式。
由于只有TI1FP1和TI2FP2连到了从模式控制器,所以PWM输入模式只能使用TIMx_CH1、TIMx_CH2信号。
输入捕获单元结构体
实验:定时器2生成PWM波形,使用定时器3测量频率和脉宽(占空比)。定时器2的输出信号引脚PA0直接接到定时器3输入引脚PA6。
pwm.c
#include "PWM.h"
void PWM_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 100 - 1;
//定时器2每10us计数自增,计数100次溢出,溢出时间1ms
TIM_TimeBaseInitStruct.TIM_Prescaler = 720 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 0;
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetOCVal(uint16_t CompareVal)
{
TIM_SetCompare1(TIM2,CompareVal);
}
void PWM_SetPrescaler(uint16_t PrescalerVal)
{ TIM_PrescalerConfig(TIM2,PrescalerVal,TIM_PSCReloadMode_Immediate);
}
IC.c
#include "IC.h"
/**
定时器通道要么输入模式,要么输出模式。一次只能选用一个。
TIM2模拟PWM波形输出,TIM3捕获波形,测量频率(测周法)
*/
void IC_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 65536 - 1;
//定时器3每1us计数自增,计数65536次溢出,溢出时间65536us
TIM_TimeBaseInitStruct.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter = 0xf;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_PWMIConfig(TIM3,&TIM_ICInitStruct);
//输入触发源
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
//从模式选择:复位模式
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
TIM_Cmd(TIM3,ENABLE);
}
uint32_t IC_GetFreq()
{
return 1000000 / TIM_GetCapture1(TIM3);
}
uint8_t IC_GetDuty()
{
return TIM_GetCapture2(TIM3) * 100 / TIM_GetCapture1(TIM3) ;
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.H"
#include "PWM.H"
#include "IC.H"
/**
功能需求:定时器2生成PWM波形,使用定时器3测量频率和脉宽(占空比)
*/
int main()
{
OLED_Init();
PWM_Init();
PWM_SetPrescaler(720-1);
PWM_SetOCVal(50);
IC_Init();
OLED_ShowString(1,1,"freq:000000Hz");
OLED_ShowString(2,1,"duty:000");
while(1)
{
OLED_ShowNum(1,6,IC_GetFreq(),6);
OLED_ShowNum(2,6,IC_GetDuty(),3);
}
}
3.编码器接口模式
编码器接口模式相当带有方向选择的外部时钟。编码器接口接收增量(正交)编码器的信号,计数器根据一信号的边沿、另一信号的电平确定正向计数或者负向计数。图中,信号TI1FP1/TI2FP2的边沿确定是否计数,其相对信号TI2FP2/TI1FP1的电平确定计数方向。
正交编码器模块通过旋转产生正交信号脉冲,两相位信号连接到编码器接口,根据两信号的边沿和电平确定计数和方向,从而指示编码器的旋转位置、方向、速度等。
编码器接口结构
实验:利用定时器的编码器接口模式测量编码器模块的旋转速度和方向。
编码器速度测量:计数前清零,每隔1s获取当前计数值,即为旋转速度。
一般不建议使用延迟函数,考虑定时器中断。
timer.c
#include "timer.h"
void Timer_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStruct);
TIM_Cmd(TIM2,ENABLE);
}
encoder.c
#include "encoder.h"
void Encoder_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICStructInit(&TIM_ICInitStruct);
//定时器通道1、2支持编码器模式
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1 | TIM_Channel_2;
TIM_ICInitStruct.TIM_ICFilter = 0xf;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInit(TIM3,&TIM_ICInitStruct);
//编码器接口模式:在TI1和TI2上计数,需要定时器的通道1和通道2
TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);
TIM_Cmd(TIM3,ENABLE);
}
int16_t Encoder_GetSpeed()
{
int16_t Temp = TIM_GetCounter(TIM3);;
TIM_SetCounter(TIM3,0);
return Temp;
}
main.c
#include "stm32f10x.h" // Device header
#include "encoder.h"
#include "OLED.h"
#include "SysTick_delay.h"
#include "timer.h"
int16_t Speed;
int main()
{
Encoder_Init();
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"speed:");
while(1)
{
OLED_ShowSignedNum(1,7,Speed,5);
// SysTick_delay_ms(1000);
}
}
/**
定时器3用来编码器输入,定时器2用作普通的定时中断
*/
void TIM2_IRQHandler()
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update))
{
Speed = Encoder_GetSpeed();
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
三、高级控制定时器
高级控制定时器在通用定时器的基础上,增加了互补输出和死区生成、断路(刹车)功能。
1、互补输出和死区生成
DTG寄存器内部结构如下图。
高级控制定时器与通用定时器在输出比较模式的区别:参考信号OCxREF先经过死区生成器DTG,生成两路互补的OCx_DT和OCxN_DT,再经过极性选择,最终生成两路互补的输出信号OCx和OCxN,
死区时间的大小具体由 BDTR 寄存器的位 DTG[7:0]配置。死区时间的大小必须根据与输出信号相连接的器件及其特性来调整。
死区时间的计算:
假设时钟分频因子选取00,则Tdts = Tck_int= 1 / 72MHz。
假设UTG[7:0]取11(00001011B),则DT = DTG[7:0] * Tdtg = 11 * Tdts =11 * 1 / 72MHz = 152 ns。
直流电机的两引脚接上电源的正负极,电机就会转动。反接,电机反转。
在直流电机实验中,采用TB6612芯片(双路H桥,实际只用一路H桥)控制直流电机。以半桥电路(半个H桥)为例,分析带死区的PWM信号。
在这个半桥驱动电路中,Q1 导通,Q2 截止,此时我想让 Q1 截止 Q2 导通,肯定是要先让Q1 截止一段时间之后,再等一段时间才让 Q2 导通,那么这段等待的时间就称为死区时间,因为 Q1 关闭需要时间(由 MOS 管的工艺决定)。如果 Q1 关闭之后,马上打开 Q2,那么此时一
段时间内相当于 Q1 和 Q2 都导通了,这样电路会短路(摘自野火零死角玩转 STM32F103—指南者)。
具体的电路分析:
IR2104驱动原理
IR2104电机驱动
ir2181内部原理图没怎么看懂,哪位大佬可以解惑?HIN给高,LIN给低,下端LO和VCC导通,Q2闭合,DC+给电容充电。但是上端呢,Q为0,VB为0,VS为0,上端HO、VB、VS什么关系?
电路中IR2182芯片内部不带死区插入功能。因此,需要借助高级控制定时器。
2、刹车功能
电机控制中,电机两引脚不能同时接相同的电平。
系统复位启动都默认关闭断路功能,将断路和死区寄存器(TIMx_BDTR)的 BKE 为置 1,使能断路功能。可通过 TIMx_BDTR 寄存器的 BKP 位设置设置断路输入引脚的有效电平,设置为 1 时输入 BRK 为高电平有效,否则低电平有效。
断路和死区结构体
高级控制定时器引脚配置:
实验:高级控制定时器1输出PWM互补波形,并添加断路和死区功能。
接线:TIM1的主输出通道引脚PA8接到TIM2的输入引脚PA0,TIM1的互补输出通道引脚PB13接到TIM3的输入引脚PA6。TIM2和3采用PWMI模式。
刹车引脚初始化时设置高为有效。先置低,置高时电机制动。
高级控制定时器再输出波形时,需要设置主输出使能。
高级控制定时器TIM1对应引脚:PB12-TIM1-BKIN,PA8-TIM1-CH1,PB13-TIM1-CH1N。
pwm.c
#include "PWM.h"
void PWM_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
//互补输出通道引脚:推完复用输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//主输出通道引脚:推完复用输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//刹车输入引脚:浮空输入
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOB,&GPIO_InitStruct);
// BKIN 引脚默认先输出低电平
GPIO_ResetBits(GPIOB,GPIO_Pin_13);
TIM_InternalClockConfig(TIM1);
//时基单元初始化
//定时器1250ns计数自增,计数8次溢出,重新计次。溢出时间10us
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
//配置死区时间需要
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 8 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 90 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
//输出比较单元初始化
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
// 互补输出通道电平极性配置
TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Enable;
// 互补输出使能
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_OCIdleState= TIM_OCIdleState_Set;
// 互补输出通道空闲电平极性配置
TIM_OCInitStruct.TIM_OCNIdleState= TIM_OCNIdleState_Reset;
//PWM占空比50%
TIM_OCInitStruct.TIM_Pulse = 4;
TIM_OC1Init(TIM1,&TIM_OCInitStruct);
//刹车和死区单元初始化
TIM_BDTRInitTypeDef TIM_BDTRInitStruct;
TIM_BDTRInitStruct.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRInitStruct.TIM_Break = TIM_Break_Enable;
// 当 BKIN 引脚检测到高电平的时候,输出比较信号被禁止,就好像是刹车一样
TIM_BDTRInitStruct.TIM_BreakPolarity = TIM_BreakPolarity_High;
// 这里配置的死区时间为 152ns
TIM_BDTRInitStruct.TIM_DeadTime = 11;
TIM_BDTRInitStruct.TIM_LOCKLevel= TIM_LOCKLevel_1;
//当定时器不工作时,一旦CCxE=1或CCxNE=1,OC/OCN首先输出其空闲电平,然后OC/OCN使能输出信号=1。
TIM_BDTRInitStruct.TIM_OSSIState= TIM_OSSIState_Enable;
//当定时器不工作时,一旦CCxE=1或CCxNE=1,首先开启OC/OCN并输出无效电平,然后置OC/OCN使能输出信号=1。
TIM_BDTRInitStruct.TIM_OSSRState= TIM_OSSRState_Enable;
TIM_BDTRConfig(TIM1,&TIM_BDTRInitStruct);
TIM_Cmd(TIM1,ENABLE);
// 主输出使能,当使用的是通用定时器时,这句不需要
TIM_CtrlPWMOutputs(TIM1,ENABLE);
}
IC2.c
#include "IC2.h"
/**
定时器通道要么输入模式,要么输出模式。一次只能选用一个。
TIM1模拟PWM波形输出,TIM2捕获主输出波形,测量频率(测周法)
*/
void IC2_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 65536 - 1;
//定时器2每125ns计数自增,计数65536次溢出
TIM_TimeBaseInitStruct.TIM_Prescaler = 9 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter = 0xf;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_PWMIConfig(TIM2,&TIM_ICInitStruct);
//输入触发源
TIM_SelectInputTrigger(TIM2,TIM_TS_TI1FP1);
//从模式选择:复位模式
TIM_SelectSlaveMode(TIM2,TIM_SlaveMode_Reset);
TIM_Cmd(TIM2,ENABLE);
}
uint32_t IC2_GetFreq()
{
return 8000000 / TIM_GetCapture1(TIM2);
}
uint8_t IC2_GetDuty()
{
return TIM_GetCapture2(TIM2) * 100 / TIM_GetCapture1(TIM2) ;
}
IC3.c
#include "IC3.h"
/**
定时器通道要么输入模式,要么输出模式。一次只能选用一个。
TIM1模拟PWM波形输出,TIM3捕获互补输出波形,测量频率(测周法)
*/
void IC3_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 65536 - 1;
//定时器3每125ns计数自增,计数65536次溢出
TIM_TimeBaseInitStruct.TIM_Prescaler = 9 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter = 0xf;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_PWMIConfig(TIM3,&TIM_ICInitStruct);
//输入触发源
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
//从模式选择:复位模式
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
TIM_Cmd(TIM3,ENABLE);
}
uint32_t IC3_GetFreq()
{
return 8000000 / TIM_GetCapture1(TIM3);
}
uint8_t IC3_GetDuty()
{
return TIM_GetCapture2(TIM3) * 100 / TIM_GetCapture1(TIM3) ;
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.H"
#include "PWM.H"
#include "IC2.H"
#include "IC3.H"
/**
功能需求:定时器1生成两路互补PWM波形,使用定时器2和3测量频率和脉宽(占空比)
*/
int main()
{
OLED_Init();
PWM_Init();
IC2_Init();
IC3_Init();
OLED_ShowString(1,1,"m_freq:000000Hz");
OLED_ShowString(2,1,"m_duty:000");
OLED_ShowString(3,1,"p_freq:000000Hz");
OLED_ShowString(4,1,"p_duty:000");
while(1)
{
OLED_ShowNum(1,8,IC_GetFreq(),6);
OLED_ShowNum(2,8,IC_GetDuty(),3);
OLED_ShowNum(3,8,IC_GetFreq(),6);
OLED_ShowNum(4,8,IC_GetDuty(),3);
}
}