cancel
Showing results for 
Search instead for 
Did you mean: 

Using DMA to create a bit stream with a TIMER on PWM mode

Hesseltjuuh
Associate III

Hello,

For the last couple of days I have tried to create a bit stream with DMA using a timer but have not yet succeeded. The problem seems to be loading the data from an array into the timer register. I have tried this exact same code on an STM32L072 and it worked perfect but when going to the STM32U083 it doesnt anymore. Since the STM32U083 is a newer model I cant find too much about it online so if anyone also has some recommendations on info about this microcontroller and the HAL library online I would appreciate it. 

The settings of the timer, when I change the pulse I can see this on the scope. Also when using the DMA function to load the data in it seems that the pulse is dependent on the length of the array, it just seems like it doesnt use the values in the array.

Hesseltjuuh_0-1765354853364.png

 

Below you will find the code, everything seems to work as the callback is also triggered when the pulse is finished.

/*
******************************************************************************
* @file:	HLD_RGB.c
* @brief:	
* @date:	Jul 2, 2025
******************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
#include "HLD_RGB.h"

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static RGB_config_t **rgbMem = NULL;
static size_t numOfRGBStrings = 0;

/* Private function prototypes -----------------------------------------------*/
/* External functions user code ----------------------------------------------*/
void HLD_RGB_init(RGB_config_t *rgb) {
	// Set LED protocol timing
	rgb->timeHigh = 10;
	rgb->timeLow = 5;
	rgb->rstPeriods = 64;

	// Allocate DMA buffer
	rgb->dmaBufLength = (rgb->numLeds * rgb->bitsPerLed) + rgb->rstPeriods;
	rgb->dmaBuffer = malloc(rgb->dmaBufLength * sizeof(uint16_t));

	if (rgb->dmaBuffer == NULL) {
		// Handle error
		return;
	}

	// Clear DMA buffer
	memset(rgb->dmaBuffer, 0, rgb->dmaBufLength * sizeof(uint16_t));

	// Allocate data buffer
	rgb->dataBufLength = rgb->numLeds;
	rgb->dataBuffer = malloc(rgb->dataBufLength * sizeof(WS2812_DATARGB));
	if (rgb->dataBuffer == NULL) {
		// Handle error
		free(rgb->dmaBuffer); // Clean up previous alloc
		return;
	}
	memset(rgb->dataBuffer, 0, rgb->dataBufLength * sizeof(WS2812_DATARGB));

	// --- Register RGB instance safely ---
	size_t newSize = (numOfRGBStrings + 1) * sizeof(RGB_config_t *);
	RGB_config_t **temp = realloc(rgbMem, newSize);
	if (temp == NULL) {
		// Allocation failed, cleanup and exit
		free(rgb->dmaBuffer);
		free(rgb->dataBuffer);
		return;
	}
	rgbMem = temp;
	rgbMem[numOfRGBStrings] = rgb;
	numOfRGBStrings++;

	rgb->dmaReady = 1;
}

void HLD_RGB_update_1MS(){
	for(int i = 0; i < numOfRGBStrings; i++){
		//Temporary pointer
		RGB_config_t *r = rgbMem[i];

		// If DMA is ready, process leds
		if(r->dmaReady == 1){
			// For every led in the string
			for (uint8_t led = 0; led < r->numLeds; led++) {
				// For every bit of a led
				for (uint8_t bits = 0; bits < r->bitsPerLed; bits++) {
					// Calculate where we need to store the data in the buffer
					uint16_t bufIndex = led * r->bitsPerLed + bits;

					// Calculate the place of the bits
					uint8_t byte = (bits / 8) * 8;
					uint8_t bit = 7 - (bits % 8);
					uint8_t bitIndex = byte + bit;

					// Set timer
					if ((r->dataBuffer[led].data >> bitIndex) & 0x01)
						r->dmaBuffer[bufIndex] = r->timeHigh;
					else
						r->dmaBuffer[bufIndex] = r->timeLow;
				}
			}

			// Transfer data through DMA
			if(HAL_TIM_PWM_Start_DMA(r->tim, r->timChannel, (uint32_t*)r->dmaBuffer, r->dmaBufLength) == HAL_OK){
				r->dmaReady = 0;
			}
		}
	}
}

void HLD_RGB_setColour(RGB_config_t *rgb, uint8_t index, uint8_t r, uint8_t g, uint8_t b){
	rgb->dataBuffer[index].colour.r = r;
	rgb->dataBuffer[index].colour.g = g;
	rgb->dataBuffer[index].colour.b = b;
}

/* Private user code ---------------------------------------------------------*/
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim){
	for(int i = 0; i < numOfRGBStrings; i++){
		//Temporary pointer
		RGB_config_t *r = rgbMem[i];
		HAL_TIM_ActiveChannel tempChannel;

		//Translation map
		switch (r->timChannel) {
			case TIM_CHANNEL_1:
				tempChannel = HAL_TIM_ACTIVE_CHANNEL_1;
				break;
			case TIM_CHANNEL_2:
				tempChannel = HAL_TIM_ACTIVE_CHANNEL_2;
				break;
			case TIM_CHANNEL_3:
				tempChannel = HAL_TIM_ACTIVE_CHANNEL_3;
				break;
			case TIM_CHANNEL_4:
				tempChannel = HAL_TIM_ACTIVE_CHANNEL_4;
				break;
			default:
				tempChannel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
				break;
		}

		//If the timer and channel match
		if(htim == r->tim && htim->Channel == tempChannel){
			HAL_TIM_PWM_Stop_DMA(r->tim, r->timChannel);
			r->dmaReady = 1;
		}
	}
}

 

1 REPLY 1
waclawek.jan
Super User

Step through the code - including Cube, it's open source - to find out what is getting set up and how/when. Read out and check/compare-to-working/post content of DMA/DMAMUX (there's no DMAMUX in 'L0 but there is one in 'U0) and TIM registers.

JW