#ifndef _HALL_H_ #define _HALL_H_ #include "cw32f030_rcc.h" #include "cw32f030_gpio.h" #include "cw32f030_gtim.h" #define HALLA_PORT (CW_GPIOA) #define HALLB_PORT (CW_GPIOB) #define HALLC_PORT (CW_GPIOA) #define HALLA_PIN (GPIO_PIN_15) #define HALLB_PIN (GPIO_PIN_3) #define HALLC_PIN (GPIO_PIN_2) extern void Commutation(unsigned int step,unsigned int OutPwmValue,unsigned int PWM_ON_flag); extern void GTIM2_IRQHandler(void); void HALL_Init(void); unsigned char HALL_Check(void); #endif HALL.c文件 #include "HALL.h"uint8_t ErrorCode; //馬達運行錯誤碼 extern uint8_t Motor_Start_F; //馬達啟動運作標誌 extern uint8_t Cur_Step; //當前HALL狀態 extern uint8_t Direction; //馬達方向,0為正轉,1為反轉 const uint8_t STEP_TAB[2][6] = {{4,0,5,2,3,1},{1,3,2,5,0,4}};//電機換相序號 extern uint32_t HALLcount; //霍爾脈衝計數 extern uint32_t OutPwm; //輸出PWM值 //初始化霍爾感測器要用到的GPIO和定時器 void HALL_Init(void) { __RCC_GTIM2_CLK_ENABLE(); //先開啟對應時鐘 __RCC_GPIOA_CLK_ENABLE(); __RCC_GPIOB_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; //再配置對應接口 GPIO_InitStruct.IT = GPIO_IT_NONE; GPIO_InitStruct.Mode =GPIO_MODE_INPUT_PULLUP;//霍爾輸入設定; GPIO_InitStruct.Pins = HALLA_PIN | HALLC_PIN;//PA15和PA2 GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_Init(HALLA_PORT, &GPIO_InitStruct); GPIO_InitStruct.IT = GPIO_IT_NONE; GPIO_InitStruct.Mode =GPIO_MODE_INPUT_PULLUP;// 霍爾輸入設定; GPIO_InitStruct.Pins = HALLB_PIN; //PB3 GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_Init(HALLB_PORT, &GPIO_InitStruct); PA15_AFx_GTIM2CH1(); //GTIM2CH1(); PB03_AFx_GTIM2CH2(); //GTIM2CH2(); PA02_AFx_GTIM2CH3(); //GTIM2CH3(); __disable_irq(); NVIC_EnableIRQ(GTIM2_IRQn); //配置GTIM2輸入擷取中斷 __enable_irq(); GTIM_InitTypeDef GTIM_InitStruct; //這裡使用GTIM2的輸入擷取功能 GTIM_ICInitTypeDef GTIM_ICInitStruct; GTIM_InitStruct.Mode = GTIM_MODE_TIME; GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE; GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV1; GTIM_InitStruct.ReloadValue = 0xFFFF; GTIM_InitStruct.ToggleOutState = DISABLE; GTIM_TimeBaseInit(CW_GTIM2, >IM_InitStruct); GTIM_ICInitStruct.CHx = GTIM_CHANNEL1; //GTIM2擷取通道配置 GTIM_ICInitStruct.ICFilter = GTIM_CHx_FILTER_PCLK_N2; GTIM_ICInitStruct.ICInvert = GTIM_CHx_INVERT_OFF; GTIM_ICInitStruct.ICPolarity = GTIM_ICPolarity_BothEdge; GTIM_ICInit(CW_GTIM2, >IM_ICInitStruct); GTIM_ICInitStruct.CHx = GTIM_CHANNEL2; GTIM_ICInit(CW_GTIM2, >IM_ICInitStruct); GTIM_ICInitStruct.CHx = GTIM_CHANNEL3; GTIM_ICInit(CW_GTIM2, >IM_ICInitStruct); GTIM_ITConfig(CW_GTIM2, GTIM_IT_CC1 | GTIM_IT_CC2 | GTIM_IT_CC3, ENABLE); GTIM_Cmd(CW_GTIM2, ENABLE); } unsigned char HALL_Check(void) //讀取霍爾狀態,決定換相順序 { static unsigned char hallerrnum=0; unsigned char Hall_State=0; if(PA15_GETVALUE()!=0)Hall_State=0x1; //對每個腳位狀態分別判斷,所以三個if而不是else if if(PB03_GETVALUE()!=0)Hall_State|=0x2; //或運算 010 if(PA02_GETVALUE()!=0)Hall_State|=0x4; //或運算 100 if(Hall_State==0||Hall_State==7) //000或111都是異常狀態 { hallerrnum++; if(hallerrnum>=10) { hallerrnum=10; ErrorCode=2; } //持續異常狀態說明霍爾感應器有問題 } else hallerrnum=0; return Hall_State; } void GTIM2_IRQHandler(void) //在GTIM2的中斷服務程式裡對霍爾脈衝計數、霍爾狀態確定、換相確定 { uint32_t Hall_State; /* USER CODE BEGIN */ if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_CC1)) //擷取輸入變化就產生中斷標誌 { GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_CC1); //清除中斷標誌 } else if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_CC2)) { GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_CC2); } else if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_CC3)) { GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_CC3); } HALLcount++; //霍爾脈衝計數 Hall_State=HALL_Check(); //讀取霍爾狀態 Cur_Step=STEP_TAB[Direction][Hall_State-1]; //取得換相序位,例如霍爾變化為513264,則Cur_Step變化為345012 if(Motor_Start_F==1&&ErrorCode==0) //依啟動停止狀態 換相 Commutation(Cur_Step,OutPwm,Motor_Start_F); /* USER CODE END */ }
#include "main.h" /*********************** PWM definition ************************ */ #define PWM_HN_PORT (CW_GPIOA) //上管接腳 #define PWM_LN_PORT (CW_GPIOB) //下管接腳 #define PWM_AH_PIN (GPIO_PIN_8) #define PWM_BH_PIN (GPIO_PIN_9) #define PWM_CH_PIN (GPIO_PIN_10) #define PWM_AL_PIN (GPIO_PIN_13) #define PWM_BL_PIN (GPIO_PIN_14) #define PWM_CL_PIN (GPIO_PIN_15) //上管PWM調變控制,下管GPIO開關控制, 上管高電平開關管導通,下管反相 #define PWM_AL_OFF GPIO_WritePin(PWM_LN_PORT,PWM_AL_PIN,GPIO_Pin_SET) #define PWM_BL_OFF GPIO_WritePin(PWM_LN_PORT,PWM_BL_PIN,GPIO_Pin_SET) #define PWM_CL_OFF GPIO_WritePin(PWM_LN_PORT,PWM_CL_PIN,GPIO_Pin_SET) #define PWM_AL_ON GPIO_WritePin(PWM_LN_PORT,PWM_AL_PIN,GPIO_Pin_RESET) #define PWM_BL_ON GPIO_WritePin(PWM_LN_PORT,PWM_BL_PIN,GPIO_Pin_RESET) #define PWM_CL_ON GPIO_WritePin(PWM_LN_PORT,PWM_CL_PIN,GPIO_Pin_RESET) #define PWM_FRQ (20000) //PWM頻率(HZ) #define PWM_TS 3200//20K #define OUTMAXPWM PWM_TS*0.25 #define OUTMINPWM PWM_TS*0.005 void BLDC_Init(void); void BLDC_Motor_Start(uint8_t Dir); void BLDC_Motor_Stop(void); void Commutation(unsigned int step,unsigned int OutPwmValue,unsigned int PWM_ON_flag); void UPPWM(void); //更新PWM佔空比 /////////////////////////
#include "BLDC.h" extern const uint8_t STEP_TAB[2][6];//馬達換相序號 uint8_t Cur_Step; //目前HALL狀態 uint8_t STEP_last; //上次HALL狀態 extern uint8_t Direction; //馬達方向,0為正轉,1為反轉 extern uint8_t Motor_Start_F; //馬達啟動運作標誌 uint32_t OutPwm; //PWM佔空比//初始化馬達要用到的GPIO與定時器,上軸為PWM,下軸為接腳電平控制 void BLDC_Init(void) { __RCC_ATIM_CLK_ENABLE(); __RCC_GPIOA_CLK_ENABLE(); __RCC_GPIOB_CLK_ENABLE(); //初始化下管GPIO GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.IT = GPIO_IT_NONE; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; G PIO_InitStruct.Pins = PWM_AL_PIN | PWM_BL_PIN | PWM_CL_PIN; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_Init(PWM_LN_PORT,&GPIO_InitStruct); //初始化上管 GPIO GPIO_InitStruct.Pins = PWM_AH_PIN | PWM_BH_PIN | PWM_CH_PIN; GPIO_Init(PWM_HN_PORT,&GPIO_InitStruct); PWM_AL_OFF; PWM_BL_OFF;PWM_CL_OFF; //初始化先關閉下管 //初始化ATIM的PWM通道 ATIM_InitTypeDef ATIM_InitStruct; ATIM_OCInitTypeDef ATIM_OCInitStruct; PA08_AFx_ATIMCH1A(); //上管ABC三相 PA09_AFx_ATIMCH2A(); PA10_AFx_ATIMCH3A(); ATIM_InitStruct.BufferState = DISABLE; ATIM_InitStruct.ClockSelect = ATIM_CLOCK_PCLK; ATIM_InitStruct.CounterAlignedMode = ATIM_COUNT_MODE_EDGE_ALIGN; ATIM_InitStruct.CounterDirection = ATIM_COUNTING_UP; ATIM_InitStruct.CounterOPMode = ATIM_OP_MODE_REPETITIVE; ATIM_InitStruct.OverFlowMask = DISABLE; ATIM_InitStruct.Prescaler = ATIM_Prescaler_DIV1; // 計算時脈1MHz ATIM_InitStruct.ReloadValue = PWM_TS - 1; // 20K ATIM_InitStruct.RepetitionCounter = 0; ATIM_InitStruct.UnderFlowMask = DISABLE; ATIM_Init(&ATIM_InitStruct); //初始化PWM通道 ATIM_OCInitStruct.BufferState = DISABLE; ATIM_OCInitStruct.OCDMAState = DISABLE; ATIM_OCInitStruct.OCInterruptSelect = ATIM_OC_IT_UP_COUNTER; ATIM_OCInitStruct.OCInterruptState = ENABLE; ATIM_OCInitStruct.OCMode = ATIM_OCMODE_PWM1; ATIM_OCInitStruct.OCPolarity = ATIM_OCPOLARITY_NONINVERT; ATIM_OC1AInit(&ATIM_OCInitStruct); ATIM_OC2AInit(&ATIM_OCInitStruct); ATIM_OC3AInit(&ATIM_OCInitStruct); ATIM_SetCompare1A(0); //初始化先關閉上管 ATIM_SetCompare2A(0); ATIM_SetCompare3A(0); ATIM_PWMOutputConfig(OCREFA_TYPE_SINGLE, OUTPUT_TYPE_COMP, 0); ATIM_CtrlPWMOutputs(ENABLE); ATIM_Cmd(ENABLE);}void ATIM_IRQHandler(void) { if (ATIM_GetITStatus(ATIM_IT_OVF)) { ATIM_ClearITPendingBit(ATIM_IT_OVF); } } //step,為目前換相序號,OutPwmValue 輸出PWM值,PWM_ON_flag=1時啟動PWM輸出 void Commutation(unsigned int step,unsigned int OutPwmValue,unsigned int PWM_ON_flag) { if(PWM_ON_flag==0) //不啟動則關閉輸出 { CW_ATIM->CH1CCRA=0;CW_ATIM->CH2CCRA=0;CW_ATIM->CH3CCRA=0; ATIM_CtrlPWMOutputs(DISABLE); PWM_AL_OFF;PWM_BL_OFF;PWM_CL_OFF; return; } PWM_AL_OFF;PWM_BL_OFF;PWM_CL_OFF; //先關閉輸出,避免意外 //輸出上橋 if(step==0||step==1){ CW_ATIM->CH1CCRA=OutPwmValue;CW_ATIM->CH2CCRA=0;CW_ATIM->CH3CCRA=0; } //0:AB; 1:AC if(step==2||step==3){ CW_ATIM->CH1CCRA=0;CW_ATIM->CH2CCRA=OutPwmValue;CW_ATIM->CH3CCRA=0; } //2:BC; 3:BA if(step==4||step==5){ CW_ATIM->CH1CCRA=0;CW_ATIM->CH2CCRA=0;CW_ATIM->CH3CCRA=OutPwmValue; } //4:CA; 5:CB //輸出下橋 if(step==0||step==5){PWM_AL_OFF;PWM_CL_OFF;PWM_BL_ON;} //AB CB ; B下橋導通 else if(step==1||step==2){PWM_AL_OFF;PWM_BL_OFF;PWM_CL_ON;}//AC BC; C下橋導通 else if(step==3||step==4){PWM_BL_OFF;PWM_CL_OFF;PWM_AL_ON;}//BA CA; A下橋導通 ATIM_CtrlPWMOutputs(ENABLE); //輸出有效 STEP_last = step; } void UPPWM(void) //更新PWM佔空比 { if(STEP_last==0||STEP_last==1){ CW_ATIM->CH2CCRA=0;CW_ATIM->CH3CCRA=0; CW_ATIM->CH1CCRA=OutPwm; } if(STEP_last==2||STEP_last==3){ CW_ATIM->CH1CCRA=0;CW_ATIM->CH3CCRA=0;CW_ATIM->CH2CCRA=OutPwm; } if(STEP_last==4||STEP_last==5){ CW_ATIM->CH1CCRA=0;CW_ATIM->CH2CCRA=0;CW_ATIM->CH3CCRA=OutPwm; } } void BLDC_Motor_Start(uint8_t Dir) //啟動電機 { uint32_t x; x=HALL_Check(); if(x==0||x==7) {x=1;} //如果霍爾異常,輸出一項,使馬達先轉起來 Cur_Step=STEP_TAB[Direction][x-1]; Motor_Start_F = 1; OutPwm = OUTMINPWM; Commutation(Cur_Step,OutPwm,Motor_Start_F); } void BLDC_Motor_Stop(void) //停止電機 { Motor_Start_F = 0; Commutation(Cur_Step,OutPwm,Motor_Start_F);; }
#ifndef _SPEED_MEASURE_H_ #define _SPEED_MEASURE_H_ #include "cw32f030_btim.h" #include "cw32f030_rcc.h" void Speed_Measure_Init(void); #endif
#include "Speed_Measure.h" extern uint32_t HALLcount; //霍爾脈衝計數 extern uint16_t ADC_TimeCount; //電位器ADC取樣計算計數 extern uint16_t Hall_TimeCount; //計數,進了2次BTIM1中斷,即20ms對轉速計算一次 extern uint16_t OLED_FRESH_TimeCount;//計數,500ms刷新一次OLED顯示 void Speed_Measure_Init(void) //BTIM1 10ms進一次中斷,在中斷裡改變標誌位 { __RCC_BTIM_CLK_ENABLE(); __disable_irq(); NVIC_EnableIRQ(BTIM1_IRQn); __enable_irq(); BTIM_TimeBaseInitTypeDef BTIM_InitStruct; BTIM_InitStruct.BTIM_Mode = BTIM_Mode_TIMER; BTIM_InitStruct.BTIM_OPMode = BTIM_OPMode_Repetitive; BTIM_InitStruct.BTIM_Prescaler = BTIM_PRS_DIV64; BTIM_InitStruct.BTIM_Period = 10000; BTIM_TimeBaseInit(CW_BTIM1, &BTIM_InitStruct); BTIM_ITConfig(CW_BTIM1, BTIM_IT_OV, ENABLE); BTIM_Cmd(CW_BTIM1, ENABLE); } void BTIM1_IRQHandler(void) { /* USER CODE BEGIN */ if(BTIM_GetITStatus(CW_BTIM1, BTIM_IT_OV)) { BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_OV); Hall_TimeCount++; //計數,進了2次BTIM1中斷,即20ms對轉速計算一次 ADC_TimeCount++; //計數,100ms檢查一次電位器的電壓大小,決定目標速度 OLED_FRESH_TimeCount++; //計數,500ms刷新一次OLED顯示 } /* USER CODE END */ }
#ifndef _ADC_BLDC_CTRL_H_ #define _ADC_BLDC_CTRL_H_ #include "cw32f030_rcc.h" #include "cw32f030_gpio.h" #include "cw32f030_adc.h" #include "cw32f030_dma.h" void ADC_Configuration(void); void ADC_DMA_Trans(void); uint32_t ADC_SampleTarget(void); #endif ADC_BLDC_Ctrl.c #include "ADC_BLDC_Ctrl.h" uint32_t ADC_Result_Array;//ADC擷取電位器的值,使用了DMA傳輸 void ADC_Configuration(void) { RCC_AHBPeriphClk_Enable(RCC_AHB_PERIPH_DMA | RCC_AHB_PERIPH_GPIOB, ENABLE); //開啟DMA和ADC使用GPIO接腳的時鐘 RCC_APBPeriphClk_Enable2(RCC_APB2_PERIPH_ADC, ENABLE); //開啟ADC時鐘 PB00_ANALOG_ENABLE(); //設定ADC測試IO口 電位器介面 //ADC初始化 ADC_InitTypeDef ADC_InitStruct; ADC_InitStruct.ADC_OpMode = ADC_SingleChContinuousMode; ADC_InitStruct.ADC_ClkDiv = ADC_Clk_Div8; //PCLK 8MHz ADC_InitStruct.ADC_SampleTime = ADC_SampTime10Clk; //10個ADC時脈週期 ADC_InitStruct.ADC_VrefSel = ADC_Vref_VDDA; //外部3.3V參考電壓 ADC_InitStruct.ADC_InBufEn = ADC_BufDisable; //開啟跟隨器 ADC_InitStruct.ADC_TsEn = ADC_TsDisable; //內建溫度感測器停用 ADC_InitStruct.ADC_DMAEn = ADC_DmaEnable; //ADC轉換完成觸發DMA傳輸 ADC_InitStruct.ADC_Align = ADC_AlignRight; //ADC轉換結果右邊對齊 ADC_InitStruct.ADC_AccEn = ADC_AccDisable; //轉換結果累積不使能 ADC_Init(&ADC_InitStruct); //初始化ADC配置 CW_ADC->CR1_f.DISCARD = FALSE; //配置資料更新策略,覆蓋未被讀取的舊數據,保留新數據 CW_ADC->CR1_f.CHMUX = ADC_ExInputCH8; //設定ADC輸入頻道 //ADC啟用 ADC_Enable(); ADC_SoftwareStartConvCmd(ENABLE); //配置DMA DMA_InitTypeDef DMA_InitStruct; DMA_StructInit( &DMA_InitStruct ); DMA_InitStruct.DMA_Mode = DMA_MODE_BLOCK; //此模式在傳輸過程中會被更進階的回應打斷 DMA_InitStruct.DMA_TransferWidth = DMA_TRANSFER_WIDTH_32BIT;//傳輸32位 DMA_InitStruct.DMA_SrcInc = DMA_SrcAddress_Fix; //來源位址增量方式固定 DMA_InitStruct.DMA_DstInc = DMA_DstAddress_Fix; //目的位址增量方式固定 DMA_InitStruct.DMA_TransferCnt =60000; DMA_InitStruct.DMA_SrcAddress = (uint32_t) &(CW_ADC->RESULT0);//(0x00000020) RESULT0 DMA_InitStruct.DMA_DstAddress = (uint32_t)&ADC_Result_Array; DMA_InitStruct.TrigMode = DMA_HardTrig; //硬體觸發 DMA_InitStruct.HardTrigSource = DMA_HardTrig_ADC_TRANSCOMPLETE; //ADC擷取完成觸發 DMA_Init(CW_DMACHANNEL3,&DMA_InitStruct); DMA_ClearITPendingBit(DMA_IT_ALL); DMA_ITConfig(CW_DMACHANNEL3, DMA_IT_TC|DMA_IT_TE , ENABLE); //啟用DMA_CHANNEL3中斷 DMA_Cmd(CW_DMACHANNEL3, ENABLE); //啟用DMA } void ADC_DMA_Trans(void) { if (CW_DMA->ISR_f.TC3) { //AD DMA 啟動 CW_DMA->ICR_f.TC3 = 0; CW_DMACHANNEL3->CNT=bv16|60000; //MUST RET AGAIN BEFORE CW_DMACHANNEL1->CNT=0 CW_DMACHANNEL3->CSR_f.EN = 1; } } uint32_t ADC_SampleTarget(void) //擷取電壓 { uint32_t Target = 0; if(ADC_Result_Array >= 4000)Target = 4000;//限制大小,12位元ADC擷取值為:0-4096 else if(ADC_Result_Array < 3)Target = 0; else Target = ADC_Result_Array; return Target; }
#include "main.h" uint8_t Direction; //馬達方向,0為正轉,1為反轉 uint8_t Motor_Start_F=0; //馬達啟動運作標誌 uint16_t ADC_TimeCount=0; //電位器ADC取樣計時計數 uint16_t Hall_TimeCount=0; //霍爾計時計數 uint16_t OLED_FRESH_TimeCount=0; //OLED刷新顯示計時計數 uint32_t HALLcount=0; //霍爾脈衝計數 uint32_t Motor_Speed = 0; //馬達實際轉速,rpm extern uint32_t OutPwm; char Buffer1[48],Buffer2[48]; uint32_t Pwm_Buffer; int main() { RCC_Configuration(); //時鐘樹初始化 I2C_init(); //OLED初始化 I2C_OLED_Init(); //I2C初始化 BLDC_Init(); //馬達初始化 HALL_Init(); //霍爾感應器初始化 Speed_Measure_Init(); //BTIM1初始化 ADC_Configuration(); //ADC初始化 I2C_OLED_Clear(1); //清螢幕 Direction = 1; //馬達方向 sprintf(Buffer1,"Speed:%d rpm ",Motor_Speed); //顯示馬達轉速 sprintf(Buffer2,"PWM:%d %% ",Pwm_Buffer); //顯示PWM佔空比 I2C_OLED_ShowString(0,0,Buffer1); I2C_OLED_ShowString(0,15,Buffer2); I2C_OLED_UPdata(); while(1) { ADC_DMA_Trans(); //DMA傳輸完成則允許下次傳輸 if(ADC_TimeCount > 10) //100ms檢查一次目標速度 { ADC_TimeCount = 0; OutPwm = ADC_SampleTarget() / 5; //設定佔空比 if(OutPwm > 0 && Motor_Start_F == 0)BLDC_Motor_Start(Direction);//轉速大於1000rpm才啟動電機 else if(OutPwm > 0 && Motor_Start_F == 1)UPPWM(); //更新佔空比 else BLDC_Motor_Stop(); //停止電機 } if(Hall_TimeCount > 1) //20ms測一次速 { Hall_TimeCount = 0; Motor_Speed = HALLcount * 500 / MotorPoles; //轉速計算,rpm HALLcount = 0; } if(OLED_FRESH_TimeCount > 50) //500ms OLED顯示刷新一次 { OLED_FRESH_TimeCount = 0; sprintf(Buffer1,"Speed:%d rpm ",Motor_Speed); //顯示馬達轉速 I2C_OLED_ShowString(0,0,Buffer1); Pwm_Buffer = OutPwm/32; //最大25%對應OutPwm的值:800 sprintf(Buffer2,"PWM:%d %% ",Pwm_Buffer); //顯示佔空比 I2C_OLED_ShowString(0,15,Buffer2); I2C_OLED_UPdata(); } } } void RCC_Configuration(void) { RCC_HSI_Enable(RCC_HSIOSC_DIV6); /* 1. 設定HCLK和PCLK的分頻係數 */ RCC_HCLKPRS_Config(RCC_HCLK_DIV1); RCC_PCLKPRS_Config(RCC_PCLK_DIV1); /* 2. 啟用PLL,經由HSI倍頻到64MHz */ RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 8); // PLL輸出頻率64MHz /*< 當使用的時脈來源HCLK大於24M,小於等於48MHz:設定FLASH 讀取等待週期為2 cycle < 當使用的時脈來源HCLK大於48M,小於等於72MHz:設定FLASH 讀取等待週期為3 cycle */ __RCC_FLASH_CLK_ENABLE(); FLASH_SetLatency(FLASH_Latency_3); /* 3. 時脈切換到PLL */ RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL); RCC_SystemCoreClockUpdate(64000000); }
int main() { RCC_Configuration(); //時鐘樹初始化 I2C_init(); //OLED初始化 I2C_OLED_Init(); //I2C初始化 BLDC_Init(); //馬達初始化 HALL_Init(); //霍爾感應器初始化 Speed_Measure_Init(); //BTIM1初始化 PID_Init(); //PID初始化 ADC_Configuration(); //ADC初始化 I2C_OLED_Clear(1); //清屏 Direction = 1; //馬達方向 sprintf(Buffer1,"Target:%d rpm",Target_Speed); //顯示目標速度 sprintf(Buffer2,"Speed:%d rpm ",Motor_Speed); //顯示馬達轉速 I2C_OLED_ShowString(0,0,Buffer1); I2C_OLED_ShowString(0,15,Buffer2); I2C_OLED_UPdata(); while(1) { ADC_DMA_Trans(); //DMA傳輸完成則允許下次傳輸 if(ADC_TimeCount > 10) //100ms檢查一次目標速度 { ADC_TimeCount = 0; Target_Speed = ADC_SampleTarget(); //擷取目標速度 if(Target_Speed > 1000 && Motor_Start_F == 0)BLDC_Motor_Start(Direction);//轉速大於1000rpm才啟動電機 else if(Target_Speed > 1000 && Motor_Start_F == 1); //沒有操作,避免重複啟動 else BLDC_Motor_Stop(); //停止電機 } if(Hall_TimeCount > 1) //20ms測一次速 { Hall_TimeCount = 0; Motor_Speed = HALLcount * 500 / MotorPoles; //轉速計算,HALLcount * 50 * 60 / 6 ,單位rpm HALLcount = 0; } if(Flag_PID_TimeCount > 1) //20ms PID控制一次 { Flag_PID_TimeCount = 0; PID_Ctrl(Target_Speed); } if(OLED_FRESH_TimeCount > 50) //500ms OLED顯示刷新一次 { OLED_FRESH_TimeCount = 0; sprintf(Buffer1,"Target:%d rpm ",Target_Speed); //顯示目標速度 sprintf(Buffer2,"Speed:%d rpm ",Motor_Speed); //顯示馬達轉速 I2C_OLED_ShowString(0,0,Buffer1); I2C_OLED_ShowString(0,15,Buffer2); I2C_OLED_UPdata(); } } }
void BTIM1_IRQHandler(void) { /* USER CODE BEGIN */ if(BTIM_GetITStatus(CW_BTIM1, BTIM_IT_OV)) { BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_OV); Hall_TimeCount++; //計數,進了2次BTIM1中斷,即20ms對轉速計算一次 Flag_PID_TimeCount++; //計數,進了2次BTIM1中斷,即20ms對PID計算一次 ADC_TimeCount++; //計數,100ms檢查一次電位器的電壓大小,決定目標速度 OLED_FRESH_TimeCount++; //計數,500ms刷新一次OLED顯示 } /* USER CODE END */ }
uint32_t ADC_SampleTarget(void) //擷取電壓 { uint32_t Target = 0; if(ADC_Result_Array >= 4000)Target = 4000;//限制大小,12位元ADC擷取值為:0-4096 else if(ADC_Result_Array < 3)Target = 0; else Target = ADC_Result_Array; Target = Target * 5; //目標速度為採集值的5被則設定最大速度20000rpm,可自行修改 return Target; }
#include "PID.h" extern uint8_t Motor_Start_F; //馬達啟動運作標誌 extern uint32_t OutPwm; //輸出PWM值,PID最終計算要得到一個確定的PWM佔空比輸出值 extern uint32_t Motor_Speed; //馬達實際轉速,rpm float V_Kp,V_Ki,V_Kd; void PID_Init(void) { V_Kp = 25; V_Ki = 5; V_Kd = 0; } void PID_Ctrl(uint32_t Target) { static int Error,LastError; int PID=0; Error = Target - Motor_Speed; PID = (V_Kp/1000) * (Error - LastError) + (V_Ki/1000) * Error; if(PID>10)PID=10; //犧牲反應速度換取穩定性,避免佔空比從0突增到25 else if(PID<-10)PID=-10; OutPwm += PID; if(OutPwm > OUTMAXPWM)OutPwm = OUTMAXPWM; //佔空比輸出限制 else if(OutPwm < OUTMINPWM) { if(Target > 100)OutPwm = OUTMINPWM; else OutPwm = 0; } if(Motor_Start_F == 0)OutPwm = 0; //啟動停止判斷 else if(Motor_Start_F == 1); else OutPwm = 0; UPPWM(); //更新佔空比 LastError = Error; }