附上:點選參考
購買元器件附帶3D封裝:https://weidian.com/?userid=1238350417&wfr=c&ifr=shopdetail
看到網上有好多做微型雷射雕刻機的DIY,手頭正好有幾個拆下來的筆記本光驅,把光驅裡的步進電機拆了出來,做了這個雕刻機。由于我手頭沒有雷射頭,偷懶用原子筆來代替雷射頭。
采用STM32F103ZET6最小系統闆做的三軸關聯,步進電機驅動沒有采用專門的驅動闆,使用了L298N或者L9110S,由STM32的PWM來控制步進電機。
現在隻實作了最基本的幾個功能,支援G代碼 G0,G1,G2,G3和M3(Z軸下降),M5(Z軸上升),圖形界面和按鈕控制現在還沒有做。
作業系統采用風舞天的MSOS作業系統,最大支援8個任務是在UCOS基礎上簡化而來的。MSOS的支援郵箱資訊和8個互斥量,GUI是自己加的。
源代碼檔案目錄中Keil_RAM目錄裡的工程檔案是直接在RAM中調試,KEIL用JLINK下載下傳到系統闆中不會擦除FLASH中的内容,直接用RAM進行調試和仿真。Keil_FLASH目錄裡的工程檔案是直接在FLASH中調試,KEIL用JLINK下載下傳到系統闆中會更新FLASH中的内容。這2個目錄共用系統檔案和驅動程式。
G代碼生成使用的是Inkscape
我G代碼下載下傳使用的是G-CODE SENDER來下載下傳到開發闆,還沒有做SD卡脫機。
MSOS任務的參數數量在OS.H 中定義
#define TaskSum 4 任務總數。現在定義了4個,如果超過4個就必須修改,最大支援8個任務,包括GUI的任務
#define TaskStackSum 100 任務棧深度,如果函數調用多的話這個值需加大
#define QueueStackSum 20 消息隊列深度,總共可以有20個等待處理的消息,超出20個的話會丢失消息
建立任務前還需定義任務句柄,建立任務後任務句柄的值同時也是任務的優先級
u8 CtrlTaskPriority ; 控制任務
u8 PWMTaskPriority ; PWM任務
u8 ShowTaskPriority ; 顯示解析任務
優先級按照建立的順序,最早建立的是優先級最高,GUI任務GUI_API.CreateTask();必須最後建立,如果不用GUI任務,則最低優先級的任務不能有OS.PendMessageQueue函數,必須為死循環。
任務資訊的傳遞使用PostMessageQueue 和PendMessageQueue 函數
static void * PendMessageQueue (u32 timeout)
static u8 PostMessageQueue (u8 Priority, void *messagePointer)
互斥量使用OSMutexLock 和OSMutexUnLock ,互斥量最大為8個,編号為0-7,其中第7個我用在GUI上了
u8 OSMutexLock (u8 OSMutexNum)
void OSMutexUnLock (u8 OSMutexNum)
同時MSOS提供軟體定時器,提供2種模式,1種由使用者指定定時器編号時間間隔為0.1MS(1s需要delay = 10000),1種由系統自動配置設定定時器編号時間間隔為1ms(1s需要delay = 1000)。
使用
OS.Timer.Start = Start;
OS.Timer.StopAt = StopAt;
OS.Timer.StartAt = StartAt;
OS.Timer.StopAt(u8 id)
OS.Timer.StartAt(u8 id, u32 delay, function registerFunction)
OS.Timer.Start(TimerhandleModeEnum handleMode, u32 delay, function registerFunction)
由于我沒有專用的步進電機控制器,隻有原來做小車時的L298N電機驅動子產品,它有2個H橋可以驅動1個兩相四線制步進電機,在網上找了步進電機的驅動方法
它按A、AB、B、BC、C、CD、D、DA的順序交替進行線圈的勵磁。
A B C D
T1 1 0 0 0
T2 1 1 0 0
T3 0 1 0 0
T4 0 1 1 0
T5 0 0 1 0
T6 0 0 1 1
T7 0 0 0 1
T8 1 0 0 1
T1-T8表示脈沖周期;ABCD表示電機的各相,1表示此時有一個脈沖,0表示沒有脈沖。
但程式編好後下載下傳到開發闆,發現步進電機的運作很不平穩,聲音很大,且1個步距隻有8個脈沖周期,定位的間距很大,出來的效果很不好。再上網查詢專用的步進電機驅動器有脈沖分頻,可以控制很小的控制角度,是以我就改用正弦波進行驅動,ABCD的正弦波角度差90度,步進電機旋轉一圈共需要256個脈沖,這樣每個脈沖的步距很小,且運作很平穩。
我的程式中使用TIM3、TIM4、TIM5為PWM脈沖輸出,TIM2作為定時器,根據控制需要來設定PWM的脈寬。
這是TIM的初始化程式
u16 psc = 49;
TIMx_Init(TIM2,1023,psc);
//定時時間為72MHz/50/1024=1.4KHz
TIM3_PWM_Init(1023,psc);
//使用TIM3-5做PWM單獨輸出,TIM3 A相;TIM4 B相;TIM5 C相
TIM4_PWM_Init(1023,psc);
TIM5_PWM_Init(1023,psc);
這個是PWM對應4相的脈寬計算
#define MAICHONG 128 //每相馬達的脈沖數量,總脈沖數為256個
#define MAICHONG1 MAICHONG/4 //馬達的脈沖數量
#define MAICHONG2 MAICHONG/2 //馬達的脈沖數量
#define MAICHONG3 MAICHONG/4*3 //馬達的脈沖數量
const
u16 Sin[]={
//128個脈沖的時序,用正弦波函數發生器産生的,自己懶得去減0x3FF,在計算每相脈沖時自動計算
0x3FF,0x431,0x463,0x495,0x4C7,0x4F8,0x528,0x558,0x587,0x5B5,0x5E1,0x60D,0x638,0x661,0x688,0x6AE
,0x6D3,0x6F5,0x716,0x735,0x752,0x76D,0x786,0x79C,0x7B1,0x7C3,0x7D2,0x7E0,0x7EB,0x7F3,0x7FA,0x7FD
,0x7FE,0x7FD,0x7FA,0x7F3,0x7EB,0x7E0,0x7D2,0x7C3,0x7B1,0x79C,0x786,0x76D,0x752,0x735,0x716,0x6F5
,0x6D3,0x6AE,0x688,0x661,0x638,0x60D,0x5E1,0x5B5,0x587,0x558,0x528,0x4F8,0x4C7,0x495,0x463,0x431
};
u16 Moto_A1[MAICHONG];
u16 Moto_A2[MAICHONG];
u16 Moto_B1[MAICHONG];
u16 Moto_B2[MAICHONG];
void
JiSuan(
void
)
{
u8 i;
for
(i=0;i<100;i++)
{
if
(i<MAICHONG1)
{
Moto_A1[i] = Sin[i]-0x3ff;
Moto_A2[i] = 0;
Moto_B1[i] = 0;
Moto_B2[i] = Sin[i+MAICHONG1]-0x3ff;
}
else
if
(i<MAICHONG2)
{
Moto_A1[i] = Sin[i]-0x3ff;
Moto_A2[i] = 0;
Moto_B1[i] = Sin[i-MAICHONG1]-0x3ff;
Moto_B2[i] = 0;
}
else
if
(i<MAICHONG3)
{
Moto_A1[i] = 0;
Moto_A2[i] = Sin[i-MAICHONG2]-0x3ff;
Moto_B1[i] = Sin[i-MAICHONG1]-0x3ff;
Moto_B2[i] = 0;
}
else
if
(i<MAICHONG)
{
Moto_A1[i] = 0;
Moto_A2[i] = Sin[i-MAICHONG2]-0x3ff;
Moto_B1[i] = 0;
Moto_B2[i] = Sin[i-MAICHONG3]-0x3ff;
}
}
}
這個是步進電機資料的結構體
typedef
struct
{
u8 Flag;
//需要脈沖輸出标志
u8 Dir;
//步進電機方向
u32 Step;
//步進電機需要的脈沖數量
s32 Current;
//步進電機目前位置
s32 Target;
//步進電機目标位置
}MOTO_Struct;
具體的步進電機控制在TIM2中斷中實作,具體的函數如下
void
TIM2_IRQHandler(
void
)
//定時器中斷函數
{
u16 t;
//目前脈沖位置的臨時變量
if
(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
//判斷是否定時中斷
{
if
(Moto_X.Flag)
{
if
(Moto_X.Step > 0)
{
Moto_X.Step--;
if
(Moto_X.Dir)
{
Moto_X.Current++;
}
else
{
Moto_X.Current--;
}
t= Moto_X.Current%MAICHONG;
TIM_SetCompare1(TIM4,Moto_A1[t]);
//改變PWM占空比
TIM_SetCompare2(TIM4,Moto_A2[t]);
//改變PWM占空比
TIM_SetCompare3(TIM4,Moto_B1[t]);
//改變PWM占空比
TIM_SetCompare4(TIM4,Moto_B2[t]);
//改變PWM占空比
}
else
{
Moto_X.Flag = 0;
TIM_SetCompare1(TIM4,0);
//改變PWM占空比
TIM_SetCompare2(TIM4,0);
//改變PWM占空比
TIM_SetCompare3(TIM4,0);
//改變PWM占空比
TIM_SetCompare4(TIM4,0);
//改變PWM占空比
}
}
if
(Moto_Y.Flag)
{
if
(Moto_Y.Step > 0)
{
Moto_Y.Step--;
if
(Moto_Y.Dir)
{
Moto_Y.Current++;
}
else
{
Moto_Y.Current--;
}
t= Moto_Y.Current%MAICHONG;
TIM_SetCompare1(TIM3,Moto_A1[t]);
//改變PWM占空比
TIM_SetCompare2(TIM3,Moto_A2[t]);
//改變PWM占空比
TIM_SetCompare3(TIM3,Moto_B1[t]);
//改變PWM占空比
TIM_SetCompare4(TIM3,Moto_B2[t]);
//改變PWM占空比
}
else
{
Moto_Y.Flag = 0;
TIM_SetCompare1(TIM3,0);
//改變PWM占空比
TIM_SetCompare2(TIM3,0);
//改變PWM占空比
TIM_SetCompare3(TIM3,0);
//改變PWM占空比
TIM_SetCompare4(TIM3,0);
//改變PWM占空比
}
}
if
(Moto_Z.Flag)
{
if
(Moto_Z.Step > 0)
{
Moto_Z.Step--;
if
(Moto_Z.Dir)
{
Moto_Z.Current++;
}
else
{
Moto_Z.Current--;
}
t= Moto_Z.Current%MAICHONG;
TIM_SetCompare1(TIM5,Moto_A1[t]);
//改變PWM占空比
TIM_SetCompare2(TIM5,Moto_A2[t]);
//改變PWM占空比
TIM_SetCompare3(TIM5,Moto_B1[t]);
//改變PWM占空比
TIM_SetCompare4(TIM5,Moto_B2[t]);
//改變PWM占空比
}
else
{
Moto_Z.Flag = 0;
TIM_SetCompare1(TIM5,0);
//改變PWM占空比
TIM_SetCompare2(TIM5,0);
//改變PWM占空比
TIM_SetCompare3(TIM5,0);
//改變PWM占空比
TIM_SetCompare4(TIM5,0);
//改變PWM占空比
}
}
}
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
//必須清除中斷标志位否則一直中斷
}
學習 幹貨 識别二維碼,加關注