PWM and code in the main loop

Post here first, or if you can't find a relevant section!
ag123
Posts: 1898
Joined: Thu Dec 19, 2019 5:30 am
Answers: 30

Re: PWM and code in the main loop

Post by ag123 »

julian_lpp wrote: Sun Mar 16, 2025 3:10 pm well, I'll start investigating about DMA and Timer combination, maybe with gpt help to speed it up, given that never looked into dma stuff, hardly imagine what it is, but need to figure out exacly how it works, how to configure a timer to start reading a buffer and increment its index, the way and order irqs are triggered, etc, etc

All your comments were higly valuable, and as soon as I can get some code to post i''ll do. Hopefully we could contibute with some usable code where neopixels management are part of much complex program, rather than adafruit approach that pretty much is "drive your leds and the rest of the code who knows :lol: "

kind regards
julian
if you are using the 'official' core
https://github.com/stm32duino/Arduino_Core_STM32

here are some tips to get started,
use the STM32CubeIDE to generate codes for DMA and timer
https://www.st.com/en/development-tools ... beide.html
that would generate some codes for setting up DMA and Timer using HAL function calls, setting up the interrupts etc.

you can then copy over the codes and work on them over in a STM32Duino project.

of course searching the web for examples, tutorial or even using ChatGPT etc would help too,

I tried that in a while trying to make a SPI lib with DMA here
viewtopic.php?t=2553
https://github.com/ag88/stm32duino_spi_dma

one of the major challenges is that practically across the different series, each series has a different DMA controller, so they work a little differently say between stm32 F1, F4, G4, H5, H7, F0, F3, L4 etc

I end up abstracting codes into c++ classes, so that codes that I can reuse across the series is perhaps in one class
https://github.com/ag88/stm32duino_spi_ ... SPIDMA.cpp
and the derived series specific class would implement the different part (especially DMA) specific to each series, e.g. for F4xx
https://github.com/ag88/stm32duino_spi_ ... A_F4XX.cpp
all my codes there currently build ok with CMake, in STM32Duino but that I've not physically tested them unfortunately

if you'd like you could try this way say with what you have on hand, e.g. generate codes for stm32f103 in STM32CubeIDE, adapt it to work in stm32duino, and later if we'd want to make that 'generic', we'd need to consider these challenges, say in making a library.

A thing is if codes are specifically written only for stm32f1 (say f103), there may be challenges later when trying to port that say into F4 or a G4 chip/board. i'd guess it make sense to prioritise portability these days, there are many newer or different chips e.g. F4, G4, F7, H7, H5, L4 these days, many are faster/better that the stm32f103.

But that doing so would mean to confront all those challenges e.g. about chip differences (e.g. DMA) between the serieses , and it'd likely result in more code bloat (if-defs), use more memory (memory is especially precious, with the smaller chips) and flash, bigger/fatter binaries etc.
But not doing so may make the same code base 'un-usable' say across a series e.g. can't 'just works' when trying to use that for an F103 say later for F4, G4 etc.
julian_lpp
Posts: 25
Joined: Tue Dec 24, 2024 4:32 pm

Re: PWM and code in the main loop

Post by julian_lpp »

i've managed to write some code to start playing with tmr+dma (gpt help)
I uploaded the binary but nothing happens, would need confirmation wheter there are pwm signal on PB6 or not
I'm using timer 4 which is the only one available in my original project

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 24
#else
    #define BITS_PER_LED 32
#endif    

#define BUFFER_SIZE (LED_COUNT * BITS_PER_LED)

// 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
void DMA1_Channel1_IRQHandler();

#if ES_RGBW == 1
    void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w);
#else
    void setColor(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_Channel1->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_Channel1->CPAR = (uint32_t)&TIM4->CCR1; 
    DMA1_Channel1->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_Channel1->CNDTR = BUFFER_SIZE;         // Number of data items to transfer
    DMA1_Channel1->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_Channel1_IRQn);

    // Configure TIM4 (Now outputs PWM on PB6)
    timer.setMode(1, TIMER_OUTPUT_COMPARE_PWM1, PB6); // CH1, PWM mode, output on PB6
    timer.setPrescaleFactor(0);                       // No prescaler (72MHz)
    //el periodo es 90
    timer.setOverflow(90, TICK_FORMAT);               // ~1.25µs total period (800kHz WS2812 signal) Esto es vàlido para 72MHZ
    timer.resume();

}

void loop() {
    
    setColor(0, 255, 0, 0); // Green
    sendBuffer();
    delay(1000);

    setColor(0, 0, 255, 0); // Blue
    sendBuffer();
    delay(1000);

    setColor(255, 0, 0, 0); // red
    sendBuffer();
    delay(1000);
}




#if ES_RGBW == 1
    void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w){
        uint8_t leds[LED_COUNT][4];

        // 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;
        }

        // 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"
                    }
                }
            }
        }
    }


