Configuring Open Drain IO output type.

Posted on
  • Is there any way to set an IO pin output type to open drain, rather than push-pull?

    I want to connect some PWM outputs to ZXLD1366 Power LED drivers. The ADJ input of these devices can be driven directly by an open collector/open drain PWM source to adjust the LED brightness.

    I guess this question also extends to IO inputs, and setting the optional internal pull-up or pull-down resistors.

  • Well, you should be able to use pinMode: http://www.espruino.com/Reference#l__glo­bal_pinMode

    However this doesn't include support for open drain outputs at the moment (although it can do internal pullups). I've created an issue for it here: https://github.com/espruino/Espruino/iss­ues/225

    If you want it right now, you might find it interesting to use peek32/poke32 to set it up by poking the STM32's peripheral registers though. It's actually pretty easy - look up GPIO registers in the STM32F103 reference (http://www.st.com/web/en/resource/techni­cal/document/reference_manual/CD00171190­.pdf).

    I haven't tried this, but something like it should work:

    digitalWrite(A0,0); // just set into output mode
    poke32(0x40010800, (peek32(0x40010800) & (~0b1100)) | 0b0100); // mask out existing bits and set open drain
    

    A0 is easy because it's the bottom 4 bits of port A :)

  • Excellent, thanks!
    I'll try that when I get home this evening.

  • It almost works...
    This code:

    console.log("Peek 0x40010800:", peek32(0x40010800));
    digitalWrite(A0,0); // just set into output mode
    console.log("Peek 0x40010800:", peek32(0x40010800));
    poke32(0x40010800, (peek32(0x40010800) & (~0b1100)) | 0b1100); // mask out existing bits and set open drain
    console.log("Peek 0x40010800:", peek32(0x40010800));
    analogWrite(A0, 0.5, { freq : 100 } ); // Set PWM 50%, 100Hz
    console.log("Peek 0x40010800:", peek32(0x40010800));
    poke32(0x40010800, (peek32(0x40010800) & (~0b1100)) | 0b1100); // mask out existing bits and set open drain again
    console.log("Peek 0x40010800:", peek32(0x40010800));
    

    Gives this output:

    Peek 0x40010800: 7
    Peek 0x40010800: 3
    Peek 0x40010800: 15
    Peek 0x40010800: 11
    Peek 0x40010800: 15
    
    1. Setting the config bits CNF1:0 to 0b01 sets the output to open-drain
      ok.
    2. The analogWrite to set the PWM value then sets them back to 0b10 (alternate function output push-pull).
    3. Writing CNF1:0 to 0b11 then sets output alternate function open-drain output, and I see open-drain PWM output.

    I think the analogWrite clearing the output type might be down to jshPinSetFunction() where it does:

    bool remap = (func&JSH_MASK_AF)!=JSH_AF0;
      if ((func&JSH_MASK_TYPE)==JSH_TIMER1)       GPIO_PinRemapConfig( GPIO_FullRemap_TIM1, remap );
      else if ((func&JSH_MASK_TYPE)==JSH_TIMER2)  GPIO_PinRemapConfig( GPIO_FullRemap_TIM2, remap );
      else if ((func&JSH_MASK_TYPE)==JSH_TIMER3)  GPIO_PinRemapConfig( GPIO_FullRemap_TIM3, remap );
    ...
    
  • In the previous post on Line 4 of the first code example, I originally set the output to 0b0100 not 0b1100.
    I cut and pasted the wrong piece of code for the output that I pasted...

  • Having a bad 5 minutes...

    Please ignore previous post.
    In post 3 above on Line 4 of the first code example, I originally set the output to 0b0100 not 0b1100 which was your original suggestion. Same result as far as PWM open-drain output is concerned.

  • So it works, but every time you say analogWrite you have to reset it?

    You could try pinMode(A0, "output") first, which should at least tell Espruino that you're setting the pin mode manually and not to touch it. analogWrite may just ignore that though...

  • Yes it works but analogWrite() function is overwriting the output type (CNF0 which defines push-pull or open-drain) back to push-pull each time.
    It is also setting CNF1 to change from general purpose output to Alternate function output, which I assume is to attach the timer to the output port.

    Using pinMode() to set pin A0 to output mode first, the code below shows this behaviour:

    pinMode(A0, "output");
    console.log("Peek 0x40010800:", peek32(0x40010800));
    poke32(0x40010800, (peek32(0x40010800) & (~0b1100)) | 0b0100); // mask out existing bits and set open drain
    console.log("Peek 0x40010800:", peek32(0x40010800));
    analogWrite(A0, 0.5, { freq : 100 } );
    console.log("Peek 0x40010800:", peek32(0x40010800));
    

    Output, comments added manually:

    Peek 0x40010800: 3   // 0b0011 - pinMode(...) sets push-pull, normal pin function
    Peek 0x40010800: 7   // 0b0111 - poke32(...) Set to open drain
    Peek 0x40010800: 11  // 0b1011 - analogWrite(...) sets back to push-pull output, alternate pin function
    
  • Thanks. Ok, so if you want to keep changing the PWM value you'll have to wait until I get around to adding output_opendrain and changing analogWrite.

    If you really can't wait, you can poke into the relevant timer's duty cycle register :) Either that or if high frequency is not vital you can use digitalPulse to implement software PWM.

  • Thanks Gordon.

    I can work around the problem for now by poking the output config back to open-drain each time I change the PWM as I'm not doing it often at the moment and the short push-pull spike can be filtered out.

    I'm really impressed with how easy the board and SW is to use. Great for prototyping and testing various hardware ideas out.

    David

  • Just to say that I've just fixed this: https://github.com/espruino/Espruino/iss­ues/234

  • ok, thanks.
    I'll test it when the next firmware level is available.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Configuring Open Drain IO output type.

Posted by Avatar for dprime @dprime

Actions