Pins that don't play well together

Posted on
  • I have run into several issues that are caused by pins fighting with each-other over peripherals - primarily an issue with PWM. I found that sometimes, a PWM pin would be impacted when another pin was set to use PWM. Once pins are in a misbehaving state, a hard reset is needed to bring them back to expected behavior.
    See github issue #142 and related issues.

    I made a little board with some LEDs on it to test this with ( board pic - in chrome, this pic displays wrong when first loaded; zooming in+out will fix http://drazzy.com/espruino/20140417_0452­54.jpg ), and so far I've found the following; all issues relate to PWM unless stated otherwise.

    C8 and B0 are mutually exclusive. Setting one turns the other off.
    A6 and C6 are mutually exclusive. Setting one turns the other off.
    C7 is linked to B0 (unpredictable behavior)
    A7 is linked to C6 (unpredictable behavior)
    B1 is linked to C8 (unpredictable behavior)
    A9 is always broken - setting it with analogWrite() turns it on 100% of the time.
    A8 is linked to B13, and kills SPI2 (B13 goes high), preventing SD card access

    A0, A1, A2, A3, C6, C7, C8, C9, and B3 through B11 all appear to be usable at the same time without issue (most combinations tested - my board only had 10 LEDs though, so I was switching back and forth - it's possible I didn't hit all combos).

    I did not find any cases of other SPI interfaces having pins pulled into the wrong state as a result of PWM on a different pin. I did try to make sure the SPI interfaces were associated with the pins by sending some dummy data down it with send4bit()) - it's actually kinda cool to see leds blink when data goes down a line :-P

    Has anyone run into any other combinations of pins/functions that don't play well together? I think it would be useful to have a list of these somewhere (ideally, imo, it should be on the Espruino Board reference page, right under the lovely pinout diagram), so we can plan around them.

  • Thanks - was it you that also reported this bug? I get confused when the usernames are different ;)

    It would be good to report this under 'Known Problems' in http://www.espruino.com/EspruinoBoard

    As a rough rule of thumb, if you hover over the PWM tag in the reference page, it'll give you something like TIMx_CHy. Espruino currently chooses the first one of these (there's a bug open to make it better at choosing).

    This means:

    • If you specify a frequency for TIMx it'll apply to all PWM channels using TIMx
    • If you use exactly the same TIMx_CHy somewhere else then you'll have problems
    • Some pins say TIMx_CHyN - this means you get a negated version of the signal that was on TIMx_CHy.
    • There are other nasties - for instance TIMx can be on one set of pins (usually having numbering near to each other) OR another set, but it can't be spread over two sets - so using TIM1_CH1 on one set will mean that you can't use TIM1_CH2 on the other.

    Hope that helps - inside the chip, the arrangement of pins is pretty complex (with a whole bunch of silicon erratas to go with it!). Espruino tries its best to hide it, but occasionally it shows through!

  • This is great, thanks for sharing!

    +1 for putting it where the pinout diagram is. I would say it's not a big deal at all to have problems like that, as long as you know beforehand and can work around it.

  • Yeah, that's me. I figured I should use my real name for GitHub, but I don't really want to use it as my forum name. Too much seeing my own name everywhere (plus, I'm used to people I know online calling me Azzy).

    Oh! I wish I'd known about those tooltips on the Espruino pinout diagram.

    Gordon, I can't reconcile the conflicts reported with your statement that the Espruino chooses the first option. For example, A7 and C6 fight - presumably over TIM8_CH1 - but I would have expected A7 to grab TIM1_CH1N...

    Is it necessary that the timers are connected to the pins in order to use SPI? B13~15 (SPI2) doesn't work if you've used the PWM on A8~A10 - but I can't seem to produce an analogous problem with the other sets of pins , which makes me wonder if this has to be this way.

    Also, it seems like some changes to how the Espruino chooses which timer to use would yield considerable improvements in behavior here, assuming there isn't some further complication.

    For example: C6~C9 are the only pins that can use TIM8. So they should always use TIM8 for PWM, and never use TIM3 (which is shared with multiple other banks of pins), and A6~B1 should never use the TIM8_CH*N channels.
    Likewise, A0~A3 are the only pins that can use TIM5 - they should use it, leaving TIM2 totally to B5, B10, B11.

    And that leaves TIM3 for A6,A7,B0,B1

    Now B4, B5 wouldn't have a TIM3 to use, but that's okay, because they're SPI1/3 MOSI and MISO, which will usually be used for an SPI device (usually the network controller), not PWM output.

    B13~15 would also not have their TIM1_CH*N's, but those pins are used for the SD card, which most people would want to keep available.

    If B13~15 can be unhooked from TIM1_CH*N, then that would free up A8, A9, A10 for use as PWM without clobbering SPI2 (and thus the SD card) - though per that github issue, A9 doesn't work out of the box (I've seen that poke to fix it, but I haven't tried it)....

    Thus we'd be left with 19 (-1 if using SPI3/1, -2 if using SPI1) PWM pins that can all work at the same time (22 if A8~10 can be made usable) without clobbering anything important or fighting with eachother, versus the current 15 simultaneously-usable PWM pins.

    Does digitalPulse() use the same hardware timers that PWM does?

    Edit: Sounds like digitalPulse() does...? From my brief reading of that scary tome from STM.

    It seems like a lot of the issues are due to the negations of the PWM being output from TIM1/TIM8 (that was what I had earlier called unpredictable behavior). It looks like it should be possible to turn these off with peek/poke - but peek() doesn't seem to be working for me, so I wasn't able to get any farther (see other thread on that).

  • Aaahahhaaha! This is so cool...

    var fs=require("fs");
    fs.readdir();  //This will work
    analogWrite(A8,0);
    fs.readdir(); //This will not
    poke16(0x40012C20,peek16(0x40012c20)&0xF­BBB); //turn off all the negated outputs on TIM1
    fs.readdir(); // Works again!
    

    Of course, every time you call analogWrite(A8), it gets reset...

    Here we go - PWM on A8~10 without clobbering SPI2. If you enable PWM for A9 or A10, it'll move the USART out of the way, too.

    //pass true for each pin you want to use. 
    
    function PWMsetup(a8,a9,a10) {
      if (a8) {
        analogWrite(A8,0);
      }
      if (a9||a10) {
        poke8(0x40010004,peek8(0x40010004)|4); //move the USART out of the way
      }
      if (a9) {
        analogWrite(A9,0);
      }
      if (a10) {
        analogWrite(A10,0);
      }
      poke16(0x40012C20,peek16(0x40012c20)&0xF­BBB);
    }
    
    function A8PWM(val) {
      poke16(0x40012C34,E.clip(val*65535,0,655­35));
    }
    function A9PWM(val) {
      poke16(0x40012C38,E.clip(val*65535,0,655­35));
    }
    function A10PWM(val) {
      poke16(0x40012C3C,E.clip(val*65535,0,655­35));
    }
    
  • So you're thinking that a simple solution might be to just remove one (or perhaps reorder) of the timer choices from some of the pins? That should be pretty easy to do...

    The code to output the timers is in build_pininfo.py. It's a bit hacky at the moment, but I wonder if there is some heuristic we could use to re-order the timers? For instance - If TIMx_CHy is only available on this pin, use that by default.

    By the way, digitalPulse uses a single timer and otherwise runs everything in software (IIRC it's either TIM4 or TIM7 - I tried to use something that didn't conflict with any of the PWM timers).

  • Well, there are a few issues here

    First off, if B13~15 are listening to alternate device, they will get PWM output if any of the TIM1_CH*N outputs are enabled.

    Same goes for A7,B0,B1 and TIM8_CH*N outputs.

    Fine... except that when the Espruino firmware enables the non-negated output (bits 0, 4, 8), it also enables the negated output for the same channel (bits 2, 6, 10). Why does analogWrite(A8) set bits 0 and 2, instead of just bit 0?

    Bit 2/6/10 in TIM1_CCER should only be set for TIM1 if PWM is desired on B13~B15, not when it is desired on A8~A10.

    Bit 2/6/10 should never be set for TIM8_CCER, because those pins have better timer options - but even if we are going to use the TIM8_CH*N outputs, they should only be set when that output is required, not when the non-negated output is.

    The second issue is the selection of the timers.

    I tried to read build_pininfo.py, but I haven't the faintest what it's doing or how.

    What it is clearly not doing is assigning the first timer on the list to each pin.

    Based on experiment (setting analogWrite() and then peeking at appropriate registers), it looks like the timer used alternates between available timers on adjacent pins, maximizing the potential for conflicts.

    analogWrite(A6) outputs on TIM3_CH1 (great!)
    analogWrite(A7) outputs on TIM8_CH1N (not great!)
    analogWrite(B0) outputs on TIM3_CH3 (great!)
    analogWrite(B1) outputs on TIM8_CH3N (not great!)

    analogWrite(A0) outputs on TIM5_CH1 (great!)
    analogWrite(A1) outputs on TIM2_CH2 (not great!)
    analogWrite(A2) outputs on TIM2_CH3 (not great!)
    analogWrite(A3) outputs on TIM5_CH4 (great!)

    analogWrite(C6) outputs on TIM3_CH1 (bad)
    analogWrite(C7) outputs on TIM8_CH2 (good)
    analogWrite(C8) outputs on TIM3_CH3 (bad)
    analogWrite(C9) outputs on TIM8_CH4 (good)

    This explains the conflicts noted above.
    When C7 starts up TIM8_CH2, that plays ontop of what B0 is getting from TIM3_CH3, because TIM8_CH2N is also enabled.
    (same with the other ones I originally called "linked").

    Meanwhile the "mutually exclusive" ones were due to fighting over which bank to point timer 3 at.

    Finally, analogWrite(A9) or analogWrite(A10) should shove the USART out of the way - but there's already an issue for this.

  • I'm not quite sure I understand what you mean about the extra bits being set? Could you give me an example with analogWrite(A8) and peek?

    The code here appears to be taking the first timer in the list of available timers: https://github.com/espruino/Espruino/blo­b/master/targets/stm32/jshardware.c#L153­2

    And the gen/jspininfo.c file looks like:

    /* PA0  */ { JSH_PORTA, JSH_PIN0, JSH_ANALOG123|JSH_ANALOG_CH0, { JSH_AF0|JSH_TIMER5|JSH_TIMER_CH1, 0, 0, 0 } },
    /* PA1  */ { JSH_PORTA, JSH_PIN1, JSH_ANALOG123|JSH_ANALOG_CH1, { JSH_AF0|JSH_TIMER2|JSH_TIMER_CH2, JSH_AF0|JSH_TIMER5|JSH_TIMER_CH2, 0, 0 } },
    /* PA2  */ { JSH_PORTA, JSH_PIN2, JSH_ANALOG123|JSH_ANALOG_CH2, { JSH_AF0|JSH_TIMER2|JSH_TIMER_CH3, JSH_AF0|JSH_TIMER5|JSH_TIMER_CH3, JSH_AF0|JSH_USART2|JSH_USART_TX, 0 } },
    /* PA3  */ { JSH_PORTA, JSH_PIN3, JSH_ANALOG123|JSH_ANALOG_CH3, { JSH_AF0|JSH_USART2|JSH_USART_RX, JSH_AF0|JSH_TIMER5|JSH_TIMER_CH4, JSH_AF0|JSH_TIMER2|JSH_TIMER_CH4, 0 } },
    /* PA4  */ { JSH_PORTA, JSH_PIN4, JSH_ANALOG12|JSH_ANALOG_CH4, { JSH_AF0|JSH_DAC|JSH_DAC_CH1, 0, 0, 0 } },
    /* PA5  */ { JSH_PORTA, JSH_PIN5, JSH_ANALOG12|JSH_ANALOG_CH5, { JSH_AF0|JSH_DAC|JSH_DAC_CH2, JSH_AF0|JSH_SPI1|JSH_SPI_SCK, 0, 0 } },
    /* PA6  */ { JSH_PORTA, JSH_PIN6, JSH_ANALOG12|JSH_ANALOG_CH6, { JSH_AF0|JSH_TIMER3|JSH_TIMER_CH1, JSH_AF0|JSH_SPI1|JSH_SPI_MISO, 0, 0 } },
    /* PA7  */ { JSH_PORTA, JSH_PIN7, JSH_ANALOG12|JSH_ANALOG_CH7, { JSH_AF0|JSH_TIMER8|JSH_TIMER_CH1|JSH_TIM­ER_NEGATED, JSH_AF0|JSH_TIMER3|JSH_TIMER_CH2, JSH_AF1|JSH_TIMER1|JSH_TIMER_CH1|JSH_TIM­ER_NEGATED, JSH_AF0|JSH_SPI1|JSH_SPI_MOSI } },
    /* PA8  */ { JSH_PORTA, JSH_PIN8, JSH_ANALOG_NONE, { JSH_AF0|JSH_TIMER1|JSH_TIMER_CH1, 0, 0, 0 } },
    /* PA9  */ { JSH_PORTA, JSH_PIN9, JSH_ANALOG_NONE, { JSH_AF0|JSH_USART1|JSH_USART_TX, JSH_AF0|JSH_TIMER1|JSH_TIMER_CH2, 0, 0 } },
    /* PA10 */ { JSH_PORTA, JSH_PIN10, JSH_ANALOG_NONE, { JSH_AF0|JSH_USART1|JSH_USART_RX, JSH_AF0|JSH_TIMER1|JSH_TIMER_CH3, 0, 0 } },
    /* PA11 */ { JSH_PORTA, JSH_PIN11, JSH_ANALOG_NONE, { JSH_AF0|JSH_TIMER1|JSH_TIMER_CH4, 0, 0, 0 } },
    /* PA12 */ { JSH_PORTA, JSH_PIN12, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PA13 */ { JSH_PORTA, JSH_PIN13, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PA14 */ { JSH_PORTA, JSH_PIN14, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PA15 */ { JSH_PORTA, JSH_PIN15, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PB0  */ { JSH_PORTB, JSH_PIN0, JSH_ANALOG12|JSH_ANALOG_CH8, { JSH_AF0|JSH_TIMER3|JSH_TIMER_CH3, JSH_AF1|JSH_TIMER1|JSH_TIMER_CH2|JSH_TIM­ER_NEGATED, JSH_AF0|JSH_TIMER8|JSH_TIMER_CH2|JSH_TIM­ER_NEGATED, 0 } },
    /* PB1  */ { JSH_PORTB, JSH_PIN1, JSH_ANALOG12|JSH_ANALOG_CH9, { JSH_AF0|JSH_TIMER8|JSH_TIMER_CH3|JSH_TIM­ER_NEGATED, JSH_AF1|JSH_TIMER1|JSH_TIMER_CH3|JSH_TIM­ER_NEGATED, JSH_AF0|JSH_TIMER3|JSH_TIMER_CH4, 0 } },
    /* PB2  */ { JSH_PORTB, JSH_PIN2, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PB3  */ { JSH_PORTB, JSH_PIN3, JSH_ANALOG_NONE, { JSH_AF1|JSH_TIMER2|JSH_TIMER_CH2, JSH_AF1|JSH_SPI1|JSH_SPI_SCK, JSH_AF0|JSH_SPI3|JSH_SPI_SCK, 0 } },
    /* PB4  */ { JSH_PORTB, JSH_PIN4, JSH_ANALOG_NONE, { JSH_AF1|JSH_TIMER3|JSH_TIMER_CH1, JSH_AF0|JSH_SPI3|JSH_SPI_MISO, JSH_AF1|JSH_SPI1|JSH_SPI_MISO, 0 } },
    /* PB5  */ { JSH_PORTB, JSH_PIN5, JSH_ANALOG_NONE, { JSH_AF0|JSH_SPI3|JSH_SPI_MOSI, JSH_AF1|JSH_SPI1|JSH_SPI_MOSI, JSH_AF1|JSH_TIMER3|JSH_TIMER_CH2, 0 } },
    /* PB6  */ { JSH_PORTB, JSH_PIN6, JSH_ANALOG_NONE, { JSH_AF0|JSH_TIMER4|JSH_TIMER_CH1, JSH_AF1|JSH_USART1|JSH_USART_TX, JSH_AF0|JSH_I2C1|JSH_I2C_SCL, 0 } },
    /* PB7  */ { JSH_PORTB, JSH_PIN7, JSH_ANALOG_NONE, { JSH_AF1|JSH_USART1|JSH_USART_RX, JSH_AF0|JSH_I2C1|JSH_I2C_SDA, JSH_AF0|JSH_TIMER4|JSH_TIMER_CH2, 0 } },
    /* PB8  */ { JSH_PORTB, JSH_PIN8, JSH_ANALOG_NONE, { JSH_AF0|JSH_TIMER4|JSH_TIMER_CH3, JSH_AF1|JSH_I2C1|JSH_I2C_SCL, 0, 0 } },
    /* PB9  */ { JSH_PORTB, JSH_PIN9, JSH_ANALOG_NONE, { JSH_AF1|JSH_I2C1|JSH_I2C_SDA, JSH_AF0|JSH_TIMER4|JSH_TIMER_CH4, 0, 0 } },
    /* PB10 */ { JSH_PORTB, JSH_PIN10, JSH_ANALOG_NONE, { JSH_AF1|JSH_TIMER2|JSH_TIMER_CH3, JSH_AF0|JSH_USART3|JSH_USART_TX, JSH_AF0|JSH_I2C2|JSH_I2C_SCL, 0 } },
    /* PB11 */ { JSH_PORTB, JSH_PIN11, JSH_ANALOG_NONE, { JSH_AF0|JSH_USART3|JSH_USART_RX, JSH_AF0|JSH_I2C2|JSH_I2C_SDA, JSH_AF1|JSH_TIMER2|JSH_TIMER_CH4, 0 } },
    /* PB12 */ { JSH_PORTB, JSH_PIN12, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PB13 */ { JSH_PORTB, JSH_PIN13, JSH_ANALOG_NONE, { JSH_AF0|JSH_TIMER1|JSH_TIMER_CH1|JSH_TIM­ER_NEGATED, JSH_AF0|JSH_SPI2|JSH_SPI_SCK, 0, 0 } },
    /* PB14 */ { JSH_PORTB, JSH_PIN14, JSH_ANALOG_NONE, { JSH_AF0|JSH_SPI2|JSH_SPI_MISO, JSH_AF0|JSH_TIMER1|JSH_TIMER_CH2|JSH_TIM­ER_NEGATED, 0, 0 } },
    /* PB15 */ { JSH_PORTB, JSH_PIN15, JSH_ANALOG_NONE, { JSH_AF0|JSH_TIMER1|JSH_TIMER_CH3|JSH_TIM­ER_NEGATED, JSH_AF0|JSH_SPI2|JSH_SPI_MOSI, 0, 0 } },
    /* PC0  */ { JSH_PORTC, JSH_PIN0, JSH_ANALOG123|JSH_ANALOG_CH10, { 0, 0, 0, 0 } },
    /* PC1  */ { JSH_PORTC, JSH_PIN1, JSH_ANALOG123|JSH_ANALOG_CH11, { 0, 0, 0, 0 } },
    /* PC2  */ { JSH_PORTC, JSH_PIN2, JSH_ANALOG123|JSH_ANALOG_CH12, { 0, 0, 0, 0 } },
    /* PC3  */ { JSH_PORTC, JSH_PIN3, JSH_ANALOG123|JSH_ANALOG_CH13, { 0, 0, 0, 0 } },
    /* PC4  */ { JSH_PORTC, JSH_PIN4, JSH_ANALOG12|JSH_ANALOG_CH14, { 0, 0, 0, 0 } },
    /* PC5  */ { JSH_PORTC, JSH_PIN5, JSH_ANALOG12|JSH_ANALOG_CH15, { 0, 0, 0, 0 } },
    /* PC6  */ { JSH_PORTC, JSH_PIN6, JSH_ANALOG_NONE, { JSH_AF1|JSH_TIMER3|JSH_TIMER_CH1, JSH_AF0|JSH_TIMER8|JSH_TIMER_CH1, 0, 0 } },
    /* PC7  */ { JSH_PORTC, JSH_PIN7, JSH_ANALOG_NONE, { JSH_AF0|JSH_TIMER8|JSH_TIMER_CH2, JSH_AF1|JSH_TIMER3|JSH_TIMER_CH2, 0, 0 } },
    /* PC8  */ { JSH_PORTC, JSH_PIN8, JSH_ANALOG_NONE, { JSH_AF1|JSH_TIMER3|JSH_TIMER_CH3, JSH_AF0|JSH_TIMER8|JSH_TIMER_CH3, 0, 0 } },
    /* PC9  */ { JSH_PORTC, JSH_PIN9, JSH_ANALOG_NONE, { JSH_AF0|JSH_TIMER8|JSH_TIMER_CH4, JSH_AF1|JSH_TIMER3|JSH_TIMER_CH4, 0, 0 } },
    /* PC10 */ { JSH_PORTC, JSH_PIN10, JSH_ANALOG_NONE, { JSH_AF0|JSH_USART4|JSH_USART_TX, JSH_AF1|JSH_USART3|JSH_USART_TX, 0, 0 } },
    /* PC11 */ { JSH_PORTC, JSH_PIN11, JSH_ANALOG_NONE, { JSH_AF1|JSH_USART3|JSH_USART_RX, JSH_AF0|JSH_USART4|JSH_USART_RX, 0, 0 } },
    /* PC12 */ { JSH_PORTC, JSH_PIN12, JSH_ANALOG_NONE, { JSH_AF0|JSH_USART5|JSH_USART_TX, 0, 0, 0 } },
    /* PC13 */ { JSH_PORTC, JSH_PIN13, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PC14 */ { JSH_PORTC, JSH_PIN14, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PC15 */ { JSH_PORTC, JSH_PIN15, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PD0  */ { JSH_PORTD, JSH_PIN0, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PD1  */ { JSH_PORTD, JSH_PIN1, JSH_ANALOG_NONE, { 0, 0, 0, 0 } },
    /* PD2  */ { JSH_PORTD, JSH_PIN2, JSH_ANALOG_NONE, { JSH_AF0|JSH_USART5|JSH_USART_RX, 0, 0, 0 } },
    

    So that would seem to back up what you're saying about what timers are chosen... The order (I think) just comes from the order that they're defined in the CSV file (that has been grabbed from the STM32F1 datasheet).

    So I guess simply ordering the PWM timers lowest to highest for each pin would really help - although the smartest solution is probably to prioritise the timer that isn't used for anything else.

  • Ok, I've just committed some changes (should appear here soon: http://www.espruino.com/binaries/git/com­mits/adbee03e57766746a91ce665fdcb0162daa­28ffa)

    These do some simple sorting rules and solve A0/1/2/3 and C6/7/8/9. A6/7/B0/1 are more tricky, because the timers being used are the least contentious ones - even if they're wrong :)

  • Thanks. What I meant about the first issue is that analogWrite() for timers with negated output enables both negated and non-negated output for a channel, even when only one or the other is needed. This causes problems.

    analogWrite(A8,0);
    peek16(0x40012c20); // should be 1 (enable non-negated output of channel 1 on A8) - is actually 5 (enable non-negated output of channel 1 on A8 and enable negated output on B13). 
    

    This is why if you've used PWM on A8~10, a PWM signal gets played "ontop of" SPI2 - the pins are put into "alternate function" mode to use the SPI, and analogWrite(A8~10) enables the negated output on B13~15.

  • Aha, thanks...

    Wow, it's a bit nasty though - I didn't expect the contents TIMx->CCER to be affecting the mapping of pins :)

    I've just fixed this - commit 499bba24b955e12807766e7846bcb0b2b1163aa6­. Hopefully it'll improve matters a lot!

    I think long-term I'll have to fix this more seriously with some code that properly understands that two peripherals can't be active on one pin at the same time (eg. USART1 and a timer on pin A9), or on different pins.

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

Pins that don't play well together

Posted by Avatar for DrAzzy @DrAzzy

Actions