WebbotLib AVR library
WebbotLib It just does it
  C++ documentation  C documentation

iopin.h

"Oh no! Not another way to use I/O pins"
The answer is 'Yes - but with good reason'.
Other libraries don't really provide any help for managing the digital I/O (input/output) pins but leave it down to you to set/clear the correct bit in the correct port. Perhaps using:
sbi(PORTD,PD4);
or
PORTD |= BV(PD4);
or
PORTD |= BV(4);
or
PORTD |= 1<<4;
or
PORTD |= 1<<PD4;
or
PORTD |= 16;
They all do exactly the same thing on PORTD Pin4 - and there are lots of extra ways as well. So the mechanism is not very tight - too many choices. Yes it works - but if you suddenly want to move the same device to PORTB Pin 2 then it's all a bit of nightmare trying to find all the places to change.
Yes - you can simplify things by defining a set of macros of your own but now you are just adding to the confusion by having 'yet another way' and there is nothing to force you to use these new macros.
Existing libraries are unaware of the board you are using. For example: the processor on the Axon has lots of I/O pins that aren't connected at all on the Axon. So you can write a program that uses these pins only to realise your mistake and change to another pin. We prevent this by removing the definition of I/O pins that are redundant on the board you are using. The library provides a number of 'pre-built' boards but you can add extra ones for the custom boards you build.
But the biggest reason for change is traceability. This is impossible when a given I/O port may be switched on/off in lots of places. Removing all of this and making all I/O go through one function makes it easier to keep track of what's going on and be able to log any changes in appropriate ways. These ways may include:-
1. Output all I/O pin changes to the SoR'scope so you can a time graph on the PC
2. Output I/O changes to an LCD display connected to the robot
3. Setting a single breakpoint for all I/O changes
So for these reasons I've changed how you do I/O.
This file is automatically included from your system file and hence knows the pins it supports. For each pin we create a two character variable. The first character is the port and the second is the pin. So PORTD Pin 6 will be called D6. Having done that for each pin then the old variables for each port and pin (PORTB, DDRB, PINB etc) are deleted to stop you using them. Hence you have to go through the functions in this file.

 

Function

 


pin_make_input(const IOPin* io, boolean pullup)

Convert an I/O pin into an input pin.
The 'pullup' parameter allows you to specify whether or not the pin should add a pull-up resistor. If in doubt then use FALSE.
So to change B6 to an input with no pull-up then you could write:-
pin_make_input(B6, FALSE);
or if you want to have a pull-up then:-
pin_make_input(B6, TRUE);
You can then test the input value on the pin using pin_is_high and/or pin_is_low.

boolean pin_is_input(const IOPin* io)

Test if a given pin is an input pin. Return TRUE if it is or FALSE if it is currently an output pin.
Example:
if(pin_is_input(B6)){
    // B6 is an input
}else{
    // B6 is an output
}

pin_make_output(const IOPin* io, boolean val)

Convert an I/O pin into an output pin and set its initial state.
The second parameter specifies the initial value of the output pin and should be TRUE if you want the output pin to be high, or FALSE if you want it to be low.
You can then change the output value on the pin using pin_high, pin_low, pin_set, pin_toggle and pin_pulseOut

boolean pin_is_output(const IOPin* io)

Test if a given pin is an output pin. Return TRUE if it is or FALSE if it is currently an input pin.
Example:
if(pin_is_output(B6)){
    // B6 is an output
}else{
    // B6 is an input
}

pin_high(const IOPin* io)

Change an output pin to high.
For example to set the output pin B6 high then use:-
// Make B6 an output pin - you only need to do it once
// So if B6 is always an output pin then you may do this in appInitHardware
pin_make_output(B6,FALSE); // Start with a value of low
// Set B6 high
pin_high(B6);
If the pin is not currently configured as an output pin, ie you haven't called pin_make_output then calling pin_high will cause a runtime error to be indicated.

pin_low(const IOPin* io)

Change an output pin to low.
For example to set the output pin B6 low then use:-
// Make B6 an output pin - you only need to do it once
// So if B6 is always an output pin then you may do this in appInitHardware
pin_make_output(B6, TRUE); // Initial value is high
// Set B6 low
pin_low(B6);
If the pin is not currently configured as an output pin, ie you haven't called pin_make_output then it will cause a runtime error to be indicated.

pin_set(const IOPin* io, boolean val)

