一、前言
????????我在学习的过程中,经历过毫无头绪,找到一堆学习资料却不知从何开始的痛苦。我希望我整理的笔记,不光可以方便自己在以后的项目中拿来即用,也希望能够对那些喜欢玩硬件、爱折腾的小伙伴有所帮助。如果你们觉得我的文章对你们有用,欢迎大家点赞+收藏,让我知道,我的工作是有价值的。????????
? ? ? ? 这篇笔记,综合参考了野火、正点原子、st官方以及网络上的一些资料,然后经过我自己的思考、尝试与实践,从而整理出来的。感谢那些愿意在学习的路上分享自己知识与经验的前辈。
二、代码和理论
(一)、PWM输出编程思路
1.定时器GPIO初始化
选用三个引脚,输出引脚,互补输出引脚,断路输入引脚,配置好引脚相关参数,选择复用功能,再连接好各个引脚的复用功能。
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启定时器相关的GPIO外设时钟*/
RCC_AHB1PeriphClockCmd (ADVANCE_OCPWM_GPIO_CLK | ADVANCE_OCNPWM_GPIO_CLK| ADVANCE_BKIN_GPIO_CLK, ENABLE);
/* 指定引脚复用功能 */
GPIO_PinAFConfig(ADVANCE_OCPWM_GPIO_PORT,ADVANCE_OCPWM_PINSOURCE, ADVANCE_OCPWM_AF);
GPIO_PinAFConfig(ADVANCE_OCNPWM_GPIO_PORT,ADVANCE_OCNPWM_PINSOURCE,ADVANCE_OCNPWM_AF);
GPIO_PinAFConfig(ADVANCE_BKIN_GPIO_PORT,ADVANCE_BKIN_PINSOURCE,ADVANCE_BKIN_AF);
/* 定时器功能引脚初始化 */
GPIO_InitStructure.GPIO_Pin = ADVANCE_OCPWM_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(ADVANCE_OCPWM_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADVANCE_OCNPWM_PIN;
GPIO_Init(ADVANCE_OCNPWM_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADVANCE_BKIN_PIN;
GPIO_Init(ADVANCE_BKIN_GPIO_PORT, &GPIO_InitStructure);
2.定时器时基结构体TIM_TimeBaseInitTypeDef初始化
高级定时器属于APB2,内部时钟是168MHZ,在时基结构体中我们设置定时器周期参数为1000,频率为1MHz,使用向上计数方式。因为我们使用的是内部时钟,所以外部时钟采样分频成员不需要设置,重复计数器我们没用到,也不需要设置。
Period(定时器周期)范围是0~65535
ClockDivision(时钟分频)设置定时器时钟CK_INT频率与死区发生器以及数字滤波器采样时钟频率分频比。可以选择1、2、4分频。
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 开启TIMx_CLK,x[1,8]
RCC_APB2PeriphClockCmd(ADVANCE_TIM_CLK, ENABLE);
/* 累计 TIM_Period个后产生一个更新或者中断*/
//当定时器从0计数到1023,即为1024次,为一个定时周期,可以自己定义
TIM_TimeBaseStructure.TIM_Period = 1024-1;
// 高级控制定时器时钟源TIMxCLK = HCLK=168MHz
// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=100000Hz
TIM_TimeBaseStructure.TIM_Prescaler = 1680-1;
// 采样时钟分频,配置死区时间要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数方式,向上
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器TIMx, x[1,8]
TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);
3.定时器输出比较结构体TIM_OCInitTypeDef初始化
ChannelPulse是调节占空比的大小可以自己定义
配置完成后要使能通道,和使能定时器
TIM_OCInitTypeDef TIM_OCInitStructure;
/*PWM模式配置*/
//配置为PWM模式1,常用的为PWM1/PWM2
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
//脉冲宽度,范围为0至65535
TIM_OCInitStructure.TIM_Pulse = ChannelPulse;
//可选择高电平还是低电平有效,决定着定时器通道有效电平
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
//使能通道1
TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
/* 使能通道1重载 */
TIM_OC1PreloadConfig(ADVANCE_TIM, TIM_OCPreload_Enable);
4.定时器刹车和死区结构体TIM_BDTRInitTypeDef初始化
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
/* 自动输出使能,断路、死区时间和锁定配置 */
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
TIM_BDTRInitStructure.TIM_DeadTime = 11;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(ADVANCE_TIM, &TIM_BDTRInitStructure);
// 使能定时器
TIM_Cmd(ADVANCE_TIM, ENABLE);
/* 主动输出使能 */
TIM_CtrlPWMOutputs(ADVANCE_TIM, ENABLE);
(二)、PWM输入编程思路
1.初始化相关GPOI口(连接通用定时器和高级定时器的)
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_AHB1PeriphClockCmd (GENERAL_OCPWM_GPIO_CLK, ENABLE);
RCC_AHB1PeriphClockCmd (ADVANCE_ICPWM_GPIO_CLK, ENABLE);
/* 定时器复用引脚 */
GPIO_PinAFConfig(GENERAL_OCPWM_GPIO_PORT,GENERAL_OCPWM_PINSOURCE,GENERAL_OCPWM_AF);
GPIO_PinAFConfig(ADVANCE_ICPWM_GPIO_PORT,ADVANCE_ICPWM_PINSOURCE,ADVANCE_ICPWM_AF);
/* 通用定时器PWM输出引脚 */
GPIO_InitStructure.GPIO_Pin = GENERAL_OCPWM_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GENERAL_OCPWM_GPIO_PORT, &GPIO_InitStructure);
/* 高级控制定时器PWM输入捕获引脚 */
GPIO_InitStructure.GPIO_Pin = ADVANCE_ICPWM_PIN;
GPIO_Init(ADVANCE_ICPWM_GPIO_PORT, &GPIO_InitStructure);
2.通用定时器产生PWM配置
再通用定时器中配置时基结构体和输出结构体初始化
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 开启TIMx_CLK,x[2,3,4,5,12,13,14]
RCC_APB1PeriphClockCmd(GENERAL_TIM_CLK, ENABLE);
/* 累计 TIM_Period个后产生一个更新或者中断*/
//当定时器从0计数到9999,即为10000次,为一个定时周期
TIM_TimeBaseStructure.TIM_Period = 10000-1;
// 通用定时器2时钟源TIMxCLK = HCLK/2=84MHz
// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=100KHz
TIM_TimeBaseStructure.TIM_Prescaler = 840-1;
// 采样时钟分频
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数方式
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 初始化定时器TIMx, x[1,8]
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
/* PWM输出模式配置 */
/* 配置为PWM模式1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
/* PWM脉冲宽度 */
TIM_OCInitStructure.TIM_Pulse = 3000-1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
/* 使能通道1 */
TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
/*使能通道1重载*/
TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 使能定时器
TIM_Cmd(GENERAL_TIM, ENABLE);
}
3.高级定时器PWM输入配置
再高级定时器中配置时基结构体,输入捕获结构体,从模式结构体初始化
先选定捕获通道IC1和IC2通道1,捕获周期,通道2,捕获占空比
I2C作为间接输入模式,我们需要配置他的从模式,即从模式复位模式,定时器触发源为TIM_TS_TI1FP1,最后使用函数HAL_TIM_SlaveConfigSynchronization进行配置。
static void TIM_PWMINPUT_Config(void)
{
//初始化三个结构体,时基结构体,输入捕获结构体,模式结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
// 开启TIMx_CLK,x[1,8]
RCC_APB2PeriphClockCmd(ADVANCE_TIM_CLK, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 0xFFFF-1;
// 高级控制定时器时钟源TIMxCLK = HCLK=168MHz
// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=100KHz
TIM_TimeBaseStructure.TIM_Prescaler = 1680-1;
// 计数方式
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 初始化定时器TIMx, x[1,8]
TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);
/* IC1捕获:上升沿触发 TI1FP1 */
TIM_ICInitStructure.TIM_Channel = ADVANCE_IC1PWM_CHANNEL;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);
/* IC2捕获:下降沿触发 TI1FP2 */
TIM_ICInitStructure.TIM_Channel = ADVANCE_IC2PWM_CHANNEL;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);
//当工作在PWM输入模式时,只需要设置触发信号的那一路即可(用于测量周期)
//另一路(用于测量占空比)会有硬件自动设置,不需要再配置
/* 选择定时器输入触发: TI1FP1 */
TIM_SelectInputTrigger(ADVANCE_TIM, TIM_TS_TI1FP1);
/* 选择从模式: 复位模式 */
TIM_SelectSlaveMode(ADVANCE_TIM, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(ADVANCE_TIM,TIM_MasterSlaveMode_Enable);
/* 使能高级控制定时器 */
TIM_Cmd(ADVANCE_TIM, ENABLE);
/* 使能捕获/比较2中断请求 */
TIM_ITConfig(ADVANCE_TIM, TIM_IT_CC1, ENABLE);
4.计算测量的频率和占空比,打印出来比较
/**
* @brief This function handles TIM interrupt request.
* @param None
* @retval None
*/
void ADVANCE_TIM_IRQHandler (void)
{
/* 清除定时器捕获/比较1中断 */
TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);
/* 获取输入捕获值 */
IC1Value = TIM_GetCapture1(ADVANCE_TIM);
IC2Value = TIM_GetCapture2(ADVANCE_TIM);
printf("IC1Value = %d IC2Value = %d ",IC1Value,IC2Value);
if (IC1Value != 0)
{
/* 占空比计算 */
DutyCycle = (float)(IC2Value * 100) / IC1Value;
/* 频率计算 */
Frequency = 168000000/1680/(float)IC1Value;
printf("占空比:%0.2f%% 频率:%0.2fHz\n",DutyCycle,Frequency);
}
else
{
DutyCycle = 0;
Frequency = 0;
}
}
|