Re: PWM and code in the main loop
Posted: Tue Mar 18, 2025 5:37 pm
Just added SetPixelColor as to let modify each led individually....
Tested with RGBW (dont have now rgb neopixels)
regards, julian
Tested with RGBW (dont have now rgb neopixels)
regards, julian
Code: Select all
#include <Arduino.h>
#include <HardwareTimer.h>
//con o sin white
#define ES_RGBW 1
#define LED_COUNT 7
#if ES_RGBW == 1
#define BITS_PER_LED 32
uint8_t leds[LED_COUNT][4];
#else
#define BITS_PER_LED 24
uint8_t leds[LED_COUNT][3];
#endif
#define BUFFER_SIZE (LED_COUNT * BITS_PER_LED + 2) // allow space for 2 dummy entries
// WS2812 Timing Values (for 72MHz clock)
#define T0H 28 // ~0.35µs high
#define T0L 56 // ~0.9µs low
#define T1H 56 // ~0.9µs high
#define T1L 28 // ~0.35µs low
uint16_t pwmBuffer[BUFFER_SIZE];
// Use TIM4
HardwareTimer timer(TIM4);
// DMA Interrupt Handler
extern "C" void DMA1_Channel7_IRQHandler();
#if ES_RGBW == 1
void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w);
void setPixelColor(unsigned int a_pixel_num, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
#else
void setColor(uint8_t r, uint8_t g, uint8_t b);
void setPixelColor(uint8_t r, uint8_t g, uint8_t b);
#endif
void sendBuffer();
void setup() {
Serial.begin(115200);
delay(1000);
digitalWrite(PB6, LOW);
pinMode(PB6, OUTPUT);
// Enable DMA Clock
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// Configure DMA for TIM4 CH1 (PB6)
/*DMA1_Channel7->CPAR = (uint32_t)&TIM4->CCR1; Peripheral address (TIM4 Capture Compare Register)
Acà se relaciona al DMA con el timer4, para que ante cada lectura de DMA, se llene
el registro CCR1 del TIMER 4 con el valor del BUFFER, que es lo que determina que el pin permanece HIGH,
una vez que se llega a ese valor, el pin pasa a LOW
*/
DMA1_Channel7->CPAR = (uint32_t)&TIM4->CCR1;
DMA1_Channel7->CMAR = (uint32_t)pwmBuffer; //"Channel x Memory Address Register" (CMAR) and is used to store the memory address where the DMA will read from or write to.
DMA1_Channel7->CNDTR = BUFFER_SIZE; // Number of data items to transfer
DMA1_Channel7->CCR = DMA_CCR_MINC // Memory increment mode
| DMA_CCR_DIR // Memory to Peripheral
| DMA_CCR_TCIE // Transfer complete interrupt
| DMA_CCR_PSIZE_0 // Peripheral size = 16-bit
| DMA_CCR_MSIZE_0 // Memory size = 16-bit
| DMA_CCR_PL; // Priority level
// Enable DMA Interrupt
NVIC_EnableIRQ(DMA1_Channel7_IRQn);
// Configure TIM4 (Now outputs PWM on PB6)
timer.setPWM(1, PB6, 800000, 0); // 800kHz. 0% dutycycle = no pulse
}
void loop() {
setColor(0, 255, 0, 0); // Green
sendBuffer();
delay(500);
setColor(0, 0, 255, 0); // Blue
sendBuffer();
delay(500);
setColor(255, 0, 0, 0); // red
sendBuffer();
delay(500);
setColor(0, 0, 0, 255); // white
sendBuffer();
delay(500);
for (int i = 0; i < LED_COUNT; i++) {
setPixelColor(i, random(255), random(255), random(255), random(255));
}
sendBuffer();
delay(500);
}
#if ES_RGBW == 1
void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w){
// Fill all LEDs with the same color
for (int i = 0; i < LED_COUNT; i++) {
leds[i][0] = g; // WS2812 uses GRB format
leds[i][1] = r;
leds[i][2] = b;
leds[i][3] = w;
}
}
void setPixelColor(unsigned int a_pixel_index, uint8_t r, uint8_t g, uint8_t b, uint8_t w){
//
// Fill all LEDs with the same color
if (a_pixel_index<LED_COUNT){
leds[a_pixel_index][0] = g; // WS2812 uses GRB format
leds[a_pixel_index][1] = r;
leds[a_pixel_index][2] = b;
leds[a_pixel_index][3] = w;
}
}
void fillBuffer(){
// Convert to PWM signal
int index = 0;
for (int i = 0; i < LED_COUNT; i++) {
for (int j = 0; j < 4; j++) {
for (int bit = 7; bit >= 0; bit--) {
if (leds[i][j] & (1 << bit)) {
pwmBuffer[index++] = T1H; // High for "1"
} else {
pwmBuffer[index++] = T0H; // High for "0"
}
}
}
}
pwmBuffer[index++] = 0; // add dummy pulse
pwmBuffer[index++] = 0;
}
#else
// Convert RGB to PWM timing buffer
void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a_unused) {
uint8_t leds[LED_COUNT][3];
// Fill all LEDs with the same color
for (int i = 0; i < LED_COUNT; i++) {
leds[i][0] = g; // WS2812 uses GRB format
leds[i][1] = r;
leds[i][2] = b;
}
// Convert to PWM signal
int index = 0;
for (int i = 0; i < LED_COUNT; i++) {
for (int j = 0; j < 3; j++) {
for (int bit = 7; bit >= 0; bit--) {
if (leds[i][j] & (1 << bit)) {
pwmBuffer[index++] = T1H; // High for "1"
} else {
pwmBuffer[index++] = T0H; // High for "0"
}
}
}
}
pwmBuffer[index++] = 0; // add dummy pulse
pwmBuffer[index++] = 0;
}
#endif
// Start DMA Transfer
void sendBuffer() {
fillBuffer();
// **Reset Pulse**: Ensure at least 50µs LOW before sending data
delayMicroseconds(50); // 50µs to be safe
TIM4->CR1 &= ~TIM_CR1_CEN; // ensure TIM4 stopped
TIM4->DIER |= TIM_DIER_UDE; /*cuando el buffer se transfirio completamente, se deshabilita este bit TIM4->DIER &= ~TIM_DIER_UDE; (ver dma irq handler)*/
// Reset DMA
/*
Reset the DMA channel: Before initiating a new DMA transfer,
it's often necessary to disable the channel to ensure that it's in a known state.
*/
DMA1_Channel7->CCR &= ~DMA_CCR_EN;
DMA1->IFCR |= DMA_IFCR_CHTIF7; // clear half-transfer interrupt
DMA1->IFCR |= DMA_IFCR_CTCIF7; // clear transfer complete interrupt
DMA1->IFCR |= DMA_IFCR_CTEIF7; // clear error flag
/*The CNDTR register holds the number of data items that the DMA channel is programmed to transfer. When the DMA transfer starts,
this register is loaded with the desired number of data items. As each data item is transferred, the CNDTR register is decremented.
When it reaches zero, the DMA transfer is complete.
*/
DMA1_Channel7->CNDTR = BUFFER_SIZE;
/*This line of code sets the "Enable" bit in the DMA1 Channel 1 Control Register. In simpler terms, it enables the DMA channel. */
DMA1_Channel7->CCR |= DMA_CCR_EN;
/*This enables the timer to generate a DMA request when an update event occurs.*/
TIM4->CR1 |= TIM_CR1_CEN; // start TIM4
}
// DMA Transfer Complete Interrupt
extern "C" void DMA1_Channel7_IRQHandler(void) {
/*DMA_ISR_TCIF1 "Transfer Complete Interrupt Flag"....
entonces con el & queda claro que ese bit del DMA1->ISR està en 1*/
if (DMA1->ISR & DMA_ISR_TCIF7) { /*// acà se setea el flag de transferencia completa*/
DMA1->IFCR = DMA_IFCR_CTCIF7; // Clear only the Transfer Complete flag
DMA1_Channel7->CCR &= ~DMA_CCR_EN;
TIM4->CR1 &= ~TIM_CR1_CEN; // stop TIM4
}
}