[WIP] Simon Merrett's Arduboy Clone

No idea yet! Just moving through the basics at tortoise speed (me, not the screen) at the moment.

1 Like

Check. Forked and have it sitting in my arduino libraries folder.

Nearly done this.

Might have forgotten to do this but will find all the // TODO: and sort.

The LEDs stuff is confusing to me and uses timers which will need sorting out to get PWM, so I’m skipping over this for now. Please say if it’s critical or significantly advantageous to do this before implementing buttons and just using digitalWrite() as the LED feedback for button presses detected by the Arduboy2 library.

I’m having a go at this now. Another post to follow with questions.

I will need a lot of help with this. I can’t see which parts of the SPI commands are ATMEGA32U4-specific and which are transferrable to the SAMD21. I have compared the two SPI.h / SPI.cpp versions in the Arduino hardware folders but I’m still confused.

Thanks for that. I could see that you were bold in deliberately not implementing some things. I can learn from that at least!

See below, soon!

much further down the line! Although Serial could be helpful during development of the screen…

So I have started trawling through Arduboy2Core.h and Arduboy2Core.cpp.
I started moving things round and adding a #if defined (_SAMD21_) way of differentiating between the SAMD and AVR versions, as I had seen the Adafruit port to Arcada do. For some things I will leave it in place but I may just remove all the existing stuff and have a version that only deals with the SAMD21, until we have all the functions working. It’s nice to have the AVR code nearby though, so leaving it in has been helpful so far, to see what the other parts of the library are expecting.

I’m trying to do the buttons at the moment. AVR has some nice port manipulation in there which reads the buttons:

 // up, right, left, down
  buttons = ((~PINF) &
              (_BV(UP_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) |
               _BV(LEFT_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT)));
  // A
  if (bitRead(A_BUTTON_PORTIN, A_BUTTON_BIT) == 0) { buttons |= A_BUTTON; }
  // B
  if (bitRead(B_BUTTON_PORTIN, B_BUTTON_BIT) == 0) { buttons |= B_BUTTON; }

and we have these kind of defines

#define LEFT_BUTTON_BIT PORTF5

And we know what those do without having to lookup PORTF5 (if we couldn’t guess) because further up we have:

#define LEFT_BUTTON _BV(5)  /**< The Left button value for functions requiring a bitmask */

However, on the SAMD family, port manipulation isn’t as neat as it is for AVR. So for now I’m thinking of just using digitalRead() and OR’ing results together to create the required return uint8_t.

Is that approach prudent at this stage and does this code appear to achieve the same result (perhaps not speed) as the orginal above?

  buttons = ((digitalRead(PIN_LEFT_BUTTON) << 5) | 
		(digitalRead(PIN_RIGHT_BUTTON) << 6) | 
		(digitalRead(PIN_UP_BUTTON) << 7) | 
		(digitalRead(PIN_DOWN_BUTTON) << 4));

