DC Motor control project part1 main.c
因為不知道馬達何時開始轉動,所以使用External interrupt來讀取編碼器的數值
STM32F407VG的External interrupt 有16條線分別對應pin腳的數字。
PD0 的外部終斷執行程式,PD0使用上緣觸發如左圖若PD0接在encoder a channel在觸發時讀取b channel 的值時為LOW低電位,則馬達CW順時針,若b channel 為HIGH高電位則馬達CCW逆時針轉。
如左圖所示,當每達有提供電源時訊號會有不穩定的狀態(量測此訊號時馬達沒有旋轉),黃色為channel a藍色為channel b若使用上面提到的方式計算馬達旋轉圈數的話會非常不穩定,所以我同時使用channel a的上緣和下緣觸發來判斷轉向,若上一次上緣觸發時channe b的讀值和下緣觸發時的讀值一至時則判斷馬達沒有再轉動。
STM32F407VG的External interrupt 有16條線分別對應pin腳的數字。
選擇PD0和PD1作為我的外部中斷腳位
選擇好要使用的pin腳後接下來要設定interrupt handlers設定方法為如下表格
IRQ | Handler | 註解
EXTI0_IRQn | EXTI0_IRQHandler | EXIT line 0的Handler
EXTI1_IRQn | EXTI1_IRQHandler | EXIT line 1的Handler
EXTI2_IRQn | EXTI2_IRQHandler | EXIT line 2的Handler
EXTI3_IRQn | EXTI3_IRQHandler | EXIT line 3的Handler
EXTI4_IRQn | EXTI4_IRQHandler | EXIT line 4的Handler
EXTI9_5_IRQn | EXTI9_5_IRQHandler | EXIT line 5到line 9的Handler
EXTI15_10_IRQn | EXTI15_10_IRQHandler | EXIT line 10到line 15的Handler
mian.c
標頭檔
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_exti.h"//外部中斷
#include "stm32f4xx_syscfg.h"
#include "misc.h"
#include "stm32f4xx_usart.h"
#include "stm32f4xx_tim.h"//做系統頻率和PWM輸出控制馬達會用到
全域變數
volatile short int motorcount=0;//馬達數值
short int goal=3000;//欲轉到的數值
int u,e,e_befor,e_sum;
const short int motor1_kp=4;//PID參數
const short int motor1_ki=5;
const short int motor1_kd=1;
volatile uint8_t state,state_last;//state 紀錄channel a上緣觸發時b channel的電位
//state_last紀錄channel a下緣觸發時b channel的電位
volatile uint8_t serialprint_freq=0;
//Pin location encoder[a]:PC11, encoder[b]:PD0 PD1
//Motor control pwm out PB7 PB6


而上緣觸發時channel b為LOW下緣觸發時channel b為HIGH則是CW順時轉
而上緣觸發時channel b為HIGH下緣觸發時channel b為LOW則是CCW逆時轉
以下是上緣觸發(PD0)時執行的程式,程式名稱不能改。
void EXTI0_IRQHandler(void) {
/* Make sure that interrupt flag is set */
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
state=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_11);
if(state_last==1&&state==0)
motorcount--;
else if(state==1&&state_last==0)
motorcount++;
else{};
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
以下是下緣觸發(PD1)時執行的程式,程式名稱不能改。
void EXTI1_IRQHandler(void) {
/* Make sure that interrupt flag is set */
if (EXTI_GetITStatus(EXTI_Line1) != RESET) {
state_last=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_11);
if(state_last==1&&state==0)
motorcount--;
else if(state==1&&state_last==0)
motorcount++;
else{};
/* Clear interrupt flag */
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
TIM2為用來控制系統頻率的timer
void TIM2_IRQHandler(){
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
state=!state;
motor_control(1);
if(serialprint_freq==200){
to_ascii_print(motorcount);
USART2_send_byte(101);
to_ascii_print(e);
USART2_send_byte(117);
to_ascii_print(u);
serialprint_freq=0;
}
serialprint_freq++;
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
}
}
TIM2是用來做系統觸發用的,因為我們要控制系統在一個固定的頻率下運作,以下程式是設定TIM2的優先級,當然PD0,PD1的外部終斷也要設定優先級,我設定在Initsys.c內
void TIM_IRQn(){
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
以下程式是用來計算目前的和目標間的誤差值error和使用PID控制器設計系統輸入u
int controlinput(uint8_t motor_index){
e=goal-motorcount;
e_sum=e_sum+e;
switch (motor_index){
case 1:
return (int)(motor1_kp*e+motor1_kd*(e-e_befor)+motor1_ki*e_sum/100);
break;
case 2:
return 0;
break;
case 3:
return 0;
break;
default:
return 0;
break;
}
}
以下程式用來輸出馬達控制指令,我使用L293D馬達驅動晶片,並且使用PWM來控制馬達的轉速。
void motor_control(uint8_t motor_index){
u=controlinput(motor_index);
int motoru;
if(u>=0)
{
if(u>10000)
u=10000 ;
motoru=u+200;
pwm_dutucycle(GPIO_Pin_6,motoru);
pwm_dutucycle(GPIO_Pin_7,0);
}
else if(u<0)
{
if(u<-10000)
u=-10000 ;
motoru=u-200;
pwm_dutucycle(GPIO_Pin_6,0);
pwm_dutucycle(GPIO_Pin_7,-motoru);
}
}
初始化程式
void setup_Periph(){
Configure_PD0();
Configure_PD1();
motorcontrolGPIO_setup();
USARTInit();
PWM_Timer_set();
TIM2_steup();
TIM_IRQn();
}
main()
int main()
{
setup_Periph();//初始設定
USART_puts(USART2,"Hello World!");//測試序列阜可以運作
while(1){
GPIO_WriteBit(GPIOD,GPIO_Pin_13,state);//用來測試系統頻率
}
}
留言
張貼留言