pinMode execution time?

Hello,

in order to ask this correctly, I'll need to explain a bit of my project.
I'm using a DFRduino duemilanove with atmega328 chip.
Basically, I have every pin in use, so I'm using a multiplexer to provide a couple of extra ones.
My goal is to wire up the LCD shield to the arduino, but I can't give it the pins it normally wants, so I've rewired it to run off of different pins, the analog pin for the buttons and the enable pin of the LCD shield have to be controlled through the multiplexer, since I don't have spare pins for those.
So in short:
I have tied to a multiplexer: a sensor (5 buttons outputting a single analog signal due to a voltage divider), and the LCD enable pin (digital)
The DB4~DB7 and RS pins have a pin on the arduino.

Now I do have this setup working, and I can write text on the LCD quite happily, even the buttons work.
But I did notice something using a modified (to use the pins I gave the LCD instead of the default setup) example sketch...
If I poll the buttons too often, it writes garbage to the LCD shield.
If I reduce the frequency of the buttons polling, the issue goes away and it writes happily again.
Since I have to switch between INPUT and OUTPUT on that multiplexer pin (wired to arduino pin 19 (analog 5), I can't help but think the issue is the switching of pinMode.

So.. does anybody know the answer to any of these questions?

  • how long does it take to switch between INPUT / OUTPUT on a pin?
  • will repeatedly switching cause that delay to increase?
  • can pinMode fail?
  • if pinMode is not to blame, any ideas what could be causing the issue?

Any answers will be greatly appreciated :slight_smile:

Are you able to post up the code?

The sketch that works without a problem:

//example use of LCD4Bit_mod library

#include <Botty_LCD4Bit_mod.h> 
//create object to control an LCD.  
//number of lines in display=1
LCD4Bit_mod lcd = LCD4Bit_mod(2);

//Key message
char msgs[5][15] = {"Right Key OK ", 
                    "Up Key OK    ", 
                    "Down Key OK  ", 
                    "Left Key OK  ", 
                    "Select Key OK" };
int  adc_key_val[5] = {30, 150, 360, 535, 760 };
int  NUM_KEYS       = 5;
int  key            = -1;
int  oldkey         = -1;
int  adc_key_in;

void setup()
{ 
    pinMode(4, OUTPUT);
    pinMode(5, OUTPUT);
    pinMode(6, OUTPUT);
    pinMode(19, OUTPUT);

    lcd.init();
    //optionally, now set up our application-specific display settings, overriding whatever the lcd did in lcd.init()
    //lcd.commandWrite(0x0F);//cursor on, display on, blink on.  (nasty!)
    lcd.clear();
    lcd.printIn("KEYPAD testing");
}

void loop()
{
    for(int i = 48; i < 58; i += 1)
    {
      lcd.cursorTo(2, 0);
      lcd.print(i);
      delay(5);
    }
    
    digitalWrite(4, HIGH);
    digitalWrite(5, HIGH);
    digitalWrite(6, LOW); //mux pin 3
    
    pinMode(19, INPUT); //set analog 5 to input
    adc_key_in = analogRead(5);    // read the value from the sensor  
    pinMode(19, OUTPUT);
    key = get_key(adc_key_in);                    // convert into key press

    // convert into key press
    if(key != oldkey)                        
    {                  
      oldkey = key;
      if(key >= 0)
      {
        lcd.cursorTo(2, 2);  //line=2, x=0
        lcd.printIn(msgs[key]);
      }
    }
}

// Convert ADC value to key number
int get_key(unsigned int input)
{
    int k;

    for(k = 0; k < NUM_KEYS; k++)
    {
        if(input < adc_key_val[k])
        {
            return k;
        }
    }

    if(k >= NUM_KEYS)
    {
        k = -1;     // No valid key pressed
    }

    return k;
}

The sketch that will produce garbage on the screen as soon as a button is pressed:

//example use of LCD4Bit_mod library

#include <Botty_LCD4Bit_mod.h> 
//create object to control an LCD.  
//number of lines in display=1
LCD4Bit_mod lcd = LCD4Bit_mod(2);

//Key message
char msgs[5][15] = {"Right Key OK ", 
                    "Up Key OK    ", 
                    "Down Key OK  ", 
                    "Left Key OK  ", 
                    "Select Key OK" };
int  adc_key_val[5] = {30, 150, 360, 535, 760 };
int  NUM_KEYS       = 5;
int  key            = -1;
int  oldkey         = -1;
int  adc_key_in;

void setup()
{ 
    pinMode(4, OUTPUT);
    pinMode(5, OUTPUT);
    pinMode(6, OUTPUT);
    pinMode(19, OUTPUT);

    lcd.init();
    //optionally, now set up our application-specific display settings, overriding whatever the lcd did in lcd.init()
    //lcd.commandWrite(0x0F);//cursor on, display on, blink on.  (nasty!)
    lcd.clear();
    lcd.printIn("KEYPAD testing");
}

void loop()
{
    digitalWrite(4, HIGH);
    digitalWrite(5, HIGH);
    digitalWrite(6, LOW); //mux pin 3

    pinMode(19, INPUT);
    adc_key_in = analogRead(5);    // read the value from the sensor
    pinMode(19, OUTPUT);
    key = get_key(adc_key_in);                    // convert into key press

    if(key != oldkey)                            // if keypress is detected
    {
        delay(50);            // wait for debounce time
        pinMode(19, INPUT);
        adc_key_in = analogRead(5);    // read the value from the sensor  
        pinMode(19, OUTPUT);
        key = get_key(adc_key_in);                    // convert into key press
        if(key != oldkey)                        
        {                  
            oldkey = key;
            if(key >=0)
            {
                lcd.cursorTo(2, 0);  //line=2, x=0
                  lcd.printIn(msgs[key]);
            }
        }
    }
}

// Convert ADC value to key number
int get_key(unsigned int input)
{
    int k;

    for(k = 0; k < NUM_KEYS; k++)
    {
        if(input < adc_key_val[k])
        {
            return k;
        }
    }

    if(k >= NUM_KEYS)
    {
        k = -1;     // No valid key pressed
    }

    return k;
}

Some notes with the second, problem causing, sketch.
The library I use will set the multiplexer to the correct pin to push data into the LCD, the library between the two sketches is the same.
I have also tried the second sketch with a delay after each pinMode change, with a delay of upto 10 milliseconds for each switch (so both after pinMode(19, INPUT) and pinMode(19, OUTPUT) ).

if i get you right, the you have a direct connection between the buttons and the keys, thus pressing a button sends a signal regardless of the pinmode. so this cannot work. get yourself some shift registers to increase the pincount.

Could you show the schematic diagram?
Something drawn by hand on paper, and photographed would be fine.

When you say multiplexer, what is the part number?

Argh, edited out the part number apparently.
It is the 4051 multiplexer, 8 channel analogue.
The buttons on the shield all work through the same analog pin, the signal can only be read when the multiplexer is set to that pin, obviously.

As for schematic, I don't have a schematic I'm afraid, not exactly sure why it'd help (will add one tomorrow, its late here).
Safe to say, I have a digital output, and a analog input attached to the 4051 multiplexer.
When rapidly changing the pinMode to switch between writing the digital output, and reading the analog input, the LCD displays garbage, so something isn't playing nice.
PS: The issue only occurs when switching rapidly.

Sorry, this won't help, but I'm anxious to hear the resolution as I have a very similar problem with a 74HC4067 16 channel Mux/Dmux.

I also share the SIG pin between a analog pin to get input and a digital pin for output. When getting input, the digital pin is set to OUTPUT which should make it Hi-Z so it doesn't interfere with the analog pin. But it does interfere. (Sounds like your interference is on the input cycle though.)

I've tried turning off the internal pullup on the digital pin (setting it LOW while set as OUTPUT) but no joy.
(Hope this doesn't interfere with your answer.)

[edit]BTW - you might want to try a different analog than 5 - maybe I2C is an issue. [/edit]

Tried putting the multiplex pin on pin14 (analog 0), but same 'garbage' will get printed on the screen. Though it showed some promise, but after a couple of button presses, it went belly up again.

Though I suppose my wiring can be at fault for this test aswell... as in, all the LCD signal lines are on the analog port.. so always one of those is on pin 19.
Don't have a schematic, sorry, but I'll provide the next best thing, a table showing my wiring:

Digital device pin
0 bluetooth rx
1 bluetooth tx
2 range finder rx (NewSoftSerial)
3 range finder tx (NewSoftSerial)
4 multiplexer y0
5 multiplexer y1
6 multiplexer y2
7 currently vacant :o
8 servo
9 servo
10 servo
11 servo
12 servo
13 servo
Analog
0 (14) LCD DB4
1 (15) LCD DB5
2 (16) LCD DB6
3 (17) LCD DB7
4 (18) LCD RS
5 (19) multiplex z
Multiplexer (output unless otherwise stated)
0 bluetooth reset
1 range finder reset
2 LCD enable
3 LCD buttons (analog input)
4 vacant
5 vacant
6 vacant
7 photo resistive sensor (analog input)

The bluetooth device is a Promi-ESD module, the range finder is a URM37 ultrasonic sensor, in case it matters.
The LCD module is the LCD keypad shield by DFRobot.
The LCD shield: http://www.droboticsonline.com/index.php/arduino-lcd-keypad-shield.html
The product entry includes a library, and under additional information they have the manual.
The modifications I did to the library is changed the pin numbers.
And offcourse, change wherever it pulses the enable pin.. (just a single method), below the code for that method:

void LCD4Bit_mod::pulseEnablePin()
{
    digitalWrite(mux[0], Enable[0]);
    digitalWrite(mux[1], Enable[1]);
    digitalWrite(mux[2], Enable[2]); //LCD 'enable' pin selected

    digitalWrite(muxPin, LOW);

    delayMicroseconds(1);

    // send a pulse to enable

    digitalWrite(muxPin, HIGH);

    delayMicroseconds(1);

    digitalWrite(muxPin, LOW);

    delay(1);  // pause 1 ms.  TODO: what delay, if any, is necessary here?

}

@Imahilus
Did some experiments on switching between OUTPUT and INPUT. The results may have something to do with your problem.

Basically, I found that if a pin is OUTPUT and it's last state was HIGH, then when the pin is switched to INPUT, it's internal pull-up will be set. (If it's last state was LOW, they will not be set and it will float.)

I'm not sure how having pullups on or off may affect your results, but the converse is also true, if pullups are set on an INPUT pin, the PIN will init as HIGH when set to OUTPUT. If they are not, the pin will init as LOW.)

Try setting pin 19 LOW either before or after you set it to INPUT. (In my test it didn't matter.) You might also try resetting your output pins.

Here is the code I used to test this. I connected a DMM to the test pin and ground. You can spot the pullups by the slightly lower output V. "Pure" floating shows up a AC on my DMM.

#define TEST_PIN   7

void setup() {
  Serial.begin(9600);
}

void loop() {
  char response;                        // response from serial in
  Serial.println("o=OUTPUT, i=INPUT, h=HIGH, l=LOW");
  while(!Serial.available());           // wait for input
  response = Serial.read();

  switch(response) {

  case 'o':
    Serial.println("Output");
    pinMode(TEST_PIN,OUTPUT);
    break;

  case 'i':
    Serial.println("Input");
    pinMode(TEST_PIN,INPUT);
    break;

  case 'h':
    Serial.println("High");
    digitalWrite(TEST_PIN,HIGH);
    break;

  case 'l':
    Serial.println("Low");
    digitalWrite(TEST_PIN,LOW);
    break;

  default:
    break;
  }
}

Maybe this is a clue, maybe it's a red herring. (Seems strange to say that to a guy from Holland :)) You might also re-check your wiring to make sure your not getting some kind of crosstalk.

1 Like

Good detective work.

There is a description of the internal pull-up resistors here

so when using pinMode to set the same pin mode for both OUTPUT and INPUT, the pull-ups can be disconnected (and should be if you don't want them to be active)

pinMode(pin, INPUT);           // set pin to input
digitalWrite(pin, LOW);       // turn on pullup resistors

Nice find, BroHogan, appears you have found the culprit!
You may keep the herring though ;D

But there was no herring!
Anyway, happy it helped.