希望看过的各位对错误之处可以帮忙指正。
长期更新。
使用高级控制定时器(TIM1或TIM8)产生PWM信号驱动马达时,可以用另一个通用TIMx(TIM2、TIM3、 TIM4或TIM5)定时器作为“接口定时器”来连接霍尔传感器 。这里选用定时器3。
stm32的通用定时器内部集成了针对霍尔/编码器信号处理的电路。如下图:
- TIMx_CH1、TIMx_CH2和TIMx_CH3三个引脚接受到的霍尔信号,经过输入异或(XOR)功能,传到输入通道TI1
- 信号从TI1的经过输入滤波器(配置滤波长度)和边沿检测器(输入触发有效极性,这里配置为双极性有效,即上升沿和下降沿都能触发)后产生脉冲信号TI1F_ED,经过选择器后作为TRC输入,再将TRC作为输入捕获通道IC1的输入信号。IC1PS就是计数器的捕获触发信号(脉冲),决定什么时候将计数器的值传进输入捕获寄存器。整个过程就是映射过程。
- 配置时基,内部时钟分频后作为计数器时钟,将从模式控制器配置为复位模式,每当3个输入之一变化时,计数器从新从0开始计数,计数到下一个变化开始为止,这个计数器值CNT就反映了两个霍尔状态之间的时间间隔,通过这个值我们可以计算出电机的转速信息。
注意,TI1F_ED是一个脉冲,根据这点之后触发也是脉冲触发。如下图,当异或后信号发生跳变,都会产生一个脉冲 TI1F_ED。
GPIO初始化#
先在头文件进行宏定义
<font size="3">#define HALL_TIMx TIM3#define HALL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd #define HALL_TIM_CLK RCC_APB1Periph_TIM3 #define HALL_TIM_PERIOD 0xFFFF //ARR,计数周期,配置为最大65535 #define HALL_TIM_PRESCALER (72-1) //预分频系数为72 #define HALL_TIM_Channel_x TIM_Channel_1 //通道1 #define HALL_TIM_GPIO_REMAP GPIO_FullRemap_TIM3 //引脚重映射 #define HALL_TIM_GPIO_CLK RCC_APB2Periph_GPIOC #define HALL_TIM_CH1_PIN GPIO_Pin_6 #define HALL_TIM_CH1_GPIO GPIOC #define HALL_TIM_CH2_PIN GPIO_Pin_7 #define HALL_TIM_CH2_GPIO GPIOC #define HALL_TIM_CH3_PIN GPIO_Pin_8 #define HALL_TIM_CH3_GPIO GPIOC #define HALL_TIM_IRQn TIM3_IRQn #define HALL_TIM_IRQHANDLER TIM3_IRQHandler</font>
复制代码需要注意的是,时基编程中,要将TIMx_ARR置为其最大值(计数器必须通过TI1的变化清零)。设置预分频器得到的最大计数器周期,它需要长于霍尔传感器上的两次变化的时间间隔,不然就会发生溢出,这里进行72分频后,计数器时钟是1Mhz,所以计数周期是1us*65535=65.535ms。
GPIO初始化配置
将定时器3的3个GPIO初始化,配置为上拉输入模式
void HALL_TIMx_GPIO_Init(void)
{
//GPIO初始化结构体
GPIO_InitTypeDef GPIO_InitStructure;
//打开GPIOC和复用功能时钟
RCC_APB2PeriphClockCmd(HALL_TIM_GPIO_CLK|RCC_APB2Periph_AFIO,ENABLE);
//GPIOC_6初始化
GPIO_InitStructure.GPIO_Pin = HALL_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(HALL_TIM_CH1_GPIO, &GPIO_InitStructure);
//GPIOC_7初始化
GPIO_InitStructure.GPIO_Pin = HALL_TIM_CH2_PIN;
GPIO_Init(HALL_TIM_CH2_GPIO, &GPIO_InitStructure);
//GPIOC_8初始化
GPIO_InitStructure.GPIO_Pin = HALL_TIM_CH3_PIN;
GPIO_Init(HALL_TIM_CH3_GPIO, &GPIO_InitStructure);
//引脚重映射
GPIO_PinRemapConfig(HALL_TIM_GPIO_REMAP,ENABLE);
}
霍尔接口配置#
- 配置定时器3的时基
- 将定时器3配置为输入捕获模式,将IC1映射到TRC
- 从模式控制器配置为复位模式
- 使能触发中断
void HALL_TIMx_Configuration(void){ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; //时基初始化结构体 TIM_ICInitTypeDef TIM_ICInitStruct; //输入捕获初始化结构体 //打开定时器3时钟 HALL_TIM_APBxClock_FUN(HALL_TIM_CLK,ENABLE); //时基参数配置 TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1; //时钟分割:TDTS = TCK_INT ,采样频率=72M TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_CenterAligned1; //中心对齐模式 TIM_TimeBaseInitStruct.TIM_Period=HALL_TIM_PERIOD; //重装值,计数周期 TIM_TimeBaseInitStruct.TIM_Prescaler=HALL_TIM_PRESCALER; //预分频系数 //时基初始化 TIM_TimeBaseInit(HALL_TIMx,&TIM_TimeBaseInitStruct); //输入捕获通道1配置 TIM_ICInitStruct.TIM_Channel=HALL_TIM_Channel_x; //通道1 TIM_ICInitStruct.TIM_ICFilter=0x00; //滤波长度:0 TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_BothEdge; //上升沿和下降沿 TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1; //配置输入信号分频,不分频 TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_TRC; //将IC1映射到 TRC上 TIM_ICInit(HALL_TIMx,&TIM_ICInitStruct); //输入捕获初始化 //配置中断优先级 HALL_IT_Configuration(); //使能Timx霍尔传感器接口,实际上就是完成将CH1、CH2和CH3异或输入 TIM_SelectHallSensor(HALL_TIMx,ENABLE); //输入触发源选择,选择TI1F_ED TIM_SelectInputTrigger(HALL_TIMx, TIM_TS_TI1F_ED); //从模式选择,复位模式 TIM_SelectSlaveMode(HALL_TIMx, TIM_SlaveMode_Reset); //主从模式选择 TIM_SelectMasterSlaveMode(HALL_TIMx, TIM_MasterSlaveMode_Enable); //允许触发中断 TIM_ITConfig(HALL_TIMx, TIM_IT_Trigger, ENABLE); //使能定时器 TIM_Cmd(HALL_TIMx, ENABLE); //清除TIMx中断标志位 TIM_ClearITPendingBit (HALL_TIMx,TIM_IT_Trigger); }
复制代码其中,中断优先级配置函数为
void HALL_IT_Configuration(void){ //NVIC初始化结构体 NVIC_InitTypeDef NVIC_InitStruct; //中断优先级配置 NVIC_InitStruct.NVIC_IRQChannel=HALL_TIM_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStruct.NVIC_IRQChannelSubPriority=0; NVIC_Init(&NVIC_InitStruct); }
复制代码中断优先级我将霍尔触发中断配置为最高级。
就是调用我们前面写的两个配置函数。
void HALL_TIMx_Init(void)
{
HALL_TIMx_GPIO_Init();
HALL_TIMx_Configuration();
}
中断服务函数#
使能了中断,不要忘记编写中断服务函数,我习惯将其放在main.c里面,也看过放在 it.c里面
值得一提的是,通用定时器中断类型并不体现在中断服务函数名称上,通用定时器的中断服务函数名就只有一个,可以在启动文件里面轻易找到她,她是通过判断中断标志位,区分哪一种中断。
void HALL_TIM_IRQHANDLER(void){ if(TIM_GetITStatus(TIM3,TIM_IT_Trigger)!=RESET) { HALL_TIMx_Callback(); //读取霍尔信号并换相 TIM_ClearITPendingBit(TIM3,TIM_IT_Trigger); //清除触发中断标志位 } }
复制代码需要提醒的是,这个中断服务函数里面一定不能有任何费时的操作,假设电机高速旋转,存在一个操作需要时间大于霍尔两个状态之间切换时间(在上一个PWM输出和惯性的影响下),那么存在漏步问题,电机将不按照顺序旋转下去,所以费时操作会影响下一个霍尔状态的读取,导致PWM输出不正确。
可以看到,中断服务函数这里调用了函数 HALL_TIMx_Callback();她的作用是判断3个输入引脚上的电平,并将其合成霍尔组合信号,根据这个组合信号,当霍尔信号每一次变化,产生中断时,我们就可以按照霍尔换相表,调用在六步PWM输出里面写的换相函数BLDC_PHASE_CHANGE(pinstate);。
void HALL_TIMx_Callback(void){ uint8_t pinstate=0; if((HALL_TIM_CH1_GPIO->IDR & HALL_TIM_CH1_PIN) != (uint32_t)Bit_RESET) //CH1状态获取 { pinstate |= 0x01; } if((HALL_TIM_CH2_GPIO->IDR & HALL_TIM_CH2_PIN) != (uint32_t)Bit_RESET) //CH2状态获取 { pinstate |= 0x02; } if((HALL_TIM_CH3_GPIO->IDR & HALL_TIM_CH3_PIN) != (uint32_t)Bit_RESET) //CH3状态获取 { pinstate |= 0x04; } //Usart_SendByte(pinstate); //把霍尔换步信号发送到串口上,不测试时将其注释掉 BLDC_PHASE_CHANGE(pinstate); //调用换相函数 }
复制代码开环波形测试#
接上BLDC,我使用的是24V的鼓风机,将三相逆变电路板通电测试,通过示波器看其中两路波形(只有两路示波器)。
个人分析:可以看到,在切换通道时候出现了较高的反向电动势,但由于MOS管体二极管将其钳位在电源电压。
转载自BLDC开发笔记3.霍尔接口与触发换相 - 懒懒阳光下的午睡 - 博客园 (cnblogs.com)