cancel
Showing results for 
Search instead for 
Did you mean: 

PWM and DMA code

David Fisher
Associate II
Posted on March 01, 2017 at 18:26

 I want to refactor this code from Betaflight

/external-link.jspa?url=https%3A%2F%2Fgithub.com%2Fcleanflight%2Fcleanflight%2Fblob%2Ffd1d8d532e8a21fec0b044e984f465b81f4cbed1%2Fsrc%2Fmain%2Fdrivers%2Fpwm_output_stm32f7xx.c

. It is to control brushless motors using a new digital protocol known as Dshot, which replaces PWM for escs for quadcopters etc. I want to use it for my robot for Piwars 2017. 

The function that I am having problems understanding is the pwmDigitalMotorHardwareConfig . It is programmed in  a generic way but I only need to use the dshot 1200 protocol, for four motors and one Nucleo board(f767zi at the moment)How do I start to understand what is going with regards to the timers, PWM and DMA so that I can modify it for my own needs ?

void

pwmDigitalMotorHardwareConfig

(

const

timerHardware_t *timerHardware,

uint8_t

motorIndex, motorPwmProtocolTypes_e pwmProtocolType,

uint8_t

output)

{

motorDmaOutput_t *

const

motor = &dmaMotors[motorIndex];

motor->timerHardware = timerHardware;

TIM_TypeDef *timer = timerHardware->tim;

const

IO_t motorIO =

IOGetByTag

(timerHardware->tag);

const

uint8_t

timerIndex =

getTimerIndex

(timer);

const

bool

configureTimer = (timerIndex == dmaMotorTimerCount-

1

);

IOInit

(motorIO, OWNER_MOTOR,

RESOURCE_INDEX

(motorIndex));

IOConfigGPIOAF

(motorIO,

IO_CONFIG

(GPIO_MODE_AF_PP, GPIO_SPEED_FREQ_VERY_HIGH, GPIO_PULLUP), timerHardware->alternateFunction);

__DMA1_CLK_ENABLE

();

if

(configureTimer) {

RCC_ClockCmd

(

timerRCC

(timer), ENABLE);

motor->TimHandle.

Instance

= timerHardware->tim;

motor->TimHandle.

Init

.

Prescaler

= (SystemCoreClock /

timerClockDivisor

(timer) /

getDshotHz

(pwmProtocolType)) -

1

;;

motor->TimHandle.

Init

.

Period

= MOTOR_BITLENGTH;

motor->TimHandle.

Init

.

RepetitionCounter

=

0

;

motor->TimHandle.

Init

.

ClockDivision

= TIM_CLOCKDIVISION_DIV1;

motor->TimHandle.

Init

.

CounterMode

= TIM_COUNTERMODE_UP;

if

(

HAL_TIM_PWM_Init

(&motor->TimHandle) != HAL_OK)

{

/*

Initialization Error

*/

return

;

}

}

else

{

motor->TimHandle = dmaMotors[timerIndex].

TimHandle

;

}

motor->timerDmaSource =

timerDmaSource

(timerHardware->channel);

dmaMotorTimers[timerIndex].

timerDmaSources

|= motor->timerDmaSource;

/*

Set the parameters to be configured

*/

motor->hdma_tim.

Init

.

Channel

= timerHardware->dmaChannel;

motor->hdma_tim.

Init

.

Direction

= DMA_MEMORY_TO_PERIPH;

motor->hdma_tim.

Init

.

PeriphInc

= DMA_PINC_DISABLE;

motor->hdma_tim.

Init

.

MemInc

= DMA_MINC_ENABLE;

motor->hdma_tim.

Init

.

PeriphDataAlignment

= DMA_PDATAALIGN_WORD;

motor->hdma_tim.

Init

.

MemDataAlignment

= DMA_MDATAALIGN_WORD;

motor->hdma_tim.

Init

.

Mode

= DMA_NORMAL;

motor->hdma_tim.

Init

.

Priority

= DMA_PRIORITY_HIGH;

motor->hdma_tim.

Init

.

FIFOMode

= DMA_FIFOMODE_DISABLE;

motor->hdma_tim.

Init

.

FIFOThreshold

= DMA_FIFO_THRESHOLD_FULL;

motor->hdma_tim.

Init

.

MemBurst

= DMA_MBURST_SINGLE;

motor->hdma_tim.

Init

.

PeriphBurst

= DMA_PBURST_SINGLE;

/*

Set hdma_tim instance

*/

if

(timerHardware->dmaStream ==

NULL

)

{

/*

Initialization Error

*/

return

;

}

motor->hdma_tim.

Instance

= timerHardware->dmaStream;

/*

Link hdma_tim to hdma[x] (channelx)

*/

__HAL_LINKDMA

(&motor->TimHandle, hdma[motor->timerDmaSource], motor->hdma_tim);

dmaInit

(timerHardware->dmaIrqHandler, OWNER_MOTOR,

RESOURCE_INDEX

(motorIndex));

/*

Initialize TIMx DMA handle

*/

if

(

HAL_DMA_Init

(motor->TimHandle.

hdma

[motor->timerDmaSource]) != HAL_OK)

{

/*

Initialization Error

*/

return

;

}

TIM_OC_InitTypeDef TIM_OCInitStructure;

/*

PWM1 Mode configuration: Channel1

*/

TIM_OCInitStructure.

OCMode

= TIM_OCMODE_PWM1;

if

(output & TIMER_OUTPUT_N_CHANNEL) {

TIM_OCInitStructure.

OCNPolarity

= (output & TIMER_OUTPUT_INVERTED) ? TIM_OCNPOLARITY_HIGH : TIM_OCNPOLARITY_LOW;

TIM_OCInitStructure.

OCNIdleState

= TIM_OCNIDLESTATE_RESET;

}

else

{

TIM_OCInitStructure.

OCPolarity

= (output & TIMER_OUTPUT_INVERTED) ? TIM_OCPOLARITY_LOW : TIM_OCPOLARITY_HIGH;

TIM_OCInitStructure.

OCIdleState

= TIM_OCIDLESTATE_SET;

}

TIM_OCInitStructure.

OCFastMode

= TIM_OCFAST_DISABLE;

TIM_OCInitStructure.

Pulse

=

0

;

if

(

HAL_TIM_PWM_ConfigChannel

(&motor->TimHandle, &TIM_OCInitStructure, motor->timerHardware->channel) != HAL_OK)

{

/*

Configuration Error

*/

return

;

}

}

null
4 REPLIES 4
Posted on March 01, 2017 at 19:33

Seems to be configuring the timer with a defined periodicity(frequency), and then sending 16-bits by modulating the pulse width using DMA to load the new TIM->CCR1 at each Update (Period)

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on March 02, 2017 at 14:20

Yes, but the pulse width is 0 or a fixed (bit) lenght. The timer outputs are 'abused' for writing digital data using DMA burst function. This is because normal Motor ESCs are driven by a variable pulse length and here the same hardware must be used.

Dieter

Posted on March 02, 2017 at 16:05

Ok, but that doesn't appear to be what this code is doing, it has fixed periodicity, and two, non-zero, pulse widths.

♯ define MOTOR_BIT_0 7

♯ define MOTOR_BIT_1 14

♯ define MOTOR_BITLENGTH 19

There may be some intervening idle time, but don't see that in the code.

'

How do I start to understand what is going with regards to the timers, PWM and DMA so that I can modify it for my own needs ?

'

I think my answer addresses this question. How it functions systemically, beyond that is another matter.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on March 02, 2017 at 16:46

You are right. The 0s are short pulses not missing pulses.