Help shifting bits around

I'm trying to figure out how I can effectively and efficiently swap the bits of a port and write it to another port.

char BTN_INPUT;
char BTN_OUT;
void loop()
{
  BTN_INPUT = PINA; // Read PORT A
 
  BTN_OUTPUT = // do bit swapping magic here

  PORTC = BTN_OUTPUT
  
}

For example:

PORTA == B10100110

I want to shift:

bit 0 to 7
bit 1 to 4
bit 2 to 6
bit 3 to 0
bit 4 to 3
bit 5 to 1
bit 6 to 2
bit 7 to 5

The result would be:

B01110001

and then set write them to PORTC.

Basically its a button swapper but I need it to be very fast so there is as little delay as possible between the input port (A) and when the arduino outputs the result (C).

What's the fastest way to shuffles these bits around? I tried but couldn't come up with any bit shifting logic and just ended up confusing myself. I would appreciate any help provided. Thank you.

At the simplest level you could use bitRead() and bitWrite() to create a variable with the result in it. How fast that would be I have no idea, but an array holding the target bits would mean that you could use a for loop to do the 8 reads and writes.

How about changing the wiring around to make the necessary shifts simpler?

What Arduino are you using?
On an Uno you cannot use all 8 bits of any of the Ports.

...R

I suspect you made an error: In your specification bit 3 is shifted to bit 0, in B10100110 bit 3 is cleared but in B01110001 bit 0 is set. This is not consitent.

Anyhow i wrote a short function to do the suffleling. Quite straitforward, there might be faster ways to do that by using xor and shift (google for bit reversal) but this does the job in a very clear way and is easy to modify.

const byte pattern[8] = {1<<7, 1<<4, 1<<6, 1<<0, 1<<3, 1<<1, 1<<2, 1<<5};

byte shuffle(byte in){
  byte result = 0;
  for(int i = 0; i<8; i++){
    result |= (pattern[i]*(in&1));
    in >>=1;
  }//for(i)
  return result;
}//shuffle()

Probably fastest when running (but a bit of time to set up) - you could define an array of 256 bytes, where each element is the transformed bit pattern corresponding to one of the input values.

byte inputPattern;
const byte outputPattern[256] = { 0b00000000, 0b10000000, 0b10010000, etc };
byte outByte = outputPattern[inputPattern];

You could write a separate (slower) program to calculate the values for you, which you could cut and paste into the main program code.

This is an Arduino Mega 1280.

Yes, the problem I'm running into is trying to avoid comparison operations and loops

I could easily achieve what I want using macros to check the bit states in one variable and set them in the other variable:

#define bit_read(p,m) ((p) & (m))
#define bit_write(p,m) ((p) |= (m))
#define bit(x) (0x01 << (x))

char BTN_IN;
char BTN_OUT;

if (bit_read(BTN_IN,BIT(1)))
{
   bit_set(BTN_OUT,BIT(4)));
}

And yes, that was an error on my part, I apologize nilton61.

I'm trying to avoid this if possible and keep the time taken to do all the button swapping under 1-2ms. I tested last night with my oscilloscope and just a simple:

char BTN_IN;
void loop()
{
PINA  = BTN_IN;
PORTC = BTN_IN;
}

It took roughly 1.49ms for the arduino to ground the pin on PORTC after the pin on PORTA was grounded. (Yellow is the input button, blue is the output)

I do plan to move this to a CPLD at some point for faster response but in the mean time I need to do it with an Arduino. So any optimizations or help in optimizing this would be appreciate. Thank you all for the replies.

Hackscribble:
Probably fastest when running (but a bit of time to set up) - you could define an array of 256 bytes, where each element is the transformed bit pattern corresponding to one of the input values.

byte inputPattern;

const byte outputPattern[256] = { 0b00000000, 0b10000000, 0b10010000, etc };
byte outByte = outputPattern[inputPattern];




You could write a separate (slower) program to calculate the values for you, which you could cut and paste into the main program code.

Would you please elaborate on this please? Since setup is not an issue and the actual main program speed is, this sounds like an attractive option. I just don't quite understand how this works. Thank you.

I haven't tried it.

Then do the following
new_value = (portA && 0x1) then do bit shift so that bit0 goes to bit7
new_value = new_value + (portA && 0x2) then do bit shift so that bit1 goes to bit4.
and so forth.

There are 256 possible values you can read in on an 8-bit port. Each of those values has one possible output value based on your mapping table. So you can set up a 256 byte array where, for example, element 42 has the output value you need if the input from the port was 42.

Hackscribble:
There are 256 possible values you can read in on an 8-bit port. Each of those values has one possible output value based on your mapping table. So you can set up a 256 byte array where, for example, element 42 has the output value you need if the input from the port was 42.

He is still going to have to do a loop to compare the portA values against the 256.
Granted he can break out when the match is found.

Im a little skeptical to your values.
When i ran this code on a UNO (it has no port A so i hade to chnge it a little) i got delays from input to output varying between 0,5 and 3 us.

char BTN_IN;

