用TIM函数配置PWM输出比较模块的函数
TIM_OCxInit();//OC就是Output Compare输出比较
TIM_OCStructInit();//初始化输出比较单元,给输出比较结构体赋一个默认值
TIM_SetCompare1();//单独更改CCR寄存器值的函数,在运行的时候更改占空比
(下面是一些小功能和运行时更改参数的函数)
TIM_ForcedOCxConfig();//配置强制输出模式,在运行中需要暂停输出波形并且强制输出高或低电平
(没啥用,因为强制输出高电平==设置100%占空比,强制输出低电平==设置0%占空比)
TIM_OCxPreloadConfig();//配置CCR寄存器的预装功能(影子寄存器——写入值不会立即生效,而是在更新事件后再生效)
TIM_CtrlPWMOutputs();//仅高级定时器使用,其输出PWM时,需要调用这个函数使能主输出,否则PWM不能正常输出
初始化输入捕获时从左至右参考该图流程
代码逻辑:初始化TIM2的通道1产生一个PWM波形,输出引脚为PA0,通过SetCompare函数在运行过程中调节CCR的值,从而控制PWM的占空比
//为了在运行过程中也能调整PWM的频率(通过调节PSC),需要再加一个函数来实现
-
PWM 频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM周期对应计数器的一个溢出更新周期——PWM频率等于计数器的更新频率
-
PWM 占空比: Duty = CCR / (ARR + 1)
-
PWM 分辨率: Reso = 1 / (ARR + 1)
通过【PWM频率】公式可知PSC和ARR都可以调节频率,但当更改ARR时涉及占空比变化,只通过PSC调节频率不会影响占空比
//此处通过【PSC】来调节【PWM频率】,且固定【ARR】为【100-1】,CCR的值直接就可以体现占空比
1、完成驱动文件导入操作和编写驱动程序基本代码(参考之前文章)
//将【PWM】配置文件放在【Hardware】文件夹里
2、在PWM.c中初始化函数
PWM
_Init
void PWM_Init(void){//第一步:开启时钟(注意使用APB1的开启时钟函数,因为TIM2是APB1总线的外设)RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//第二步:选择时基单元的时钟(在stm32f10x_tim.h文件中查找函数)TIM_InternalClockConfig(TIM2);//这里选择为内部时钟//时基单元就由内部时钟驱动了//第三步:配置时基单元(在stm32f10x_tim.h文件中查找函数)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//指定计数器模式(此处选择向上计数)TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//指定要在下一次更新事件时加载到ARR自动重新加载寄存器中的周期值 ARR取值[0,65535]TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//指定用于划分TIM时钟的预分频器值 PSC取值[0,65535]TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//指定重复计数器的值(高级定时器才用得上,本项目给0)TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化时基单元//第四步:初始化输出比较单元(通道)【引脚定义表】//使用PA0口对应第一个输出比较通道TIM_OCInitTypeDef TIM_OCInitStructure;//为了避免出现不确定因素,PWM需要先把结构体成员完整配置一遍默认初始值,再根据需要更改个别地方TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋初始值TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//设置输出比较的模式TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR的值TIM_OC1Init(TIM2,&TIM_OCInitStructure);//到此通道初始化完成,可以在TIM2的OC1通道上输出PWM波形//波形需要借用GPIO口进行输出//TIM2的OC1的通道借用了PA0【引脚定义表】//第五步:初始化GPIO口RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);//不需要打开中断和配置NVIC//第六步:启动定时器(在stm32f10x_tim.h文件中查找函数)TIM_Cmd(TIM2,ENABLE);//至此PWM波形就能通过PA0输出了}
3、在PWM.c中编写更改CCR函数
PWM_SetCompare1
//在运行过程中更改CCR值(改变占空比)CCR在初始化时为0void PWM_SetCompare1(uint16_t Compare)//单独更改通道1的CCR值{TIM_SetCompare1(TIM2,Compare);}//TIM_SetCompare函数设置的是CCR的值,不代表直接设置占空比,占空比是由【CCR】和【ARR+1】共同决定的//在PWM.c中编写更改PSC函数PWM_SetPrescalervoid PWM_SetPrescaler(uint16_t Prescaler){TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Immediate);//单独写入PSC的函数}//第三个参数(此处要求不高,选择立即重装)* @arg TIM_PSCReloadMode_Update: The Prescaler is loaded at the update event.//预分频器在更新事件后重装(会有一个缓存器,延迟参数的写入时间,等一个周期结束更新事件时,再统一改变参数)* @arg TIM_PSCReloadMode_Immediate: The Prescaler is loaded immediately.//预分频器立即重装,可能会在值改变时产生切断波形的现象(在频率变化时会出现一个不完整的周期)
4、在PWM.h中声明初始化函数
PWM
_In
it和
更改CCR
函数
PWM_SetCompare1更改PSC函数
PWM_SetPrescaler
void PWM_Init(void);void PWM_SetCompare1(uint16_t Compare);void PWM_SetPrescaler(uint16_t Prescaler);
5、在主程序main.c中
#includ
e
"PWM
.h
"
#include "PWM.h"
6、在主循环之前先初始化PWM
7、在主循环中编写程序主体
int main(void){OLED_Init();PWM_Init();//Freq = [72M/(PSC+1)]/(ARR+1)//Duty = CCR/(ARR+1)PWM_SetPrescaler(720-1);//设置PSCPWM_SetCompare1(50);//设置CCR值,本例固定ARR为[100-1]//此时PA0输出频率1KHz,占空比为50%的待测信号while(1){}}
实现功能:PA0输出频率1KHz,占空比为50%的PWM信号(可在Keil虚拟示波器中看到)
用TIM函数配置输入捕获模块的函数
TIM_ICInit();//用结构体配置输入捕获单元的函数
另外:OC输出比较和IC输入捕获都有4个通道,OCInit()每个通道单独占一个函数,而ICInit()4个通道共用一个函数,在函数内部选择通道
TIM_PWMIConfig();//也是用
构体配置输入捕获单元的函数,但可一次配置两个通道
TIM_ICStructInit();//给输入捕获结构体赋一个初始值
TIM_SelectInputTrigger();//选择输入触发源TRGI
TIM_SelectOutputTrigger();//选择输出触发源TRGO
TIM_SelectSlaveMode();//选择从模式
TIM_SetICxPrescaler();//分别单独配置通道1、2、3、4的分频器
TIM_GetCapturex();//分别读取4个通道的CCR
//
TIM_SetComparex();//单独更改CCR寄存器值的函数,在运行的时候更改占空比
//不同之处在于:OC输出比较模式下CCR是只写的,要用
SetComparex写入
IC输入捕获模式下CCR是只读的,要用
GetCapturex读出
输入捕获完整部分
初始化输入捕获对照PPT基本结构配置
1、建立【输入捕获模块IC】完成驱动文件导入操作和编写驱动程序基本代码
//将【IC】配置文件放在【Hardware】文件夹里
2、在IC.c中初始化函数
IC
_Init
void IC_Init(void){//第一步:开启TIM和GPIO的时钟(注意使用APB1的开启时钟函数,因为TIM3是APB1总线的外设)//【TIM2】用于产生PWM波形,输入捕获要用别的通道,此处选择【TIM3】RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//第二步:初始化GPIO口,把GPIO配置成输入模式,一般选择【上拉输入】或者【浮空输入】//【引脚定义表】TIM3的通道1和通道2分别对应PA6和PA7,通道3和通道4分别对应PB0和PB1//本例选择用TIM3的通道1进行输入捕获,故初始化PA6GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);//第三步:选择时基单元的时钟(在stm32f10x_tim.h文件中查找函数)TIM_InternalClockConfig(TIM3);//这里选择为内部时钟,选择TIM3//时基单元就由内部时钟驱动了//第四步:配置时基单元(在stm32f10x_tim.h文件中查找函数)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//指定计数器模式(此处选择向上计数)TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//指定要在下一次更新事件时加载到ARR自动重新加载寄存器中的周期值 ARR取值[0,65535]TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//指定用于划分TIM时钟的预分频器值 PSC取值[0,65535]TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//指定重复计数器的值(高级定时器才用得上,本项目给0)TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//将以上参数配置到TIM3的时基单元//此处ARR设置为最大,因为ARR(重装计数值)越大,输入捕获测量的频率越精准//PSC的值决定了测周法的标准频率fc=[72MHz/(PSC+1)]//此处给PSC=72-1,则fc=72MHz/72=1MHz//第五步:配置输入捕获单元(滤波器、极性选择、直连/交叉通道、分频器)TIM_ICInitTypeDef TIM_ICInitStructure;TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//指定配置的TIM通道TIM_ICInitStructure.TIM_ICFilter = 0xF;//配置输入捕获的滤波器,数越大滤波效果越好TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//极性选择(上升沿触发还是下降沿触发)TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//配置触发信号分频器(此处不分频),不分频就是每次触发都有效,2分频就是每隔一次有效一次TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入(此处选择直连通达),可以选择直连/交叉通道(配置TIxFPx后的【数据选择器】)TIM_ICInit(TIM3,&TIM_ICInitStructure);//初始化输入捕获单元//第六步:选择从模式的触发源(这里选择TI1FP1)TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);//第七步:选择触发之后执行的从模式操作(本例配置为Reset)TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);//第八步:启动定时器(在stm32f10x_tim.h文件中查找函数)TIM_Cmd(TIM3,ENABLE);//配置完成//当需要读取最新一个周期的频率时,直接读取CCR寄存器,按照【Freq=fc/N】计算得到频率}
3、在IC.c中编写用于读取CCR以进行频率计算的函数
IC_GetFreq
//读取CCR进行频率计算//之前在配置时基单元给PSC=72-1,则fc=72MHz/72=1MHz//取CCR寄存器,按照【Freq=fc/N】计算得到频率uint32_t IC_GetFreq(void)//本函数返回最新一个周期的频率值(Hz){return ( 1000000 / TIM_GetCapture1(TIM3) ) - 1;//实测多1,减回去//读取TIM3通道1的CCR(计数N值)}
4、在IC.h中声明初始化函数
IC
_Init和用于读取CCR以进行频率计算的函数
IC_GetFreq
void IC_Init(void);uint32_t IC_GetFreq(void);
5、在主程序main.c中
#includ
e “IC
.h
"
#include "IC.h"
6、在主循环之前先初始化IC
7、在主循环中编写程序主体
int main(void){OLED_Init();PWM_Init();IC_Init();OLED_ShowString(1,1,"Freq:00000Hz");//Freq = [72M/(PSC+1)]/(ARR+1)//Duty = CCR/(ARR+1)PWM_SetPrescaler(720-1);PWM_SetCompare1(50);//此时PWM模块已将待测信号输出到PA0了while(1){OLED_ShowNum(1,6,IC_GetFreq(),5);}}
实现功能:上电后由PA0产生PWM波形,PA6进行输入捕获并在OLED显示
Freq数值