三霍尔BLDC——已知霍尔元件输出与相线输入电压的关系表,如何写程序

无刷直流电机比较流行,很多电机内部加入霍尔元件,通过霍尔元件可以知道电机转子的位置,根据这个位置给电机相线供电,这样电机就转起来了。框图如下所示

image

霍尔元件输出与相线输入电压的关系可以让BLDC电机厂家提供,一般都提供这个对应关系表,如下图所示

image

有了上面这些足可以让我们设计电路编写程序让电机转起来!

 

有上面的框图可以看出,3相电机的驱动需要六个mos管,一般用的是NMOS,大功率的NMOS比较便宜。大功率MOS管有较大的结电容,控制电压也高些,无法用单片机直接驱动,所以需要驱动电路。驱动芯片组成的驱动电路比较简单,常见的有IR2110S,我这里用IR2110S设计了MOS驱动电路,如下图所示,其中C24和D5是自举电路,为了控制Q3抬高电压。(然后组成3组(IR2110S+2NMOS),从而驱动6个NMOS, 下图为1组IR2110S)

image

霍尔元件供电是5V,他的输出一般也是5V,可以分压后给单片机用。

image

单片机用流行STM32,他有高级定时器T1,T8,可以输出3对互补的PWM波,还有刹车信号输入,这些特性对于电机的可控制非常合适。电路如下图所示

image

BLDC电机控制用到单片机定时器及IO中断,初始化部分如下

/*
*@三霍尔BLDC驱动:初始化部分
*/
void TIM_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseInitStruct;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;//
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_ICInitTypeDef TIM_ICInitStructure;
	
	// 初始化时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_TIM1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
   //IR2110S HIN 三相
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//IR2110S LIN 三相
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  
  //HALL 输入三相
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7 ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0 ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
  //将HALL输入的三相,均使能其中断(使能上升沿和下降沿触发)
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
	
	EXTI_InitStructure.EXTI_Line=EXTI_Line0; 
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;  
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
  NVIC_Init(&NVIC_InitStructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
	
	EXTI_InitStructure.EXTI_Line= EXTI_Line6; 
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;  
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
  NVIC_Init(&NVIC_InitStructure); 
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource7);
	
	EXTI_InitStructure.EXTI_Line= EXTI_Line7; 
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;  
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
  NVIC_Init(&NVIC_InitStructure); 


	//初始化定时器1
	TIM_DeInit(TIM1);        //关闭定时器
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1 ;    
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up ;  
	TIM_TimeBaseInitStruct.TIM_Period = 1000 ;       
	TIM_TimeBaseInitStruct.TIM_Prescaler = 3 ;     
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct ) ;       

  	//初始化定时器1的输出比较功能(PWM输出功能)
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	 
  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; 
  TIM_OCInitStructure.TIM_Pulse = 1000;
  
  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_Set;
  
  TIM_OC1Init(TIM1, &TIM_OCInitStructure);
  TIM_OC2Init(TIM1, &TIM_OCInitStructure);
  TIM_OC3Init(TIM1, &TIM_OCInitStructure);

  /* Automatic Output enable, Break, dead time and lock configuration*/
  TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
  TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
  TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
  TIM_BDTRInitStructure.TIM_DeadTime = 1;
  TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
  TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
  TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
  TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
  
  //配置定时器的3个输出通道
  TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);
  TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable);
  TIM_OC3PreloadConfig(TIM1,TIM_OCPreload_Enable);
	
  TIM_ARRPreloadConfig(TIM1, ENABLE);
  TIM_Cmd(TIM1, ENABLE);
  TIM_CtrlPWMOutputs(TIM1, ENABLE); 
  
  TIM_CCxCmd(TIM1,TIM_Channel_1,TIM_CCx_Enable);
  TIM_CCxNCmd(TIM1,TIM_Channel_1,TIM_CCxN_Enable);
  TIM_CCxCmd(TIM1,TIM_Channel_2,TIM_CCx_Enable);
  TIM_CCxNCmd(TIM1,TIM_Channel_2,TIM_CCxN_Enable);
  TIM_CCxCmd(TIM1,TIM_Channel_3,TIM_CCx_Enable);
  TIM_CCxNCmd(TIM1,TIM_Channel_3,TIM_CCxN_Enable);
}

让电机转起来需要知道电机转子的位置,根据霍尔相位对应表驱动电机相线,程序里用中断获取霍尔电平的变化。

void EXTI0_IRQHandler(void) 
{ 
	int i,j;
	if(EXTI_GetITStatus(EXTI_Line0)!=RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line0);


		step=((GPIOA->IDR & GPIO_Pin_6)>>6)+((GPIOA->IDR & GPIO_Pin_7)>>6)+((GPIOB->IDR & GPIO_Pin_0)<<2);
		if(start==1)
		{
			TIM1->CCER=PHASE_CHANGE[step];
			int_count1++;
		}
	}
}

void EXTI9_5_IRQHandler(void) 
{ 
	int i,j;
	if(EXTI_GetITStatus(EXTI_Line6)!=RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line6);

		step=((GPIOA->IDR & GPIO_Pin_6)>>6)+((GPIOA->IDR & GPIO_Pin_7)>>6)+((GPIOB->IDR & GPIO_Pin_0)<<2);
		if(start==1)
		{
			TIM1->CCER=PHASE_CHANGE[step];
			int_count2++;
		}

	}
	if(EXTI_GetITStatus(EXTI_Line7)!=RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line7);

		step=((GPIOA->IDR & GPIO_Pin_6)>>6)+((GPIOA->IDR & GPIO_Pin_7)>>6)+((GPIOB->IDR & GPIO_Pin_0)<<2);
		if(start==1)
		{
			TIM1->CCER=PHASE_CHANGE[step];
			int_count3++;
		}
	}
}

