【stm32f10x系列学习笔记二】


前言

参考资料:江协科技
参考资料:野火
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);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值