- 打卡等级:初来乍到
- 打卡总天数:1
- 最近打卡:2024-05-30 20:45:00
新手上路
- 积分
- 16
|
发表于 2024-6-1 10:22:01
|
显示全部楼层
请教梁工及各位前辈大侠,我把这个移植到STC12C5A60S2,PCA0 输出10ms周期的波形,改好几天都没改明白,麻烦各位看下。
#include "STC12C5A60S2.h"
#include <intrins.h>
#define MAIN_Fosc 24000000UL //定义主时钟
#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long
sbit STRAT_STOP = P2^0; //开始键
void Delay_ms(unsigned char ms)
{ // 延时子程序 毫秒
unsigned char i;
while(ms--)
{
for(i = 0; i< 82; i++)
{
_nop_();_nop_();_nop_();_nop_();
}
}
}
#define PCA1_16bit_Timer() CCAPM0 = (0x48 + 1) /* 16位软件定时器模式 */
#define PCA_VECTOR 7
#define TIMER0_VECTOR 1
/************* 功能说明 **************
用PCA高速脉冲输出控制步进电机驱动器.
为了简单, 利于初学者, 本例使用线性加减速, 如要使用别的加减速算法, 用户自行设计.
使用外设:
Timer0: 工作于1ms中断, 提供1ms时隙标志和串口超时处理.
Timer2: 串口1波特率.
串口1: 命令控制, 串口设置115200,8,1,n.
PCA0: 从P2.3输出驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).
从P2.0输出转向信号, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转).
串口命令设置:
L1,500,1000 --> 马达1以500Hz正转1000个脉冲, 脉冲数为0则连续转动.
R1,500,1000 --> 马达1以500Hz反转1000个脉冲, 脉冲数为0则连续转动
s --> 停止所有电机
******************************************/
/************* 本地变量声明 **************/
u16 CCAP1_tmp;
//================== 步进电机相关变量定义 ===================
sbit P_M1_DIR = P1^0; // 运行方向, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转)
sbit P_M1_PULSE = P1^3; // 驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).
bit B_M1_RunEn; //运行允许
bit B_f1_update; //请求刷新频率值
bit B_TIMER1_12T; //定时器1时钟模式
u16 f1_period; //当前频率对应的周期(半周期)(中断使用, 应用层不可操作)
u16 f1_period_set; //需要刷新的目标频率对应的周期(半周期)
u16 f1; //当前频率
u16 f1_set; //目标频率
u16 f1_step; //加减速频率变化的步长
u16 M1_UpPulse; //加(减)速脉冲数
u16 M1_PulseCnt; //电机运行总脉冲数, 为0则连续运行
u16 M1_DownCnt; //运行到要减速输出的脉冲数
//===========================================================
bit B_1ms; //1ms时隙标志
/************* 本地函数声明 **************/
void PCA_config(u8 clk); //io: 选择IO, 0: 选择P12 P17 P16 P15 P14, 1: 选择P22 P23 P24 P25 P26, 2: 选择P74 P70 P71 P72 P73, 3: 选择P35 P33 P32 P31 P30.
//clk: 选择时钟, 0: 12T, 1: 2T, 2: Timer0溢出率, 3: ECI引脚输入, 4: 1T, 5: 4T, 6: 6T, 7: 8T
u8 Timer0_Config(u8 t, u32 reload); //t=0: reload值是主时钟周期数, t=1: reload值是时间(单位us), 返回0正确, 返回1装载值过大错误.
u16 GetStep(u16 f, u16 f_set); // 计算速度变化步进长度
void GetFreq1(void); // 计算加减速频率
void StopMotor1(void); // 停止运行一个电机
void RunMotor1(u16 p); // 启动运行一个电机
/******************** 主函数 **************************/
void main(void)
{
P_M1_DIR = 1; // 运行方向, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转)
P_M1_PULSE = 1; // 驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).
PCA_config(1); //io: 选择IO, 0: 选择P12 P17 P16 P15 P14, 1: 选择P22 P23 P24 P25 P26, 2: 选择P74 P70 P71 P72 P73, 3: 选择P35 P33 P32 P31 P30.
//clk: 选择时钟, 0: 12T, 1: 2T, 2: Timer0溢出率, 3: ECI引脚输入, 4: 1T, 5: 4T, 6: 6T, 7: 8T
Timer0_Config(0, MAIN_Fosc / 1000); //t=0: reload值是主时钟周期数, t=1: reload值是时间(单位us)
EA = 1;
B_M1_RunEn = 0;
CCAPM0 &= ~0x04; //禁止高速输出脉冲
while (1)
{
if(B_1ms) //1ms时隙
{
B_1ms = 0;
if(B_M1_RunEn) //加减速处理
{
GetFreq1();
if(f1 < 100)
{
B_M1_RunEn = 0; //停止
P_M1_DIR = 1; // 运行方向
CCAPM0 &= ~0x04; //禁止高速输出脉冲
}
}
if(STRAT_STOP==0)
{
Delay_ms(10);//延时去抖动
if(STRAT_STOP==0)
{
while(STRAT_STOP==0);//等待按键释放
if(!B_M1_RunEn) f1 = 200; //电机未启动则从200HZ开始启动
f1_set=1000;
RunMotor1(10000);
}
}
}
}
}
/**********************************************/
#define UpTime 100 //加(减)速时间(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) //停止运行一个电机
{
f1_set = 95; //小于100Hz则停止
f1_step = GetStep(f1, f1_set);
}
//========== 准备好 "当前频率f1 目标频率f1_set 运行总脉冲数" 后才能启动运行 =================
void RunMotor1(u16 p) //启动运行一个电机, p为要运行的脉冲数
{
u16 pulse;
f1_step = GetStep(f1, 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;
B_M1_RunEn = 1;
CCAPM0 |= 0x04; //允许高速输出脉冲
EA = 1;
}
/************************************/
void GetFreq1(void) // 计算加减速频率
{
F0 = 0;
if(f1 < f1_set) //当前速度小于目标速度, 加速
{
F0 = 1; //需要调速
f1 += f1_step;
if(f1 > f1_set) f1 = f1_set; //目标频率已到
}
else if(f1 > f1_set) //当前速度大于目标速度, 减速
{
F0 = 1; //需要调速
if(f1 < f1_step) f1 = 0;
else f1 -= f1_step;
if(f1 < f1_set) f1 = f1_set; //目标频率已到
}
if(F0) //需要调速
{
f1_period_set = MAIN_Fosc/2/2/f1; //PCA时钟2T, 半周期
B_f1_update = 1; //请求刷新
}
}
//========================================================================
// 函数: void PCA_config(u8 io, u8 clk)
// 描述: PCA配置函数。
// 参数: io: 选择IO, 0: 选择P12 P17 P16 P15 P14, 1: 选择P22 P23 P24 P25 P26, 2: 选择P74 P70 P71 P72 P73, 3: 选择P35 P33 P32 P31 P30.
// clk: 选择时钟, 0: 12T, 1: 2T, 2: Timer0溢出率, 3: ECI引脚输入, 4: 1T, 5: 4T, 6: 6T, 7: 8T
// 返回: none.
// 版本: VER1.0
// 版本: V1.0, 2016-5-10
// 备注:
//========================================================================
void PCA_config(u8 clk)
{
CR = 0;
CH = 0;
CL = 0;
CMOD = (CMOD & ~0x0E) | ((clk & 7) << 1); // PCA时钟选择, 0: 12T, 1: 2T, 2: Timer0溢出率, 3: ECI引脚输入, 4: 1T, 5: 4T, 6: 6T, 7: 8T
PPCA = 1; //高优先级中断
PCA1_16bit_Timer(); //工作于16位软件定时模式
// PCA0_High_PulseEnable(); //匹配时允许高速输出
CCAP1_tmp = 0; //按用户需要给一个初值
CCAP1L = 0; //先写CCAP0L
CCAP1H = 0; //后写CCAP0H
CR = 1; //运行PCA定时器
}
//========================================================================
// 函数: void PCA_ISR (void) interrupt PCA_VECTOR
// 描述: PCA中断服务程序.
// 参数: 无.
// 返回: 无.
// 作者: Coody
// 版本: VER1.0
// 日期: 2020-7-31
// 备注:
//========================================================================
void PCA_ISR (void) interrupt PCA_VECTOR
{
if(CCF0 == 1) //PCA模块0中断
{
CCF0 = 0; //清PCA模块0中断标志
if(B_M1_RunEn)
{
if(B_f1_update) //刷新频率值
{
B_f1_update = 0;
f1_period = f1_period_set;
}
CCAP1_tmp += f1_period;
CCAP1L = (u8)CCAP1_tmp; //将影射寄存器写入捕获寄存器,先写CCAP0L
CCAP1H = (u8)(CCAP1_tmp >> 8); //后写CCAP0H
if(P_M1_PULSE) //产生了完整的一个脉冲
{
if(M1_PulseCnt != 0) // 脉冲数未完成
{
if(--M1_PulseCnt == 0) //若 脉冲数-1 == 0
{
B_M1_RunEn = 0; // 关停电机
P_M1_DIR = 1; // 转向光耦关闭
CCAPM0 &= ~0x04; //禁止高速输出脉冲
}
}
if(M1_DownCnt != 0) // 减速脉冲未完
{
if(--M1_DownCnt == 0) f1_set = 200; //设置目标频率, 开始减速
}
}
}
else P_M1_PULSE = 1;
}
}
//========================================================================
// 函数: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时隙
} |
|