Allows you to set an output pin to high or low depending on the second parameter.
This is useful when the pin can be set high or low depending on the rest of your code. Normally you would use a boolean variable to store the result - ie TRUE if you want the pin to be set HIGH, or FALSE if you want it to be set LOW.
For example to change the output pin B6 then use:-
// Make B6 an output pin - you only need to do it once
// So if B6 is always an output pin then you may do this in appInitHardware
pin_make_output(B6, FALSE); // Start with a low value
 
// The variable used to note whether it should be set high or low
boolean setB6;
 
// Add your code here to change 'setB6' to TRUE or FALSE
...
 
// And then change the pin
pin_set(B6, setB6);
If the pin is not currently configured as an output pin, ie you haven't called pin_make_output then it will cause a runtime error to be indicated.

pin_toggle(const IOPin* io)

This will flip a given output pin.
If it was low it becomes high, if it was high it becomes low.
For example to flip B6 then use:-
// Make B6 an output pin - you only need to do it once
// So if B6 is always an output pin then you may do this in appInitHardware
pin_make_output(B6, FALSE); // Start with B6 low
// Set B6 high
pin_high(B6);
// Toggle B6 - since it was high then it will become low
pin_toggle(B6);
// Toggle B6 - since it was low then it will go back to high
pin_toggle(B6);
If the pin is not currently configured as an output pin, ie you haven't called pin_make_output then it will cause a runtime error to be indicated.
The main advantage of this function is when sending out pulses via an output pin. Sometimes you want the pin to be high and send a low pulse. Other times the pin is low and you want to send a high pulse. So this can be achieved by toggling the pin, waiting for the required delay, and then toggling it back to its previous state.
Hence the following will set the pin low and then send a positive pulse:-
void appInitHardware(void){
    // Make B6 an output and set its normal state to low
    pin_make_output(B6, FALSE);
}
 
// Then in your main code you can send a pulse using
pin_toggle(B6);
delay_ms(10);
pin_toggle(B6);
However: there may be a scenario where the output pin needs to be high and a pulse is sent by setting the pin to low for a short duration. This could be done using:
void appInitHardware(void){
    // Make B6 an output and set its normal state to high
    pin_make_output(B6,TRUE);
}
 
// Then in your main code you can send a pulse using
pin_toggle(B6);
delay_ms(10);
pin_toggle(B6);
Note that the initial state is set within appInitHardware but the remaining code stays the same.

boolean pin_is_high(const IOPin* io)

Test if a given pin is high. This works for both input and output pins.
For input pins it will return TRUE if the attached signal is high or FALSE if not.
For output pins it will return TRUE if the output is currently high or FALSE if not.
So if you want your code to do something different depending on whether B6 is high or low then you could write:
if(pin_is_high(B6)){
    // B6 is high so do scenario 1
}else{
    // B6 is low so do scenario 2
}

boolean pin_is_low(const IOPin* io)

Test if a given pin is low. This works for both input and output pins.
For input pins it will return TRUE if the attached signal is low or FALSE if not.
For output pins it will return TRUE if the output is currently low or FALSE if not.
So if you want your code to do something different depending on whether B6 is high or low then you could write:
if(pin_is_low(B6)){
    // B6 is low so do scenario 1
}else{
    // B6 is high so do scenario 2
}

pin_pulseOut(const IOPin* io, TICK_COUNT us, boolean activeHigh)

Emit a single pulse to the pin.
This will change the pin to an output pin and then send a pulse of the given number of microseconds.
On return the pin will be an output pin set low.
So to send a 10ms high pulse to B6 you write:-
pin_pulseOut(B6, 10000, TRUE);
and to send a 10ms low pulse to B6 you write:-
pin_pulseOut(B6, 10000, FALSE);

TICK_COUNT pin_pulseIn(const IOPin* io, boolean activeHigh)

Measure the duration of an incoming pulse.
This will change the pin to an input and then measure and return the duration of an incoming pulse.
If the second parameter is TRUE then the duration is how long the pulse was set to high, otherwise it is how long it was set to low.
On return the pin will be set as an input and the returned value is in microseconds.
So if you want to measure an incoming pulse on B6 that is active high, ie __|-----|___ then you would write:-
TICK_COUNT us = pin_pulseIn(B6, TRUE);
But if you want to measure an incoming pulse on B6 that is active low, ie ---|___|--- then you would write:-
TICK_COUNT us = pin_pulseIn(B6, FALSE);

Valid XHTML 1.0 Transitional