希望看過的各位對錯誤之處可以幫忙指正。
長期更新。
霍爾接口初始化
使用高級控制定時器(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初始化
先在頭文件進行宏定義
#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
需要注意的是,時基編程中,要將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管體二極管將其鉗位在電源電壓。