cancel
Showing results for 
Search instead for 
Did you mean: 

Bug in X-CUBE-PWM-DITHR

In the X-CUBE-PWM-DITHR example application, which is supposed to accompany AN4507, PWM dithering is demonstrated by TIM1_CCR1 modified using timer-update-triggered circular DMA from an array of values, which is in turn generated in the DMA half-complete and complete interrupts.

While the array is supposed to contain 16 values, and the two interrupts update 8 and 8 values accordingly; the DMA itself is started (in TIM1_PWM_UPDATE_DMA()) by calling HAL_DMA_Start_IT() with parameter Length set to 8, which is probably a bug.

It wouldn't help to just simply change this value to 16, as the DMA source buffer is defined as

uint32_t aDitherTable[16];

but DMA is set to read halfwords, and the interrupts updating the values acess this array through following macros

#define DITHER_TABLE_FIRST_PART        ((uint16_t*) aDitherTable)

#define DITHER_TABLE_SECOND_PART       ((uint16_t*)(aDitherTable + 8))

i.e. the second 8 halfwords are updated in aDitherTable[8..11] whereas the DMA, would it be set to transfer 16 halfwords, would read them from aDitherTable[0..7].

I don't Cube so I might've overlooked something.

This bug has been originally reported by a user in a local hw-related mailing list.

JW

PS. There is no appropriate tag ("Topic") for appnotes and software examples.

2 REPLIES 2
VPetr.11
Associate

Hi, the user was me... thank you Jan for posting this...

While I was trying to understand the principle and its implementation I rewritten some of the functions, I will post this code tomorrow, I have it on another computer... (just for information, it worked fine for me...)

Anyway, in the appnote, there is mentioned that for 3-bit resolution enhancement, one needs "Table_size = 2 × 2N".

My opinion is that an array of 8 items is enough. for example: uint32_t vector[8].. then in the DMA-half-transfer-complete-interrupt items vector[0-3] would be updated, while for the DMA-transfer-complete-interrupt items vector[4-7] would be updated...  but maybe I am missing some point.

And one more thing - in the implementation, the code /* Update the Dither generation table */ is placed in "HAL_TIM_PeriodElapsedCallback" and not in "TIM_DMAPeriodElapsedCplt", not sure why..

v.p.

VPetr.11
Associate

OK, below are the parts of the original appnote code which were somehow modified...

/* USER CODE BEGIN PV */

/* Private variables ---------------------------------------------------------*/

#define DITHER_TABLE_FIRST_PART     ((uint32_t*) aDitherTable) // 3 bits EXTRA

#define DITHER_TABLE_SECOND_PART    ((uint32_t*)(aDitherTable + 4)) //was +8

//the defines could be removed as we have two different functions to update the aDitherTable[8]

#define TIMER_PERIOD ((uint32_t)16)

uint32_t aDitherTable[8]; //was [16]

uint32_t DitherIndex = 0;

uint32_t DCycleIndex = 0;

uint32_t UpCounting = 1;

//this table gives optimal dithering pattern in contrast to the original function "UpdateDitherTable"

uint32_t ditable[8][8]={{0,0,0,0,0,0,0,0}, //0

{1,0,0,0,0,0,0,0}, //1

{1,0,0,0,1,0,0,0}, //2

{1,0,0,1,0,0,1,0}, //3

{1,0,1,0,1,0,1,0}, //4

{1,1,1,0,1,0,1,0}, //5

{1,1,1,0,1,1,1,0}, //6

{1,1,1,1,1,1,1,0}}; //7

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/

void SystemClock_Config(void);

void Error_Handler(void);

static void MX_GPIO_Init(void);

static void MX_DMA_Init(void);

static void MX_TIM1_Init(void);

           

void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);

/* USER CODE BEGIN PFP */

/* Private function prototypes -----------------------------------------------*/

HAL_StatusTypeDef TIM1_PWM_UPDATE_DMA(TIM_HandleTypeDef *htim, uint32_t *pData,uint16_t Length);

void UpdateDitherTableA(uint32_t *pDitherTable, uint32_t dutyCycle, uint32_t ditherV);

void UpdateDitherTableB(uint32_t *pDitherTable, uint32_t dutyCycle, uint32_t ditherV);

void UpdateIndex(void);

void TIM_DMAPeriodElapsedCplt(DMA_HandleTypeDef *hdma);

void HalfTransferComplete(DMA_HandleTypeDef *DmaHandle);

/* USER CODE END PFP */

int main(void)

{

  /* MCU Configuration----------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

 HAL_Init();

 /* Configure the system clock */

 SystemClock_Config();

 /* Initialize all configured peripherals */

 MX_GPIO_Init();

 MX_DMA_Init();

 MX_TIM1_Init();

 /* USER CODE BEGIN 2 */

 /* Initialize the first part of the DitherTable with the needed duty cycle and

 dithering value */

 UpdateDitherTableA(DITHER_TABLE_FIRST_PART, 0, 0);

  

 /* Configure the TIM CCR1 update in DMA mode */

 TIM1_PWM_UPDATE_DMA(&htim1, (uint32_t*) aDitherTable, 8);

 /* Start the TIM1 base */

 HAL_TIM_Base_Start(&htim1);

 /* Starts the PWM1 signal generation */

 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

  

 /* USER CODE END 2 */

 /* Infinite loop */

 /* USER CODE BEGIN WHILE */

 while (1)

 {

 /* USER CODE END WHILE */

 }

}

