Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Jakub_Szubzda
Posts: 10
Joined: Wed Jun 10, 2020 4:10 pm

Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by Jakub_Szubzda »

So, I've been trying to use pwm pin of STM32F103C8T6 board (bluepill) to drive WS2812b RGB ring, but i'm not such a pro to achive that. Firstly I tried to use this: https://github.com/adafruit/Adafruit_NeoPixel library, but it seems to work only on arduino board. Then I tried this: https://github.com/rogerclarkmelbourne/ ... 2_Libmaple , but it uses SPI1 port, which i'm using to drive NRF24L01 module, and i think that it's impossible to drive them both like I2C bus devices. I though about using second SPI line which STM32F103C8T6 provides, like in this post: https://github.com/rogerclarkmelbourne/ ... 2/pull/536 , but again this doesn't seem to be possible to change default SPI1 line in https://github.com/rogerclarkmelbourne/ ... 2_Libmaple library. I also tried to set PWM line to 800kHz like :
HardwareTimer timer(1);
void setup() {
pinMode(PA10, PWM);
timer.setPrescaleFactor(1);
timer.setOverflow(90);

}

void loop() {
pwmWrite(PA10, 90);

}, which also didn't work.

So what would be really helpful is: help me to drive it somehow with PWM signal, use second SPI line to drive WS2812b Neopixels, use one SPI line to drive NRF24L01 and WS2812b Neopixels. I hope for your help ;)
User avatar
fpiSTM
Posts: 1738
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 91
Location: Le Mans
Contact:

Re: Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by fpiSTM »

Hi @Jakub_Szubzda
First, avoid to create 4 times the same topic!

About the Adafruit_Neopixel it works with the STM32 core. I don't know for LibMaple core.
Jakub_Szubzda
Posts: 10
Joined: Wed Jun 10, 2020 4:10 pm

Re: Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by Jakub_Szubzda »

Thank you for your answer!
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by stevestrong »

SPI2 supports only 18MHz, while SPI1 supports 36MHz.
You can use SPI2 instead of SPI1 if you use

Code: Select all

SPI.setModule(x);
before the calls of NRF and WS library functions.

Example:

Code: Select all

SPI.setModule(1);
// call here functions related to NRF module using SPI1
...
SPI.setModule(2);
// call here functions related to WS using SPI2
The correct handling of timers requires this sequence:

Code: Select all

timer.pause(); // stop the timer - this allows to set timer parameters
pinMode(PA10, PWM);
timer.set...
...
timer.resume(); // this will start the timer
Jakub_Szubzda
Posts: 10
Joined: Wed Jun 10, 2020 4:10 pm

Re: Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by Jakub_Szubzda »

stevestrong wrote: Thu Jun 11, 2020 9:40 am SPI.setModule(1);
// call here functions related to NRF module using SPI1
...
SPI.setModule(2);
// call here functions related to WS using SPI2
I tried this, but all it does is lights leds full white and nothing more, some suggestions?
Here is code i use:

Code: Select all

#include <WS2812B.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define NUM_LEDS 16
RF24 radio(PB0, PA4);
const uint64_t address = 0xF0F0F0F0E1LL;
/*
 * Note. Library uses SPI1
 * Connect the WS2812B data input to MOSI on your board.
 * 
 */
WS2812B strip = WS2812B(NUM_LEDS);
// Note. Gamma is not really supported in the library, its only included as some functions used in this example require Gamma
uint8_t LEDGamma[] = {
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,
    2,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,
    5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9, 10,
   10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
   17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
   25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
   37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
   51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
   69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
   90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
  115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
  144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
  177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };

void setup() 
{
  /*Serial.begin(115200);
  SPI.setModule(1);
  radio.begin();                  //Starting the Wireless communication
radio.openWritingPipe(address); //Setting the address where we will send the data
radio.setPALevel(RF24_PA_MIN);  //You can set it as minimum or maximum depending on the distance between the transmitter and receiver.
radio.stopListening();          //This sets the module as transmitter
Serial.println("Successfully opened");
Serial.println("Set as transmitter");*/
  SPI.setModule(2);
  strip.begin();// Sets up the SPI
  strip.show();// Clears the strip, as by default the strip data is set to all LED's off.
  strip.setBrightness(35);
}

void loop() 
{ 
  /*const char text[] = "Your Radio is working";
radio.write(&text, sizeof(text));                  //Sending the message to receiver 
Serial.println("Sent");*/
pulseWhite(10);
}
void pulseWhite(uint8_t wait) {
  for(int j = 0; j < 256 ; j++){
      for(uint16_t i=0; i<strip.numPixels(); i++) {
          strip.setPixelColor(i, strip.Color(LEDGamma[j],LEDGamma[j],LEDGamma[j] ) );
        }
        delay(wait);
        strip.show();
      }

  for(int j = 255; j >= 0 ; j--){
      for(uint16_t i=0; i<strip.numPixels(); i++) {
          strip.setPixelColor(i, strip.Color(LEDGamma[j],LEDGamma[j],LEDGamma[j] ) );
        }
        delay(wait);
        strip.show();
      }
}
ozcar
Posts: 143
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by ozcar »

So, you are still using Roger's core and WS2812B library?