void setup(){
  for(int i = 0; i<6; i++){
    pinMode(14+i, OUTPUT);
    pinMode(8+i, INPUT_PULLUP);
  }//for(i)
}//setup()

void loop(){
  BTN_IN = PINB;
  PORTC = BTN_IN;
}//loop()

Ah yes, that wouldn't be what I'm looking for.

I haven't tried it.

Then do the following
new_value = (portA && 0x1) then do bit shift so that bit0 goes to bit7
new_value = new_value + (portA && 0x2) then do bit shift so that bit1 goes to bit4.
and so forth.

I tried something similar to this and it just failed. However, I will give this another go when I am able to test later this evening.

Thanks.

ieee488:
He is still going to have to do a loop to compare the portA values against the 256.
Granted he can break out when the match is found.

No, that's the beauty of this memory-consuming solution: the port value itself serves as the array index, so there's no search time (or O(1) if you insist :slight_smile: )

void loop()
{
PINA  = BTN_IN;
PORTC = BTN_IN;
}

This doesn't tie the two together at all.

Try this:

void setup(){
// code to make PORTA inputs with pullups enabled
DDRA= 0x00; // all inputs
PORTA = 0xFF; // write to input pins to enable pullups
// code to make PORTC outputs
DDRC = 0xFF; // all outputs
//

}
void loop()
{
PORTC =PINA;  // port c outputs will follow port a inputs
}

From 1280 Spec:
13.2.1 Configuring the Pin
Each port pin consists of three register bits: DDxn, PORTxn, and PINxn. As shown in “Table 13-34 and Table 13-
35 relates the alternate functions of Port L to the overriding signals shown in Figure 13-5 on page 73.” on page 95,
the DDxn bits are accessed at the DDRx I/O address, the PORTxn bits at the PORTx I/O address, and the PINxn
bits at the PINx I/O address.
The DDxn bit in the DDRx Register selects the direction of this pin. If DDxn is written logic one, Pxn is configured
as an output pin. If DDxn is written logic zero, Pxn is configured as an input pin.
If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is activated. To switch
the pull-up resistor off, PORTxn has to be written logic zero or the pin has to be configured as an output pin. The
port pins are tri-stated when reset condition becomes active, even if no clocks are running.
If PORTxn is written logic one when the pin is configured as an output pin, the port pin is driven high (one). If
PORTxn is written logic zero when the pin is configured as an output pin, the port pin is driven low (zero).

How about this?

(Make sure you setup PORTA to input and PORTC to output)

void loop()
{
  BTN_INPUT = PINA; // Read PORT A
 
  BTN_OUTPUT = convert(BTN_INPUT);

  PORTC = BTN_OUTPUT;
}

// the function 

byte convert(byte data)
{
  static const byte Pat[8] = {7,4,6,0,3,1,2,5};
  byte Output = 0;

  for(byte i = 0; i < 8; i++)
  {
    Output |= (((data >> Pat[i]) & 0x01) << i);
  }

  return Output;
}

Ohhhh I see. Yes that would work then!

Thank you. I can write something in .NET to produce the results for me in the array.

I will need 3-4 configurations however. Hope the mega 1280 can hold that adequately.

This doesn't tie the two together at all.

Error on my part. I'm going by memory from work. I believe it was

char BTN_INPUT;
void loop()
{
  BTN_INPUT = PINA;
  PORTC = BTN_INPUT;
}

Port A was set as INPUT_PULLUP using pinMode() in setup() iirc.

I did have a Serial.print() as well in the loop, but after the button mapping was completed. I don't think that would have effected the time it took the Arduino to ground the pin on PORTC.

Just tried this code on a Uno (therefore uses different registers).

Time from grounding pin 8 to seeing change on pin A0 is about 0.4us. So still worth pursuing the algorithm-based solutions rather than array lookup.

void setup() 
{
  pinMode(8, INPUT_PULLUP);
  pinMode(A0, OUTPUT);
  byte lastInput = PINB & 0x01;
  byte input = lastInput;
  PORTC = input;
  while (input == lastInput)
  {
    lastInput = input;
    input = PINB & 0x01
    ;
  }
  PORTC = input; 
}

void loop() 
{
}

I'll check it out. To be fair, the buttons being pressed were going to a seperate pcb that also outputs. So there could be some delay between the button, the pcb, and then the arduino.

When I get home tonight, I will try just the button straight to the arduino and give all the suggestions a shot. Thank you all so much for your contributions!

igendel:
No, that's the beauty of this memory-consuming solution: the port value itself serves as the array index, so there's no search time (or O(1) if you insist :slight_smile: )

Ah, I get!

Ok I'm home from work and did some testing, without the middle man PCB and using this code:

char buttons = 0;

void setup()
{
 DDRA = B00000000;
 DDRC = B11111111; 
// pinMode(22,INPUT_PULLUP);  // Not used, have a 10K pullup on the bread board
}

void loop()
{
  buttons = PINA;
  PORTC = buttons;
}

It's about 9.54us

Just some insight for those curious. I am going to try to implement the suggestions given to me. Thank you all again for the help.