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.

Reconfiguring analog inputs to digital outputs

Hi,

I have an application where I often need to reconfigure pins from analog inputs to outputs (and eventually back to analog inputs again).  It works as expected on one of the pins I tried it with, but not on any other pin.  This inconsistency really has me stumped.  Why does it work in one case and not others?

I created a simplified example that recreates this problem on the LM4F232H5QD (rev A1) eval board using Stellarisware (rev 9453):

#include "inc/hw_adc.h"
#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "inc/hw_sysctl.h"
#include "inc/hw_types.h"
#include "driverlib/adc.h"
#include "driverlib/gpio.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"

/*******************************************************************************
* GPIO peripheral addresses
*******************************************************************************/
const unsigned long gpioPeripherals[] = {
    SYSCTL_PERIPH_GPIOA,
    SYSCTL_PERIPH_GPIOB,
    SYSCTL_PERIPH_GPIOC,
    SYSCTL_PERIPH_GPIOD,
    SYSCTL_PERIPH_GPIOE,
    SYSCTL_PERIPH_GPIOF,
    SYSCTL_PERIPH_GPIOG,
    SYSCTL_PERIPH_GPIOH,
    SYSCTL_PERIPH_GPIOJ,
    SYSCTL_PERIPH_GPIOK,
    SYSCTL_PERIPH_GPIOL,
    SYSCTL_PERIPH_GPIOM,
    SYSCTL_PERIPH_GPION,
    SYSCTL_PERIPH_GPIOP,
    SYSCTL_PERIPH_GPIOQ,
    SYSCTL_PERIPH_GPIOR,
    SYSCTL_PERIPH_GPIOS
};

/*******************************************************************************
* Ports, pins, adc channels
*******************************************************************************/
typedef struct {
    unsigned long port;
    unsigned char pin;
    unsigned long adcChannel;
} _reconfigurePins;

const _reconfigurePins reconfigurePins[] = {
    GPIO_PORTK_BASE,    GPIO_PIN_1,        ADC_CTL_CH17,
    GPIO_PORTD_BASE,    GPIO_PIN_5,        ADC_CTL_CH6,
    GPIO_PORTD_BASE,    GPIO_PIN_6,        ADC_CTL_CH5,
    GPIO_PORTK_BASE,    GPIO_PIN_3,        ADC_CTL_CH19,
    GPIO_PORTK_BASE,    GPIO_PIN_2,        ADC_CTL_CH18,
};

#define NUMBER_OF_PINS sizeof(reconfigurePins) / sizeof(_reconfigurePins)

