Driving LED dim/glow using PWM pins and MOSFET

Generic boards that are not Maple or Maple mini clones, and don't contain the additional USB reset hardware
Posts: 2043
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Driving LED dim/glow using PWM pins and MOSFET

Post by stevestrong » Tue Dec 05, 2017 9:48 am

Have you measured the voltage on the output pins for full brightness?

Posts: 53
Joined: Sun Sep 27, 2015 3:26 am
Location: Kuala Lumpur, Malaysia

Re: Driving LED dim/glow using PWM pins and MOSFET

Post by stanleyseow » Tue Dec 05, 2017 3:36 pm

Good idea, will plug into power supply into MOSFET and slowly increase the voltage ...

I turns on at 3.1V and turn off at 0.9V as per my power supply voltage readings ..

Posts: 15
Joined: Sun Aug 20, 2017 4:35 pm

Re: Driving LED dim/glow using PWM pins and MOSFET

Post by KHODIDAS11 » Sat Feb 03, 2018 12:17 pm

please refer datasheets of both mosfets. I think you need to increase gate voltage for turning, this can be observed through your experiment. as arduino uno 5voutput is 5v pulses from pwm and is fed to mosfet gate. while your stm32 runs at 3.3v so you have gpio pulses at 3.3v.

Also as per theory, if you increase gate voltage, then mosfet internal resistance decreases and current flows more.

User avatar
Posts: 1729
Joined: Sat Mar 26, 2016 3:26 pm
Location: Rapa Nui

Re: Driving LED dim/glow using PWM pins and MOSFET

Post by Pito » Sat Feb 03, 2018 1:04 pm

You need mosfets with 1.0-1.5V threshold (called "logic level" - you have to double check the Vgs(th) in the datasheet). Mind the threshold is not sharp, it is rather a knee (the threshold voltage does not mean the mosfet is fully "on" running the full Ids current). For example Vgs(th) of the BSS223pw (p-channel low power) is typically 0.9V measured at Ids=1.5uA current (datasheet).
Also mind in order to achieve high switching speed you must drive the mosfet with rather higher peak current because of the gate and miller capacitance (based on mosfet type 40pF - 15nF).
When using pwm via the X channels you do not use analogWrite() - see for example viewtopic.php?f=19&t=3037&start=10#p39347
Pukao Hats Cleaning Services Ltd.

Posts: 228
Joined: Wed May 11, 2016 4:29 pm

Re: Driving LED dim/glow using PWM pins and MOSFET

Post by dannyf » Sun Feb 11, 2018 12:47 am

As there are only 4 timers on he F103C, you will only be able to output 4 unique PWM values
The chip itself is much more powerful than that. I don't have a datasheet handy but tim1..5 eagh has 4 channels. And others with 1 to 2 channels as well. Tim6 and 7 no channels.

So take a look at the datasheet. I haven't checked the stm32arduino code but I imagine it supports, or can be made to support, all of that.

Posts: 14
Joined: Wed Jan 24, 2018 8:57 am

Re: Driving LED dim/glow using PWM pins and MOSFET

Post by Wolfie » Mon Feb 12, 2018 9:51 am

Since I've been looking at the STM32F4 I've been ignoring this forum. But it happens that a few weeks ago I did exactly what you're looking for.

Originally I had created some Christmas fish bowls that had parallel led strings that were scaling up and down in brightness controlled by a pro-mini, there are six strings each controlled by one of the PWM-capable pins.


Each string's current can rise to ~150mA and so the controlling pro-mini pin went to the gate of a mosfet (2N7000 200mA max or a BS170 500mA max).

Naturally, I wanted to progress to more strings, and I really like the STM32F103 so that was the obvious next step, but with the following considerations:
1. Can the 3.3V signal effectively control the mosfet rather than the pro-mini 5V signal?

2. Looking at the various pinout images available on the internet, there is some inconsistency about those STM32F103 pins that are PWM capable, and how many there actually are. Can we create a definitive list?

3. I prefer to program and debug the STMF103 using serial, but I note that pins A9 & A10 are denoted as PWM pins for timer 1. Can I still use serial to program and debug without losing two PWM pins?

I am happy to say that, after a while investigating and experimenting, the answers are yes, yes, and, yes.

The mosfets work quite happily with a 3.3V signal.

There are four timers on the STM32F103, each of which has four PWM channels, the default pin allocations are:
TIM1 PA8, PA9, PA10, PA11
TIM2 PA0, PA1, PA2, PA3
TIM3 PA6, PA7, PB0, PB1
TIM4 PB6, PB7, PB8, PB9

Basically, with each time you effectively set the PWM frequency (via the preset and ARR auto-reload register) for its four channels, and then set the duty cycle for each channel. An example later.

Having a look at the STMCube thingy, it shows that, for timer 1, the pins can be reassigned. This is great news for A9 & A10, all we have to do is figure out how to do it in the Arduino IDE. After looking in timer.h (thanks to the guys here for providing this) and correlating this with the STMCube options, much to my surprise, the first thing I tried worked. Very lucky!