I lifted up your code and tried it - I commented out the nRF24L01 and RF24 because I don't have those.

When I run it, I have no LED string connected, but I can see output on SPI2 - PB15. However, the timing is not as expected. It is in the 1/3 2/3 ratio as expected, but the "0" pulse is just under 900ns and the "1`" pulse just under 1800ns. An 800kHz LED might interpret both of those as "1" so might explain why you get all white.

I ripped out the " SPI.setModule(2) " and now there is output on SPI1 - PA7, but the timing is different: "0" pulse around the expected 440ns, and the "1" pulse double that. I suspect the WS2812B library is maybe setting the "baud rate" divider in the wrong SPI when you try to use SPI2. Maybe that would be easy to fix, I did not try to find it.

Otherwise, I did not see that you had some reason to not try the ST core and the Adafruit library for the LEDs?
Jakub_Szubzda
Posts: 10
Joined: Wed Jun 10, 2020 4:10 pm

Re: Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by Jakub_Szubzda »

ozcar wrote: Sat Jun 13, 2020 7:17 am So, you are still using Roger's core and WS2812B library?
Yes i'm using Roger's library, because when i try to use adafruit neopixels library it just doesn't work.
ozcar wrote: Sat Jun 13, 2020 7:17 am When I run it, I have no LED string connected, but I can see output on SPI2 - PB15. However, the timing is not as expected. It is in the 1/3 2/3 ratio as expected, but the "0" pulse is just under 900ns and the "1`" pulse just under 1800ns. An 800kHz LED might interpret both of those as "1" so might explain why you get all white.
It makes sense, and i think that this part of WS2812B library sets some kind of divider
// Set the SPI clock divisor to as close to 400ns as we can;
// the WS2812 spec allows for +/- 150ns
#if F_CPU == 72000000L
#define WS2812B_SPI_DIVISOR SPI_CLOCK_DIV32 // 444ns
#elif F_CPU == 64000000L
#define WS2812B_SPI_DIVISOR SPI_CLOCK_DIV32 // 500ns
#elif F_CPU == 48000000L
#define WS2812B_SPI_DIVISOR SPI_CLOCK_DIV16 // 333ns
#elif F_CPU == 40000000L
#define WS2812B_SPI_DIVISOR SPI_CLOCK_DIV16 // 400ns
#elif F_CPU == 36000000L
#define WS2812B_SPI_DIVISOR SPI_CLOCK_DIV16 // 444ns
#elif F_CPU == 24000000L
#define WS2812B_SPI_DIVISOR SPI_CLOCK_DIV8 // 333ns
#elif F_CPU == 16000000L
#define WS2812B_SPI_DIVISOR SPI_CLOCK_DIV8 // 500ns
#elif F_CPU == 8000000L
#define WS2812B_SPI_DIVISOR SPI_CLOCK_DIV4 // 500ns
#else
#error No clock divisor available for this F_CPU
#endif
but i don't know how to modify it, and in what "if", to achive around 400ns, so maby you have some advice how to do that?
ozcar
Posts: 143
Joined: Wed Apr 29, 2020 9:07 pm
Answers: 5

Re: Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by ozcar »

I found I was using the WS2812B library included with the core, instead of WS2812B_STM32_Libmaple-master, and it turns out they are not the same (your guess is better than mine why). The version in the core does not have those #defines depending F_CPU , it just assumes F_CPU is 72MHz, but since my F_CPU is 72MHz, that make no difference, so I still get the same, SPI1 works, but with SPI2 the pulses are twice the length that they should be.

So, looking at it, I think the problem is not that it is setting the rate in the wrong SPI (my wild guess yesterday), but just setting the wrong rate. That means if you could swap your usage of the two SPIs, it would probably be OK.

Otherwise, assuming your F_CPU is 72Mhz, another circumvention is to change the #define to:

Code: Select all

#if F_CPU == 72000000L
  #define WS2812B_SPI_DIVISOR SPI_CLOCK_DIV16 // 444ns  
...
That should make it work on SPI2 (but will cause trouble for WS2812B on SPI1, but OK for you).

I believe the problem arises because SPI1 is on APB2, which is running at same frequency as F_CPU, while SPI2 is on APB1, running at F_CPU/2. But I'm no expert on how the core hangs together, so I'll leave it to somebody else to figure the best way to properly fix this.

What problem did you have with the ST core and the Adafruit library? fpiSTM said it works, but I have not tried it.
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by stevestrong »

As I mentioned above, on SP2 you can only get max 18MHz, that is why the pulses are twice as long.
Jakub_Szubzda
Posts: 10
Joined: Wed Jun 10, 2020 4:10 pm

Re: Using STM32F103C8T6 PWM pin to drive ws2812b Neopixel Ring in arduino IDE

Post by Jakub_Szubzda »

ozcar wrote: Sat Jun 13, 2020 7:24 pm What problem did you have with the ST core and the Adafruit library? fpiSTM said it works, but I have not tried it.
I don't really know what's wrong with it, but no matter what i try to do it just doesn seem to light leds.
stevestrong wrote: Mon Jun 15, 2020 6:53 am As I mentioned above, on SP2 you can only get max 18MHz, that is why the pulses are twice as long.
Can you propose some solution? :)
Post Reply

Return to “STM32F1 based boards”