For controlling stepper motors, the most important control parameters are the number and frequency of pulses. The combination of the two can achieve the required motor acceleration and deceleration curve. In some occasions where the number of motor applications is small, timer interrupts are usually used to send pulses to control stepper motors. The advantage is that the principle is simple and the code is easy to implement. However, once more motors are controlled, a large amount of MCU resources will be occupied, which is unacceptable in most cases, not to mention multi-axis linkage. So how can we take up very few MCU resources and achieve precise control of pulse sending?
So I thought of using DMA function to update PWM output. DMA stands for Direct Memory Access. DMA transfer copies data from one address space to another, providing high-speed data transfer between peripherals and memory or between memory and memory. It allows hardware devices of different speeds to communicate without relying on a large interrupt load of MPU.
By setting the number of DMA transfer data, the number of pulses sent can be controlled. By setting different load values and sequences, different frequencies and pulse widths can be used. When a large number of pulses need to be sent, the DMA transfer completion interrupt can be used to switch the DMA transfer data start address and the number of pulses to be sent, and then continue to send. This method is convenient and reduces the burden on the MPU. It can drive multiple motors at the same time, and different frequencies can be used according to the start, run, and stop of the motor.
Timer DMA Mode The
MM32F0270's timers tiM1, TIM2, TIM3, TIM15, TIM16/17 have a DMA mode that can generate multiple DMA requests when a single event occurs. The main purpose is to reprogram a portion of the timer multiple times without software overhead, and it can also be used to read several registers in cycles. The following is an example of TIM1:
The TIM1_DCR and TIM1_DMAR registers are related to the DMA mode. The target of the DMA controller is unique and must point to the TIM1_DMAR register. After turning on the DMA enable, when a given TIM1 event occurs, TIM1 will send a request to the DMA.
Each write operation to the TIM1_DMAR register is redirected to a TIM1 register. The DBL bit of the TIM1_DCR register defines the length of the DMA continuous transfer, that is, the number of transfer registers; when reading and writing to the TIM1_DMAR, the timer recognizes the DBL and determines the number of transfer registers. The DBA bit of the TIM1_DCR register defines the base address of the DMA transfer, which defines the offset from the TIM1_CR1 register address (00000 for TIM1_CR1; 00001 for TIM1_CR2; ...; 00110 for TIM1_CCMR1, etc.).
The PWM is updated through the timer's DMA mode. This article refers to the official website routine "TIM1_DMA_UPData" to illustrate the specific implementation method.
Experiment
This experiment uses the DMA mode of TIM1. When an update event occurs, the contents of the TIM1_CCR1, TIM1_CCR2 and TIM1_CCR3 registers are updated. In the program, the channels 1, 2 and 3 of TIM1 are configured to output PWM, and the PWM duty cycle is changed by moving data through DMA. Every time the timer generates an overflow event (i.e., the count is completed), a DMA request is sent to generate the required timing according to the order of the data in the array.
Program section
GPIO initialization
Configure the GPIO corresponding to TIM1_CH1, TIM1_CH2 and TIM1_CH3. 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);
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 initializes TIM1_CH3 corresponding to DMA1 channel 5, transfers the data in data[] to the TIM1_DMAR register, the transfer direction is from memory to peripherals, the data width is half word, and enables DMA transfer completion interrupt. 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_InitStruc t.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_Init Struct.DMA_Auto_reload = DMA_Auto_Reload_Disable;
DMA_Init(DMA1_Channel5, &DMA_InitStruct);
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
}
TIM1 PWM initializes
TIM1 output PWM, configures the clock division coefficient and preload value, counts up, uses PWM mode 1, outputs high level valid, specifies the pulse values to be loaded into the capture compare register as arr/2, arr/4, arr/6 for TIM1_CH1, TIM1_CH2, and TIM1_CH3 respectively, enables TIM1's DMA mode, the starting address is TIM1_CCR1, and the transfer length is 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);
}
Enable DMA1 channel 5
DMA_Cmd(DMA1_Channel5, ENABLE);Configure NVIC NVIC_Configure(DMA1_Channel4_5_6_7_IRQn, 1, 1);DMA1 interrupt service subroutine void DMA1_Channel4_5_6_7_IRQHandler(){
IF (DMA_GetITStatus(DMA1_IT_TC5)) {
//clear IRQ flag
DMA_ClearITPendingBit(DMA1_IT_TC5);
}
}Define array data[]static u16 data[] = {2000, 3000, 4000, 8000, 7000, 6000};Main() function 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) {
}
}
Demonstration
Download the program to the target board. Connect the logic analyzer to test the output of PA8, PA9, and PA10, open the corresponding host computer software to start sampling, run the program, and the PWM output of each channel is as follows:
Take one of the cycles and observe:
The output PWM duty cycle of TIM1_CH1 is 20% and 80%, the output PWM duty cycle of TIM1_CH1 is 30% and 70%, and the output PWM duty cycle of TIM1_CH1 is 40% and 60%. The operating results are consistent with expectations.
The experiment simply demonstrates the DMA method of updating PWM of the timer TIM1 of MM32F0270. Through this solution, multiple PWM waves with different frequencies, different pulse widths and precisely controllable quantities can be realized.
|