rogerclarkmelbourne / Arduino_STM32

Arduino STM32. Hardware files to support STM32 boards, on Arduino IDE 1.8.x including LeafLabs Maple and other generic STM32F103 boards
Other
2.53k stars 1.26k forks source link

PWM_OPEN_DRAIN not open #842

Closed suuuehgi closed 3 years ago

suuuehgi commented 3 years ago

Hey,

I connected 5 V -> 1K -> Pin PB9 on an STM32F103C8T6 with the following code

const int pin_pwm = PB9;
pinMode(pin_pwm, PWM_OPEN_DRAIN);
pwmWrite(pin_pwm, 20000);

and I get a PWM signal at the pin of slightly above 3.3 V, not 5 V. So open is not really open but rather around 2 K to GND. :/

stevstrong commented 3 years ago

How do you measure the output voltage? With a volt-meter? You should check it with an oscilloscope. With the volt-meter you measure the average voltage of an alternative signal dependent on the PWM duty cycle.

suuuehgi commented 3 years ago

I used an Oscilloscope.

stevstrong commented 3 years ago

I just checked your code in a sketch for a bluepill board with PB9 having 10k to 5V. Sorry, but I cannot reproduce your problem, in my setup the output is a PWM with high level at 5 V. So I suggest to check your HW setup.

suuuehgi commented 3 years ago

Thank you very much for the investigation so far! Just in case I miss something obvious, here's the complete minimal setup:

5 V ---> 1K ---> Pin PB9
                   |
GND --------- Oscilloscope

setup

MWE

const int pin_pwm = PB9;

void setup() {
    pinMode(pin_pwm, PWM_OPEN_DRAIN);
    pwmWrite(pin_pwm, 20000);
}

void loop() {
}

The resulting signal. pwm

The signal with the connection to PB9 pulled. pwm_pulled

stevstrong commented 3 years ago

I tested with exactly the same HW and SW setup, and here is my plot:

PWM

Try with another resistor and/or board.

suuuehgi commented 3 years ago

Same on another board from the same order. I have one last from the other batch I'm gonna test now ...

suuuehgi commented 3 years ago

The one from the old batch gives 5 V PWM. 😒

I guess they messed up the open drain functionality within the μC. 😒 😒

JiriBilek commented 3 years ago

Just curious, are you sure your CPU is a genuine one? There is a lot of clones/counterfeits on the market.

suuuehgi commented 3 years ago

Just curious, are you sure your CPU is a genuine one? There is a lot of clones/counterfeits on the market.

Well, I don't know. For the sake of ST, I guess it's fake.

stm32

Tested now pin PB6-9 + PA8-10. 3.3 V PWM on all of them.

JiriBilek commented 3 years ago

The CPU in the picture looks good.

suuuehgi commented 3 years ago

I think I can close this as it seems to be μC-related. Thank you both for your efforts!

suuuehgi commented 3 years ago

I got it now working in software toggling between pinMode OUTPUT, LOW and INPUT HIGH and LOW with OUTPUT_OPEN_DRAIN. It appears as just PWM_OPEN_DRAIN is broken.

stevstrong commented 3 years ago

Can you please share how exactly you did it?

suuuehgi commented 3 years ago

Can you please share how exactly you did it?

Yeah, sure!

#define PWM_PERIOD 313    // in microseconds / 1.6 --> ~ 2000 Hz

const int pin_pwm = PB9;

HardwareTimer timer(1);

void setup() {

    pinMode(pin_pwm, OUTPUT_OPEN_DRAIN);

    timer.pause();
    timer.setPeriod(PWM_PERIOD);
    timer.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE);

    // Fire interrupt at 50 % of the counter
    timer.setCompare(TIMER_CH1, timer.getOverflow()/2);
    timer.attachInterrupt(TIMER_CH1, handler_pwm_high);

    // Channel 0 is for overflow interrupt (update interrupt)
    timer.attachInterrupt(0, handler_pwm_low);

    timer.refresh();
    timer.resume();

}

void loop() {
}

void handler_pwm_high(void) {
    digitalWrite(pin_pwm, HIGH);
}
void handler_pwm_low(void) {
    digitalWrite(pin_pwm, LOW);
}

Some additional notes:


Edit Prior, I set the register during setup once using digitalWrite(pin_pwm, LOW) and handler_pwm_X toggled the mode via pinMode(pin_pwm, INPUT) / pinMode(pin_pwm, OUTPUT).

stevstrong commented 3 years ago

I suppose that this workaround you apply only for the "fake" MCU.

This code generates the correct timing and level:

#define PWM_PIN     PB9 // Timer4 Ch 4
#define PWM_PERIOD  500 // in µs, 2000 HZ

#define myTimer     Timer4 // corresponding to PB9
#define myTimerChannel  TIMER_CH4 // corresponding to PB9

void setup()
{
    pinMode(PWM_PIN, PWM_OPEN_DRAIN);

    myTimer.pause();
    myTimer.setPeriod(PWM_PERIOD);
    myTimer.setCompare(myTimerChannel, myTimer.getOverflow()/2); // set duty cycle
    myTimer.refresh();
    myTimer.resume();
}

void loop() {
  // put your main code here, to run repeatedly:
}

If the PWM period or level is not correct, it can be a sign for a fake MCU, or defective MCU/board.

suuuehgi commented 3 years ago

I suppose that this workaround you apply only for the "fake" MCU.

Yes, certainly. Otherwise I wouldn't have to use interrupts and could just let it run "in hardware".

Thank you for the double check and the hint that this might be another deviation from the original. 😒

It might be coincidence ... but I noticed that 74 MHz / 45 MHz is about 1.6. Might it be that the μC runs at 45 MHz?

stevstrong commented 3 years ago

Not sure about the deviation, but normally it runs with 72MHz. Maybe 72/48? That would be 1.5. Have you checked the onboard crystal?

suuuehgi commented 3 years ago

Ah, right, I mistook it with 48 MHz. Then it's probably meaningless. The onboard crystal is the std 8 MHz.