2025-12-10 12:25 AM
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.
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;
}
}
}
2025-12-10 6:06 AM
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