视频链接:[1-1] 课程简介_哔哩哔哩_bilibili
STM32F103c8t6引脚定义表:(橙色是电源相关的引脚,蓝色是最小系统相关的引脚。绿色的是IO口和功能引脚,FT意为能容忍5V电压,主功能是上电默认的功能,默认复用功能是IO口上连接的外设功能引脚,加粗的引脚是能直接用的,没加粗的引脚可能要进行其他配置才能使用)
6-1TIM定时器中断
有关定时器的库函数就更多了,先将"stm32f10x_tim.h"内的函数都列出来大致看一下,用到再详细说明
//将时钟配置恢复到上电时的默认状态
void TIM_DeInit(TIM_TypeDef* TIMx);
//时基单元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
//这四个函数就是配置输出比较模块的,OC(Output Compare)的意思是输出比较
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
//输入通道的初始化
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
//PWM输入捕获的设置
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
void TIM_BDTRConfig(TIM_TypeDef* TIMx, TIM_BDTRInitTypeDef *TIM_BDTRInitStruct);
//将之前配置的结构体内的变量重置成默认值,以便重新进行配置
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
//给结构体赋默认值的,当我们要重新配置结构体或有些参数用不上时,就使用此函数把结构体赋默认值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
//初始化输入捕获配置结构体,以便于赋默认值或重新配置
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
void TIM_BDTRStructInit(TIM_BDTRInitTypeDef* TIM_BDTRInitStruct);
//用来使能定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
//仅高级定时器使用,在使用高级定时器输出PWM时,要调用此函数,否则无法正常输出PWM
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
//使能定时器产生的中断信号
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
void TIM_GenerateEvent(TIM_TypeDef* TIMx, uint16_t TIM_EventSource);
void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase, uint16_t TIM_DMABurstLength);
void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource, FunctionalState NewState);
//选择内部时钟,如果不用此函数,芯片默认也是使用内部时钟,追求严谨可加上此函数
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
//选择ITRx其他定时器的时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
//选择TIx捕获通道的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
//选择ETR通过外部时钟模式1输入的时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
//选择ETR通过外部时钟模式2输入的时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
//单独用来配置ETR引脚的预分频器、极性、滤波器这些参数
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
//直接单独配置预分配值PSC,参数2是写入模式,可选择是听从安排,在更新事件生效;或者是在写入后手动
//产生一个更新事件,让这个值立刻生效
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
//改变计数器的计数模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);
//配置强制输出模式,运行中想要强制改变输出的波形,直接输出高或低电平就用这个
//不过其效果和改变PWM占空比100%或0%的效果是一样的
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
//自动重装器预装功能配置
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectCOM(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectCCDMA(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_CCPreloadControl(TIM_TypeDef* TIMx, FunctionalState NewState);
//配置CCR寄存器的预装功能,也就是影子寄存器(写入的值不会立即生效,而是在事件更新后才生效)
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
//配置快速使能
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
//外部事件时清除REF信号
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
//用来单独设置输出比较极性的,带N的就是高级定时器的互补通道的配置,OC4无互补通道
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
//单独修改输出使能参数的
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);
//单独更改输出比较模式
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);
void TIM_UpdateDisableConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_UpdateRequestConfig(TIM_TypeDef* TIMx, uint16_t TIM_UpdateSource);
void TIM_SelectHallSensor(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectOnePulseMode(TIM_TypeDef* TIMx, uint16_t TIM_OPMode);
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
void TIM_SelectMasterSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_MasterSlaveMode);
//给计数器直接写入一个值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
//给自动重装值直接写入一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
//单独用来更改CCR寄存器值的,PWM输出更改占空比就用这个函数
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetClockDivision(TIM_TypeDef* TIMx, uint16_t TIM_CKD);
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
//获取当前计数器的值,返回值就是当前计数器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
//获取当前预分频器的值,返回值就是当前预分频器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
//获取TIM标志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
//清除TIM标志位
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
//获取定时器中断标志位
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
//清除定时器中断标志位
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
6-2 定时器定时中断&定时器外部时钟
main.c
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "timer.h"
#include "oled.h"
int16_t num = 0;
//任务1 定时器定时中断
//OLED的SCL连B8,SDA连B9,显示屏上显示时间秒
void task01()
{
OLED_Init();
Task01_Timer_Init();
OLED_ShowString(1,1,"Num:");
while(1)
{
OLED_ShowNum(1,5,num,6);
}
}
//任务2 定时器外部时钟
//对射式红外传感器接A0,遮挡传感器计数值count+1,满十次使num+1
void task02()
{
OLED_Init();
Task02_Timer_Init();
OLED_ShowString(1,1,"Count:");
while(1)
{
OLED_ShowNum(1,6,Timer_GetCounter(),6);
}
}
int main(void)
{
while (1)
{
}
}
//TIM2中断回调函数
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
time.c
#include "Device/Include/stm32f10x.h" // Device header
/*
开启时钟步骤
1.RCC开启时钟
2.选择时基单元的时钟源,对于定时中断,选择内部时钟源
3.配置时基单元,包括预分频器PSC,自动重装器ARR,计数器CNT等
4.配置输出中断控制,允许更新中断输出到NVIC
5.配置NVIC,在NVIC中打开定时器中断的通道,并分配优先级
6.运行控制,使能定时器,编写中断函数
*/
void Task01_Timer_Init()//初始化TIM2通用定时器
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//选择时基单元的时钟
TIM_InternalClockConfig(TIM2);
//初始化时基单元 定时频率 = 72M / (PSC + 1) / (ARR + 1)
TIM_TimeBaseInitTypeDef My_TimerBaseInitstructure;
My_TimerBaseInitstructure.TIM_ClockDivision = TIM_CKD_DIV1; //预分频值
My_TimerBaseInitstructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
My_TimerBaseInitstructure.TIM_Period = 10000 - 1; //ARR自动重装器的值
My_TimerBaseInitstructure.TIM_Prescaler = 7200 - 1; //PCS预分频器的值
My_TimerBaseInitstructure.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM2,&My_TimerBaseInitstructure);
TIM_ClearFlag(TIM2,TIM_CounterMode_Up);//避免刚初始化完就进行一次中断
//使能定时器中断,TIM_IT_Update为更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//配置中断控制器NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//配置中断通道以及优先级相关,开启中断
NVIC_InitTypeDef My_NVICStructure;
My_NVICStructure.NVIC_IRQChannel = TIM2_IRQn;
My_NVICStructure.NVIC_IRQChannelCmd = ENABLE;
My_NVICStructure.NVIC_IRQChannelPreemptionPriority = 2;
My_NVICStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&My_NVICStructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
//编写TIM2的中断函数模板
//void TIM2_IRQHandler(void)
//{
// if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
// {
//
// TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
// }
//}
void Task02_Timer_Init()//初始化TIM2通用定时器
{
//开启TIM2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//通过ETR引脚的外部时钟模式2配置
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);
//初始化时基单元 定时频率 = 72M / (PSC + 1) / (ARR + 1)
TIM_TimeBaseInitTypeDef My_TimerBaseInitstructure;
My_TimerBaseInitstructure.TIM_ClockDivision = TIM_CKD_DIV1; //预分频值
My_TimerBaseInitstructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
My_TimerBaseInitstructure.TIM_Period = 10000 - 1; //ARR自动重装器的值
My_TimerBaseInitstructure.TIM_Prescaler = 7200 - 1; //PCS预分频器的值
My_TimerBaseInitstructure.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM2,&My_TimerBaseInitstructure);
TIM_ClearFlag(TIM2,TIM_CounterMode_Up);//避免刚初始化完就进行一次中断
//使能定时器中断,TIM_IT_Update为更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//配置中断控制器NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//配置中断通道以及优先级相关,开启中断
NVIC_InitTypeDef My_NVICStructure;
My_NVICStructure.NVIC_IRQChannel = TIM2_IRQn;
My_NVICStructure.NVIC_IRQChannelCmd = ENABLE;
My_NVICStructure.NVIC_IRQChannelPreemptionPriority = 2;
My_NVICStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&My_NVICStructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
//配置GPIO口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure);
}
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2);
}
6-3TIM输出捕获
PWM配置步骤:
1.开启RCC时钟,将要使用的定时器和GPIO外设时钟打开
2.配置时基单元,时钟源选择
3.配置输出比较单元,设置CCR的值,输出比较模式,极性选择,输出使能这些参数
4.配置GPIO,初始化为复用推挽输出的配置
5.运行控制,启动计数器
6-4 PWM驱动LED呼吸灯&PWM驱动直流电机
一个定时器TIM有四个通道,每个通道均可输出PWM波,但当我们用一个定时器输出4路PWM时,因为共用一个计数器,频率必须是一样的,占空比由各自通道的CCR决定,四路通道的相位也是一样的。
main.c
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "OLED.h"
#include "PWM.h"
#include "servo.h"
#include "motor.h"
//任务1 PWM驱动LED呼吸灯
//A0接LED
void task01()
{
uint8_t i;
OLED_Init();
Task01_PWM_Init();
while(1)
{
for(i = 0;i < 100;i++)
{
Task01_PWM_SetComparel(i);
Delay_ms(10);
}
for(i = 0;i < 100;i++)
{
Task01_PWM_SetComparel(100-i);
Delay_ms(10);
}
}
}
//任务2 PWM驱动舵机
//A1接舵机的信号线 PA1是TIM2的CH2 B1接一个按键控制PWM改变,即把io口配置为上拉输入
void Task02()
{
OLED_Init();
Servo_Init();
Servo_SetAngle(0);//初始把舵机设置为0度
uint16_t Angle = 0;
while(1)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == RESET)//检测按键是否按下
{
Delay_ms(10);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == RESET);
Angle += 20;
}
// for(Angle = 0;Angle <= 180; Angle+=20)
// {
// Servo_SetAngle(Angle);
// Delay_ms(50);
// }
// for(Angle = 0;Angle <= 180; Angle+=20)
// {
// Servo_SetAngle(180-Angle);
// Delay_ms(50);
// }
if(Angle > 180)
{
Angle = 0;
}
}
}
//任务3 PWM驱动直流电机
//用TB6612来驱动直流电机
/*
AO1接电机正极,AO2接电机负极 VM接5V VCC接3.3V GND共地 STBY接3.3V
PWMA接PA2,AIN1接PA4,AIN2接PA5 PA2是TIM2的CH3
按键接B1,端口为上拉输入
*/
void Task03()
{
OLED_Init();
Task03_PWM_Init();
int8_t temp = 0;//Speed的范围是0到100
while(1)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == RESET)
{
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == RESET){}
temp+=20;
if(temp > 100)
{
temp = -100;
}
Motor_SetSpeed(temp);
}
}
}
int main(void)
{
Task03();
while (1)
{
}
}
PWM.c
#include "PWM.h"
/*
PWM配置步骤:
1.开启RCC时钟,将要使用的定时器和GPIO外设时钟打开
2.配置时基单元,时钟源选择
3.配置输出比较单元,设置CCR的值,输出比较模式,极性选择,输出使能这些参数
4.配置GPIO,初始化为复用推挽输出的配置
5.运行控制,启动计数器
PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比: Duty = CCR / (ARR + 1)
PWM分辨率: Reso = 1 / (ARR + 1)
*/
void Task01_PWM_Init()
{
//RCC开启
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//重映射将TIM2的CH1的PWM输出转到复用引脚
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
//选择内部时钟
TIM_InternalClockConfig(TIM2);
//初始化时基单元
TIM_TimeBaseInitTypeDef My_TimeBaceInitStructure;
My_TimeBaceInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择预分配倍数
My_TimeBaceInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //选择向上计数模式
My_TimeBaceInitStructure.TIM_Period = 100 - 1; //ARR
My_TimeBaceInitStructure.TIM_Prescaler = 720 - 1; //PSC
My_TimeBaceInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&My_TimeBaceInitStructure);
//配置输出比较单元
TIM_OCInitTypeDef My_TIM_OCInitStructure;
TIM_OCStructInit(&My_TIM_OCInitStructure);
My_TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
My_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
My_TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
My_TIM_OCInitStructure.TIM_Pulse = 30;//CCR
TIM_OC1Init(TIM2,&My_TIM_OCInitStructure);
//配置GPIO输出,A0为TIM2_CH1
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15 TIM2CH1复用时使用
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void Task01_PWM_SetComparel(uint16_t Compare)//输入值改变占空比
{
TIM_SetCompare1(TIM2,Compare);
}
void Task02_PWM_Init()
{
//开启RCC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //A1是PWM的CH2输出口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB的时钟,B1要接一个按键控制pwm值改变,这样就可以靠按键实现舵机转动
//配置GPIO输出,A1为TIM2_CH2
GPIO_InitTypeDef My_GPIOInitStructure;
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_1;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);
//再配置按键的B1
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_1;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIOInitStructure);
//选择TIM的时钟源,选内部时钟
TIM_InternalClockConfig(TIM2);
//初始化时基单元
TIM_TimeBaseInitTypeDef My_TimeBaceInitStructure;
My_TimeBaceInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TimeBaceInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TimeBaceInitStructure.TIM_Period = 20000 - 1;//ARR
My_TimeBaceInitStructure.TIM_Prescaler = 72 - 1;//PSC
My_TimeBaceInitStructure.TIM_RepetitionCounter = 30;
TIM_TimeBaseInit(TIM2,&My_TimeBaceInitStructure);
//配置输出比较单元
TIM_OCInitTypeDef My_OCInitStructure;
TIM_OCStructInit(&My_OCInitStructure);
My_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
My_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
My_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
My_OCInitStructure.TIM_Pulse = 0;//CCR 500到2500
TIM_OC2Init(TIM2,&My_OCInitStructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void Task02_PWM_SetComparel(uint16_t Compare)//输入数值改变PWM占空比
{
TIM_SetCompare2(TIM2,Compare);
}
void Task03_PWM_Init()
{
//开启RCC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//配置时基单元,选择时钟源
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef My_TimeBaseInitStructure;
My_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TimeBaseInitStructure.TIM_Period = 20000 - 1;//ARR
My_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//PSC
My_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&My_TimeBaseInitStructure);
//配置输出比较单元
TIM_OCInitTypeDef My_TIM_OCInitStructure;
TIM_OCStructInit(&My_TIM_OCInitStructure);
My_TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
My_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
My_TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
My_TIM_OCInitStructure.TIM_Pulse = 30;//CCR
TIM_OC3Init(TIM2,&My_TIM_OCInitStructure);
//配置GPIO A4和A5分别是AIN1和AIN2 按键是B1
//并且IO口如果不是默认功能,要使用复用功能时,也要配置 A2是TIM2的CH3
GPIO_InitTypeDef My_GPIOInitStructure;
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_1;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIOInitStructure);//配置PB1为按键,上拉输入
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_4;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA,&My_GPIOInitStructure);
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_5;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA,&My_GPIOInitStructure);//PA4和PA5是控制电机旋转方向的,推挽输出即可
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_2;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//设置PA2为复用推挽输出,这样就可以输出PWM了
GPIO_Init(GPIOA,&My_GPIOInitStructure);
//启动控制器
TIM_Cmd(TIM2,ENABLE);
}
//输入改变TIM2的CH3的PWM占空比
void Task03_PWM_SetComparel(uint16_t Compare)
{
TIM_SetCompare3(TIM2,Compare);
}
servo.c
#include "Device/Include/stm32f10x.h" // Device header
#include "PWM.h"
void Servo_Init(void)
{
Task02_PWM_Init();
}
//0度对于500,180度对于2500
void Servo_SetAngle(float Angle)
{
Task02_PWM_SetComparel(Angle / 180 * 2000 + 500);
}
motor.c
#include "motor.h"
void Motor_Init()
{
//初始化AIN1和AIN2两个引脚,控制正反转
RCC_APB2PeriphClockCmd(RCC_APBPeriph_AIN,ENABLE);
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = AIN1 | AIN2;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(AIN1_Port,&My_GPIO_InitStructure);
//默认将电机锁住,防止乱转
GPIO_ResetBits(AIN1_Port,AIN1);
GPIO_ResetBits(AIN1_Port,AIN2);
}
void Motor_SetSpeed(int8_t Speed)
{
if(Speed > 0)//正转
{
GPIO_SetBits(AIN1_Port,AIN1);
GPIO_ResetBits(AIN1_Port,AIN2);
Task03_PWM_SetComparel(Speed);
}
if(Speed < 0)//反转
{
GPIO_ResetBits(AIN1_Port,AIN1);
GPIO_SetBits(AIN1_Port,AIN2);
Task03_PWM_SetComparel(-Speed);
}
}
6-5 TIM输入捕获
高级定时器和基本定时器有输入捕获通道,但基本定时器没有。
PWMI模式是专门测量输入的频率和占空比的。
TIM输入捕获也可配置为主从触发模式,从而实现硬件全自动测量。
测频率就是测量一秒钟内有几个周期,测频法适用于测量高频率,测周法适用于测低频率,频率是高还是低就看中介频率。
配置输入捕获的步骤:
1.RCC开启时钟,把GPIO和TIM的时钟打开
2.GPIO初始化,将GPIO配置成输入模式,上拉输入或浮空输入
3.配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
4.配置输入捕获单元,包括滤波器、极性、直连通道还是交叉通道,分频器这些参数
5.选择从模式的触发源,这里选择TI1FP1
6.选择触发后执行的操作,执行Reset操作
7.调用TIM_Cmd函数,开启定时器
6-6 输入捕获模式测频率&PWMI模式测频率占空比
main.c
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "task01.h"
#include "task02.h"
#include "OLED.h"
//任务1 输入捕获模式测频率
//PA0输出PWM信号,用PA6输入捕获来测量信号
//PA0是TIM2的CH1 PA6是TIM3的CH1
void Task01()
{
OLED_Init();
Task01_Init();
OLED_ShowString(1,1,"Freq:00000Hz");
Task01_PWM_SetPrescaler(720 - 1); //Freq = 72M / (PSC + 1) / 100
Task01_PWM_SetComparel(50); //Duty = CCR / 100
while(1)
{
OLED_ShowNum(1,6,Task01_IC_GetFreq(),5);
}
}
//任务2 PWMI模数测频率占空比
void Task02()
{
OLED_Init();
Task02_Init();
OLED_ShowString(1,1,"Freq:00000Hz");
OLED_ShowString(2,1,"Duty:00%");
Task01_PWM_SetPrescaler(7200 - 1); //Freq = 72M / (PSC + 1) / 100
Task01_PWM_SetComparel(50); //Duty = CCR / 100
while(1)
{
OLED_ShowNum(1,6,Task02_IC_GetFreq(),5);
OLED_ShowNum(2,6,Task02_IC_GetDuty(),2);
}
}
int main(void)
{
// Task02();
while (1)
{
}
}
task01.c
#include "task01.h"
//PA0和PA6
void Task01_Init()
{
//初始化时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//选择内部时钟
TIM_InternalClockConfig(TIM2);
TIM_InternalClockConfig(TIM3);
//配置TIM2时基单元
TIM_TimeBaseInitTypeDef My_TIM_TimeBaseInitStructure;
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&My_TIM_TimeBaseInitStructure);//TIM2的CH1作为PWM输出
//配置PA0io口作为PWM输出
GPIO_InitTypeDef My_GPIOInitStructure;
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_0;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);
//设置输出比较单元
TIM_OCInitTypeDef My_TIM_OCInitStructure;
TIM_OCStructInit(&My_TIM_OCInitStructure);
My_TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出模式选择
My_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性选择
My_TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能
My_TIM_OCInitStructure.TIM_Pulse = 20;//RCC
TIM_OC1Init(TIM2,&My_TIM_OCInitStructure);
//启动定时器TIM2
TIM_Cmd(TIM2,ENABLE);
//配置TIM3时基单元
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 65535 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&My_TIM_TimeBaseInitStructure);//TIM3的CH1作为输入捕获
//配置TIM3输入捕获单元
TIM_ICInitTypeDef My_TIM_InitStructure;
My_TIM_InitStructure.TIM_Channel = TIM_Channel_1;//选择定时器通道
My_TIM_InitStructure.TIM_ICFilter = 0xF;//滤波数值
My_TIM_InitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//触发模式,选上升沿触发
My_TIM_InitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//预分频值
My_TIM_InitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入
TIM_ICInit(TIM3,&My_TIM_InitStructure);
//配置PA6GPIO口为输入模式
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_6;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);
//配置从模式的触发源
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
//配置从模式为Reset
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
//启动定时器TIM3
TIM_Cmd(TIM3,ENABLE);
}
void Task01_PWM_SetComparel(uint16_t Comparel)
{
TIM_SetCompare1(TIM2,Comparel);
}
void Task01_PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Immediate);
}
uint32_t Task01_IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1);//+1消除误差
}
task02.c
#include "task02.h"
void Task02_Init()
{
//初始化时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//选择内部时钟
TIM_InternalClockConfig(TIM2);
TIM_InternalClockConfig(TIM3);
//配置TIM2时基单元
TIM_TimeBaseInitTypeDef My_TIM_TimeBaseInitStructure;
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&My_TIM_TimeBaseInitStructure);//TIM2的CH1作为PWM输出
//配置PA0io口作为PWM输出
GPIO_InitTypeDef My_GPIOInitStructure;
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_0;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);
//设置输出比较单元
TIM_OCInitTypeDef My_TIM_OCInitStructure;
TIM_OCStructInit(&My_TIM_OCInitStructure);
My_TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出模式选择
My_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性选择
My_TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能
My_TIM_OCInitStructure.TIM_Pulse = 20;//RCC
TIM_OC1Init(TIM2,&My_TIM_OCInitStructure);
//启动定时器TIM2
TIM_Cmd(TIM2,ENABLE);
//配置TIM3时基单元
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 65535 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&My_TIM_TimeBaseInitStructure);//TIM3的CH1作为输入捕获
//配置TIM3输入捕获单元
TIM_ICInitTypeDef My_TIM_InitStructure;
My_TIM_InitStructure.TIM_Channel = TIM_Channel_1;//选择定时器通道
My_TIM_InitStructure.TIM_ICFilter = 0xF;//滤波数值
My_TIM_InitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//触发模式,选上升沿触发
My_TIM_InitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//预分频值
My_TIM_InitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入
TIM_ICInit(TIM3,&My_TIM_InitStructure);
//裴PWMI
TIM_PWMIConfig(TIM3,&My_TIM_InitStructure);
//配置PA6GPIO口为输入模式
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_6;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);
//配置从模式的触发源
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
//配置从模式为Reset
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
//启动定时器TIM3
TIM_Cmd(TIM3,ENABLE);
}
void Task02_PWM_SetComparel(uint16_t Comparel)
{
TIM_SetCompare1(TIM2,Comparel);
}
void Task02_PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Immediate);
}
uint32_t Task02_IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1);//+1消除误差
}
uint32_t Task02_IC_GetDuty(void)
{
return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);
}
6-7 TIM编码器接口
配置编码器测量步骤:
1.RCC开启时钟,开启GPIO和定时器时钟
2.配置相关GPIO,将CH1和CH2的IO口设置为输入模式
3.配置时基单元,预分频器一般不分频,自动重装一般给最大65535
4.配置输入捕获单元,只需配置通道、滤波器和极性即可,其他参数用不上
5.配置编码器接口模式,直接调用库函数
6.调用TIM_Cmd启动定时器
6-8 编码器接口测速
main.c
#include "stm32f10x.h" // Device header
#include "encoder.h"
#include "OLED_I2C.h"
#include "delay.h"
#include <stdio.h>
//编码器接口测速
//PA6接A相 PA7接B相 分别是TIM3的CH1 和 TIM3的CH2
uint16_t temp = 0;
void DisplayIntegerValue(int value)
{
char str[16]; // 用于存储转换后的字符串
snprintf(str, sizeof(str), "temp: %d", value); // 将整数转换为字符串
OLED_ShowStr(1, 1, (unsigned char *)str, 1); // 在OLED上显示字符串
}
int main(void)
{
DelayInit();
I2C_Configuration();
OLED_Init();
Encoder_Init();
OLED_Fill(0xFF);
DelayS(2);
OLED_CLS();
while (1)
{
temp = Encoder_Get();
DisplayIntegerValue(temp);
DelayS(1);
}
}
encoder.c
#include "encoder.h"
void Encoder_Init()
{
//开启RCC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//配置GPIO,CH1和CH2都配成输入模式 PA6 PA7
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure);
//配置时基单元
TIM_TimeBaseInitTypeDef My_TIM_TimeBaseInitStructure;
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 65535 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 1-1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&My_TIM_TimeBaseInitStructure);
//配置输入捕获单元
TIM_ICInitTypeDef My_TIM_ICInitStructure;
TIM_ICStructInit(&My_TIM_ICInitStructure);
My_TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
My_TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
My_TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3,&My_TIM_ICInitStructure);
My_TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
My_TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
My_TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3,&My_TIM_ICInitStructure);
//配置编码器接口
TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
int16_t Encoder_Get(void)
{
int16_t temp;
temp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3,0);
return temp;
}