Pins A9 & A10 can be redirected to PB14 & PB15 using:

Here's my TEST setup (obviously I have leds in place of the led strings, but each is controlled by the mosfet, which in turn is controlled by the PWM signal from the STM32F103 pin):


You can see that serial UART1 is connected via A9 & A10. The cable colour corresponds to the timer TIM1=blue, TIM2=grey, TIM3=yellow, TIM4=white.

The colour of the led indicates the channel: red=1, yellow=2, green=3, blue=4.

(NOTE: in a "production" environment I would have a 10k ohm pull-down resistor between the mosfet gate and ground, and a ~470 ohm resistor between the gate and the STM32 pin, both are omitted here. The former keeps the leds off for the small time period when the pins are floating between the power turning on and the MCU starting. Another (better) solution would be to have separate power supplies to the STM32 and the leds, the latter only turning on after the STM is initialised. The 470 ohm resistor is not needed if everythng is working happily, but in the event of a problem at the mosfet / led end, it prevents any undesirable voltage / current getting back to the STM.)

The code (NOTE: when I wrote this I was quite literally "messing about with timers" I did not expect anyone else to see it, and it was simply knocked together to check that each of the sixteen channels could be independently controlled):

Code: Select all

struct pin_struct {
  uint8_t id;
  bool active;
  uint8_t timer;
  uint8_t channel;
  uint16_t intensity;
  int8_t increment;

pin_struct pins[16];

void setup() {
  Serial.begin(115200); // PB10 TX, PB11 RX

  TIMER1_BASE->PSC = 72;
  TIMER1_BASE->ARR = 1000;

  TIMER2_BASE->PSC = 72;
  TIMER2_BASE->ARR = 2000;

  TIMER3_BASE->PSC = 72;
  TIMER3_BASE->ARR = 3000;

  TIMER4_BASE->PSC = 72;
  TIMER4_BASE->ARR = 4000;

  pins[0] = (pin_struct){PA8, true, 1, 1, 0, 1};
  pins[1] = (pin_struct){PB14, true, 1, 2, 500, 1};
  pins[2] = (pin_struct){PB15, true, 1, 3, 1000, -1};
  pins[3] = (pin_struct){PA11, true, 1, 4, 500, -1};
  pins[4] = (pin_struct){PA6, true, 3, 1, 0, 1};
  pins[5] = (pin_struct){PA7, true, 3, 2, 1500, 1};
  pins[6] = (pin_struct){PB0, true, 3, 3, 3000, -1};
  pins[7] = (pin_struct){PB1, true, 3, 4, 1500, -1};
  pins[8] = (pin_struct){PB6, true, 4, 1, 0, 1};
  pins[9] = (pin_struct){PB7, true, 4, 2, 2000, 1};
  pins[10] = (pin_struct){PB8, true, 4, 3, 4000, -1};
  pins[11] = (pin_struct){PB9, true, 4, 4, 2000, -1};
  pins[12] = (pin_struct){PA0, true, 2, 1, 0, 1};
  pins[13] = (pin_struct){PA1, true, 2, 2, 1000, 1};
  pins[14] = (pin_struct){PA2, true, 2, 3, 2000, -1};
  pins[15] = (pin_struct){PA4, true, 2, 4, 1000, -1};
  for (uint8_t i=0; i<16; i++) pinMode(pins[i].id, PWM);

void loop() {
  timer_gen_reg_map* timer_base;
  uint32_t limit;

  for (uint8_t i=0; i<16; i++) {

    if (pins[i].timer==1) {
      limit = TIMER1_BASE->ARR;
    else {
      switch (pins[i].timer) {
        case 2: timer_base = TIMER2_BASE; break;
        case 3: timer_base = TIMER3_BASE; break;
        case 4: timer_base = TIMER4_BASE; break;
      limit = timer_base->ARR;

    pins[i].intensity += pins[i].increment;
    if (pins[i].intensity+pins[i].increment<0 || pins[i].intensity+pins[i].increment>limit) pins[i].increment = -pins[i].increment;

    if (pins[i].timer==1) {
      switch (pins[i].channel) {
        case 1: TIMER1_BASE->CCR1 = pins[i].intensity;; break;
        case 2: TIMER1_BASE->CCR2 = pins[i].intensity;; break;
        case 3: TIMER1_BASE->CCR3 = pins[i].intensity;; break;
        case 4: TIMER1_BASE->CCR4 = pins[i].intensity;; break;
    else {
      switch (pins[i].channel) {
        case 1: timer_base->CCR1 = pins[i].intensity;; break;
        case 2: timer_base->CCR2 = pins[i].intensity;; break;
        case 3: timer_base->CCR3 = pins[i].intensity;; break;
        case 4: timer_base->CCR4 = pins[i].intensity;; break;

Hopefully this is helpful and I've remembered everything. If anything seems dodgy please question it, I've probably mis-typed something, I'm watching the Winter Olympics and probably paying less attention than I should to this post!

Post Reply