該例程所用的硬件設備:算法
直流電機驅動模塊YYH-LWZ: H橋 大功率 正反轉 剎車 PWM 調速 5/12/24Vide
12V直流減速電機JGB37-520B:ASLONG JGB37-520B編碼器減速電機直流減速馬達A/B相碼盤信號測速 帶編碼器 A/B相輸出 噪音小函數
芯片:IAP15w4k58s4編碼
因該電機驅動模塊沒法直接經過單片機的IO口位的拉高,拉低來控制,故用PWM來控制。軟件模擬PWM不夠穩定快速,故採用硬件PWM,然而硬件PWM只可以使用IAP15w4k58s4芯片固定的PWM輸出IO口,來輸出PWM波形:spa
P0.6/P0.7/P1.6/P1.7/P2.1/P2.2
P2.3/P2.7/P3.7/P4.2/P4.4/P4.5code
芯片資料:http://www.stcmcudata.com/datasheet/stc/STC-AD-PDF/STC15.pdf(要用自取)blog
PWM波形輸入到電機驅動模塊的IO口後,被MOC管放大,在輸出到電機電源線it
硬件PWM的PWM.C程序以下:io
#include "STC15W.H" //單片機頭文件 #include "Uart.h" #include "PWM.h" /*系統晶振頻率爲28Mhz ,PWM輸出信號頻率爲20khz之內*/ //參考stc15系列單片機指南1056頁, void PWM_Init(void) { P_SW2 |= 0x80; PWMCFG = 0x00; //PWM的輸出初始電平爲低電平 PWMCKS = 0x0f; //PWM的時鐘爲Fosc/(0+1) PWMC = CYCLE; //PWM週期,定義PWM週期(最大值爲32767) PWM2CR = 0x00; //PWM2波形輸出到P37,不使能PWM2中斷 PWM3CR = 0x00; //PWM3波形輸出到P21,不使能PWM3中斷 PWM4CR = 0x00; //PWM4波形輸出到P22,不使能PWM4中斷 PWM5CR = 0x00; //PWM5波形輸出到P23,不使能PWM5中斷 PWM2T1 = 0x0001; PWM2T2 = 0; PWM3T1 = 0x0001; PWM3T2 = 0; PWM4T1 = 0x0001; PWM4T2 = 0; PWM5T1 = 0x0001; PWM5T2 = 0; PWMCR |= 0x80; //使能PWM模塊 P_SW2 &=~0x80; } void IN_1( unsigned int DUTY) //PWM2 { if(DUTY==0) //經過DUTY來控制佔空比,進而控制PWM輸出電壓,最終實現轉速的變化 { PWMCR &=~0x01; PWM2=0; } else if(DUTY==100) { PWMCR &=~0x01; PWM2=1; } else { P_SW2 |= 0x80; //使能訪問PWM在擴展RAM區的特殊功能寄存器XSFR PWM2T1 = 0x0001; //設置PWM2第1次反轉的PWM計數 PWM2T2 = CYCLE * DUTY / 100; //設置PWM2第2次反轉的PWM計數 P_SW2 &=~0x80; //佔空比爲(PWM2T2-PWM2T1)/PWMC PWMCR |= 0x01; //使能PWM信號輸出 } } void IN_2(unsigned int DUTY) //PWM3 { if(DUTY==0) { PWMCR &=~0x02; PWM3=0; } else if(DUTY==100) { PWMCR &=~0x02; PWM3=1; } else { P_SW2 |= 0x80; PWM3T1 = 0x0001; PWM3T2 = CYCLE * DUTY / 100; P_SW2 &=~0x80; PWMCR |= 0x02; } } void IN_3(unsigned int DUTY) //PWM4 { if(DUTY==0) { PWMCR &=~0x04; PWM4=0; } else if (DUTY==100) { PWMCR &=~0x04; PWM4=1; } else { P_SW2 |= 0x80; PWM4T1 = 0x0001; PWM4T2 = CYCLE * DUTY / 100; P_SW2 &=~0x80; PWMCR |= 0x04; } } void IN_4(unsigned int DUTY) //PWM5 { if(DUTY==0) { PWMCR &=~0x08; PWM5=0; } else if (DUTY==100) { PWMCR &=~0x08; PWM5=1; } else { P_SW2 |= 0x80; PWM5T1 = 0x0001; PWM5T2 = CYCLE * DUTY / 100; P_SW2 &=~0x80; PWMCR |= 0x08; } } //功能:電機驅動模塊的輸入端口控制函數 void IN_SetPwm(int wide_1,int wide_2,int wide_3,int wide_4,int uDir) { if(uDir==1) { IN_1(wide_1); IN_2(wide_2); IN_3(wide_3); IN_4(wide_4); } }
硬件PWM的PWM.h程序以下:ast
#ifndef _PWM_H_ #define _PWM_H_ #include "STC15W.H" //芯片晶振頻率設置爲28mhz #define CYCLE 0x6500L //定義PWM週期(最大值爲32767) sbit PWM2=P3^7; sbit PWM3=P2^1; sbit PWM4=P2^2; sbit PWM5=P2^3; extern void PWM_Init(void);extern void IN_SetPwm(int wide_1,int wide_2,int wide_3,int wide_4,int uDir); #endif
該電機自帶的編碼器爲A/B相霍爾計數編碼器,根據編碼器的旋轉產生A,B相的不一樣方波,每有A相的4個方波,編碼器轉了90度,轉一圈故有12個方波信號。根據旋轉方向的不一樣,A波產生的上升降低沿時,B波同時刻處於不一樣的電平。電機輸出軸轉一圈的時間內,根據電機的轉速,減速比和PWM的頻率不一樣,編碼器所轉的圈數是不固定的,要精確計數要使用算法,該例程只是前提量不變的估量值。
編碼器的encoder.c程序:
#include "STC15W.H" //單片機頭文件 #include "Uart.h" #include "encoder.h" #include "center.h" unsigned char Last_io=0; unsigned char Curr_io=0; //編碼器結構體的初始化 void Encoder_Init(Encoder_HandleTypeDef * encoder) { encoder-> zheng_count =0; encoder-> fan_count =0; encoder-> end_count =0; } void Delay50us() //@28MHz { unsigned char i, j; i = 2; j = 89; do { while (--j); } while (--i); } void Initial_INT0(void) //用外部中斷來實現A波的觸發 { IT0=0; // 設置成上升沿和降低沿均觸發 EX0=1; //使能INT0中斷 EA=1; } int exint0() interrupt 0 //外部中斷入口 { Delay50us(); if(PIN_A==1) //上升沿觸發 { Curr_io=PIN_B; //記錄PIN_B的觸發信號 } else if(PIN_A==0) //降低沿觸發 { Last_io=PIN_B; //記錄PIN_B的觸發信號 } } //掃描編碼器的計數 void scan_encoder(Encoder_HandleTypeDef *encoder) { if((Curr_io==1)&&(Last_io==0)) //編碼器逆時針旋轉時,A波上升沿時,B波爲1,A波降低沿時,B波爲0; { encoder->zheng_count++; //每有12個判斷信號,編碼器轉一圈 SendString(" 證 轉 \n"); Curr_io=0; // 判斷信號置0,若是不置0會有偏差 Last_io=0; } if((Curr_io==0)&&(Last_io==1)) //編碼器順時針旋轉時,A波上升沿時,B波爲0,A波降低沿時,B波爲1; { encoder->fan_count++; //每有12個判斷信號,編碼器轉一圈 SendString(" 反 轉 \n"); Curr_io=0; // 判斷信號置0 Last_io=0; } if(encoder->zheng_count==360*15) //當編碼器所轉圈數到達必定數量時,電機的輸出軸轉一圈,該數字爲估量值 { // SendString("輸出軸 證 轉 了 一 圈\n"); encoder->zheng_count=0; encoder->end_count++; } if(encoder->fan_count==360*15) { // SendString("輸出軸 反 轉 了 一 圈\n"); encoder->fan_count=0; encoder->end_count++; } }
編碼器的encoder.h程序:
#ifndef _ENCODER_H_ #define _ENCODER_H_ #include "STC15W.H" sbit PIN_B=P4^1; //B相接P41 sbit PIN_A=P3^2; //A相接外部中斷使能端口P41 typedef struct Encoder //編碼器結構體 { unsigned int zheng_count; //編碼器正轉圈數 unsigned int fan_count; //編碼器反轉圈數 unsigned int end_count; //輸出軸已轉圈數 }Encoder_HandleTypeDef; extern void scan_encoder(Encoder_HandleTypeDef *motor); extern void Encoder_Init(Encoder_HandleTypeDef * encoder); extern void Initial_INT0(void); #endif
控制電機正轉或反轉,並旋轉指定圈數
控制電機的motor.c程序
#include "STC15W.H" //單片機頭文件 #include "Uart.h" #include "encoder.h" #include "PWM.h" #include"motoc.h" void Motor_Init(Motor_HandleTypeDef *motor) { motor->H_PWM =0; motor->L_PWM =0; motor->number =0; motor->flag =1; } void Motor_Start(Motor_HandleTypeDef *motor,int tack) //啓動電機 { motor->H_PWM=20; //佔空比恆爲20% if(tack==1) { IN_SetPwm(motor->H_PWM, motor->L_PWM, motor->L_PWM, motor->H_PWM,1); //MOC管顯示爲 (1 0 0 1) 電機正轉 } if(tack==2) { IN_SetPwm(motor->L_PWM, motor->H_PWM, motor->H_PWM, motor->L_PWM,1); //MOC管顯示爲 (0 1 1 0) 電機反轉 } } void Motor_Stop(Motor_HandleTypeDef *motor,int tack) //關閉電機 { motor->H_PWM=0; if(tack==1) { IN_SetPwm(motor->H_PWM,motor->H_PWM,motor->L_PWM,motor->L_PWM,1); //MOC管顯示爲 (0 0 0 0) 電機剎車 } if(tack==2) { IN_SetPwm(motor->L_PWM,motor->L_PWM,motor->H_PWM,motor->H_PWM,1); //MOC管顯示爲 (0 0 0 0) 電機剎車 } } //電機開關函數 void Motor_key(Motor_HandleTypeDef *motor) { if( motor->flag==0) { motor->flag=1; } } //tack:電機方向 count:目的圈數 void Motor_control(Motor_HandleTypeDef *motor,Encoder_HandleTypeDef * encoder,int tack,int count) { if(motor->flag==1) //flag=1時,電機才能運行 { Motor_Start(motor,tack); //啓動電機 motor->number=encoder->end_count ; if(motor->number >= count) //當輸出軸轉的圈數到達目的圈數時,中止旋轉 { Motor_Stop(motor,tack); //關閉電機 encoder->end_count=0; //編碼器圈數置0 motor->flag=0; //flag=0 motor->number=0; //電機圈數置0 } } }
控制電機的motor.h程序
#ifndef _MOTOR_H_ #define _MOTOR_H_ #include "STC15W.H" typedef struct Motor //電機結構體 { int L_PWM; //PWM低電位 int H_PWM; //PWM高電位 int number; //電機已轉圈數 int flag; //電機開關 }Motor_HandleTypeDef; extern void Motor_Init(Motor_HandleTypeDef *motor); extern void Motor_key(Motor_HandleTypeDef *motor); extern void Motor_Start(Motor_HandleTypeDef *motor,int tack); extern void Motor_Stop(Motor_HandleTypeDef *motor,int tack); extern void Motor_control(Motor_HandleTypeDef *motor,Encoder_HandleTypeDef * encoder,int tack,int count);
主函void main()
{ Motor_HandleTypeDef motor; Encoder_HandleTypeDef encoder; STC15W_IOinit(); //單片機初始化 Core_Init_Uart(); //串口初始化 PWM_Init(); //PWM初始化 Encoder_Init(&encoder); //編碼器初始化 Motor_Init(&motor); //電機初始化 Initial_INT0(); //外部中斷初始化 while(1) { Motor_control(&motor,&encoder,1,5); scan_encoder(&encoder); } }
只不過一個簡簡單單的控制電機和計算圈數的程序,就前先後後花了我兩個多星期的業餘時間。如今看來,單片機有不少硬件功能是我瞭解不足的,差很少是從零開始寫的。學不以至用不可取以。指望對後來者有參考幫助。
看官們以爲好,有用就給個推薦,若是何處不足,有錯請大方留言指出。
謝謝瀏覽。