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