STC8H系列 驱动步进电机

STC8H 驱动步进电机

  • 一、引言
  • 二、硬件设计
  • 三、软件设计
    • Step_Motor2.c文件
    • Step_ Motor2.h文件

一、引言

在这里插入图片描述
众所周知STC8H系列有两个PWM,分别为PWMA和PWMB外设模块,我全都用上,岂不是就有两个带动电机的脉冲信号?!哈哈哈哈哈哈
说实在的 只能给出单独两个脉冲信号真的很可怜,没有STM定时器那么灵活。

那么就续STC论坛的梁工 STC8H系列-高级PWM-两相步进电机-细分驱动 (具有类似梯型加减功能)制作出另外一条PWMB通道输出脉冲信号带动电机。

在这里插入图片描述

二、硬件设计

主要是以STC8H8K64U的MUC使用引脚如下:
在这里插入图片描述
使用P2.3引脚作为PWMB的输出信号,PWMA的就不展示了可使用梁工的代码看实际效果。

三、软件设计

梁工给出的例子如下:


#define MAIN_Fosc		24000000UL	//定义主时钟#include	"..\STC8Hxxx.h"/*************	功能说明	**************用高级PWMA匹配取反输出脉冲控制步进电机驱动器.
为了简单, 利于初学者, 本例使用线性加减速, 如要使用别的加减速算法, 用户自行设计.使用外设:
Timer0: 工作于1ms中断, 提供1ms时隙标志和串口超时处理.
Timer2: 串口1波特率.
串口1:  命令控制, 串口设置115200,8,1,n.
PWMA1P: 从P1.0输出驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).从P1.1输出转向信号, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转).串口命令设置:
L1,500,1000   --> 马达1以500Hz正转1000个脉冲, 脉冲数为0则连续转动.
R1,500,1000   --> 马达1以500Hz反转1000个脉冲, 脉冲数为0则连续转动
s             --> 停止所有电机******************************************//*************	本地常量声明	**************//*************	本地变量声明	**************/u16	M1_CCAP1_tmp;
u8	M1_PWMA_ISR_En;	//每个通道可以单独允许中断处理, bit4:通道4, bit3:通道3, bit2:通道2, bit1:通道1.//================== 步进电机相关变量定义 ===================
sbit	M1_P_DIR   = P1^1;	// 运行方向, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转)
sbit	M1_P_PULSE = P1^0;	// 驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).
sbit	M1_tmp = P1^2;	// 驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).bit		M1_B_RunEn;				//运行允许
bit		M1_f1_update;			//请求刷新频率值
u16		M1_f1_period;			//当前频率对应的周期(半周期)(中断使用, 应用层不可操作)
u16		M1_f1_period_set;	//需要刷新的目标频率对应的周期(半周期)
u16		M1_f1;							//当前频率
u16		M1_f1_set;					//目标频率
u16		M1_f1_step;				//加减速频率变化的步长
u16		M1_UpPulse;		//加(减)速脉冲数
u16		M1_PulseCnt;	//电机运行总脉冲数, 为0则连续运行
u16		M1_DownCnt;		//运行到要减速输出的脉冲数
//===========================================================//================== 串口相关变量定义 =======================
#define	RX1_LENGTH	32
u8		RX1_Cnt;		//接收字节计数
u8		RX1_TimerOut;	//接收超时计数
u8		xdata RX1_Buffer[RX1_LENGTH];	//接收缓冲
bit 	B_RX1_OK;		//接收到一块数据
bit		B_TX1_Busy;		//发送忙标志
//===========================================================bit	B_1ms;	//1ms时隙标志/*************	本地函数声明	**************/
void 	PWMA_config(void);
u8		Timer0_Config(u8 t, u32 reload);	//t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us), 返回0正确, 返回1装载值过大错误.
void	UART1_config(u32 brt, u8 timer, u8 io);	// brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7,  =3: 切换到P4.3 P4.4
void 	UART1_PrintString(u8 *puts);
void 	UART1_TxByte(u8 dat);		// 串口1发送一个字节	
void 	RX1_process(void);			// 串口1处理函数
u16		GetStep(u16 f, u16 f_set);	// 计算速度变化步进长度
void	GetFreq1(void);				// 计算加减速频率
void	StopMotor1(void);			// 停止运行一个电机
void	RunMotor1(u16 p);			// 启动运行一个电机/******************** 主函数 **************************/
void main(void)
{M1_B_RunEn = 0;		//停止运行M1_P_DIR   = 1;	// 运行方向, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转)M1_P_PULSE = 1;	// 驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).P1n_push_pull(0x03);	// P1.0 P1.1设置为推挽输出PWMA_config();Timer0_Config(0, MAIN_Fosc / 1000);	//t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)EA = 1;UART1_config(115200UL, 2, 0);	// brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7,  =3: 切换到P4.3 P4.4UART1_PrintString("2相步进电机细分驱动程序\r\n");UART1_PrintString("L1,500,1000   --> 马达1以500Hz正转1000个脉冲\r\n");UART1_PrintString("R1,500,1000   --> 马达1以500Hz反转1000个脉冲\r\n");UART1_PrintString("s  --> 停止所有电机\r\n");while (1){if(B_1ms)	//1ms时隙{B_1ms = 0;M1_tmp = ~M1_tmp;if(M1_B_RunEn)	//加减速处理{GetFreq1();if(M1_f1 < 100){M1_B_RunEn = 0;		//停止M1_P_DIR   = 1;	// 运行方向PWMA_CCMR1 = 0;	//禁止翻转输出脉冲}}if(B_RX1_OK)	//串口收到数据块{RX1_process();	//串口数据处理B_RX1_OK = 0;RX1_Cnt = 0;}}}
}
/**********************************************/#define	UpTime	500		//加(减)速时间(ms)u16	GetStep(u16 f, u16 f_set)	//计算速度变化步进长度
{u16	i;M1_UpPulse = (u16)((u32)(f + f_set)*UpTime / 2000);	// 理论加速脉冲数if(f_set >= f)	f_set = f_set - f;		//计算频率差else			f_set = f - f_set;		//计算频率差i = f_set / UpTime;				// 加(减)速步进if(i == 0)	i = 1;	//步进不能为0return	i;			//返回加减速步进值
}void	StopMotor1(void)		//停止运行一个电机
{M1_f1_set  = 95;	//小于100Hz则停止 M1_f1_step = GetStep(M1_f1, M1_f1_set);
}//========== 准备好 "当前频率M1_f1 目标频率M1_f1_set 运行总脉冲数" 后才能启动运行 ================= 
void	RunMotor1(u16 p)	//启动运行一个电机, p为要运行的脉冲数
{u16	pulse;M1_f1_step = GetStep(M1_f1, M1_f1_set);	//计算步进if(p != 0)	//运行总脉冲数非0才有开始减速脉冲数{pulse = M1_UpPulse * 2;		//加减速脉冲数之和 = M1_UpPulse * 2if(p >= pulse)	pulse = M1_UpPulse;			//运行脉冲数 >= 加减速脉冲数之和, 则减速脉冲数按理论计算值else			pulse = p / 2;	//脉冲数 <  加减速脉冲数之和, 则平分脉冲pulse = p - pulse;						// 电机开始减速需要走过的脉冲数;}else	pulse = 0;EA = 0;	//临界保护M1_PulseCnt = p;M1_DownCnt  = pulse;M1_B_RunEn  = 1;PWMA_CCMR1  = (3<<4);	//允许翻转输出脉冲.  通道1模式配置, 禁止预装载. 0: 无输出, 1:匹配时输出高, 2:匹配时输出低, 3:匹配时输出翻转.EA = 1;
}/************************************/
void		GetFreq1(void)	// 计算加减速频率
{F0 = 0;if(M1_f1 < M1_f1_set)		//当前速度小于目标速度, 加速{F0 = 1;			//需要调速M1_f1 += M1_f1_step;if(M1_f1 > M1_f1_set)	M1_f1 = M1_f1_set;		//目标频率已到}else if(M1_f1 > M1_f1_set)		//当前速度大于目标速度, 减速{F0 = 1;			//需要调速if(M1_f1 < M1_f1_step)	M1_f1 = 0;else				M1_f1 -= M1_f1_step;if(M1_f1 < M1_f1_set)		M1_f1 = M1_f1_set;	//目标频率已到}if(F0)	//需要调速{M1_f1_period_set = MAIN_Fosc/2/2/M1_f1;	//PCA时钟2T, 半周期M1_f1_update = 1;	//请求刷新}
}/**********************************************/
void RX1_process(void)			//串口1处理函数
{u8	i;u16	f, p;if(RX1_Cnt == 1){i = RX1_Buffer[0];if((i == 's') || (i == 'S'))	//大小写均停止{StopMotor1();			//"s"   --> 停止所有电机UART1_TxByte(i);		//返回一个提示}}if((RX1_Buffer[2] == ',') && (RX1_Cnt >= 5))	//有参数{for(f=0,i=3; i<RX1_Cnt; i++)	//取频率	"L1,500,1000"   --> 马达1以500Hz正转1000个脉冲{								//			"R1,500,1000"   --> 马达1以500Hz反转1000个脉冲if(RX1_Buffer[i] == ',')	break;		//碰到逗号结束f = f * 10 + RX1_Buffer[i] - '0';}if(RX1_Buffer[i] != ',')	f = 0;	//数据异常i++;for(p=0; i<RX1_Cnt; i++)	//取脉冲数{p = p * 10 + RX1_Buffer[i] - '0';}if(f >= 200)	//有频率{if(RX1_Buffer[1] == '1')		//电机1{M1_f1_set  = f;				//目标频率if(!M1_B_RunEn)	M1_f1 = 200;	//电机未启动则从200HZ开始启动if(RX1_Buffer[0] == 'L')	//顺时针{M1_P_DIR = 1;RunMotor1(p);	//准备好 "当前频率M1_f1 目标频率M1_f1_set 运行总脉冲数" 后才能启动运行UART1_TxByte('L');		//返回一个提示}if(RX1_Buffer[0] == 'R')	//逆时针{M1_P_DIR = 0;RunMotor1(p);			//准备好 "当前频率M1_f1 目标频率M1_f1_set 运行总脉冲数" 后才能启动运行UART1_TxByte('R');		//返回一个提示}}}}
}//========================================================================
// 函数: void PWMA_config(void)
// 描述: PPWM配置函数。
// 参数: noe.
// 返回: none.
// 版本: V1.0, 2021-5-10
// 备注: 
//========================================================================
void PWMA_config(void)
{P_SW2 |= 0x80;		//SFR enable   PWMA_PSCR   = 1;	// 预分频寄存器, 分频 Fck_cnt = Fck_psc/(PSCR[15:0]+1), 边沿对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)), 中央对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)*2).PWMA_DTR    = 0;	// 死区时间配置, 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    = 0xffff;	// 自动重装载寄存器,  控制PWM周期PWMA_CCER1  = 0;PWMA_CCER2  = 0;PWMA_SR1    = 0;PWMA_SR2    = 0;PWMA_CCMR1  = 0;PWMA_CCMR2  = 0;PWMA_CCMR3  = 0;PWMA_CCMR4  = 0;PWMA_ENO    = 0;PWMA_PS     = 0;PWMA_IER    = 0;PWMA_CCR1   = 0;			// 计数器比较值, 匹配时刻
//	PWMA_CCMR1  = (3<<4);		// 通道1模式配置, 禁止预装载. 0: 无输出, 1:匹配时输出高, 2:匹配时输出低, 3:匹配时输出翻转.PWMA_CCER1 |= 0x07;			// 开启比较输出, 低电平有效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: ENO1PPWMA_IER   |= 0x02;			// 使能中断
/*PWMA_CCR2   = 0;			// 计数器比较值, 匹配时刻
//	PWMA_CCMR2  = (3<<4);		// 通道1模式配置, 禁止预装载. 0: 无输出, 1:匹配时输出高, 2:匹配时输出低, 3:匹配时输出翻转.PWMA_CCER1 |= 0x70;			// 开启比较输出, 低电平有效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: ENO1PPWMA_IER   |= 0x04;			// 使能中断PWMA_CCR3   = 0;			// 计数器比较值, 匹配时刻
//	PWMA_CCMR3  = (3<<4);		// 通道1模式配置, 禁止预装载. 0: 无输出, 1:匹配时输出高, 2:匹配时输出低, 3:匹配时输出翻转.PWMA_CCER2 |= 0x07;			// 开启比较输出, 低电平有效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: ENO1PPWMA_IER   |= 0x08;			// 使能中断PWMA_CCR4   = 0;			// 计数器比较值, 匹配时刻
//	PWMA_CCMR4  = (3<<4);		// 通道1模式配置, 禁止预装载. 0: 无输出, 1:匹配时输出高, 2:匹配时输出低, 3:匹配时输出翻转.PWMA_CCER2 |= 0x70;			// 开启比较输出, 低电平有效PWMA_PS    |= (0<<6);		// 选择IO, 0:选择P1.6 P1.7, 1:选择P2.6 P2.7, 2:选择P6.6 P6.7, 3:选择P3.3 P3.4PWMA_ENO   |= 0x40;			// IO输出允许,  bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P,  bit3: ENO2N,  bit2: ENO2P,  bit1: ENO1N,  bit0: ENO1PPWMA_IER   |= 0x10;			// 使能中断
*/M1_PWMA_ISR_En = PWMA_IER;	//设置标志允许通道1~4中断处理PWMA_EGR    = 0x01;		//产生一次更新事件, 清除计数器和预分频计数器, 装载预分频寄存器的值PWMA_BKR    = 0x80;		//主输出使能 相当于总开关PWMA_CR1    = 0x01;		// 使能计数器, 允许自动重装载寄存器缓冲, 边沿对齐模式, 向上计数,  bit7=1:写自动重装载寄存器缓冲(本周期不会被打扰), =0:直接写自动重装载寄存器本(周期可能会乱掉)
}//	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      --    --       --    --       --    --//========================================================================
// 函数: void PWMA_ISR(void) interrupt PWMA_VECTOR
// 描述: PWMA中断处理程序. 捕获数据通过 TIM1-> CCRnH / TIM1-> CCRnL 读取
// 参数: None
// 返回: none.
// 版本: V1.0, 2021-6-1
//========================================================================
void PWMA_ISR(void) interrupt PWMA_VECTOR
{u8	M1_sr1;
//	u8	sr2;M1_sr1 = PWMA_SR1;	//为了快速, 中断标志用一个局部变量处理PWMA_SR1 = 0;	//清除中断标志
//	sr2 = PWMA_SR2;	//为了快速, 中断标志用一个局部变量处理PWMA_SR2 = 0;	//清除中断标志M1_sr1 &= M1_PWMA_ISR_En;	//每个通道可以单独允许中断处理if(M1_sr1 & 0x02)	//通道1中断标志{if(M1_B_RunEn)	//电机运行中{if(M1_f1_update)	//刷新频率值{M1_f1_update = 0;M1_f1_period = M1_f1_period_set;}M1_CCAP1_tmp += M1_f1_period;PWMA_CCR1  = M1_CCAP1_tmp;	// 计数器比较值, 匹配时刻if(M1_P_PULSE)	//产生了完整的一个脉冲{if(M1_PulseCnt != 0)	// 脉冲数未完成{if(--M1_PulseCnt == 0)	//若 脉冲数-1 == 0{M1_B_RunEn = 0;		// 关停电机M1_P_DIR   = 1;		// 转向光耦关闭PWMA_CCMR1 = 0;		//禁止取反输出脉冲}}if(M1_DownCnt != 0)		// 减速脉冲未完{if(--M1_DownCnt == 0)	M1_f1_set = 200;	//设置目标频率, 开始减速}}}else  M1_P_PULSE = 1;}
/*if(M1_sr1 & 0x04)	//通道2中断标志{CCAP2_tmp += f2_period;PWMA_CCR2  = CCAP2_tmp;	// 计数器比较值, 匹配时刻}if(M1_sr1 & 0x08)	//通道3中断标志{CCAP3_tmp += f3_period;PWMA_CCR3  = CCAP3_tmp;	// 计数器比较值, 匹配时刻}if(M1_sr1 & 0x10)	//通道4中断标志{CCAP4_tmp += f4_period;PWMA_CCR4  = CCAP4_tmp;	// 计数器比较值, 匹配时刻}
*/
}//========================================================================
// 函数:u8	Timer0_Config(u8 t, u32 reload)
// 描述: timer0初始化函数.
// 参数:      t: 重装值类型, 0表示重装的是系统时钟数, 其余值表示重装的是时间(us).
//       reload: 重装值.
// 返回: 0: 初始化正确, 1: 重装值过大, 初始化错误.
// 版本: V1.0, 2018-12-20
//========================================================================
u8	Timer0_Config(u8 t, u32 reload)	//t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)
{TR0 = 0;	//停止计数if(t != 0)	reload = (u32)(((float)MAIN_Fosc * (float)reload)/1000000UL);	//重装的是时间(us), 计算所需要的系统时钟数.if(reload >= (65536UL * 12))	return 1;	//值过大, 返回错误if(reload < 65536UL)	AUXR |= 0x80;		//1T modeelse{AUXR &= ~0x80;	//12T modereload = reload / 12;}reload = 65536UL - reload;TH0 = (u8)(reload >> 8);TL0 = (u8)(reload);ET0 = 1;	//允许中断TMOD = (TMOD & ~0x03) | 0;	//工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断TR0 = 1;	//开始运行return 0;
}//========================================================================
// 函数: void timer0_ISR(void) interrupt TIMER0_VECTOR
// 描述:  timer0中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2018-12-20
//========================================================================
void timer0_ISR(void) interrupt TIMER0_VECTOR
{B_1ms = 1;	//标志1ms时隙if(RX1_TimerOut != 0)		//串口接收超时处理{if(--RX1_TimerOut == 0){if(RX1_Cnt != 0)	B_RX1_OK = 1;	//接收到一块数据}}
}//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注: 
//========================================================================
void	SetTimer2Baudraye(u16 dat)	// 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{AUXR &= ~(1<<4);	//Timer stopAUXR &= ~(1<<3);	//Timer2 set As TimerAUXR |=  (1<<2);	//Timer2 set as 1T modeTH2 = (u8)(dat >> 8);TL2 = (u8)dat;IE2  &= ~(1<<2);	//禁止中断AUXR |=  (1<<4);	//Timer run enable
}//========================================================================
// 函数: void	UART1_config(u32 brt, u8 timer, u8 io)
// 描述: UART1初始化函数。
// 参数:   brt: 通信波特率.
//       timer: 波特率使用的定时器, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率.
//          io: 串口1切换到的IO,  io=1: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7,  =3: 切换到P4.3 P4.4
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注: 
//========================================================================
void	UART1_config(u32 brt, u8 timer, u8 io)	// brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =3: 切换到P4.3 P4.4
{brt = 65536UL - (MAIN_Fosc / 4) / brt;if(timer == 2)	//波特率使用定时器2{AUXR |= 0x01;		//S1 BRT Use Timer2;SetTimer2Baudraye((u16)brt);}else		//波特率使用定时器1{TR1 = 0;AUXR &= ~0x01;		//S1 BRT Use Timer1;AUXR |=  (1<<6);	//Timer1 set as 1T modeTMOD &= ~(1<<6);	//Timer1 set As TimerTMOD &= ~0x30;		//Timer1_16bitAutoReload;TH1 = (u8)(brt >> 8);TL1 = (u8)brt;ET1 = 0;			// 禁止Timer1中断INT_CLKO &= ~0x02;	// Timer1不输出高速时钟TR1  = 1;			// 运行Timer1}if(io == 1)	{S1_USE_P36P37();	P3n_standard(0xc0);}	//切换到 P3.6 P3.7else if(io == 2)	{S1_USE_P16P17();	P1n_standard(0xc0);}	//切换到 P1.6 P1.7else if(io == 3)	{S1_USE_P43P44();	P4n_standard(0x18);}	//切换到 P4.3 P4.4else				{S1_USE_P30P31();	P3n_standard(0x03);}	//切换到 P3.0 P3.1SCON = (SCON & 0x3f) | (1<<6);	// 8位数据, 1位起始位, 1位停止位, 无校验ES  = 1;	//允许中断REN = 1;	//允许接收
}//========================================================================
// 函数: void UART1_PrintString(u8 *puts)
// 描述: 串口1字符串打印函数
// 参数: puts: 字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注: 
//========================================================================
void UART1_PrintString(u8 *puts)
{for (; *puts != 0;	puts++){B_TX1_Busy = 1;		//标志发送忙SBUF = *puts;		//发一个字节while(B_TX1_Busy);	//等待发送完成}
}void UART1_TxByte(u8 dat)	//串口1发送一个字节
{B_TX1_Busy = 1;		//标志发送忙SBUF = dat;			//发一个字节while(B_TX1_Busy);	//等待发送完成
}//========================================================================
// 函数: void UART1_ISR(void) interrupt UART1_VECTOR
// 描述: 串口1中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注: 
//========================================================================
void UART1_ISR(void) interrupt UART1_VECTOR
{if(RI){RI = 0;if(!B_RX1_OK){if(RX1_Cnt >= RX1_LENGTH)		RX1_Cnt = 0;RX1_Buffer[RX1_Cnt++] = SBUF;RX1_TimerOut = 5;}}if(TI){TI = 0;B_TX1_Busy = 0;}
}

稍微改写梁工的代码应用到具体实际的单片机引脚,主要的 第一难点 在于PWMB_config的配置到对应的P2.3引脚上输出PWMB,这里有切换引脚的寄存器。第二难点 在于(PWMB_CCRx)的比较值 一定要选对,不然比较个啥?这里是P2.3引脚在PWMB模块上,在PWMB_CCR8 捕获/比较寄存器里面。

代码如下:

Step_Motor2.c文件


/*------------------------------------------------------------------*/
/* --- STC MCU International Limited -------------------------------*/
/* --- STC 1T Series MCU Demo --------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ---------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ---------------------*/
/* --- Web: www.stcai.com ------------------------------------------*/
/* --- BBS: www.stcaimcu.com ---------------------------------------*/
/* If you want to use the program or the program referenced in the  */
/* article, please specify in which data and procedures from STC    */
/*------------------------------------------------------------------*/#include	"APP.h"
#include 	"Step_Motor2.h"
#include 	"Step_Motor1.h"
#include	"STC8H_PWM.h"
#include	"STC8G_H_GPIO.h"
#include	"STC8G_H_NVIC.h"/*************	功能说明	**************用高级PWMB匹配取反输出脉冲控制步进电机驱动器.
调用托盘运动******************************************//*************	本地常量声明	**************/
#define motor2_StopThreshold 40//电机停止阈值/*************	本地变量声明	**************/u16	M2_CCAP1_tmp;
u8	M2_PWMB_ISR_En;	//每个通道可以单独允许中断处理, bit4:通道4, bit3:通道3, bit2:通道2, bit1:通道1.//================== 步进电机相关变量定义 ===================bit		M2_B_RunEn;					//运行允许
bit		M2_f1_update;				//请求刷新频率值
u16		M2_f1_period;				//当前频率对应的周期(半周期)(中断使用, 应用层不可操作)
u16		M2_f1_period_set;		//需要刷新的目标频率对应的周期(半周期)
u16		M2_f1;							//当前频率
u16		M2_f1_set;					//目标频率
u16		M2_f1_step;					//加减速频率变化的步长
u16		M2_UpPulse;					//加(减)速脉冲数
u16		M2_PulseCnt;				//电机运行总脉冲数, 为0则连续运行
u16		M2_DownCnt;					//运行到要减速输出的脉冲数
u16		M2_UpTime;					//加(减)速时间(ms)
u16 	M2_Pulse_counter;		//脉冲计数器
//===========================================================void Motor2_clearZero(void)
{M2_f1_period = 0;			//当前频率对应的周期(半周期)(中断使用, 应用层不可操作)M2_f1_period_set = 0;	//需要刷新的目标频率对应的周期(半周期)M2_f1 = 0;						//当前频率M2_f1_set = 0;				//目标频率M2_f1_step = 0;				//加减速频率变化的步长M2_UpTime = 0;				//加(减)速时间(ms)M2_PulseCnt = 0;			//电机运行总脉冲数, 为0则连续运行M2_DownCnt = 0;				//运行到要减速输出的脉冲数M2_UpPulse = 0;				//加(减)速脉冲数
}/******************** 主函数 **************************/
void Motor2_Init(void)
{
//	lightSource_PWM = 1;PWMB_phase = YAXIS_MOTOR;M2_B_RunEn = 0;		//停止运行M2_P_DIR   = 1;	// 运行方向, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转)M2_P_PULSE = 1;	// 驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).
//	P1_MODE_OUT_PP(GPIO_Pin_1 | GPIO_Pin_0);//P2.1 驱动器PWMM2_PWMB_config();}void Sample_Motor6(void)
{M2_f1_set  = 5000;M2_f1 = 200;	//电机未启动则从200HZ开始启动RunMotor2(3200);
}void Sample_Motor2(void)
{if(M2_B_RunEn)	//加减速处理{GetFreq2();if(M2_f1 < motor2_StopThreshold){M2_B_RunEn = 0;		//停止PWMB_CCMR4 = 0;	//禁止翻转输出脉冲Pallet_status = 0;}}
}/**********************************************/u16	M2_GetStep(u16 f, u16 f_set)	//计算速度变化步进长度
{u16	i;M2_UpPulse = (u16)((u32)(f + f_set)*M2_UpTime / 2000);	// 理论加速脉冲数if(f_set >= f)	f_set = f_set - f;		//计算频率差else			f_set = f - f_set;		//计算频率差i = f_set / M2_UpTime;				// 加(减)速步进if(i == 0)	i = 1;	//步进不能为0return	i;			//返回加减速步进值
}void	StopMotor2(void)		//停止运行一个电机
{M2_f1_set  = motor2_StopThreshold;	//小于100Hz则停止 M2_f1_step = M2_GetStep(M2_f1, M2_f1_set);
}void	E_StopMotor2(void)		//停止运行一个电机
{M2_f1_set  = motor2_StopThreshold;	//小于100Hz则停止 M2_f1 = 200;Pallet_status = 0;M2_f1_step = M2_GetStep(M2_f1, M2_f1_set);
}void	Emergency_StopMotor2(void)		//停止运行一个电机
{M2_f1_set  = motor2_StopThreshold;	//小于100Hz则停止 M2_f1 = 0;Pallet_status = 0;M2_B_RunEn = 0;		//停止PWMB_CCMR4 = 0;		//禁止翻转输出脉冲
}//========== 准备好 "当前频率M2_f1 目标频率M2_f1_set 运行总脉冲数" 后才能启动运行 ================= 
void	RunMotor2(u16 p)	//启动运行一个电机, p为要运行的脉冲数
{u16	pulse;M2_f1_step = M2_GetStep(M2_f1, M2_f1_set);	//计算步进if(p != 0)	//运行总脉冲数非0才有开始减速脉冲数{pulse = M2_UpPulse * 2;		//加减速脉冲数之和 = M2_UpPulse * 2if(p >= pulse)	pulse = M2_UpPulse;			//运行脉冲数 >= 加减速脉冲数之和, 则减速脉冲数按理论计算值else			pulse = p / 2;	//脉冲数 <  加减速脉冲数之和, 则平分脉冲pulse = p - pulse;						// 电机开始减速需要走过的脉冲数;}else	pulse = 0;EA = 0;	//临界保护M2_PulseCnt = p;M2_DownCnt  = pulse;M2_B_RunEn  = 1;PWMB_CCMR4  = (3<<4);	//允许翻转输出脉冲.  通道1模式配置, 禁止预装载. 0: 无输出, 1:匹配时输出高, 2:匹配时输出低, 3:匹配时输出翻转.EA = 1;
}/************************************/
void	GetFreq2(void)	// 计算加减速频率
{F0 = 0;//内部寄存器if(M2_f1 < M2_f1_set)		//当前速度小于目标速度, 加速{F0 = 1;			//需要调速M2_f1 += M2_f1_step;if(M2_f1 > M2_f1_set)	M2_f1 = M2_f1_set;		//目标频率已到}else if(M2_f1 > M2_f1_set)		//当前速度大于目标速度, 减速{F0 = 1;			//需要调速if(M2_f1 < M2_f1_step)	M2_f1 = 0;else				M2_f1 -= M2_f1_step;if(M2_f1 < M2_f1_set)		M2_f1 = M2_f1_set;	//目标频率已到}if(F0)	//需要调速{M2_f1_period_set = MAIN_Fosc/2/2/M2_f1;	//PCA时钟2T, 半周期M2_f1_update = 1;	//请求刷新}
}/**********************************************///========================================================================
// 函数: void M2_PWMB_config(void)
// 描述: PPWM配置函数。
// 参数: noe.
// 返回: none.
// 版本: V1.0, 2021-5-10
// 备注: 
//========================================================================
void M2_PWMB_config(void)
{PWMx_InitDefine		PWMx_InitStructure;NVIC_PWM_Init(PWMB,ENABLE,Priority_3);P_SW2 |= 0x80;		//SFR enable   PWMB_PSCR   = 1;	// 预分频寄存器, 分频 Fck_cnt = Fck_psc/(PSCR[15:0]+1), 边沿对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)), 中央对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)*2).PWMB_DTR    = 0;	// 死区时间配置, 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,PWMB_ARR    = 0xffff;	// 自动重装载寄存器,  控制PWM周期PWMB_CCER1  = 0;PWMB_CCER2  = 0;PWMB_SR1    = 0;PWMB_SR2    = 0;PWMB_CCMR1  = 0;PWMB_CCMR2  = 0;PWMB_CCMR3  = 0;PWMB_CCMR4  = 0;PWMB_ENO    = 0;PWMB_PS     = 0;PWMB_IER    = 0;PWMB_CCR8   = 5000;		// 比较值, 控制占空比(高电平时钟数)PWMB_CCER2 |= 0x30;		// 开启比较输出, 高电平有效PWMB_PS    |= (0<<6);	// 选择IO, 0:选择P2.3, 1:选择P3.4, 2:选择P0.3, 3:选择P7.7, PWMB_ENO   |= 0x40;		// IO输出允许,  bit6: ENO8P, bit4: ENO7P,  bit2: ENO6P,  bit0: ENO5PPWMB_IER   |= 0x30;		// 使能中断PWMB_EGR    = 0x01;		//产生一次更新事件, 清除计数器和预分频计数器, 装载预分频寄存器的值PWMB_BKR    = 0x80;		// 主输出使能 相当于总开关PWMB_CR1    = 0x01;		// 使能计数器, 允许自动重装载寄存器缓冲, 边沿对齐模式, 向上计数,  bit7=1:写自动重装载寄存器缓冲(本周期不会被打扰), =0:直接写自动重装载寄存器本(周期可能会乱掉)}//========================================================================
// 函数: void PWMB_ISR(void) interrupt PWMB_VECTOR
// 描述: PWMB中断处理程序. 捕获数据通过 TIM2-> CCRnH / TIM2-> CCRnL 读取
// 参数: None
// 返回: none.
// 版本: V1.0, 2021-6-1
//========================================================================
void PWMB_ISR(void) interrupt PWMB_VECTOR
{u8	M2_sr1;
//	u8	sr2;M2_sr1 = PWMB_SR1;	//为了快速, 中断标志用一个局部变量处理PWMB_SR1 = 0;	//清除中断标志
//	sr2 = PWMB_SR2;	//为了快速, 中断标志用一个局部变量处理PWMB_SR2 = 0;	//清除中断标志M2_sr1 &= M2_PWMB_ISR_En;	//每个通道可以单独允许中断处理//	PWMB_CCR8   = 500;
//	if(M2_sr1 & 0x02)	//通道1中断标志{if(M2_B_RunEn)	//电机运行中{if(M2_f1_update)	//刷新频率值{M2_f1_update = 0;M2_f1_period = M2_f1_period_set;}M2_CCAP1_tmp += M2_f1_period;
//			PWMB_CCR5  = M2_CCAP1_tmp;	// 计数器比较值, 匹配时刻PWMB_CCR8  = M2_CCAP1_tmp;	// 计数器比较值, 匹配时刻if(M2_P_PULSE)	//产生了完整的一个脉冲{if(!M2_P_DIR)M2_Pulse_counter++;elseM2_Pulse_counter--;				if(M2_PulseCnt != 0)	// 脉冲数未完成{if(--M2_PulseCnt == 0)	//若 脉冲数-1 == 0{M2_B_RunEn = 0;		// 关停电机M2_P_DIR   = 1;		// 转向光耦关闭PWMB_CCMR4 = 0;		//禁止取反输出脉冲Pallet_status = 0;}}if(M2_DownCnt != 0)		// 减速脉冲未完{if(--M2_DownCnt == 0)	M2_f1_set = 200;	//设置目标频率, 开始减速}}}else  M2_P_PULSE = 1;}
}

Step_ Motor2.h文件

/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC 1T Series MCU Demo Programme -------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com  -----------------------------------------*/
/* --- QQ:  800003751 -------------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序            */
/*---------------------------------------------------------------------*/#ifndef __STEP_MOTOR2_H_
#define __STEP_MOTOR2_H_#include "config.h"
#include	"APP.h"
#include	"APP_Task.h"#define M2_P_DIR_PORT P4
#define M2_P_DIR_BIT 4
//sbit	M2_P_DIR   = P1^6;	// 裸板测试
sbit	M2_P_DIR   = P4^4;	// 运行方向, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转)
sbit	M2_P_PULSE = P2^3;	// 驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).
sbit	M2_EN = P4^2;				// 驱动器使能端extern bit		M2_B_RunEn;				//运行允许
extern bit		M2_f1_update;			//请求刷新频率值
extern u16		M2_f1_period;			//当前频率对应的周期(半周期)(中断使用, 应用层不可操作)
extern u16		M2_f1_period_set;	//需要刷新的目标频率对应的周期(半周期)
extern u16		M2_f1;							//当前频率
extern u16		M2_f1_set;					//目标频率
extern u16		M2_f1_step;				//加减速频率变化的步长
extern u16		M2_UpPulse;		//加(减)速脉冲数
extern u16		M2_PulseCnt;	//电机运行总脉冲数, 为0则连续运行
extern u16		M2_DownCnt;		//运行到要减速输出的脉冲数
extern u16		M2_UpTime;			//加(减)速时间(ms)
extern u16 		M2_Pulse_counter;		//脉冲计数器extern u8 Pallet_status;
/*************	本地函数声明	**************/void 	M2_PWMB_config(void);
u16		GetStep(u16 f, u16 f_set);	// 计算速度变化步进长度
void	GetFreq2(void);				// 计算加减速频率
void	StopMotor2(void);			// 停止运行一个电机
void	RunMotor2(u16 p);			// 启动运行一个电机
void Sample_Motor2(void);void Sample_Motor6(void);
void	E_StopMotor2(void);		//停止运行一个电机
void	Emergency_StopMotor2(void);		//停止运行一个电机#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/82968.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python高阶函数:从入门到精通

目录 Python高阶函数详解&#xff1a;从概念到高级应用引言&#xff1a;函数式编程的魅力一、高阶函数基础概念1.1 什么是高阶函数1.2 Python中的一等函数 二、内置高阶函数详解2.1 map函数&#xff1a;数据转换利器2.2 filter函数&#xff1a;数据筛选专家2.3 reduce函数&…

腾讯开源视频生成工具 HunyuanVideo-Avatar,上传一张图+一段音频,就能让图中的人物、动物甚至虚拟角色“活”过来,开口说话、唱歌、演相声!

腾讯混元团队提出的 HunyuanVideo-Avatar 是一个基于多模态扩散变换器&#xff08;MM-DiT&#xff09;的模型&#xff0c;能够生成动态、情绪可控和多角色对话视频。支持仅 10GB VRAM 的单 GPU运行&#xff0c;支持多种下游任务和应用。例如生成会说话的虚拟形象视频&#xff0…

DeepSeek-R1-0528:开源推理模型的革新与突破

一、 发布日期与背景 2025年5月29日&#xff0c;备受业界关注的DeepSeek推理模型DeepSeek-R1迎来重要更新——DeepSeek-R1-0528模型正式发布。此次更新采取了“静默发布”策略&#xff0c;未提前预告&#xff0c;而是通过官方渠道&#xff08;官网、App、小程序&#xff09;及…

LeetCode 1723: 完成所有工作的最短时间

给你一个整数数组 jobs &#xff0c;其中 jobs[i] 是完成第 i 项工作要花费的时间。 请你将这些工作分配给 k 位工人。所有工作都应该分配给工人&#xff0c;且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作…

JDK8新特性之Steam流

这里写目录标题 一、Stream流概述1.1、传统写法1.2、Stream写法1.3、Stream流操作分类 二、Stream流获取方式2.1、根据Collection获取2.2、通过Stream的of方法 三、Stream常用方法介绍3.1、forEach3.2、count3.3、filter3.4、limit3.5、skip3.6、map3.7、sorted3.8、distinct3.…

split方法

在编程中&#xff0c;split 方法通常用于将字符串按照指定的分隔符拆分成多个部分&#xff0c;并返回一个包含拆分结果的列表&#xff08;或数组&#xff09;。不同编程语言中的 split 方法语法略有不同&#xff0c;但核心功能相似。以下是常见语言中的用法&#xff1a; ​1. P…

深入理解 x86 汇编中的符号扩展指令:从 CBW 到 CDQ 的全解析

引入 在汇编语言的世界里&#xff0c;数据宽度的转换是一项基础却至关重要的操作。尤其是在处理有符号数时&#xff0c;符号扩展&#xff08;Sign Extension&#xff09;作为保持数值符号一致性的核心技术&#xff0c;直接影响着运算结果的正确性。本文将聚焦 x86 架构中最常用…

计算机基础知识(第五篇)

计算机基础知识&#xff08;第五篇&#xff09; 架构演化与维护 软件架构的演化和定义 软件架构的演化和维护就是对架构进行修改和完善的过程&#xff0c;目的就是为了使软件能够适应环境的变化而进行的纠错性修改和完善性修改等&#xff0c;是一个不断迭代的过程&#xff0…

前端开发三剑客:HTML5+CSS3+ES6

在前端开发领域&#xff0c;HTML、CSS和JavaScript构成了构建网页与Web应用的核心基础。随着技术标准的不断演进&#xff0c;HTML5、CSS3以及ES6&#xff08;ECMAScript 2015及后续版本&#xff09;带来了诸多新特性与语法优化&#xff0c;极大地提升了开发效率和用户体验。本文…

c++ 头文件

目录 防止头文件重复包含 头文件的作用 如何让程序的多个 .cpp 文件之间共享全局变量&#xff08;可能是 int、结构体、数组、指针、类对象&#xff09;? 防止头文件重复包含 为什么要防止头问件重复包含&#xff1f; 当然一般也不会把变量定义放到头问件&#xff0c;那为…

深入解析 JavaScript 中 var、let、const 的核心区别与实践应用

一、历史背景与语法基础 JavaScript 作为动态弱类型语言&#xff0c;变量声明机制经历了从 ES5 到 ES6 的重大变革。在 ES5 及更早版本中&#xff0c;var 是唯一的变量声明方式&#xff0c;而 ES6&#xff08;2015 年&#xff09;引入了 let 和 const&#xff0c;旨在解决 var…

【Linux庖丁解牛】—自定义shell的编写!

1. 打印命令行提示符 在我们使用系统提供的shell时&#xff0c;每次都会打印出一行字符串&#xff0c;这其实就是命令行提示符&#xff0c;那我们自定义的shell当然也需要这一行字符串。 这一行字符串包含用户名&#xff0c;主机名&#xff0c;当前工作路径&#xff0c;所以&a…

应用案例 | 设备分布广, 现场维护难? 宏集Cogent DataHub助力分布式锅炉远程运维, 让现场变“透明”

在日本&#xff0c;能源利用与环保问题再次成为社会关注的焦点。越来越多的工业用户开始寻求更高效、可持续的方式来运营设备、管理能源。而作为一家专注于节能与自动化系统集成的企业&#xff0c;日本大阪的TESS工程公司给出了一个值得借鉴的答案。 01 锅炉远程监控难题如何破…

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…

jdk同时安装多个版本并自由切换

一、安装不同版本的JDK 二、配置环境变量&#xff08;多版本JDK&#xff09; 1. 新建版本专用环境变量&#xff08;用于切换&#xff09; 操作位置&#xff1a;系统变量 > 新建 变量名&#xff1a;JAVA_HOME_1.8 变量值&#xff1a;JDK 8安装路径变量名&#xff1a;JAVA1…

java中装饰模式

目录 一 装饰模式案例说明 1.1 说明 1.2 代码 1.2.1 定义数据服务接口 1.2.2 定义基础数据库服务实现 1.2.3 日志装饰器 1.2.4 缓存装饰器 1.2.5 主程序调用 1.3 装饰模式的特点 一 装饰模式案例说明 1.1 说明 本案例是&#xff1a;数据查询增加缓存&#xff0c;使用…

【论文阅读】YOLOv8在单目下视多车目标检测中的应用

Application of YOLOv8 in monocular downward multiple Car Target detection​​​​​ 原文真离谱&#xff0c;文章都不全还发上来 引言 自动驾驶技术是21世纪最重要的技术发展之一&#xff0c;有望彻底改变交通安全和效率。任何自动驾驶系统的核心都依赖于通过精确物体检…

在uni-app中如何从Options API迁移到Composition API?

uni-app 从 Options API 迁移到 Composition API 的详细指南 一、迁移前的准备 升级环境&#xff1a; 确保 HBuilderX 版本 ≥ 3.2.0项目 uni-app 版本 ≥ 3.0.0 了解 Composition API 基础&#xff1a; 响应式系统&#xff1a;ref、reactive生命周期钩子&#xff1a;onMount…

408第一季 - 数据结构 - 图

图的概念 完全图 无向图的完全图可以这么想&#xff1a;如果有4个点&#xff0c;每个点都会连向3个点&#xff0c;每个点也都会有来回的边&#xff0c;所以除以2 有向图就不用除以2 连通分量 不多解释 极大连通子图的意思就是让你把所有连起来的都圈出来 强连通图和强连通…

31.2linux中Regmap的API驱动icm20608实验(编程)_csdn

regmap 框架就讲解就是上一个文章&#xff0c;接下来学习编写的 icm20608 驱动改为 regmap 框架。 icm20608 驱动我们在之前的文章就已经编写了&#xff01; 因为之前已经对icm20608的设备树进行了修改&#xff0c;所以大家可以看到之前的文章&#xff01;当然这里我们还是带领…