#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"
                    }
                }
            }
        }
    }
#endif


// Start DMA Transfer
void sendBuffer() {

    // **Reset Pulse**: Ensure at least 50µs LOW before sending data
    digitalWrite(PB6, LOW);
    delayMicroseconds(80); // 80µs to be safe

    // 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_Channel1->CCR &= ~DMA_CCR_EN;
    
    /*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_Channel1->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_Channel1->CCR |= DMA_CCR_EN;

   /*This line of code sets the TIM_DIER_UDE bit in the TIM4 DIER register.
    This enables the timer to generate a DMA request when an update event occurs.*/
    TIM4->DIER |= TIM_DIER_UDE; /*cuando el buffer se transfirio completamente, se deshabilita este bit TIM4->DIER &= ~TIM_DIER_UDE; (ver dma irq handler)*/
}

// DMA Transfer Complete Interrupt
void DMA1_Channel1_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_TCIF1) { /*// acà se setea el flag de transferencia completa*/
        DMA1->IFCR = DMA_IFCR_CTCIF1;  // Clear only the Transfer Complete flag
        /*This DISABLES the timer to generate a DMA request when an update event occurs.*/
        TIM4->DIER &= ~TIM_DIER_UDE;  // Disable DMA Request
    }
}


ozcar
Posts: 176
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: PWM and code in the main loop

Post by ozcar »

Julian,

I think your main problem is that you are using the wrong DMA channel. You must have seen the "DMA1 request mapping" table in the reference manual, which shows "TIM4_CH1" as one of the sources of requests for DMA1 Channel1. The trouble is, you need DMA request for update event (which is the same for all the channels on a timer), and that has a different request line to the DMA controller. In that same table, you will see "TIM4_UP" goes to DMA1 Channel7.

There may be a few other minor things to tweak.

For a start, it should not be necessary to do digitalWrite(PB6, LOW) in sendBuffer(). You should make sure that the you leave PB6 low when you get to the end of the buffer. Note that just stopping the timer from sending DMA requests is going to surprise you. What will happen is that the timer will continue to generate pulses with length equal to the last data bit. You could maybe make use of a "dummy" pulse on the end with CCR = 0, but there are probably many other ways.

It is not a bad idea to leave handling of the "reset" (absence of pulses) to the next time that sendBuffer() is called. However to do that you would have to make a note of the time (using micros() maybe) at the end of the pulse train, and then in sendBuffer() only delay if not enough time has elapsed (there is a minimum time for "reset" but no maximum).

You could probably see if you are generating pulses at all with just an ordinary LED (with suitable series resistance) from the output pin to ground. You might even be able to tell the difference between sending different data from the brightness of the LED. Or, you could just slow the timer down so that you can see the LED blinking.

Edit - I noticed a few other things later:

Is ES_RGBW equal to 1 supposed to mean that you do have RGBW LEDs, or maybe that you do not have RGBW? There seems to be some conflict with the way you use ES_RGBW that could cause you to clobber whatever happens to be beyond the end of pwmBuffer.

In order to replace the existing empty default irq handler, your one should have extern "C", so when you have sorted out the DMA channel issue, like this:

Code: Select all

extern "c" void DMA1_Channel7_IRQHandler();
...
extern "C" void DMA1_Channel7_IRQHandler(void) {
     ...
}
Then a very minor point. The numbers that you define for the timing values could be perfectly reasonable for the LEDs, but are a bit at odds with the times given in the comments.

Maybe the timer register values could be calculated at run-time from the clock frequency. When I did the PWM test (without DMA or interrupts), I used something like:

Code: Select all

uint32_t  ftimer = timer.getTimerClkFreq();
...
ccr1 =  ((ftimer+625000) / 1250000);     // 0.8µs
That calculation was a slight modification of what the Adafruit library does, but I have to admit it is horrible to understand! There must be a better way.
julian_lpp
Posts: 25
Joined: Tue Dec 24, 2024 4:32 pm

Re: PWM and code in the main loop

Post by julian_lpp »

oscar,
I've done a lot and lot of tests without any signigicant progress, but i'm using a tft display to get the debug messages so I've to clean the code to post it here to let someone (like you've been doing) can keep an eye of it. Something very wrong i'm doing that something almost simple as sending a train of pulses to a pin cant be acomplished as expected, so as soon as I manage to clean up the code i'll post it. Really besides all, i think is important to get a working code for all the users that'd like to control neopixels through timer+dma.

regards
julian
julian_lpp
Posts: 25
Joined: Tue Dec 24, 2024 4:32 pm

Re: PWM and code in the main loop

Post by julian_lpp »

ozcar wrote: Mon Mar 17, 2025 8:46 am Is ES_RGBW equal to 1 supposed to mean that you do have RGBW LEDs, or maybe that you do not have RGBW?
yes, ES_RGBW == 1 means "the neopixels have White led"
ozcar
Posts: 176
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: PWM and code in the main loop

