Go Down

Topic: Manage ports? (Read 3598 times) previous topic - next topic

Nadir

As I mentioned I saw these functions in Wiring source. After testing them I put them in wiring_digital.c to make them built-in.

They works well in regular code also.  But you should add "false" after return of statement "if (inputregister == NOT_A_PORT) return;"
in portRead function.

CrossRoads

if (inputregister == NOT_A_PORT) return false;

Like that?
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Nick Gammon


in the last 2 cases, what does
cli();
do?


He is protecting the port change from interrupts. So to comment it:

Code: [Select]

        uint8_t oldSREG = SREG;   // remember interrupt flag (and other flags)
cli();          // turn interrupts off
*portregister = val;    // do something, knowing interrupts won't occur
SREG = oldSREG;     // put interrupt flag back (set it again, if it was set before)
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon


They works well in regular code also.  But you should add "false" after return of statement "if (inputregister == NOT_A_PORT) return;"
in portRead function.


You could return 0 (false is a bit misleading), but you can't tell the difference between supplying an invalid port (which returns zero) and reading a valid port, and them all being off (which returns zero).
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

CrossRoads

So what should this line be then?

if (inputregister == NOT_A_PORT) return;
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Nick Gammon

Since portRead is declared to return a byte (uint8_t) it seems reasonable to return a number in the range 0 to 255. (Returning true or false would be more reasonable if it was declared to return a boolean type).

A reasonable value would be 0, like this:

Code: [Select]
uint8_t portRead(uint8_t port)
{
volatile uint8_t *inputregister;

inputregister = portInputRegister(port);

if (inputregister == NOT_A_PORT) return 0;

uint8_t oldSREG = SREG;
cli();
uint8_t portValue = *inputregister;
SREG = oldSREG;
return portValue;
}


Bear in mind, as I said before, a return of 0 could either mean "not a port" or "it is a port and all bits are off".
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

CrossRoads

Okay, I changed my sample code to be return 0, altho hopefully I'll be awake enough when I write some code to use this to no try & access nonexistent ports.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.


I have mooched the port manipulation functions from wiring platform. Copy them into "wiring_digital.c" to have built-in port manipulation.

Functions
Code: [Select]
void portMode(uint8_t port, uint8_t mode)
{
volatile uint8_t *moderegister;

moderegister = portModeRegister(port);

if (moderegister == NOT_A_PORT) return;

uint8_t oldSREG = SREG;
cli();

//*moderegister = mode;
if (mode == OUTPUT)
*moderegister = 0xff;
else
*moderegister = 0x00;

SREG = oldSREG;
}

uint8_t portRead(uint8_t port)
{
volatile uint8_t *inputregister;

inputregister = portInputRegister(port);

if (inputregister == NOT_A_PORT) return;

uint8_t oldSREG = SREG;
cli();
uint8_t portValue = *inputregister;
SREG = oldSREG;
return portValue;
}

void portWrite(uint8_t port, uint8_t val)
{
volatile uint8_t *portregister;

portregister = portOutputRegister(port);

if (portregister == NOT_A_PORT) return;

uint8_t oldSREG = SREG;
cli();
*portregister = val;
SREG = oldSREG;
}


Example
Code: [Select]

void setup() {
  portMode(PD, OUTPUT);
}

void loop() {
  portWrite(PD, B0001000);
  delay(500);
  portWrite(PD, B0000000);
  delay(500);
}



Well... I can't understand this code :smiley-eek:
--> www.cortoc.com
--> www.jrletranc.com

#23
Sep 19, 2011, 03:47 am Last Edit: Sep 19, 2011, 03:57 am by jrlc Reason: 1
My Wiring_digital.c have this:

Code: [Select]
/*
 wiring_digital.c - digital input and output functions
 Part of Arduino - http://www.arduino.cc/

 Copyright (c) 2005-2006 David A. Mellis

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General
 Public License along with this library; if not, write to the
 Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 Boston, MA  02111-1307  USA

 Modified 28 September 2010 by Mark Sproul

 $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $
*/