void main(void)
{
    unsigned long pinType;
    unsigned long strength;

    MAP_SysCtlClockSet(    SYSCTL_OSC_MAIN        |          *Configure system clock to 50MHz        */
                        SYSCTL_USE_PLL        |                                    /*    using 200 MHz PLL                    */
                        SYSCTL_SYSDIV_4        |                                   /*    divided by four                        */
                        SYSCTL_XTAL_16MHZ    );                                /*    clocked from 16MHz external crystal    */

    unsigned int i, ports;

    //
    //Enable all GPIO peripherals
    //
    ports = sizeof(gpioPeripherals) / sizeof(unsigned long);                    /*Get the number of gpio peripherals    */

    for (i = 0; i < ports; i++)                                                                                /*For all GPIO peripherals                */
    {
        if(    MAP_SysCtlPeripheralPresent(gpioPeripherals[i]))                  /*If peripheral is present on this part    */
        {
            MAP_SysCtlPeripheralEnable(gpioPeripherals[i]);                       /*Enable the peripheral                    */
        }
    }

    //
    //Configure pins as analog inputs
    //
    for (i = 0; i < NUMBER_OF_PINS; i++)
    {
        MAP_GPIOPinTypeADC(reconfigurePins[i].port, reconfigurePins[i].pin);
    }

    //
    //All pins are successfully configured as analog inputs here
    //
    for (i = 0; i < NUMBER_OF_PINS; i++)
    {
        MAP_GPIOPadConfigGet(                                                          /*Get pin configuration                    */
            reconfigurePins[i].port,                                                           /*Pin base address                        */
            reconfigurePins[i].pin,                                                             /*Pin                                    */
            &strength,                                                                                   /*Location to store pin strength        */
            &pinType);                                                                                /*Location to store pin type            */
    }

    //
    //Reconfigure pins as outputs
    //
    for (i = 0; i < NUMBER_OF_PINS; i++)
    {
        MAP_GPIOPinTypeGPIOOutput(                                                /*Reconfigure pin as output                */
                reconfigurePins[i].port,
                reconfigurePins[i].pin);
    }

    //
    //Only PK1 successfully reconfigures as an output
    //Changing the location of PK1 in the array results in the same behavior
    //
    for (i = 0; i < NUMBER_OF_PINS; i++)
    {
        MAP_GPIOPadConfigGet(                                                    /*Get pin configuration                    */
            reconfigurePins[i].port,                                                     /*Pin base address                        */
            reconfigurePins[i].pin,                                                       /*Pin                                                   */
            &strength,                                                                            /*Location to store pin strength        */
            &pinType);                                                                            /*Location to store pin type            */
    }

    for(;;);
}

  • Have long used the Analog Inputs on several LX4F - but had never the requirement to reconfigure back to GPIO Outputs.

    Reading our LX4F data manual - my suspicion is that your "re-configuration, back from ADC into GPIO Outputs" - is most likely failing @ the GPIOAMSEL Register.  Support for this theory is based upon the fact that this register is charged w/ managing an isolation circuit to protect the sensitive analog circuitry - when not used in their analog function.  This process may intrude into normal/customary - GPIO Output operation - if not fully/properly "wound down."   (my suspicion)

    In our set-up and config. of the ADC pins - we've not often directly/deliberately written to this register.  (GPIOAMSEL - relying instead upon GPIOPinTypeADC() function.  Suspect that it will prove useful for you to review that function's operation - which should reveal w/in file adc.c.

    Only other "likely suspects" appear to be: RCGCADC, RCGCGPIO, GPIOAFSEL, & GPIODEN.   However my bet remains on aforementioned GPIOAMSEL.  Somehow your re-config from ADC to GPIO Outputs seems likely incomplete - or is being bypassed/hindered...

    Another approach would involve you "first" initializing the ADC pins as GPIO Outputs - exercising them - and logging results.  Examination of the key registers (as listed herein) then better enables comparison - after you revert to ADC set-up - and then attempt to shift them back to GPIO Outputs.

    Your PK1 situation - - bit beyond this reporter's, "pay grade."

    While a "long-shot" - suppose that it is possible that set-up/config of a pin as ADC - may "restrict" its reformat to GPIO.  Work-around here would likely be MCU reset - and fresh config to "either" ADC or GPIO.  The opposite path - "GPIO to ADC" - remains to be explored...

    Hope this proves of some value...  (I'm not lazy - will attempt on our custom LX4F board - soon as higher priority "tech-fires" somewhat quenched...)

     

     

  • Hi Ben,

    I'm seeing the same thing here when I run your code.  It'll need some investigation, so I'll need to get back to you.  Nothing looks too out of line in your code, but like cb1 says, it's a bit unusual to perform multiple configuration changes on a mixed-signal I/O.  In the meantime, perhaps you could reset the [entire] port using to SysCtlPeripheralReset then reconfigure it.

  • Thanks for the advice, cb1.

    My project now works as expected.

    As usual, it ended up being trivial...  The pins were in fact being reconfigured correctly.  The problem was that all Stellarisware calls I've used refer to pins as bit fields; however, GPIOPadConfigGet uses the actual pin number.  Before I posted I had already skimmed another post that points this out, but I thought it was simply explaining that Stellarisware calls use bit fields instead of pin numbers...

    http://e2e.ti.com/support/microcontrollers/stellaris_arm/f/471/t/89569.aspx

  • Jonathan Guy said:
    reset the [entire] port using to SysCtlPeripheralReset - then reconfigure

    @ Jonathan - believe yours is a great suggestion - superior to earlier "brute force" MCU reset.  (author of that - unknown...)

    @ Ben - You now have 2/2 guys who believe your mixed-signal I/O handling - bit out of the ordinary.  We're not told the extent of your "steering/gating" circuitry - but you should minimize any extra capacitance or loading (or signal reflections) upon your analog inputs.  And - any form of voltage "kick-back" should be anticipated - and prevented.  I'd not "share" any critical ADC measurement channels in this manner - reserving this resourceful GPIO extension for less critical signal monitoring...

    As a "GPIO extender" alternative - you may consider use of old standby "SIPO" (serial in parallel out) shift register (may cost 4 pins but gains you 8) - or either I2C or SPI based - GPIO extender ICs.  (we've used these successfully in both 8 and 16 channel incarnations)  We/others often design these into our boards - and "DNF" unless they're needed. 

    Believe you'll find that the larger pin-count MCUs - cannot be matched for GPIO programming speed, ease & efficiency - and reduce overall purchase costs as well...