Arduino Button Mappings

It maybe too late in the development cycle for the kick-starter arduinos but has there been any attention paid to what buttons are placed on which pins on the arduino. Looking at the code you have. Buttons on pins
9, 8, 5, 10, A0, A1.

These pins map to

9 -> PB5
8 -> PB4
5 -> PC6
10 -> PB6
A0 -> PF7
A1 -> PF6

If the pins were to be placed all on port B for example getting input would be trivial

#define BUTTON_LEFT b00010000

input = PORTB;
if (input & BUTTON_LEFT) {
   ... do something

I believe the above sudo code would work highly efficiently.

Controls are already read in through a single byte

*#define B_BUTTON 1 (B00000001)*
*#define A_BUTTON 2 (B00000010)*
*#define DOWN 4 (B00000100)*
*#define RIGHT 8 (B00001000)*
*#define UP 16 (B00010000)*
*#define LEFT 32 (B00100000)*
uint8_t buttons;
buttons = display.getInput();

if(buttons & LEFT) {
  //do stuff
else if(buttons & RIGHT) {
  //do other stuff

getInput method

uint8_t Arduboy::getInput()
  // b00lurdab
  uint8_t value = B00000000;

  if (digitalRead(9) == 0) { value = value | B00100000; }  // left
  if (digitalRead(8) == 0) { value = value | B00010000; }  // up
  if (digitalRead(5) == 0) { value = value | B00001000; }  // right
  if (digitalRead(10) == 0) { value = value | B00000100; }  // down
  if (digitalRead(A0) == 0) { value = value | B00000010; }  // a?
  if (digitalRead(A1) == 0) { value = value | B00000001; }  // b?
  return value;

They aren’t placed on the same pin, but this method converts it to a single byte. Nice and simple!

The point is that the entire getInput() method, with all of those slooow digitalReads, could be replaced by a single instruction:

buttons = PINB;

But I suspect the reason the buttons are mapped over several different ports is to accommodate the pins needed for the display?

uXe you understand my point exactly I am going through the code out there with a fine tooth comb and trying to identify speedups and memory efficiencies.

You maybe right that there is some hardware decision that prevents this I do not know.

buttons = PORTB    // Takes 1 Clock cycle 

buttons = Arduboy::getInput();  // Takes much longer

I have no idea how much longer but to put this in perspective every function call takes 5 cycles (based on datasheet) and a return from the function takes 5 cycles. getInput() is a function that calls 6 digitalReads which again call more functions. I count 70 cycles right there. I do not believe I would be exaggerating to say it could mean a 500x speedup in the code.

That being said I am testing some ideas on speeding up that routine as we speak.

Don’t be bashful @kernel - all aspects of the source need cleanup - if a PR is made, it will most likely be accepted. It seems like you have some great ideas, and the project is open source so everyone’s ideas can be included. This is one of the reasons the source was released as a sample game and not as a standalone library - it just isn’t ready.

I did some speed testing on the getInput() routine. Each test consisted of getting the start time, getting the input value 10000 times, getting the end time, then displaying the difference on screen. Here are the results.

getInput (Current)    320060 micro seconds
digital Read           62256 micro seconds
replacement1           39620 micro seconds
replacement2           27672 micro seconds
PORTB                   3148 micro seconds

Here is my replacement code for getInput() it returns the value exactly the same as the original.

  uint8_t value = (PORTB & B00110000);    
  value = value | ((PORTC & B01000000) >> 3); 
  value = value | ((PORTB & B01000000) >> 4);
  value = value | ((PORTF & B11000000) >> 6);  

The second replacement code is a little faster but it does not return the keys in the same order.

  uint8_t value = ((PORTB & B01110000));   
  value = value | ((PORTC & B01000000) >> 4); 
  value = value | ((PORTF & B11000000) >> 6); 

Please Note that this code will need to be changed if the hardware were to change.

1 Like

This is nice. I think right now the bit-order could be changed… we should be asking people to use CONSTANTs for these things anyways, not hard coded #s. You also don’t need to call getInput if you only want to check a single button, but we don’t have function for that yet.

Using ports directly looks like a really nice optimization though.

I don’t think polling for buttons is the method that needs speed optimization the MOST, but hey we’ll take it where we can get it. :slight_smile:

+1 for getting all buttons on a single port for more efficient poling.
It would be nice to also maintain a sane scheme for the Leonardo board (to help keep the Dev platform broad). For reference:

Putting them on a single pin would certainly be a good move to make it a bit more efficient although by how much, I do not know. I can’t suspect it be much and I really don’t think its the CPU that is our biggest bottleneck here.

Having them all on one pin would still leave a bit open after arrows, AB and start. Perhaps a C button? Most other similar portable systems have bumpers on the top left and top right but that wouldn’t really be possible with this design. So an extra action button would perhaps be a nice idea?

Yeah, because you only need the poll the buttons every few ms. Gamebuino games get away with pulling every 50ms. If it takes 20 cycles to poll vs 3 no one is going to notice. So this is a niceness/storage (a few bytes) issues, not a CPU issue.

It’s less about CPU speed, more about reducing compiled Sketch sizes…

Yep, so all we need is a pull request. :smile:

PR for hardware changes? Bring it!

I vote for the C button. Compared to the original sega genesis controller, the only difference is shape. Plus, all the people who have complaints about a/b ordering will have slightly less reason to complain about, although I prefer ditching gameboy style and using the initials by far anyways. It’s not like any major gaming system uses that layout marking anymore. Nintendo ain’t what it used to be, and it makes me sad.

Since @bateske has said, in the comments section on Kickstarter, that he intends to add a Start button, there will now be 7 buttons total. Getting them all on one port would be difficult:

Port B:
2 pins, are needed to talk to the display in SPI mode. Two others can’t be used for GPIO when in SPI mode. That leaves only 3 pins on Port B.

Port C: Only 2 pins are physically present.

Port D: This is possible but it would be really nice if PD0 - PD3 were left unused and brought out to pads for hacking. PD0 and PD1 are for the I2C bus and PD2 and PD3 are Rx and Tx for hardware UART serial communication.

Port E:
Only two pins are physically present.

Port F:
Close, but only 6 pins are physicaly present.

This does not really matter. It simply doesn’t. To track extended button states (which quite a few games will need) you have to loop over an array and store hold counts anyways, and then there is turning the pull-ups on and off to save power… so at that point there isn’t much advantage at all. The hardware polling takes no time either way (for a normal 100+ FPS game), so we’re only talking about storage now… and reading from 3 ports and a few bit operations just is not that heavy code wise. Now someone will show the exact difference and might be a lot for JUST the polling but if you figure in the full button management and power saving code the % diff starts to get a lot less.

The above code could be optimized further to do less bitshifting since each shift is a single op against the processor, but I don’t know if that would result in a smaller compiled size.

There are lot of fatter places we could be looking it… maybe this just has caught peoples interest because it seems easy?

Again, I think a PR would be welcome here, but either @bateske will change the hardware or he won’t. I think he’s heard us by now.

To be clear my response is because I get this “OMG, the sky is falling if we can’t have one port” vibe from this thread - and that’s ridiculous. We’ll be fine either way. There would be a lot more benefit to the SSD1306 on the single port (parallel) we have than the buttons.

First, I’ll state that I agree with you that there isn’t much to gain, in the grand scheme of things, by having all the buttons on one port.

The internal pull-up on a pin only uses power while the button is being held down. In most games you would only be pressing one button at a time. I would guess that, worst case for most games, over all the pull-ups you would see the equivalent average power draw of one button down 100% of the time.

The minimum pull-up resistance is 20K. At 4V the current draw would be 0.2mA. The display alone will average about 14mA, which is 70 times what the pull-ups will use with the above estimation. Therefore, turning the pull-ups on and off is about as useful, from a power point of view, as putting all pins on one port is, from a speed point of view.

Ok, that closes off one other thought; all the Port B pins can be configured as Pin Change Interrupt sources. For some games, you can tremendously streamline your design if the game “just runs” on a main loop, and button presses are always an interrupt. But that would need 6 of 8 Port B pins, and that went away the moment the display used SPI. And I don’t see much advantage in having only some of the buttons being interrupt capable.

I have seen buttons overloaded on single analog pins using a resistor chain, but that removes the ability to handle multiple button press tracking at the same time. NKRO is valuable in a game system!!!

Hey Dreamer3- agreed this isn’t a burning issue. However, I think we all see the benefits of a PWM pin for audio. At this point, there’s no ‘small’ change to the PCB design IMHO- so why not prepare a wishlist! At least it’s useful for Gen2 :wink:

Some data from a quick test, compiling ‘Digital Input Pull Up’ - Ardunio IDE example:
5,466 bytes (19%) - As provided.
4,874 bytes (16%) - Removed Serial commands
4,764 bytes (16%) - Removed Digital read
4,774 bytes (16%) - Digital read replaced with direct PORT read
4,252 bytes (14%) - Empty default sketch, just void() and loop()

So you might save ~100 bytes for each digitalRead you can avoid. The significance of this is pretty subjective and relative to your needs… My take home message, is that for code requiring real performance you will avoid the Arduino IDE…

I"m not sure that’s right… the buttons are 1 when unpressed - they are held high all the time and only go low when pressed. I assume gamebuino wouldn’t have gone to all the trouble if there wasn’t some real power savings to be had (though I didn’t scan back thru the commit history - but that wouldn’t be hard).