Search

Technical Discussion Group Forum

This forum is provided for user discussion. While Beacon EmbeddedWorks support staff and engineers participate, Beacon EmbeddedWorks does not guarantee the accuracy of all information within in the Technical Discussion Group (TDG).

The "Articles" forums provide brief Articles written by Beacon EmbeddedWorks engineers that address the most frequently asked technical questions.

To receive email notifications when updates are posted for a Beacon EmbeddedWorks product download, please subscribe to the TDG Forum of interest.

TDG Forum

PrevPrev Go to previous topic
NextNext Go to next topic
Last Post 28 Sep 2004 03:38 PM by  mikee@logicpd.com
Controlling GPIOs from applications
 7 Replies
Sort:
You are not authorized to post a reply.
Author Messages
rdubrawski
New Member
New Member
Posts:


--
08 Apr 2004 01:26 PM
    I am writing code to interract with an external sensor. In order to control the sensor, I have 4 GPIO lines (PG0:3) that are used for power activation, reset etc.
    I have written code to allow me to access the GPIO peripheral registers to control these lines as well as to read any of the GPIOs.
    I would like to make this code function properly in the multi-threaded environment. In order to set a single bit of the GPIO, I must first read the current value of the register and then OR or AND with a mask to set or clear the indicated pin. If an interrupt occurs or a task switch during the 2 step process, I will potentially be setting another GPIO pin on the same port to an incorrect value.
    Since I use the standard:
    *(volatile PUCHAR)(reg) |= BIT(pin)
    where
    #define BIT(x) 1<<x
    and reg is accessible correctly using the MmMapIoSpace call, I know that this requires >1 assemble instruction, so I cannot assume that there is not risk.

    Is there a better call to do this with, or is there a way to disable interrupts for the period where I make the call? I tried to use INTERRUPTS_OFF() but it generates a tremendous number of errors at compile time.

    ANy suggestions as to how this can be accomplished?

    Thanks

    Rich
    mpinton
    New Member
    New Member
    Posts:


    --
    07 Sep 2004 02:26 PM
    Rich,
    Did you figure out a way to make this more atomic or at least protected?

    /michel
    mikee@logicpd.com
    New Member
    New Member
    Posts:


    --
    08 Sep 2004 09:36 AM
    First off,

    I'm sorry that the first post got missed. Now that it has floated to the top again, I'll answer the question.

    Mainly, it depends on what code is accessing this port. If the port is only being accessed by a single driver, and you are worried about multiple threads within the driver stepping on each other, use a critical section. Something like:


    /* Declare section globally or inside a data structure shared between threads. */
    CRITICAL_SECTION reg_protect;

    /* When the driver/application initializes itself, register the critical section with the kernel. */
    InitializeCriticalSection(&reg_protect);


    /* Within the driver/application, do the following when accessing the register. */
    EnterCriticalSection(&reg_protect);
    .....
    LeaveCriticalSection(&reg_protect);


    /* Make sure to de-register it when your driver/application exits. */
    DeleteCriticalSection(&reg_protect);


    The critical section will allow only one thread at a time to access the register.

    If you are trying to synchronize access between different processes, then, you either need to share the critical section between them (check the help file). Or find another way such as only read the register within an ISR and copy it's contents to a global memory area, then have your seperate threads act on that memory area instead of the hardware register itself.

    regards,
    --mikee
    rdubrawski
    New Member
    New Member
    Posts:


    --
    27 Sep 2004 10:50 AM
    My implementation did as Mike described.
    My concern was not about my library, but rather about the potential for chaos by the BSP code and drivers not using my library accessing the same GPIO registers. I was never able to solve that problem, but to date it has never caused me problems. (keep fingers crossed.)
    The only way to solve this globally is for the OS (or BSP) to provide a common library used by everything accessing the GPIO registers.
    My solution was to use a DLL for GPIO access, that way every thread used the same CriticalSection identifier. This could still cause problems though if multiple processes were accessing GPIOs.
    Other, more traditionally embeddod OSs have a BSP level interface for accessing GPIOs. Optionally, one could write a device driver for the GPIO interface, but that seemed excessive.
    scobb
    New Member
    New Member
    Posts:


    --
    27 Sep 2004 11:48 AM
    Yes, I'm just now entering into the GPIO zone and had similar concerns.

    Hoping that you remain lucky isn't all that comforting is it?

    Rich: did you do anything other than mikee suggested in your dll?

    How big of a hit is the CRITICAL_SECTION code - if I want to use this from an interrupt?

    Cheers,
    Steve
    mikee@logicpd.com
    New Member
    New Member
    Posts:


    --
    28 Sep 2004 02:13 PM
    scobb,

    I'm not sure I understand your statement of:
    Quote:
    - if I want to use this from an interrupt?


    If you are inside an ISR, the system is non-preemptable. So you only need to worry about it in the context of a higher priority interrupt (FIQ) firing and accessing the same hardware. Otherwise, your ISR will run, uninterrupted to completion.

    Within an IST, you should use a CRITICAL_SECTION. I don't have exact measurments concerning the performance hit, but I don't believe it is too big. They are used throughout almost all IST code, so they are pretty well optimized.

    Regards,
    --mikee
    scobb
    New Member
    New Member
    Posts:


    --
    28 Sep 2004 02:48 PM
    Thanks Michael,
    Though I now have a new question. Suppose that I have one bit on a port that is being referenced by an IST or application level code, and another bit on that same port that needs to be controlled by an ISR.

    I want to use a CRITICAL_SECTION in the app or IST, but I can't use it in the ISR. If I don't use it in the ISR, the ISR's change could be lost if it interrupted the critical section of the app or IST. If I do use a CRITICAL_SECTION in the ISR, you have a deadlock situation. However, I suppose that you never want to use a CRITICAL_SECTION in an ISR.

    Typically you get around this by choosing ports carefully such that you don't have this particular problem. Pins accessed by the ISR are relegated to one particular port.

    I do share rdubrawski's concern about GPIO port collision with other drivers and/or applications. Granted that the Read/Modify/Write time is extremely small in the grand scheme of things; there is still a non-zero chance for error.

    Steve
    mikee@logicpd.com
    New Member
    New Member
    Posts:


    --
    28 Sep 2004 03:38 PM
    scobb,

    Good question. In general, I don't have an answer for you other than to pick either your ISR, or your IST and modify the bits in only one place. Is there a specific reason you need to modify a bit in the ISR? Most of the time, you should only use the ISR to check if an interrupt really did occur, and then handle everything up in the IST.

    Also, think about it this way. When the ISR finishes running, that particular interrupt is going to be disabled. It will not be re-enabled until you do so up in the driver-level IST. Therefore, there is (or should be) a one-to-one correspondence between ISR runs and IST runs. So, your ISR shouldn't change anything in the middle of your IST and vice-versa.

    It looks like this:

      Interrupt fires.

      ISR runs, decides an interrupt occurred, returns a SysIntr.

      Kernel gets SysIntr, IST gets scheduled to run.

      IST can do whatever it wants to as the interrupt is disabled.

      When IST returns, interrupt is re-enabled.


    This doesn't help with the case of multiple drivers running and accessing the same port though. In that case, you need to be able to share critical sections between the ISTs of the two drivers.

    The most common occurance of this is GPIO pins used as interrupts. The easiest way to handle it is to make one driver to handle all of the GPIO interrupts. Then, have other drivers for each individual interrupt wait on it.

    For instance, the A400 allows each pin in GPIO port-F to serve as an external interrupt. If I had to manage multiple drivers responding to these pins I would do the following.

      Write a driver that responds to any and all port-F interrupts.

      Have this driver maintain an internal list of events corresponding to each interrupt.

      Write other drivers for each seperate interrupt that register with the first driver.


    So, we have gpio.dll which is the driver to handle all GPIO interrupts. Then, lets take drivers foo.dll and bar.dll. Foo.dll is interested in pin-0 and Bar.dll is interested in pin-1.


      Gpio.dll installs an ISR for IRQs; 0,5,6,7, 23-26. These are the bits within the Interrupt Controller Register that correspond to GPIO interrupts.

      Gpio.dll spins an IST which will wait on a SysIntr value it gets from the OAL.

      The installed ISRs all return the SysIntr above.

      Gpio.dll exports the standard stream interface.

      Foo.dll creates an event. It then calls gpio.dll's IOCTL function to say something like, "When bit zero fires, pulse my event."

      Foo.dll then goes to sleep, waiting on its event.

      Bar.dll does the same thing foo.dll just did.

      Interrupt fires.

      The chain of installed ISR's get called. One of our installed GPIO interrupts is going to run, returning the SysIntr that will wake up gpio.dll's IST.

      Inside gpio.dll's IST, it looks at the interrupt controller registers to determine which GPIO pin(s) fired. Then, it looks through its internal data structure to see if any drivers have "registered" for that particular pin.

      If it finds one, it pulses the event the registered driver indicated.

      The registered driver wakes up and does its thing.


    The main things you have to deal with are whether or not gpio.dll will re-enable the interrupts, or whether each registered driver will re-enable its own interrupts.

    As you can see, it isn't a really tough problem to solve, it's just tough to solve the general case. That is why we at Logic don't have some sort of generic GPIO driver. But, it shouldn't be too hard for you to whip something together that suits your particular needs.

    Regards,
    --mikee
    You are not authorized to post a reply.