This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
I want to use an open drain gpio output for a wired-and connection - that is, connected to other open drain signals. When I drive it low I know it is low but when I drive it high I need to determine if another device is driving it low - that is, I need to be able to access the actual pin state. How do you read the pin state of an open drain output?
Thanks,
Matt
The first solution that comes to mind is to bring the signal in to two pins, one for open-drain output, and the other for input.
You can connect to two pins - one open drain and one input - but on about every other micro I've used it isn't necessary - the pin state and the output state are usually accessible separately. Oftentimes you have to emulate an open drain pin by setting the output value to 0 and controlling the direction instead of the output value but it behaves exactly as you would expect. I don't think this 'trick' works with Stellaris but you wouldn't really want to resort to that anyway since Stellaris directly supports open drain outputs - although Stellaris's open drains are strictly low-side drive and with the 'trick' you can do high-side open drains as well (wired-or).
If you support open drain outputs then you should be able to read the state of a wired-and - that is one of the main uses of open drains. But I can't see how to do it.
I think there are other problems with the Stellaris gpio structure which I want to discuss but I need to make sure I understand this simple case first.
Thanks,
Matt
I agree that it is a weakness in the GPIO design of the Stellaris family that a pin is either an output or an input, and that when configured as an output, you cannot read the digital state at the pin as you can on many other MCUs.
While not exposed to the CPU, clearly peripherals can read the state of open-drain pins, as it's a requirement for I2C operation.
The I2C peripheral even exposes the state of the pins via the I2CMBMON register.
So a follow-on question to this is, how do you initialize the state of an output before making it an output? That is, in general I want to set the output value first and then make the pin an output so that as soon as it becomes an output it is in the proper state - no glitches. I cannot seem to do this either - I have to first make the pin an output and then set its value.
Matt
Did this ever get resolved? I need to use an open drain IO also, and it seems that the GPIO doesn't allow you to read an open drain when it is high. Am I forced to use the glitch method of changing the direction, then the output value to zero after making it an output? There must be a better way. My board is already built, so I can't connect 2 pins.
My solution to glitching outputs was to write my own version of GPIOPinTypeGPIOOutput(). I suggest:
void InitGPOut(uint32_t port, uint8_t pins, uint8_t InitVal)
{
ASSERT(GPIOBaseValid(port));
if (0 != pins)
{
GPIODirModeSet(port, pins, GPIO_DIR_MODE_OUT);
// write won't stick unless pins cfg as output
GPIOPinWrite(port, pins, InitVal);
// pins enabled here (DEN[] = 1)
GPIOPadConfigSet(port, pins, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
}
}
similarly, InitGPOutOD() would replace GPIO_PIN_TYPE_STD with GPIO_PIN_TYPE_OD
Andy Sapuntzakis said:write my own version of GPIOPinTypeGPIOOutput().
May I begin by thanking you - and applauding your effort? (far too rare - in today's unguided forum, "gimme, I wanna, does not work" wasteland)
Yet - as inventive & strategic as your, "rearrangement & tweaking of the "normal" GPIOPinType() is - might you (still) be held hostage to any/all MCU "default" conditions - which may visit/inflict, "GPIOPadConfigSet() & GPIOPinWrite()?"
By my (10 minute, earlier) reread/review of gpio.c - you've promoted GPIOPinWrite() above GPIOPadConfigSet() while adding a (hoped for) initialization to targeted pins. Should "GPIOPadConfigSet()" default (away) from your liking - that may, "do in" your neat attempt. (i.e. allow glitching)
I'm unsure also (how or if, even) "GPIOPinWrite()" may behave - when that function's called prior to the (more normal) GPIOPadConfigSet().
Unclear if you've placed a good (fast) storage scope - and perhaps a stiff pull-up R - on the targeted port pin for test/verify - and noted, "glitch-free" operation under your code scheme.
Please don't interpret this as negative commentary - my attempt is to build further support for your contribution - based upon non-obvious MCU behaviors & real world test/verify...
And lastly - there exist older LM3S, LX4F and newer TM4C devices. Post does not assign this "fix" to any particular device family - due to the varying output structure (surely present LM3S/LX4F) this fix may not be "universal."
Good job - thank you...
InitGPOut() doesn't try to do much more than add an initial state to GPIOPinTypeGPIOOutput(), which I feel is the minimum to avoid glitches when configuring outputs with external pull-ups.
I originally developed and tested it on LM3S. I have not yet tested it on TM4C, but this aspect of the GPIO peripheral seems unchanged.
I don't expect it to be glitch-proof in all scenarios, esp if applied to a pin which is already enabled (DEN = 1). Perhaps this is the reason that it hasn't been incorporated into the Tiva (Stellaris) library.
I would try leaving the output configured as an open drain output and using the GPIORIS register after setting the equivalent bit as level sensitive in GPIOIS. Leave the interrupt masked assuming you don't actually want interrupts. The level read will be inverted depending upon the corresponding bit in GPIOIEV. I haven't tried this but the data sheet implies the generation of interrupts is tied to the actual pin always.
I think that is a good suggestion but I'm not sure it will work or at not least work as you described. Once the interrupt occurs I think the interrupt has to be cleared via a write to the GPIOICR register. This suggests the interrupt condition is "latched" - that is, for example, if the interrupt is configured as a low level-sensitive interrupt the interrupt will remain pending even if the signal returns high. Because of this "latching" behavior, the level sensitive interrupts are not truly level sensitive but they are "edge *and* level sensitive". Although the pin gpio structure itself is not edge edge sensitive the interrupt system effectively makes it edge sensitive because it has memory.
I have observed this exact behavior with the Freescale Kinetis gpio's - they are documented to be level sensitive but their behavior is that of edge and level sensitive.
I think what you could do is to clear a possible pending interrupt via a write to GPIOICR immediately before reading the GPIOIS register. However, if some interrupt occurs and an ISR is executed between the write and the read, or equivalently if a task switch occurs (if you are using preemptive threading) between the write and the read, then the read is "unreliable" because of the delay inserted after the write (because of the intervening ISR or task) - so to be interrupt or thread safe the write/read sequence needs to be atomic. I suppose this could be an acceptable albeit clumsy workaround.
A consequence of this edge and level sensitive interrupt behavior is that when using "level" sensitive interrupts you still have to clear the interrupt in the ISR, even if the active level is no longer asserted - it is this that makes them effectively edge *and* level, and not truly edge. This is not really the 'fault' of the gpio structure (the signal passed to the interrupt system truly does bypass the edge detector circuitry), but since the interrupt system itself has memory it latches the condition.
This also means that if you truly want level only interrupts and not edge and level interrupts then you also need to include the write/read construct in your ISR to "discard" unwanted edge (glitch or pulse) "interrupts" - again, keeping the atomic issue in mind.
As I said, the Freescale Kinetis also has this same edge and level behavior but in its case it doesn't have this same gpio deficiency - you can always read the pin state. But I still find the documentation misleading in that what is said to be level sensitive interrupts actually behave as edge and level.
Matt
Liked your writing - great detail & a useful history of, "what, where, how." (almost never provided - this hallowed, "Does not work" setting)
Now if memory serves - we've observed your identified "edge & level" sensitivity w/in (yet) another ARM MCU. (not among those mentioned)
Years back - we employed a simple minded technique to resolve - we routed the "interrupting signal" to 2 ports - one triggering upon edge - the second upon level. Only when the "edge" interrupt remained "silent" - were we assured that a level transition had occurred. (I think my recall is correct - but reading your neat post motivated this ramble...)
Surprising that this topic has not received more "air-time" - perhaps our drumbeat here will inspire officialdom to visit/expound... (we so love "locked" insider info...)