/* USER CODE BEGIN 4 */

/**

 * @brief Update Dither Table - updates the first four items of aDitherTable[8]

 * @param None

 * @retval None

 */

void UpdateDitherTableA(uint32_t *pDitherTable, uint32_t DutyCycle, uint32_t DitherV){

uint32_t table_index;

 for (table_index = 0; table_index <= 3; table_index++){

  if(ditable[DitherV][table_index]){

   pDitherTable[table_index] = DutyCycle + 1;

  }else{

   pDitherTable[table_index] = DutyCycle;

  }

 }//for

}

/**

 * @brief Update Dither Table - updates the last four items of aDitherTable[8]

 * @param None

 * @retval None

 */

void UpdateDitherTableB(uint32_t *pDitherTable, uint32_t DutyCycle, uint32_t DitherV){

 uint32_t table_index;

 for (table_index = 0; table_index <= 3; table_index++){

  if(ditable[DitherV][table_index + 4]){ //notice the +4 here...

   pDitherTable[table_index] = DutyCycle + 1;

  }else{

   pDitherTable[table_index] = DutyCycle;

  }

 }//for

}

/**

 * @brief Update Indexes - handles better dithering in both slopes of the triang. wave...

 * @param None      (with respect to original function)

 * @retval None

 */

void UpdateIndex(void){

if(UpCounting == 1){

if(DitherIndex < 7){DitherIndex++;}else{

 if(DCycleIndex < TIMER_PERIOD){

 DCycleIndex++;

 DitherIndex = 0;

 }else{

 UpCounting = 0;

 };

 };

}else{//(UpCounting == 0)

if(DitherIndex > 0){DitherIndex--;}else{

if(DCycleIndex > 0){

DCycleIndex--;

DitherIndex = 7;

}else{

UpCounting = 1;

};

};

};

};

/**

 * @brief TIM DMA Period Elapse complete callback. 

 * @param hdma : pointer to DMA handle.

 * @retval None

 */

void TIM_DMAPeriodElapsedCplt(DMA_HandleTypeDef *hdma){

if(UpCounting == 1){

UpdateDitherTableB(DITHER_TABLE_SECOND_PART, DCycleIndex, DitherIndex); //AA

}else{

UpdateDitherTableB(DITHER_TABLE_SECOND_PART, DCycleIndex, DitherIndex); //BB

  }

  UpdateIndex();

}

/**

 * @brief Period elapsed callback in non blocking mode 

 * @param TimHandle : TIM handle

 * @retval None

 */

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *TimHandle){

HAL_TIM_PeriodElapsedCallback(&htim1);

}

/**

 * @brief DMA half conversion complete callback

 * @note  This function is executed when the transfer half complete interrupt 

 *     is generated

 * @retval None

 */

void HalfTransferComplete(DMA_HandleTypeDef *DmaHandle){

if(UpCounting == 1){

     UpdateDitherTableA(DITHER_TABLE_FIRST_PART, DCycleIndex, DitherIndex); //CC

    }else{

     UpdateDitherTableA(DITHER_TABLE_FIRST_PART, DCycleIndex, DitherIndex); //DD

  }

  UpdateIndex();

}

-------------------------------------------

hal_msp.c

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* htim_pwm)

{

 if(htim_pwm->Instance==TIM1)

 {

  /* Peripheral clock enable */

  __HAL_RCC_TIM1_CLK_ENABLE();

  /* Peripheral DMA init*/

  

  hdma_tim1_up.Instance = DMA1_Channel5;

  hdma_tim1_up.Init.Direction = DMA_MEMORY_TO_PERIPH;

  hdma_tim1_up.Init.PeriphInc = DMA_PINC_DISABLE;

  hdma_tim1_up.Init.MemInc = DMA_MINC_ENABLE;

  hdma_tim1_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

  hdma_tim1_up.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; //was DMA_MDATAALIGN_HALFWORD;

  hdma_tim1_up.Init.Mode = DMA_CIRCULAR;

  hdma_tim1_up.Init.Priority = DMA_PRIORITY_HIGH;

  if (HAL_DMA_Init(&hdma_tim1_up) != HAL_OK)

  {

   Error_Handler();

  }

  __HAL_LINKDMA(htim_pwm,hdma[TIM_DMA_ID_UPDATE],hdma_tim1_up);

 }

}

note - I was not sure what is the magic of using ((uint16_t*) aDitherTable when the array was defined as uint32_t aDitherTable[16]... so I used uint32_t...

0690X00000BvbC7QAJ.png

output when there is "DitherIndex" supplied to the function "UpdateDitherTable" at AA, BB, CC, DD places.

0690X00000BvbCCQAZ.png

output when "0" placed instead of DitherIndex for AA and CC

0690X00000BvbCRQAZ.png

output when "0" placed instead of DitherIndex for BB and DD

0690X00000BvbCbQAJ.png

output when "0" placed instead of DitherIndex for AA,BB,CC and DD