#include "wiring_private.h"
#include "pins_arduino.h"

void pinMode(uint8_t pin, uint8_t mode)
{
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *reg;

if (port == NOT_A_PIN) return;

// JWS: can I let the optimizer do this?
reg = portModeRegister(port);

if (mode == INPUT) {
uint8_t oldSREG = SREG;
               cli();
*reg &= ~bit;
SREG = oldSREG;
} else {
uint8_t oldSREG = SREG;
               cli();
*reg |= bit;
SREG = oldSREG;
}
}

// Forcing this inline keeps the callers from having to push their own stuff
// on the stack. It is a good performance win and only takes 1 more byte per
// user than calling. (It will take more bytes on the 168.)
//
// But shouldn't this be moved into pinMode? Seems silly to check and do on
// each digitalread or write.
//
// Mark Sproul:
// - Removed inline. Save 170 bytes on atmega1280
// - changed to a switch statment; added 32 bytes but much easier to read and maintain.
// - Added more #ifdefs, now compiles for atmega645
//
//static inline void turnOffPWM(uint8_t timer) __attribute__ ((always_inline));
//static inline void turnOffPWM(uint8_t timer)
static void turnOffPWM(uint8_t timer)
{
switch (timer)
{
#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A:   cbi(TCCR1A, COM1A1);    break;
#endif
#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B:   cbi(TCCR1A, COM1B1);    break;
#endif

#if defined(TCCR2) && defined(COM21)
case  TIMER2:   cbi(TCCR2, COM21);      break;
#endif

#if defined(TCCR0A) && defined(COM0A1)
case  TIMER0A:  cbi(TCCR0A, COM0A1);    break;
#endif

#if defined(TIMER0B) && defined(COM0B1)
case  TIMER0B:  cbi(TCCR0A, COM0B1);    break;
#endif
#if defined(TCCR2A) && defined(COM2A1)
case  TIMER2A:  cbi(TCCR2A, COM2A1);    break;
#endif
#if defined(TCCR2A) && defined(COM2B1)
case  TIMER2B:  cbi(TCCR2A, COM2B1);    break;
#endif

#if defined(TCCR3A) && defined(COM3A1)
case  TIMER3A:  cbi(TCCR3A, COM3A1);    break;
#endif
#if defined(TCCR3A) && defined(COM3B1)
case  TIMER3B:  cbi(TCCR3A, COM3B1);    break;
#endif
#if defined(TCCR3A) && defined(COM3C1)
case  TIMER3C:  cbi(TCCR3A, COM3C1);    break;
#endif

#if defined(TCCR4A) && defined(COM4A1)
case  TIMER4A:  cbi(TCCR4A, COM4A1);    break;
#endif
#if defined(TCCR4A) && defined(COM4B1)
case  TIMER4B:  cbi(TCCR4A, COM4B1);    break;
#endif
#if defined(TCCR4A) && defined(COM4C1)
case  TIMER4C:  cbi(TCCR4A, COM4C1);    break;
#endif
#if defined(TCCR5A)
case  TIMER5A:  cbi(TCCR5A, COM5A1);    break;
case  TIMER5B:  cbi(TCCR5A, COM5B1);    break;
case  TIMER5C:  cbi(TCCR5A, COM5C1);    break;
#endif
}
}

void digitalWrite(uint8_t pin, uint8_t val)
{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *out;

if (port == NOT_A_PIN) return;

// If the pin that support PWM output, we need to turn it off
// before doing a digital write.
if (timer != NOT_ON_TIMER) turnOffPWM(timer);

out = portOutputRegister(port);

if (val == LOW) {
uint8_t oldSREG = SREG;
               cli();
*out &= ~bit;
SREG = oldSREG;
} else {
uint8_t oldSREG = SREG;
               cli();
*out |= bit;
SREG = oldSREG;
}
}

int digitalRead(uint8_t pin)
{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);

