STC8H 驱动步进电机
- 一、引言
- 二、硬件设计
- 三、软件设计
-
- Step_Motor2.c文件
- [Step_ Motor2.h文件](#Step_ Motor2.h文件)
一、引言

众所周知STC8H系列有两个PWM,分别为PWMA和PWMB外设模块,我全都用上,岂不是就有两个带动电机的脉冲信号?!哈哈哈哈哈哈
说实在的 只能给出单独两个脉冲信号真的很可怜,没有STM定时器那么灵活。
那么就续STC论坛的梁工 STC8H系列-高级PWM-两相步进电机-细分驱动 (具有类似梯型加减功能)制作出另外一条PWMB通道输出脉冲信号带动电机。

二、硬件设计
主要是以STC8H8K64U的MUC使用引脚如下:
使用P2.3引脚作为PWMB的输出信号,PWMA的就不展示了可使用梁工的代码看实际效果。
三、软件设计
梁工给出的例子如下:
c
#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.4
UART1_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; //步进不能为0
return 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 * 2
if(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: ENO1P
PWMA_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: ENO1P
PWMA_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: ENO1P
PWMA_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.4
PWMA_ENO |= 0x40; // IO输出允许, bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P, bit3: ENO2N, bit2: ENO2P, bit1: ENO1N, bit0: ENO1P
PWMA_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 mode
else
{
AUXR &= ~0x80; //12T mode
reload = 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 stop
AUXR &= ~(1<<3); //Timer2 set As Timer
AUXR |= (1<<2); //Timer2 set as 1T mode
TH2 = (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 mode
TMOD &= ~(1<<6); //Timer1 set As Timer
TMOD &= ~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.7
else if(io == 2) {S1_USE_P16P17(); P1n_standard(0xc0);} //切换到 P1.6 P1.7
else if(io == 3) {S1_USE_P43P44(); P4n_standard(0x18);} //切换到 P4.3 P4.4
else {S1_USE_P30P31(); P3n_standard(0x03);} //切换到 P3.0 P3.1
SCON = (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文件
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 驱动器PWM
M2_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; //步进不能为0
return 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 * 2
if(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: ENO5P
PWMB_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++;
else
M2_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文件
c
/*---------------------------------------------------------------------*/
/* --- 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