Society of Robots - Robot Forum

Electronics => Electronics => Topic started by: DTM22 on November 17, 2011, 02:59:59 PM

Title: Arduino Port Manipulation
Post by: DTM22 on November 17, 2011, 02:59:59 PM
I'm attempting to use port manipulation for a project I'm working on where the timing is very crucial.  Ive read several tutorials about it online but I am still unclear on how to read whether the appropriate INPUT pins read HIGH or LOW.  So far I've determined I need to write: "DDRD=B00111100;" to set Pins 1,2,6 and 7 as INPUTS and the rest as OUTPUTS. 
The only ones I'm actually using are 6 and 7.  I read not to change pins 1 and 2 as altering their state could disrupt the functionality of the micro controller. 
Ive determined I need to use the "PIND" function but Im not certain how to write the appropriate code for what I want to do....

Does anyone have any knowledge about Port Manipulation that could help me out?  I need to check when pins 6 and 7 respectively change state from LOW to HIGH..  Any help would be much appreciated!   
Title: Re: Arduino Port Manipulation
Post by: rbtying on November 17, 2011, 03:10:18 PM
So, pins 6 and 7 in Arduino are PD6 and PD7 in the ATMega328P header definitions.

To set them as inputs:
Code: [Select]
DDRD &= ~( ( 1 << 6 ) | ( 1 << 7 ) );

This does a bitwise AND to set the 6th and 7th bits of DDRD to 0, thereby putting them in input mode.

To check the values:
Code: [Select]
bool pin_6_is_high = PIND & ( 1 << 6 );
bool pin_7_is_high = PIND & ( 1 << 7 );

DDRD, PIND, and PORTD are not functions, they're registers in the memory of the microcontroller. As such, you manipulate them via bitwise operations--it is rather dangerous to do as you said ("DDRD=B00111100;") as you're not taking into account the current state of the register, which may lead to unexpected consequences.

Some useful things to know:

Setting a bit in a register:
Code: [Select]
REGISTER |= ( 1 << BIT );

Clearing a bit in a register:
Code: [Select]
REGISTER &= ~( 1 << BIT );

Flipping a bit in a register:
Code: [Select]
REGISTER ^= ( 1 << BIT );

Checking a bit in a register:
Code: [Select]
bool status = REGISTER & ( 1 << BIT );
Title: Re: Arduino Port Manipulation
Post by: DTM22 on November 17, 2011, 03:43:47 PM
Thanks alot!  Now that I know how to check the value of a PIN I have just one more question...how would I go about checking the pins state and having a particular function performed when they are read as a HIGH.  typical "If" function, "while"?? how would I write that in code? 
Title: Re: Arduino Port Manipulation
Post by: joe61 on November 17, 2011, 05:53:35 PM
I depends a little on what the input will be. For most things other than a button press you can use a pin change interrupt. For example, if you have a sensor connected to pin 6, which signals an event by putting 5V on the pin, then a pin change interrupt is fine. You don't want to mix button presses and interrupts though, because buttons bounce when pressed, and you're likely to get more than one interrupt for any given press.

The ATmega328 data sheet (http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf) gives details in section 12 "External Interrupts", but basically you can set it either pin to trigger an interrupt when it toggles. See also the avr-libc page (http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html) on interrupt handling

I haven't done much with these interrupts, so take this for what it's worth. Hook up LEDs to pins PB0 and PB1, and toggle PD6 and PD7 high and low.

Code: [Select]
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>

#define pin6 (1 << PD6)
#define pin7 (1 << PD7)

ISR (PCINT2_vect)
{
    static uint8_t pd6 = 0;
    static uint8_t pd7 = 0;
    uint8_t tmp6;
    uint8_t tmp7;

    // Get the current state of the two pins
    tmp6 = (PIND & (1 << PD6));
    tmp7 = (PIND & (1 << PD7));

    if (tmp6 != pd6)
    {
        // PD6 toggled, save current value and
        // toggle associated LED
        pd6 = tmp6;
        PORTB ^= (1 << PB0);
    }

    if (tmp7 != pd7)
    {
        // PD7 toggled, save current value and
        // toggle associated LED
        pd7 = tmp7;
        PORTB ^= (1 << PB1);
    }
}

int main()
{
    // Set PD6 and PD7 as inputs
    DDRD &= ~((1 << PD6) | (1 << PD7));

    // Set PB0 and PB1 as output (LED indicators)
    DDRB |= (1 << PB0) | (1 << PB1);

    // Specify pins PD6 and PD7 as interrupt
    // sources. See data sheet 12.2.6
    PCMSK2 |= (1 << PCINT23) | (1 << PCINT22);

    // enable pin change interrupt for pins
    // PCINT23 - 16
    PCICR |= (1 << PCIE2);

    // Enable interrupts globaly
    sei ();

    for (;;)
    {
    }
}

This is just throwaway code that may not even work under some circumstances. The interrupt handler could be more efficient, etc, But hopefully it gives the general idea

Joe

Title: Re: Arduino Port Manipulation
Post by: DTM22 on November 18, 2011, 09:15:44 AM
Thanks, but Id rather not use interrupts if I can avoid it, is there any other way to perform what I want?
Title: Re: Arduino Port Manipulation
Post by: joe61 on November 18, 2011, 10:24:09 AM
Ignore this, I hit tab out of habit and wound up posting before I was done.
Title: Re: Arduino Port Manipulation
Post by: joe61 on November 18, 2011, 10:26:59 AM
Sure, you can just poll the pins at whatever interval you like, for example

Code: [Select]
int main()
{
    DDRD &= ~ ((1 << PD6) | (1 << PD7));
    
    for (;;)
    {
        if (PIND & (1 <<PD6))
            handlePD6 ();
        if (PIND & (1 <<PD7))
            handlePD7 ();

        _delay_ms (100);
    }
}

Where handlePD[67] are functions you write to do what you want.

Joe
Title: Re: Arduino Port Manipulation
Post by: DTM22 on November 18, 2011, 02:19:10 PM
Thanks, Im trying to apply this to my code but I keep getting the error message saying PD6 and PD7 were not declared in this scope, how do I got about declaring them?
Title: Re: Arduino Port Manipulation
Post by: Soeren on November 18, 2011, 02:27:16 PM
Hi,

No offense ment, but why are you posting software questions in "electronics"?
They really ought to go into "software".

Quote from: joe61
You don't want to mix button presses and interrupts though, because buttons bounce when pressed, and you're likely to get more than one interrupt for any given press.
Interrupt control is the best way for lots of button pres decoding cases (depending on the app of course). Contact bounce is easily handled in an ISR.
Title: Re: Arduino Port Manipulation
Post by: DTM22 on November 18, 2011, 02:39:04 PM
Sorry about that, Ive moved the post over to the appropriate section.
Title: Re: Arduino Port Manipulation
Post by: bens on November 19, 2011, 11:43:35 PM
Thanks, Im trying to apply this to my code but I keep getting the error message saying PD6 and PD7 were not declared in this scope, how do I got about declaring them?

PD6 and PD7 are automatically defined when you are using the ATmega168, but the ATmega328P include files replaced this style of pin definition with PORTD6 and PORTD7.  If you still want to use "PD6" and "PD7", you can insert the following at the top of your program:

#define PD6 PORTD6
#define PD7 PORTD7

Alternatively, you can just use the numbers 6 and 7 for PD6 and PD7, respectively, as this is how they're ultimately defined on the ATmega328P.

- Ben