【电机控制】基于STC8H1K28的六步换向——方波驱动(软件篇)

【电机控制】基于STC8H1K28的六步换向------方波驱动(软件篇)


### 文章目录

  • [@[TOC](文章目录)](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [前言](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [一、main.c](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [二、GPIO.c](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [三、PWMA.c](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [四、ADC.c](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [五、CMP.c](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [六、Timer.c](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [七、PMSM.c](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [八、参考资料](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)
  • [总结](#文章目录 @TOC 前言 一、main.c 二、GPIO.c 三、PWMA.c 四、ADC.c 五、CMP.c 六、Timer.c 七、PMSM.c 八、参考资料 总结)

前言

【电机控制】STC8H无感方波驱动---反电动势过零检测六步换向法


提示:以下是本篇文章正文内容,下面案例可供参考

一、main.c

c 复制代码
void main(void)
{
	GPIO_Init();			//IO初始化
	Uart1_Init();			//串口初始化
	PWMA_config();			//PWMA初始化
	ADC_config();			//ADC初始化
	CMP_config();			//比较器初始化
	Timer0_config();		// Timer0初始化函数 4ms定时器,用于事件处理
	Timer3_Config();		// Timer3初始化函数 1ms定时器,用于换向时间计算
	Timer4_Config();		// Timer4初始化函数 4us定时器,用于计算六步换向
	PWW_Set = 0;			//初始PWM设定值
	TimeOut = 0;			//超时设定值初始化
	EA  = 1; 				// 打开总中断
	while (1)
	{
			Event_Deal();
	}
}

二、GPIO.c

配置IO

c 复制代码
void GPIO_Init(void)
{
	P2n_standard(0xf8);
	P3n_standard(0xbf);
	P5n_standard(0x10);
}

三、PWMA.c

c 复制代码
u8	PWM_Value;	// 决定PWM占空比的值
u8	PWW_Set;	//目标PWM设置
void PWMA_config(void)
{
	P_SW2 |= 0x80;		//SFR enable   

	PWM1   = 0;
	PWM1_L = 0;
	PWM2   = 0;
	PWM2_L = 0;
	PWM3   = 0;
	PWM3_L = 0;
	P1n_push_pull(0x3f);
// 预分频寄存器, 分频 Fck_cnt = Fck_psc/(PSCR[15:0}+1),  
// 边沿对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)),
// 中央对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)*2).
	PWMA_PSCR = 3;		
	PWMA_DTR  = 24;		// 死区时间配置, n=0~127: DTR= n T,   0x80 ~(0x80+n), n=0~63: DTR=(64+n)*2T,  
						//				0xc0 ~(0xc0+n), n=0~31: DTR=(32+n)*8T,   0xE0 ~(0xE0+n), n=0~31: DTR=(32+n)*16T,
	PWMA_ARR    = 255;	// 自动重装载寄存器,  控制PWM周期
	PWMA_CCER1  = 0;
	PWMA_CCER2  = 0;
	PWMA_SR1    = 0;
	PWMA_SR2    = 0;
	PWMA_ENO    = 0;
	PWMA_PS     = 0;
	PWMA_IER    = 0;
//	PWMA_ISR_En = 0;

	PWMA_CCMR1  = 0x68;		// 通道模式配置, PWM模式1, 预装载允许
	PWMA_CCR1   = 0;		// 比较值, 控制占空比(高电平时钟数)
	PWMA_CCER1 |= 0x05;		// 开启比较输出, 高电平有效
	PWMA_PS    |= 0;		// 选择IO, 0:选择P1.0 P1.1, 1:选择P2.0 P2.1, 2:选择P6.0 P6.1, 
//	PWMA_ENO   |= 0x01;		// IO输出允许,  bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P,  bit3: ENO2N,  bit2: ENO2P,  bit1: ENO1N,  bit0: ENO1P
//	PWMA_IER   |= 0x02;		// 使能中断

	PWMA_CCMR2  = 0x68;		// 通道模式配置, PWM模式1, 预装载允许
	PWMA_CCR2   = 0;		// 比较值, 控制占空比(高电平时钟数)
	PWMA_CCER1 |= 0x50;		// 开启比较输出, 高电平有效
	PWMA_PS    |= (0<<2);	// 选择IO, 0:选择P1.2 P1.3, 1:选择P2.2 P2.3, 2:选择P6.2 P6.3, 
//	PWMA_ENO   |= 0x04;		// IO输出允许,  bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P,  bit3: ENO2N,  bit2: ENO2P,  bit1: ENO1N,  bit0: ENO1P
//	PWMA_IER   |= 0x04;		// 使能中断

	PWMA_CCMR3  = 0x68;		// 通道模式配置, PWM模式1, 预装载允许
	PWMA_CCR3   = 0;		// 比较值, 控制占空比(高电平时钟数)
	PWMA_CCER2 |= 0x05;		// 开启比较输出, 高电平有效
	PWMA_PS    |= (0<<4);	// 选择IO, 0:选择P1.4 P1.5, 1:选择P2.4 P2.5, 2:选择P6.4 P6.5, 
//	PWMA_ENO   |= 0x10;		// IO输出允许,  bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P,  bit3: ENO2N,  bit2: ENO2P,  bit1: ENO1N,  bit0: ENO1P
//	PWMA_IER   |= 0x08;		// 使能中断

	PWMA_BKR    = 0x80;		// 主输出使能 相当于总开关
	PWMA_CR1    = 0x81;		// 使能计数器, 允许自动重装载寄存器缓冲, 边沿对齐模式, 向上计数,  bit7=1:写自动重装载寄存器缓冲(本周期不会被打扰), =0:直接写自动重装载寄存器本(周期可能会乱掉)
	PWMA_EGR    = 0x01;		//产生一次更新事件, 清除计数器和与分频计数器, 装载预分频寄存器的值
//	PWMA_ISR_En = PWMA_IER;	//设置标志允许通道1~4中断处理
}

//	PWMA_PS   = (0<<6)+(0<<4)+(0<<2)+0;	//选择IO, 4项从高到低(从左到右)对应PWM1 PWM2 PWM3 PWM4, 0:选择P1.x, 1:选择P2.x, 2:选择P6.x, 
//  PWMA_PS    PWM4N PWM4P    PWM3N PWM3P    PWM2N PWM2P    PWM1N PWM1P
//    00       P1.7  P1.6     P1.5  P1.4     P1.3  P1.2     P1.1  P1.0
//    01       P2.7  P2.6     P2.5  P2.4     P2.3  P2.2     P2.1  P2.0
//    02       P6.7  P6.6     P6.5  P6.4     P6.3  P6.2     P6.1  P6.0
//    03       P3.3  P3.4      --    --       --    --       --    --

四、ADC.c

c 复制代码
void ADC_config(void)	//ADC初始化函数(为了使用ADC输入端做比较器信号, 实际没有启动ADC转换)
{
	P1n_pure_input(0xc0);	//设置为高阻输入
	P0n_pure_input(0x0f);	//设置为高阻输入
	ADC_CONTR = 0x80 + 6;	//ADC on + channel
	ADCCFG = RES_FMT + ADC_SPEED;
	P_SW2 |=  0x80;	//访问XSFR
	ADCTIM = CSSETUP + CSHOLD + SMPDUTY;
}
//========================================================================
// 函数: u16	Get_ADC10bitResult(u8 channel))	//channel = 0~15
//========================================================================
u16	Get_ADC10bitResult(u8 channel)	//channel = 0~15
{
	u8 i;
	ADC_RES = 0;
	ADC_RESL = 0;
	ADC_CONTR = 0x80 | ADC_START | channel; 
	NOP(5);			//
//	while((ADC_CONTR & ADC_FLAG) == 0)	;	//等待ADC结束
		i = 255;
		while(i != 0)
		{
			i--;
			if((ADC_CONTR & ADC_FLAG) != 0)	break;	//等待ADC结束
		}
	ADC_CONTR &= ~ADC_FLAG;
	return	((u16)ADC_RES * 256 + (u16)ADC_RESL);
}

五、CMP.c

c 复制代码
void CMP_config(void)	//比较器初始化程序
{
	CMPCR1 = 0x8C;			// 1000 1100 打开比较器,P3.6作为比较器的反相输入端,ADC引脚作为正输入端 
	CMPCR2 = 60;			//60个时钟滤波   比较结果变化延时周期数, 0~63
	P3n_pure_input(0x40);	//CMP-(P3.6)设置为高阻.
	
	P_SW2 |= 0x80;		//SFR enable   
//	CMPEXCFG |= (0<<6);	//bit7 bit6: 比较器迟滞输入选择: 0: 0mV,  1: 10mV, 2: 20mV, 3: 30mV
//	CMPEXCFG |= (0<<2);	//bit2: 输入负极性选择, 0: 选择P3.6做输入,   1: 选择内部BandGap电压BGv做负输入.
//	CMPEXCFG |=  0;		//bit1 bit0: 输入正极性选择, 0: 选择P3.7做输入,   1: 选择P5.0做输入,  2: 选择P5.1做输入,  3: 选择ADC输入(由ADC_CHS[3:0]所选择的ADC输入端做正输入).
//	CMPEXCFG = (0<<6)+(0<<2)+3;
}

void CMP_ISR(void) interrupt 21		//比较器中断函数, 检测到反电动势过0事件
{
	u8	i;
	CMPCR1 &= ~0x40;	// 需软件清除中断标志位

	if(XiaoCiCnt == 0)	//消磁后才检测过0事件,   XiaoCiCnt=1:需要消磁, =2:正在消磁, =0已经消磁
	{
		T4T3M &= ~(1<<3);	// Timer3停止运行
		if(B_Timer3_OverFlow)	//切换时间间隔(Timer3)有溢出
		{
			B_Timer3_OverFlow = 0;
			PhaseTime = 8000;	//换相时间最大8ms, 2212电机12V空转最高速130us切换一相(200RPS 12000RPM), 480mA
		}
		else
		{
			PhaseTime = (((u16)T3H << 8) + T3L) >> 1;	//单位为1us
			if(PhaseTime >= 8000)	PhaseTime = 8000;	//换相时间最大8ms, 2212电机12V空转最高速130us切换一相(200RPS 12000RPM), 480mA
		}
		T3H = 0;	T3L = 0;
		T4T3M |=  (1<<3);	//Timer3开始运行

		PhaseTimeTmp[TimeIndex] = PhaseTime;	//保存一次换相时间
		if(++TimeIndex >= 8)	TimeIndex = 0;	//累加8次
		for(PhaseTime=0, i=0; i<8; i++)	PhaseTime += PhaseTimeTmp[i];	//求8次换相时间累加和
		PhaseTime = PhaseTime >> 4;		//求8次换相时间的平均值的一半, 即30度电角度
		if((PhaseTime >= 40) && (PhaseTime <= 1000))	TimeOut = 125;	//堵转500ms超时
		if( PhaseTime >= 60)	PhaseTime -= 40;	//修正由于滤波电容引起的滞后时间
		else					PhaseTime  = 20;
		
	//	PhaseTime = 20;	//只给20us, 则无滞后修正, 用于检测滤波电容引起的滞后时间
		T4T3M &= ~(1<<7);				//Timer4停止运行
		PhaseTime  = PhaseTime  << 1;	//2个计数1us
		PhaseTime = 0 - PhaseTime;
		T4H = (u8)(PhaseTime >> 8);		//装载30度角延时
		T4L = (u8)PhaseTime;
		T4T3M |=  (1<<7);	//Timer4开始运行
		XiaoCiCnt = 1;		//1:需要消磁, 2:正在消磁, 0已经消磁
	}
}

六、Timer.c

c 复制代码
void Timer0_config(void)	//Timer0初始化函数
{
	Timer0_16bitAutoReload(); // T0工作于16位自动重装
	Timer0_12T();
	TH0 = (65536UL-MAIN_Fosc/12 / 250) / 256; //4msv4000us=4ms
	TL0 = (65536UL-MAIN_Fosc/12 / 250) % 256;
	TR0 = 1; // 打开定时器0
	ET0 = 1;// 允许ET0中断
}
void Timer0_ISR(void) interrupt 1	//Timer0中断函数, 20us
{
	B_4ms = 1;	//4ms定时标志
}
//============================ timer3初始化函数 ============================================
void	Timer3_Config(void)
{
	P_SW2 |= 0x80;		//SFR enable   
	T4T3M &= 0xf0;		//停止计数, 定时模式, 12T模式, 不输出时钟
	T3H = 0;
	T3L = 0;

	T3T4PIN = 0x01;		//选择IO, 0x00: T3--P0.4, T3CLKO--P0.5, T4--P0.6, T4CLKO--P0.7;    0x01: T3--P0.0, T3CLKO--P0.1, T4--P0.2, T4CLKO--P0.3;
	IE2   |=  (1<<5);	//允许中断
	T4T3M |=  (1<<3);	//开始运行
}
//=========================== timer3中断函数 =============================================
void timer3_ISR (void) interrupt TIMER3_VECTOR
{
	B_Timer3_OverFlow = 1;	//溢出标志
}
//============================ timer4初始化函数 ============================================
void	Timer4_Config(void)
{
	P_SW2 |= 0x80;		//SFR enable   
	T4T3M &= 0x0f;		//停止计数, 定时模式, 12T模式, 不输出时钟
	T4H = 0;
	T4L = 0;

	T3T4PIN = 0x01;		//选择IO, 0x00: T3--P0.4, T3CLKO--P0.5, T4--P0.6, T4CLKO--P0.7;    0x01: T3--P0.0, T3CLKO--P0.1, T4--P0.2, T4CLKO--P0.3;
	IE2   |=  (1<<6);	//允许中断
//	T4T3M |=  (1<<7);	//开始运行
}
//=========================== timer4中断函数 =============================================
void timer4_ISR (void) interrupt TIMER4_VECTOR
{
	T4T3M &= ~(1<<7);	//Timer4停止运行
	if(XiaoCiCnt == 1)		//标记需要消磁. 每次检测到过0事件后第一次中断为30度角延时, 设置消磁延时.
	{
		XiaoCiCnt = 2;		//1:需要消磁, 2:正在消磁, 0已经消磁
		if(B_RUN)	//电机正在运行
		{
			if(++step >= 6)	step = 0;
			StepMotor();
		}
		//消磁时间, 换相后线圈(电感)电流减小到0的过程中, 出现反电动势, 电流越大消磁时间越长, 过0检测要在这个时间之后
		//100%占空比时施加较重负载, 电机电流上升, 可以示波器看消磁时间.
		//实际上, 只要在换相后延时几十us才检测过零, 就可以了
		T4H = (u8)((65536UL - 40*2) >> 8);	//装载消磁延时
		T4L = (u8)(65536UL - 40*2);
		T4T3M |=  (1<<7);	//Timer4开始运行
	}
	else if(XiaoCiCnt == 2)	XiaoCiCnt = 0;		//1:需要消磁, 2:正在消磁, 0已经消磁
}

七、PMSM.c

c 复制代码
/******************* 强制电机启动函数 ***************************/
void StartMotor(void)
{
	u16 timer,i;
	CMPCR1 = 0x8C;	// 关比较器中断

	PWM_Value  = D_START_PWM+10;	// 初始占空比, 根据电机特性设置
	PWMA_CCR1L = PWM_Value;
	PWMA_CCR2L = PWM_Value;
	PWMA_CCR3L = PWM_Value;
	step = 0;	StepMotor();	Delay_n_ms(100);	//Delay_n_ms(250);// 初始位置
	timer = 300;	//电机启动值
	PWM_Value  = D_START_PWM/1.2;	// 根据电机特性设置

	while(1)
	{
		for(i=0; i<timer; i++)	delay_us(70);  //根据电机加速特性, 最高转速等等调整启动加速速度
		timer -= timer /10;  			//设置加速时间
		if(++step >= 6)	step = 0;	//设置换向次数
		StepMotor();							//开启电机换向
		if(timer < 40)	return;		//剩余启动值
	}
}
void StepMotor(void) // 换相序列函数
{
	switch(step)
	{
	case 0:  // AB  PWM1, PWM2_L=1
			PWMA_ENO = 0x00;	PWM1_L=0;	PWM3_L=0;
			Delay_500ns();
			PWMA_ENO = 0x01;		// 打开A相的高端PWM
			PWM2_L = 1;				// 打开B相的低端
			ADC_CONTR = 0x80+10;	// 选择P0.2作为ADC输入 即C相电压
			CMPCR1 = 0x8c + 0x10;	//比较器下降沿中断
			break;
	case 1:  // AC  PWM1, PWM3_L=1
			PWMA_ENO = 0x01;	PWM1_L=0;	PWM2_L=0;	// 打开A相的高端PWM
			Delay_500ns();
			PWM3_L = 1;				// 打开C相的低端
			ADC_CONTR = 0x80+9;		// 选择P0.1作为ADC输入 即B相电压
			CMPCR1 = 0x8c + 0x20;	//比较器上升沿中断
			break;
	case 2:  // BC  PWM2, PWM3_L=1
			PWMA_ENO = 0x00;	PWM1_L=0;	PWM2_L=0;
			Delay_500ns();
			PWMA_ENO = 0x04;		// 打开B相的高端PWM
			PWM3_L = 1;				// 打开C相的低端
			ADC_CONTR = 0x80+8;		// 选择P0.0作为ADC输入 即A相电压
			CMPCR1 = 0x8c + 0x10;	//比较器下降沿中断
			break;
	case 3:  // BA  PWM2, PWM1_L=1
			PWMA_ENO = 0x04;	PWM2_L=0;	PWM3_L=0;	// 打开B相的高端PWM
			Delay_500ns();
			PWM1_L = 1;				// 打开C相的低端
			ADC_CONTR = 0x80+10;	// 选择P0.2作为ADC输入 即C相电压
			CMPCR1 = 0x8c + 0x20;	//比较器上升沿中断
			break;
	case 4:  // CA  PWM3, PWM1_L=1
			PWMA_ENO = 0x00;	PWM2_L=0;	PWM3_L=0;
			Delay_500ns();
			PWMA_ENO = 0x10;		// 打开C相的高端PWM
			PWM1_L = 1;				// 打开A相的低端
			ADC_CONTR = 0x80+9;		// 选择P0.1作为ADC输入 即B相电压
			CMPCR1 = 0x8c + 0x10;	//比较器下降沿中断
			break;
	case 5:  // CB  PWM3, PWM2_L=1
			PWMA_ENO = 0x10;	PWM1_L=0;	PWM3_L=0;	// 打开C相的高端PWM
			Delay_500ns();
			PWM2_L = 1;				// 打开B相的低端
			adc11 = ((adc11 *7)>>3) + Get_ADC10bitResult(6);
			ADC_CONTR = 0x80+8;		// 选择P0.0作为ADC输入 即A相电压
			CMPCR1 = 0x8c + 0x20;	//比较器上升沿中断
			break;

	default:
			break;
	}
	if(B_start)		CMPCR1 = 0x8C;	// 启动时禁止下降沿和上升沿中断
}

八、参考资料

【电机控制】六步换向------方波驱动(算法篇)

总结

本文仅仅简单介绍了【电机控制】基于STC8H1K28的六步换向------方波驱动(软件篇),评论区欢迎讨论。

相关推荐
cjy_Somnr2 小时前
keil5报错显示stm32的SWDIO未连接不能烧录
stm32·单片机·嵌入式硬件
Lay_鑫辰3 小时前
西门子诊断-状态和错误位(“轴”工艺对象 V1...3)
服务器·网络·单片机·嵌入式硬件·自动化
无垠的广袤5 小时前
【工业树莓派 CM0 NANO 单板计算机】本地部署 EMQX
linux·python·嵌入式硬件·物联网·树莓派·emqx·工业物联网
雲烟7 小时前
嵌入式设备EMC安规检测参考
网络·单片机·嵌入式硬件
泽虞7 小时前
《STM32单片机开发》p7
笔记·stm32·单片机·嵌入式硬件
田甲8 小时前
【STM32】 数码管驱动
stm32·单片机·嵌入式硬件
up向上up8 小时前
基于51单片机垃圾箱自动分类加料机快递物流分拣器系统设计
单片机·嵌入式硬件·51单片机
纳祥科技17 小时前
Switch快充方案,内置GaN,集成了多个独立芯片
单片机
单片机日志19 小时前
【单片机毕业设计】【mcugc-mcu826】基于单片机的智能风扇系统设计
stm32·单片机·嵌入式硬件·毕业设计·智能家居·课程设计·电子信息
松涛和鸣19 小时前
从零开始理解 C 语言函数指针与回调机制
linux·c语言·开发语言·嵌入式硬件·排序算法