if(digitalRead(PIN_A_BUTTON == 0) { buttons |= (1 << 3); }			
if(digitalRead(PIN_B_BUTTON == 0) { buttons |= (1 << 2); }

Forgive the magic numbers - at this stage I’m not sure if the SAMD21 in Arduino IDE will support _BV().

Having the LEDs in place is mainly just so you have some kind of visual feedback so you can test what is and isn’t working.

If Serial is already working then you can use a serial monitor instead.

To start with you might be best off using u8g2 as a back-end if it implements all the necessary functions.

Technically it’s still in development.

I’ve got more stuff working on my local copy than what I’ve published,
but I haven’t got round to finishing it because I keep finding new things to work on.

The main reason I directed you to it is because I’ve replaced all the assembly in the functions that I have implemented, and apart from one or two functions the code I’ve replaced the assembly with should be applicable to your console too.

In particular, instead of replacing all the drawing functions with functions that draw to the Pokitto’s backbuffer,
I chose to maintain Arduboy2’s 1KB backbuffer and then have a function that draws the SSD1306-format backbuffer onto the Pokitto’s backbuffer, changing the format as it goes.

I specifically chose to do that for maximum compatibility.
It allows some of the 3rd party graphics libraries (e.g. @Botisaurus’s Tinyfont) to function without any extra effort.

Is that the royal we? :P

_BV is a non-standard macro introduced by avr-libc (specifically in the file avr/sfr_defs.h).

It’s defined as: #define _BV(bit) (1 << (bit)),
so that example translates to (1 << 5).

Are there some examples somewhere?

The only ARM Cortex port manipulation code I’ve ever seen uses the mbed library, which I’m guessing isn’t available for SAMD21.

I’m guessing from the code you’ve written all the buttons are technically on different ports,
whereas AVR has them all mapped to the same port so they can be read in parallel?

That should work, but it might be somewhat slower.

If you get an error from _BV or bitRead or something similar then chances are the version of the Arduino library for SAMD21 CPUs doesn’t fully implement avr-libc.

Assuming:

  • UP_BUTTON_BIT is 7
  • RIGHT_BUTTON_BIT is 6
  • LEFT_BUTTON_BIT is 5
  • DOWN_BUTTON_BIT is 4

Then yes, it should be equivalent.
Though you’re missing a close round bracket on the bottom two digitalReads:

if(digitalRead(PIN_A_BUTTON == 0) { buttons |= (1 << 3); }			
if(digitalRead(PIN_B_BUTTON == 0) { buttons |= (1 << 2); }

Should be:

if(digitalRead(PIN_A_BUTTON) == 0) { buttons |= (1 << 3); }			
if(digitalRead(PIN_B_BUTTON) == 0) { buttons |= (1 << 2); }

Personally I’d suggest writing it a bit more like:

uint8_t buttons = 0;

buttons |= (digitalRead(PIN_LEFT_BUTTON) << 5);
buttons |= (digitalRead(PIN_RIGHT_BUTTON) << 6);
buttons |= (digitalRead(PIN_UP_BUTTON) << 7);
buttons |= (digitalRead(PIN_DOWN_BUTTON) << 4);

if(digitalRead(PIN_A_BUTTON) == 0)
	buttons |= (1 << 3);
			
if(digitalRead(PIN_B_BUTTON) == 0)
	buttons |= (1 << 2);

So it’s a bit clearer.

Either that or:

uint8_t leftButtonBit = (digitalRead(PIN_LEFT_BUTTON) << 5);
uint8_t rightButtonBit = (digitalRead(PIN_RIGHT_BUTTON) << 6);
uint8_t upButtonBit = (digitalRead(PIN_UP_BUTTON) << 7);
uint8_t downButtonBit = (digitalRead(PIN_DOWN_BUTTON) << 4);

uint8_t buttons = (leftButtonBit | rightButtonBit | upButtonBit | downButtonBit);

if(digitalRead(PIN_A_BUTTON) == 0)
	buttons |= (1 << 3);
			
if(digitalRead(PIN_B_BUTTON) == 0)
	buttons |= (1 << 2);

One thing I am concerned about though is whether digitalRead definitely returns only 1 or 0.
According to the documentation they only return LOW or HIGH, which probably are 0 or 1,
but even so it’s best to double-check or you could end up with something unexpected.

In the original what’s happening is a little bit different because it’s reading a single port and then masking it, so (_BV(UP_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) | _BV(LEFT_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT)) should be constant-folded into a single byte mask.

That would be a bit clearer if it were written more like:

constexpr uint8_t buttonMask = (_BV(UP_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) | _BV(LEFT_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT));
buttons = ((~PINF) & buttonMask);

Don’t worry about it too much, as long as you make a note of what they’re supposed to be somewhere.

Personally I think using _BV is actually bad idea anyway, for two main reasons:

  • Because it’s an undocumented feature of avr-libc which would theoretically be removed because it isn’t part of the public API
    • The fact they put a _ at the front is an indication that they were probably trying to hide it, or indicate that it wasn’t meant for public use
  • Because it obfuscates the meaning. (1 << x) should be perfectly well understood by all C++ programmers (and many other programmers), whilst a programmer not familiar with AVR or Arduino will have no idea what _BV means
1 Like

On high contrast the LCD is I think right on the verge of being passable. I wish it was just a bit better because games with a lot of fast action (most) will still struggle!

Nice work I’m really impressed with the progress!

2 Likes

I have hit a bootloader issue. I’m now pretty sure I want a crystalless board but I can’t find a bootloader that will work for crystalless and the pinout that @MLXXXp used with the Sparkfun SAMD21 board for his prototype. Before I try and modify and make a custom one, does anyone know of anything suitable?

I’m also wondering about the screen - should I cut my losses on the 2.8" LCD and go for a new one or consolidate the progress made on getting it running in the Arduboy2 library and live with any residual ghosting that I can’t erase in eg contrast settings?


EDIT: I think I could possibly continue to use the CRYSTALLESS Arduino Zero bootloader that I have but define the SPI as per the Sparkfun board used by @MLXXXp in a new variant.h, variant.cpp and boards.txt and place them in the local hardware folder for Arduino IDE to find. Will update with progress when I can find a time to try this. I’m not even sure a bootloader has much to do with the SPI settings that I’m interested in (pads, pins etc)!

Anyone know how to flip this display? :laughing:


Don’t worry, I’m sure I’ll work it out! Very pleased to have got this far. Note to self - if sticking with LCDs, don’t ignore the datasheet where it talks about viewing angle…

I’ll expand on the bootloader/variant/boards.txt stuff over on the Porting Arduboy2 library to SAMD thread as I have questions about where these kinds of hardware config files should be shared/stored.

image

image

When you get a chance could you run some more tests of the refresh rate?

Run any fast speed games or some horizontal and vertical scrolling tests?

Is there any way to overdrive the refresh rate/strength? i.e. maybe a slightly higher voltage helps?

1 Like

:rofl:

Yes, I need to try and compile a game from source but will just do a basic screen print and move characters if that doesn’t work.

That’s what I’m hopeful changing some settings in the display controller will do. There’s stuff in the datasheet about LCD charge pump voltage, bias ratios, temperature compensation etc. All to play for.

1 Like
Assuming you already haven't discovered, have you tried sending a "Set Inverse Display" command?
Action C/D W/R D7 D6 D5 D4 D3 D2 D1 D0
Set Inverse Display, DC [0] 0 0 1 0 1 0 0 1 1 DC0

It’s #11 in the command table on page 12, and it’s mentioned again on page 15.

From what I gather it doesn’t immediately flip the display,
it changes the direction that data is written to the display in,
so you’ll have to send another frame worth of data for the command to take effect.

Disregard, @filmote’s right:

Set DC[0] to force all SEG drivers to output the inverse of the data (bit-wise) stored in display RAM.

In which case it’s probably either Set SEG Direction or Set COM Direction.

1 Like

Isn’t inverse just white on black?

1 Like

I inverted both these to orientate the screen with PCB. Not sure what those spurious pixels are but I won’t likely use this config for the next HW version as I’ll either change to another screen or rotate it in the PCB.

1 Like

So having decided that the LCD screen was not the way forward for my build, I have gone back to the schematic for a 0.2 revision. One other aim was to reduce component count from V0.1. Key changes/points to note are:

  • SH1106 OLED 1.3" screen. Has anyone integrated one of these without using a module and do you have anything to say about the way I have it wired below? What value for the IREF resistor did you select?
  • Lithium battery is swapped for 2*AAA batteries and a boost to 3.3V. Regulated 3.3V from USB power is switched (with hardware SPDT switch) as an input to the boost circuit so it can be purely powered from USB if needed. I saw this as helping to avoid the need for schottky diodes, P-MOSFETs etc while enabling the use of a single polyfuse and a power switch when under battery power alone. Lost the USB C for ease of assembly.
  • Lost the crystal. The SAMD21 I’m using seems to work well enough with a crystalless bootloader and doesn’t rely on anything too high speed/tolerance timing-wise in normal operation.
  • Lost the SD card slot. Have kept the footprint for the flash memory.
  • Revised the sound wiring to try and match suggested configuration by @MLXXXp. A solder jumper gives options. Another SPDT switch allows the speaker to be turned off (but headphones remain active). EDIT: The audio circuit is messed up in this image. I’ll redo. Suggestions most welcome on how to drive a piezo and be able to switch to headphones, while accommodating future options to use DAC and Speaker2 pin.
  • Got rid of the 5 way joystick. For now. I really like that little thing but I wanted to simplify this board.
  • Left fitted for IR comms. Not sure why but I really want to keep the possibility of this alive.
  • Added the SOICbite connector, as the 0.05" pin headers for the ICE programmer are not the cheapest and more importantly are a bit fiddly and delicate, so not worth populating if I can flash the bootloader with a SOIC clip. I have already done this with a SAMD51 board so if I was feeling bold I’d remove the 0.05" header completely. But it’s in for this rev.
  • Battery meter now has a protection resistor in series, as no voltage divider is necessary on two AAA cells in series for a 3.3V microcontroller.
  • Speaker will be an SMD 12x12mm piezo.

Comments are most welcome.

2 Likes

You could consider driving the piezo speaker directly from the pins, as you are doing now, but add an amplifier for the headphones.

The TI TPA2005D1 may be a good candidate for the amp. Its differential inputs could be treated the same as the two speaker pins.

If you wanted to change from a piezo speaker to a regular voice coil speaker, you could use the amp for both the headphones and the speaker.

1 Like

Thanks @MLXXXp. I’m definitely in the mood for Muntzing with this design so I won’t include an audio amp. I decided to test something basic and it seems to have worked. I took an Arduino M0 and got it playing a short tune using the tones() library (on repeat - I have almost gone potty listening to it). Having read around a bit my main aim was to control the voltage peaks so that the headphone line voltage would result in a) no damage to headphones and b) acceptable volume - not too high for my children but still decent.

Some of the reading suggested using a series capacitor:


I haven’t tried this but am open to the idea. The high series resistor value in my design means I don’t think the absence of a capacitor is a significant issue as even if the pin were left high, the leakage current would be less than 0.5mA.

As I was playing with the voltage divider idea, I thought about how nice it might be to have software controlled headphone volume, so that I could get rid of the need for a nice wheel potentiometer. So I have comandeered two unused GPIOs to act as high impedence inputs or grounded current sinks, thereby varying the line voltage and resultant volume. I have tested this with some standard Apple earbuds and I have three voltages/volumes available. I will probably set volume to medium as default and perhaps add a momentary push button to cycle through the three modes. This can all live in a separate library or become part of the Arduboy2 SAMD port, depending on whether people want headphones and volume control via software.


Feels like this is nearly ready for layout. Any further comments?

1 Like

Just playing with component positions here - no traces yet.



The battery holders will hopefully contribute to the ergonomics. Speaker, inductor, IR LED, IR rx, 3.5mm jack and SWD header are all on the rear, as they take up more height than other components on the front (apart from the control buttons which are supposed to sit proud of the other components!). I’m thinking of sticking the OLED on top of the passives, using double sided foam tape and maybe some kapton for insulation if needed. The slot at the top is for the oled flat flex.

5 Likes

And failing that they might make good hand warmers. :P

1 Like

Probably best to put the OLED onto flat pcb, the glass is really fragile and does well with a rigid backing.

1 Like

Hopefully not or there is something seriously wrong with the batteries and you should toss them away like a hand grenade :wink:

2 Likes

Now moved the components that were under the OLED to the rear and have routed (messily). I think this will do until I can actually bring one up.


4 Likes