對于控制步進電機來說,最重要的控制參數是脈沖的數量和頻率,兩者結合可以實現滿足要求的電機加減速曲線。在一些電機應用數量不多的場合,通常使用定時器中斷發送脈沖來控制步進電機,優點是原理簡單代碼易于實現。但是一旦控制的電機多起來,就會占用大量的MCU資源,這在大多數情況下是不可接受的,更不用說多軸聯動了。那么如何做到占用很少的MCU資源,又能實現脈沖發送的精確控制?
于是就想到了使用DMA功能更新PWM的輸出, DMA全稱Direct Memory Access,即直接存儲器訪問。DMA傳輸將數據從一個地址空間復制到另一個地址空間,提供在外設和存儲器之間或者存儲器和存儲器之間的高速數據傳輸。它允許不同速度的硬件裝置來溝通,而不需要依賴于MPU的大量中斷負載。
通過設置DMA傳輸數據的數量,可以控制發送的脈沖數。通過設置不同的裝載值和順序,可以使用不同頻率和脈寬。當需要發送較多數量的脈沖時,則可以使用DMA傳輸完成中斷中切換DMA傳輸的數據起始地址及發送數量,繼續發送。這個方法即方便,又減輕MPU的負擔,可以同時驅動多個電機工作,還可以根據電機的啟動、運行、停止使用不同的頻率。
定時器DMA模式
MM32F0270的定時器TIM1、TIM2、TIM3、TIM15、TIM16/17有DMA模式,能夠在發生單個事件時生成多個DMA 請求。主要目的是在沒有軟件開銷的情況下,多次重新編程定時器的一部分,也可以用于按周期讀取數個寄存器。下面以TIM1為例介紹:
TIM1_DCR 和 TIM1_DMAR 寄存器跟 DMA 模式相關。DMA 控制器的目標是唯一的,必須指向TIM1_DMAR 寄存器。開啟 DMA 使能后,在給定的 TIM1 事件發生時, TIM1 會給 DMA 發送請求。
對 TIM1_DMAR 寄存器的每次寫操作都被重定向到一個 TIM1 寄存器。TIM1_DCR寄存器的DBL位定義了DMA連續傳送的長度,即傳輸寄存器數量;當對TIM1_DMAR進行讀寫操作時,定時器識別 DBL,確定傳輸的寄存器數量。TIM1_DCR 寄存器的 DBA 位定義了DMA 傳輸的基地址, 定義從 TIM1_CR1 寄存器地址開始的偏移量(00000 為 TIM1_CR1;00001 為TIM1_CR2;……; 00110 為 TIM1_CCMR1 等)。
通過定時器的DMA模式來更新PWM,本文參官網例程“TIM1_DMA_UPData”進行說明具體實現方法。
實驗
本實驗使用TIM1的DMA模式,當更新事件發生時,更新 TIM1_CCR1、TIM1_CCR2 和 TIM1_CCR3 寄存器的內容。程序中配置TIM1的通道1、通道2、通道3輸出PWM,再通過DMA搬運數據來改變PWM的占空比。定時器每產生一個溢出事件(即計數完成),就發送DMA請求,根據數據在數組中的排列順序以生成所需要的時序。
程序部分
GPIO初始化
配置TIM1_CH1、TIM1_CH2 和 TIM1_CH3對應的GPIO。
void TIM1_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE); GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_2); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStruct); }
TIM1 DMA初始化
TIM1_CH3對應DMA1通道5,將data[]中的數據傳送到TIM1_DMAR寄存器,傳輸方向從存儲器到外設,數據寬度為半字,使能DMA傳輸完成中斷。
void TIM_DMA_Init(void) { DMA_InitTypeDef DMA_InitStruct; RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1, ENABLE); DMA_DeInit(DMA1_Channel5); DMA_StructInit( DMA_InitStruct); //Transfer register address DMA_InitStruct.DMA_PeripheralBaseAddr = (u32) (TIM1->DMAR); //Transfer memory address DMA_InitStruct.DMA_MemoryBaseAddr = (u32)data; //Transfer direction, from memory to register DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStruct.DMA_BufferSize = 6; DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Transfer completed memory address increment DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; DMA_InitStruct.DMA_Priority = DMA_Priority_High; DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; DMA_InitStruct.DMA_Auto_reload = DMA_Auto_Reload_Disable; DMA_Init(DMA1_Channel5, DMA_InitStruct); DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE); }
TIM1 PWM初始化
TIM1輸出PWM,配置時鐘分頻系數和預裝載值,遞增計數,使用PWM模式1,輸出高電平有效,分別對TIM1_CH1、TIM1_CH2、TIM1_CH3指定要加載到捕獲比較寄存器中的脈沖值為arr/2、arr/4、arr/6,使能TIM1的DMA模式,起始地址為TIM1_CCR1,傳輸長度為3。
void TIM1_PWM_Init(u16 arr, u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_OCInitTypeDef TIM_OCInitStruct; RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE); TIM_TimeBaseStructInit( TIM_TimeBaseStruct); TIM_TimeBaseStruct.TIM_Period = arr; TIM_TimeBaseStruct.TIM_Prescaler = psc; //Setting Clock Segmentation TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStruct.TIM_RepetitionCounter = 0; ///TIM Upward Counting Mode TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStruct); TIM_OCStructInit( TIM_OCInitStruct); //Select Timer Mode: TIM Pulse Width Modulation Mode 2 TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; //Setting the Pulse Value of the Capture Comparison Register to be Loaded TIM_OCInitStruct.TIM_Pulse = arr / 2; TIM_OC1Init(TIM1, TIM_OCInitStruct); TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_OCInitStruct.TIM_Pulse = arr / 4; TIM_OC2Init(TIM1, TIM_OCInitStruct); TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_OCInitStruct.TIM_Pulse = arr / 6; TIM_OC3Init(TIM1, TIM_OCInitStruct); TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM1, ENABLE); TIM_DMAConfig(TIM1, TIM_DMABase_CCR1, TIM_DMABurstLength_3Bytes); TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE); TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_Cmd(TIM1, ENABLE); }
使能DMA1通道5
DMA_Cmd(DMA1_Channel5, ENABLE);
配置NVIC
NVIC_Configure(DMA1_Channel4_5_6_7_IRQn, 1, 1);
DMA1中斷服務子程序
void DMA1_Channel4_5_6_7_IRQHandler() { if (DMA_GetITStatus(DMA1_IT_TC5)) { //clear IRQ flag DMA_ClearITPendingBit(DMA1_IT_TC5); } }
定義數組data[]
static u16 data[] = {2000, 3000, 4000, 8000, 7000, 6000};
Main()函數
s32 main(void) { TIM1_GPIO_Init(); TIM1_PWM_Init(10000, 0); TIM_DMA_Init(); NVIC_Configure(DMA1_Channel4_5_6_7_IRQn, 1, 1); DMA_Cmd(DMA1_Channel5, ENABLE); while (1) { } }
演示
下載程序到目標板。連接邏輯分析儀測試PA8、PA9、PA10的輸出,打開對應上位機軟件啟動采樣,運行程序,各通道的PWM輸出情況如下:
截取其中1個周期觀察:
TIM1_CH1輸出PWM占空比為20%和80%, TIM1_CH1輸出PWM占空比為30%和70%, TIM1_CH1輸出PWM占空比為40%和60%,運行結果和預期一致。
實驗簡單演示了MM32F0270的定時器TIM1的DMA方式更新PWM,通過該方案可以實現多路、不同頻率、不同脈寬、數量精確可控的PWM波。
工程路徑如下:
~ MM32FMM32F0270_Lib_SamplesMM32F0270_SamplesLibSamplesTIMTIM1_DMA_UPData。
來源:靈動MM32MCU
免責聲明:本文為轉載文章,轉載此文目的在于傳遞更多信息,版權歸原作者所有。本文所用視頻、圖片、文字如涉及作品版權問題,請聯系小編進行處理
審核編輯 黃宇
-
PWM
+關注
關注
114文章
5186瀏覽量
213957 -
定時器
+關注
關注
23文章
3248瀏覽量
114819 -
dma
+關注
關注
3文章
561瀏覽量
100587
發布評論請先 登錄
相關推薦
評論