检测到变化后改变定时器输出,从而使电机相线得到驱动,我在程序里做好了数组,把得到了位置通过数组给定时器CCER寄存器,这样电机就转起来了

//程序中PHASE_CHANGE的下标为step
//step 的值就是 (C << 2) | (B << 1) | A,即一个由 C B A 组成的3位二进制数对应的十进制数
//构建此表的依据:
// CBA = 0b000 时, CCER = 0x0000;
// CBA = 0b001 时, CCER = 0x0104;
int PHASE_CHANGE[7]={0x0000,0x0104,0x0041,0x0140,0x0410,0x0014,0x0401};

扩展:TIM1的CCER寄存器的定义

我们来详细介绍一下STM32的高级定时器TIM1的CCER寄存器。

CCER寄存器概述

CCER 是 Capture/Compare Enable Register 的缩写,即捕获/比较使能寄存器。在高级定时器TIM1中,这是一个至关重要的寄存器,它直接控制着4个输出通道(CH1-CH4)的输出使能输出极性

寄存器位结构

TIM1->CCER是一个16位寄存器,其位分配如下:

image

名称 功能描述
0 CC1E 通道1输出使能
1 CC1P 通道1输出极性
2 CC1NE 通道1互补输出使能
3 CC1NP 通道1互补输出极性
4 CC2E 通道2输出使能
5 CC2P 通道2输出极性
6 CC2NE 通道2互补输出使能
7 CC2NP 通道2互补输出极性
8 CC3E 通道3输出使能
9 CC3P 通道3输出极性
10 CC3NE 通道3互补输出使能
11 CC3NP 通道3互补输出极性
12 CC4E 通道4输出使能
13 CC4P 通道4输出极性
14-15 保留 保留位

关键功能详解

1. 输出使能控制

  • CCxE:主输出使能位

    • 0:禁止通道x输出(OCx输出无效)

    • 1:使能通道x输出(OCx信号输出到对应的输出引脚)

  • CCxNE:互补输出使能位

    • 0:禁止通道x互补输出(OCxN输出无效)

    • 1:使能通道x互补输出(OCxN信号输出到对应的输出引脚)

2. 输出极性控制

  • CCxP:主输出极性选择

    • 0:OCx高电平有效

    • 1:OCx低电平有效

  • CCxNP:互补输出极性选择

    • 0:OCxN高电平有效

    • 1:OCxN低电平有效

在BLDC驱动中的应用

三相桥式电路配置

在BLDC电机驱动中,通常使用三相全桥电路,每个相需要两个MOSFET(上桥臂和下桥臂):

Phase U: CH1 (上桥臂) + CH1N (下桥臂)
Phase V: CH2 (上桥臂) + CH2N (下桥臂)  
Phase W: CH3 (上桥臂) + CH3N (下桥臂)

代码中的PHASE_CHANGE查找表解析

回顾代码中的查找表:

int PHASE_CHANGE[7] = {0x0000, 0x0104, 0x0041, 0x0140, 0x0410, 0x0014, 0x0401};

让我们解析其中一个值,例如 0x0104(二进制:0000 0001 0000 0100):

  • 位0 (CC1E):0 - 通道1输出禁止

  • 位1 (CC1P):0 - 通道1极性(未使用)

  • 位2 (CC1NE):1 - 通道1互补输出使能

  • 位3 (CC1NP):0 - 通道1互补输出极性(高电平有效)

  • 位4 (CC2E):0 - 通道2输出禁止

  • 位5 (CC2P):0 - 通道2极性(未使用)

  • 位6 (CC2NE):0 - 通道2互补输出禁止

  • 位7 (CC2NP):0 - 通道2互补输出极性

  • 位8 (CC3E):1 - 通道3输出使能

  • 位9 (CC3P):0 - 通道3极性(高电平有效)

  • 位10-15:其他位

这意味着:

  • U相下桥臂使能(CC1NE=1)

  • W相上桥臂使能(CC3E=1)

  • 其他桥臂均关闭

典型的六步换相CCER配置

 
步进 导通相 CCER值 二进制表示 使能的通道
0 U-V+ 0x0104 0000 0001 0000 0100 CH1N, CH3
1 U-W+ 0x0041 0000 0000 0100 0001 CH1N, CH2
2 V-W+ 0x0140 0000 0001 0100 0000 CH2N, CH3
3 V-U+ 0x0410 0000 0100 0001 0000 CH2N, CH1
4 W-U+ 0x0014 0000 0000 0001 0100 CH3N, CH2
5 W-V+ 0x0401 0000 0100 0000 0001 CH3N, CH1

安全特性

TIM1作为高级定时器,CCER寄存器还与以下安全机制相关:

  1. 死区插入:防止上下桥臂直通

  2. 刹车功能:紧急情况下快速关闭所有输出

  3. 互补输出:专门为电机驱动设计

编程注意事项

  1. 配置顺序:通常先配置TIM1的其他寄存器(ARR、PSC、CCMR等),最后配置CCER

  2. 原子操作:建议直接赋值整个CCER寄存器,而不是使用位操作

  3. 同步更新:修改CCER后,PWM输出会立即生效

  4. 安全考虑:在电机运行期间修改CCER要特别小心,避免产生短路

通过精确控制CCER寄存器,可以实现BLDC电机的精确六步换相控制,这也是您提供的代码中TIM1->CCER = PHASE_CHANGE[step];这行代码的核心作用。

 

这是霍尔输出与PWM输出波形的截图

image

这是定时器3对PWM的输出截图

image

这是电机和电路板

image

posted @ 2025-10-03 22:47  FBshark  阅读(7)  评论(0)    收藏  举报