if (port == NOT_A_PIN) return LOW;

// If the pin that support PWM output, we need to turn it off
// before getting a digital reading.
if (timer != NOT_ON_TIMER) turnOffPWM(timer);

if (*portInputRegister(port) & bit) return HIGH;
return LOW;
}

void portMode(uint8_t port, uint8_t mode)
{
volatile uint8_t *moderegister;

moderegister = portModeRegister(port);

if (moderegister == NOT_A_PORT) return;

uint8_t oldSREG = SREG;
cli();

//*moderegister = mode;
if (mode == OUTPUT)
*moderegister = 0xff;
else
*moderegister = 0x00;

SREG = oldSREG;
}

uint8_t portRead(uint8_t port)
{
volatile uint8_t *inputregister;

inputregister = portInputRegister(port);

if (inputregister == NOT_A_PORT) return;

uint8_t oldSREG = SREG;
cli();
uint8_t portValue = *inputregister;
SREG = oldSREG;
return portValue;
}

void portWrite(uint8_t port, uint8_t val)
{
volatile uint8_t *portregister;

portregister = portOutputRegister(port);

if (portregister == NOT_A_PORT) return;

uint8_t oldSREG = SREG;
cli();
*portregister = val;
SREG = oldSREG;
}


now how to use it? :S

Well I tried to add it to regular code and it doesn't work... sends me a lot of errors :/ even if I put the functions on the C program...
--> www.cortoc.com
--> www.jrletranc.com

CrossRoads

The example was given back reply #11

void setup() {
 portMode(PD, OUTPUT);  // sets the pins in Port D to be outputs
}

void loop() {
 portWrite(PD, B0001000);  //sets the Port D outputs to B00001000
 delay(500);
 portWrite(PD, B0000000);  //sets the Port D outputs to B00000000
 delay(500);
}
and I imagine that
byte variablePort = portRead (PD);
would read the port bit states into variablePort
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.


The example was given back reply #11

void setup() {
 portMode(PD, OUTPUT);  // sets the pins in Port D to be outputs
}

void loop() {
 portWrite(PD, B0001000);  //sets the Port D outputs to B00001000
 delay(500);
 portWrite(PD, B0000000);  //sets the Port D outputs to B00000000
 delay(500);
}
and I imagine that
byte variablePort = portRead (PD);
would read the port bit states into variablePort



yes, I copied that and added the functions but it sends me a lot of errors.

I copied into the c progrman under the void loop function

also I copied into the wiring_digital.c but it still sending me errors...
--> www.cortoc.com
--> www.jrletranc.com

Nadir

Sorry my fault you also have to put function prototypes in "Arduino.h".
Function prototypes are;
Code: [Select]
void portMode(uint8_t port, uint8_t mode);
uint8_t portRead(uint8_t port);
void portWrite(uint8_t port, uint8_t val);

WizenedEE

#27
Sep 19, 2011, 07:43 am Last Edit: Sep 20, 2011, 02:14 am by WizenedEE Reason: 1
How is
Code: [Select]

value = portRead(PB);

different from  
Code: [Select]

value = PINB;


besides being a lot slower to execute? Pretty much the same thing for the other two.

Edit: fixing code tags...

Nadir

@WizenedEE

Yes you are right, direct hardware usage is always faster than prototype functions.  But most of Arduino users including me have not electronic background. For us really hard to understand MCU hardware basics. And the aim of Arduino platform is to make the usage of microcontroller easier to everyone. IMO with these port functions every Arduino user can easily use the ports.

Graynomad

I think there are some potential gotchas with this code

Code: [Select]
uint8_t oldSREG = SREG;
cli();
uint8_t portValue = *inputregister;
SREG = oldSREG;
return portValue;


What if you get an interrupt after you save SREG but before the cli()? I think you should swap those two lines.

And what if you get one that modifies the port after the SREG restore but before the return? In this case I guess it's essentially the same as a port changing state just after you read it so maybe no harm done.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Go Up