Hi everyone!
I’d like to drive a set of ws2812 LEDs with hardware timer and DMA. I have a code written in Mbed Studio (with mbed os version 5) that works fine, but - for historical reasons - I have to get this work with gcc4mbed, and that’s the point where I need help. The program runs well (led2 blinks), except that I can’t recognize anything on PB_11, it’s permanently on L level.
. What can be the difference between the mbed os version used by gcc4mbed (5.4 I guess?) and 5.15 that causes that? Is there something initialised with this pin that I have to deinit or something like that?
The board is a Nucleo F767ZI.
main.cpp (simplified)
#include <mbed.h>
#include "ws2812b-dma-driver.h"
DigitalOut led2(LED2);
InterruptIn btn(BUTTON1);
ws2812dmaWrapper ws;
volatile int prg_pntr = 0;
volatile int l_prg_pntr = 0;
int main(){
ws2812b_init();
Color_t color;
color.b = 255;
color.g = 150;
color.r = 0;
while(1){
led2 = !led2;
setAll_GRB(&color);
drawFrame();
wait_ms(500);
clearAll();
drawFrame();
wait_ms(500);
}
}
stm32f7xx_hal_msp.c
#include "ws2812b-dma-driver.h"
extern DMA_HandleTypeDef hdma_timx_chn;
/**
* @brief TIM_Base MSP Initialization
* This function configures the hardware resources used in this example
* @param htim_base: TIM_Base handle pointer
* @retval None
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
if(htim_base->Instance==TIMx)
{
/* Peripheral clock enable */
TIMx_CLK_ENABLE();
hdma_timx_chn.Instance = TIMx_CC1_DMA_STREAM;
hdma_timx_chn.Init.Channel = TIMx_CC1_DMA_CHANNEL;
hdma_timx_chn.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_timx_chn.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_timx_chn.Init.MemInc = DMA_MINC_ENABLE;
if (TIMx == TIM2){
hdma_timx_chn.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_timx_chn.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
}
else {
hdma_timx_chn.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_timx_chn.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
}
hdma_timx_chn.Init.Mode = DMA_NORMAL;
#if WS2812B_FIFO_MODE
hdma_timx_chn.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
#else
hdma_timx_chn.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
#endif
hdma_timx_chn.Init.Priority = DMA_PRIORITY_HIGH;
hdma_timx_chn.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_timx_chn.Init.MemBurst = DMA_MBURST_INC8;
hdma_timx_chn.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_timx_chn) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(htim_base,hdma[TIM_DMA_HANDLE_ID],hdma_timx_chn);
#ifdef TIM_DMA_HANDLE_ID2
__HAL_LINKDMA(htim_base,hdma[TIM_DMA_HANDLE_ID2],hdma_timx_chn);
#endif
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim->Instance==TIMx)
{
TIMx_GPIO_CLK_ENABLE();
/**TIM2 GPIO Configuration
PA0-WKUP ------> TIM2_CH1
*/
GPIO_InitStruct.Pin = TIMx_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF_TIMx;
HAL_GPIO_Init(TIMx_GPIO_PORT, &GPIO_InitStruct);
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
{
if(htim_base->Instance==TIMx)
{
/* Peripheral clock disable */
__HAL_RCC_TIM1_CLK_DISABLE();
/* TIM1 DMA DeInit */
HAL_DMA_DeInit(htim_base->hdma[TIM_DMA_HANDLE_ID]);
}
}
ws2812b-dma-driver.h
#ifndef __WS2812B_DMA_DRIVER_H__
#define __WS2812B_DMA_DRIVER_H__
#define WS2812B_LEDS_NUM 24
#define WS2812_TIM2CH4
#ifdef __cplusplus
extern "C" {
#endif
#include "stm32f7xx_hal.h"
#include "math.h"
#ifdef WS2812_TIM2CH4
/* Definition of TIM instance */
#define TIMx TIM2
#define TIMER_PRESCALER (1)
#define TIMER_CLOCK_FREQ (108000000)
/* Definition for TIMx clock resources */
#define TIMx_CLK_ENABLE __HAL_RCC_TIM2_CLK_ENABLE
#define TIMx_CLK_DISABLE __HAL_RCC_TIM2_CLK_DISABLE
#define DMAx_CLK_ENABLE __HAL_RCC_DMA1_CLK_ENABLE
/* Definition for TIMx Pins */
#define TIMx_GPIO_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE
#define TIMx_GPIO_PORT GPIOB
#define TIMx_GPIO_PIN GPIO_PIN_11
#define GPIO_AF_TIMx GPIO_AF1_TIM2
/* Definition for TIMx channels */
#define TIMx_CHANNEL TIM_CHANNEL_4
#define TIM_DMA_HANDLE_ID TIM_DMA_ID_CC4
#define TIM_DMA_HANDLE_ID2 TIM_DMA_ID_CC2
/* Definition for TIMx's DMA */
#define TIMx_CC1_DMA_CHANNEL DMA_CHANNEL_3
#define TIMx_CC1_DMA_STREAM DMA1_Stream6
/* Definition for DMAx's NVIC */
#define TIMx_DMA_IRQn DMA1_Stream6_IRQn
#define TIMx_DMA_IRQHandler DMA1_Stream6_IRQHandler
#endif
/* ws2812b defines */
#define WS2812_FREQ (800000) // it is fixed: WS2812 require 800kHz
#define WORDS_PER_LED (24)
#define TIMER_PERIOD (TIMER_CLOCK_FREQ / WS2812_FREQ)
#ifdef WS2812B
#define WS2812_TH1 (round(TIMER_PERIOD * 18 / 25))
#define WS2812_TH0 (round(TIMER_PERIOD * 7 / 25))
#else
#define WS2812_TH1 (round(TIMER_PERIOD * 2 / 3))
#define WS2812_TH0 (round(TIMER_PERIOD * 1 / 3))
#endif
#define NUM_LEDS (WS2812B_LEDS_NUM)
#define RESET_SLOT (50)
#define LED_BUFFER_SIZE ((NUM_LEDS * WORDS_PER_LED) + RESET_SLOT * 2)
typedef struct Color
{
uint8_t r; // red
uint8_t g; // green
uint8_t b; // blue
} Color_t;
/* ws2812b-dma-driver API */
void ws2812b_init(void);
void drawFrame(void);
void clearAll(void);
void getPixelColor(int px_index, Color_t * px_color);
void setPixel_GRB(const Color_t * Color, int px_index);
void setAll_GRB(const Color_t * color);
void setRange_GRB(const Color_t * color, int start_px_index, int len);
void setColorBrightness(const Color_t * in, Color_t * out, float brightness);
/* ******************** */
void TIMx_DMA_IRQHandler(void);
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
void Error_Handler(void);
#ifdef __cplusplus
}
#endif
#endif
ws2812b-dma-driver.c
#include "ws2812b-dma-driver.h"
TIM_HandleTypeDef htimx;
DMA_HandleTypeDef hdma_timx_chn;
//static uint32_t * ws2812b_data;
static uint32_t ws2812b_data[LED_BUFFER_SIZE];
static void MX_DMA_Init(void);
static void MX_TIMx_Init(void);
void ws2812b_init(void)
{
//ws2812b_data = (uint32_t *)calloc(LED_BUFFER_SIZE, sizeof(uint32_t));
for (int i = 0; i < LED_BUFFER_SIZE; i++){
ws2812b_data[i] = 0;
}
assert_param(data);
__disable_irq();
MX_DMA_Init();
MX_TIMx_Init();
__enable_irq();
}
void drawFrame(void)
{
HAL_TIM_PWM_Start_DMA(&htimx, TIMx_CHANNEL, (uint32_t *)ws2812b_data, LED_BUFFER_SIZE);
}
void setPixel_GRB(const Color_t * color, int px_index)
{
int index = px_index * WORDS_PER_LED + RESET_SLOT;
for(int i=0;i<8;i++)
{
// high bit send at first
ws2812b_data[index+i] = (color->g >> (7-i)) & 1 ? WS2812_TH1 : WS2812_TH0;
ws2812b_data[index+i+8] = (color->r >> (7-i)) & 1 ? WS2812_TH1 : WS2812_TH0;
ws2812b_data[index+i+16] = (color->b >> (7-i)) & 1 ? WS2812_TH1 : WS2812_TH0;
}
}
void setAll_GRB(const Color_t * color)
{
for(int i=0;i<NUM_LEDS;i++)
{
setPixel_GRB(color, i);
}
}
void getPixelColor(int px_index, Color_t * px_color){
int index = px_index * WORDS_PER_LED;
for(int i=0;i<8;i++)
{
px_color->g |= (ws2812b_data[index+i] == WS2812_TH1 ? 1 : 0) << (7-i);
px_color->r |= (ws2812b_data[index+i+8] == WS2812_TH1 ? 1 : 0) << (7-i);
px_color->b |= (ws2812b_data[index+i+16] == WS2812_TH1 ? 1 : 0) << (7-i);
}
}
void setRange_GRB(const Color_t * color, int start_px_index, int len)
{
for(int i=start_px_index;i<start_px_index+len;i++)
{
setPixel_GRB(color, i);
}
}
void clearAll(void)
{
for(int i = RESET_SLOT; i<(LED_BUFFER_SIZE - RESET_SLOT); i++)
ws2812b_data[i] = WS2812_TH0;
}
void setColorBrightness(const Color_t * in, Color_t * out, float brightness)
{
out->r = brightness * in->r;
out->g = brightness * in->g;
out->b = brightness * in->b;
}
void TIMx_DMA_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_timx_chn);
}
void Error_Handler(void){}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
HAL_TIM_PWM_Stop_DMA(htim, TIMx_CHANNEL);
}
static void MX_TIMx_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
htimx.Instance = TIMx;
htimx.Init.Prescaler = (uint32_t)TIMER_PRESCALER-1;
htimx.Init.CounterMode = TIM_COUNTERMODE_UP;
htimx.Init.Period = (uint32_t)TIMER_PERIOD - 1;
htimx.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htimx.Init.RepetitionCounter = 0;
htimx.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htimx) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htimx, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htimx) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htimx, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState=TIM_OCIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htimx, &sConfigOC, TIMx_CHANNEL) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htimx, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htimx);
}
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
DMAx_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Stream5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(TIMx_DMA_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIMx_DMA_IRQn);
}