Post by ozcar »

julian_lpp wrote: Tue Mar 18, 2025 5:15 am
ozcar wrote: Mon Mar 17, 2025 8:46 am Is ES_RGBW equal to 1 supposed to mean that you do have RGBW LEDs, or maybe that you do not have RGBW?
yes, ES_RGBW == 1 means "the neopixels have White led"
So, did you fix this then?

Code: Select all

#if ES_RGBW == 1
    #define BITS_PER_LED 24
#else
    #define BITS_PER_LED 32
#endif  
ag123
Posts: 1898
Joined: Thu Dec 19, 2019 5:30 am
Answers: 30

Re: PWM and code in the main loop

Post by ag123 »

julian_lpp wrote: Tue Mar 18, 2025 5:13 am oscar,
I've done a lot and lot of tests without any signigicant progress, but i'm using a tft display to get the debug messages so I've to clean the code to post it here to let someone (like you've been doing) can keep an eye of it. Something very wrong i'm doing that something almost simple as sending a train of pulses to a pin cant be acomplished as expected, so as soon as I manage to clean up the code i'll post it. Really besides all, i think is important to get a working code for all the users that'd like to control neopixels through timer+dma.

regards
julian
if you have a stm32f401/f411 pill board at hand, you can try using this little logic analyzer
viewtopic.php?t=116
https://github.com/ag88/SumpSTM32F401cc

if you need an app to go with that, there are a few sump clients listed in the readme
https://github.com/ag88/SumpSTM32F401cc ... v-file#use

there are still quite a few shops selling stm32f401 pill boards
https://www.aliexpress.com/w/wholesale-stm32f401.html
otherwise weact has one
https://github.com/WeActStudio/WeActStu ... iSTM32F4x1

hope that could help with troubleshooting etc.
oops, that is made with the 'old' stm32duino (libmaple core)
https://github.com/rogerclarkmelbourne/Arduino_STM32
I've uploaded a bin file under releases at
https://github.com/ag88/SumpSTM32F401cc
just in case you are lazy to compile it.
ozcar
Posts: 176
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: PWM and code in the main loop

Post by ozcar »

It shouldn't be that hard.

I took the code as provided, and made a few changes in line with what I already said.

It was generating pulses, but the whole timing was way out - pulses were like 25ms or 50ms! Maybe the prescaler was wrong, but instead of trying to see exactly what was happening there, I just took the timer setup that I had, and which I knew worked, and used that instead.

It looks OK now, but I did not spend a lot of time checking. The times it gets for the 0 and 1 pulses are about 386ns and 778ns, which is about right for the T0H and T1H values specified (I mean the actual numbers, not the comments, which as I mentioned before, don't quite match the numbers).

I left this in sendBuffer()

Code: Select all

  delayMicroseconds(50);                 // 50µs to be safe
But don't believe the comment there. Even for the 7 LEDs the transmission takes way longer than 50μs, during which time the CPU is free to do anything, including calling sendBuffer() again, and starting another transmission before the last one is complete. In other words, I did not add anything to ensure the reset gap is done. Also remember that when you start a transmission, you should not reuse the pwmBuffer until you know the transmission has ended.

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
#else
    #define BITS_PER_LED 24
#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);
#else
    void setColor(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(1000);

    setColor(0, 0, 255, 0); // Blue
    sendBuffer();
    delay(1000);

    setColor(255, 0, 0, 0); // red
    sendBuffer();
    delay(1000);
}




#if ES_RGBW == 1
    void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w){
        uint8_t leds[LED_COUNT][4];

        // 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;
        }

        // 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() {

    // **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
    }
}
julian_lpp
Posts: 25
Joined: Tue Dec 24, 2024 4:32 pm

Re: PWM and code in the main loop

Post by julian_lpp »

Hey ozcar it works like a charm! :P

I'll carefully be reading line by line as to know where I was wrong with my original attempt. The extra dummy "0" cycles are very apropiate. I had been trying with one extra cycle as a reset pulse as well (with no luck)

Thank you for your time
regards
julian
julian_lpp
Posts: 25
Joined: Tue Dec 24, 2024 4:32 pm

Re: PWM and code in the main loop

Post by julian_lpp »

ozcar wrote: Tue Mar 18, 2025 11:07 am It was generating pulses, but the whole timing was way out - pulses were like 25ms or 50ms! Maybe the prescaler was wrong
Pros and cons of libraries (and not reading carefully the documentation) vs direct registry config, so when I wrote:

timer.setPrescaleFactor(0); // No prescaler (72MHz) ***<<<WRONG

From the api:
void setPrescaleFactor(uint32_t prescaler); // set prescaler register (which is factor value - 1)

So it must've been
timer->setPrescaleFactor(1); :shock:
Post Reply

Return to “General discussion”