[WIP] Simon Merrett's Arduboy Clone

Just Wow. I know it seems simple but that’s what I really love about that little test you did there. Thanks so much for looking at this.

Next version of the pinout table:


Should just be the additional column to separate out Input/Output from Analog/Digital. BTW, the colour coding has blue for Port A and Green for Port B. I have too often missed that something is PB## rather than PA## because I just assumed everything would be on that. Hyphens are meant to indicate the cell is intentionally blank, rather than an empty cell which indicates I have omitted putting something in there.

Thanks for the tip - I do know u8g2 has succeeded it but I thought the original library was likely to have fewer layers of abstraction because it was trying to serve fewer controllers and displays, so might be easier to find the specifics for the UC1701.

I’m glad I’m not the only one who thinks so!

Yes, I had to sniff the protocol on our remote when I wanted to use it as the D pad for the robots I built my children.

BTW, ref the joystick, I desoldered it this evening and put it on the “correct” way round. Fortunately, it now works without needing to depress the centre button at the same time as changing direction (centre button and common pads were swapped when soldered on the wrong way!). The bad news is that somehow I have lined it up 180 degrees out of kilter. This makes up = down and left = right… The D pad buttons work fine and I will revise in a future board spin. I love the feel of that joystick though. Waaay better than my rubbish tactile switches and you can click in different directions really quickly because there’s so little travel from one side to another.

Have a good rest and I hope to catch up with you soon.

1 Like

Sometimes the dumbest solutions are the best.

“I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.” - Bill Gates

In all honesty I only thought of doing it in the first place because I was being too lazy to sit through and read all the code - I was actively looking for an easy solution. :P

(Programming is first and foremost about problem solving after all.)

Remind me, what are the ports?

I’m assuming they’re just a collection of pins,
but I’m used to there being 8 pins per port for AVR chips (unless I’ve misread the labels),
and yet here there seems to be more.

:P

Perhaps. I’ll have a look into it tomorrow, though it’s probably not going to be needed unless there’s some particularly interesting combinations of instructions.

For controlling the screen the only complicated parts are booting it, sleeping it, possibly setting the data ‘cursor’ and finding out how to interact with it in the first place.

Most of the commands are pretty simple.
It’s usually just a matter of throwing a byte or two at it over SPI.
For example, inverting the display (so white renders as black and vice versa) should be something along the lines of:

LCD_CD = 0;
LCD_WR = 0;
SPITransfer(0x53);

(Or something like that - it depends on what the APIs are like for sending data to the screen.)

I’ve previously considered building a ‘sonic screwdriver’ that would basically just be an IR remote so I could do stuff like turn off lights in my room without having to get up.

Like many things the idea fell to the wayside.

I found a pretty decent library for that sort of thing once,
but I don’t think I bookmarked it, or if I did I can’t find where I put it.

Ah, you must be one of those ‘cool’ parents I hear about.
My dad never built me a robot…

(If my parents ever had anything that needed to be assembled they’d usually get me to do it.:P)

I’ve always known that’s a possibility but never known anyone who’s actually done it before. :P

As someone who uses an XBox360 controller for various Steam games, I can believe it.

Oh yeah, sleep, I knew there was something I was supposed to be doing… :P

2 Likes

SWD stands for “Serial Wire Debug”

https://en.wikipedia.org/wiki/JTAG#Serial_Wire_Debug

Yes, that’s the exact same article that I linked to - that’s what my search found.

So, some good news. I used the u8g2 library with the Arduino Zero (same SAMD21G18A chip as on my DIY clone) and got the LCD working with the graphics full frame demo. Initially it was very pale and you could barely see the image moving but it was definitely there. I had added a u8g2.setContrast() to my previous setup with the Arduino pro mini 3.3v clone, so I did that again and I could see the images nicely:


I will try and sort some video out to show what I mean but there was definitely an issue with the speed at which pixels would turn on and off, making the animations look blurry. I upped the contrast value to 255 and this seems to have vastly improved the speed at which pixels turn on and off.

What would be a good next step for getting the Arduboy2Core to work with this test setup? I can wire any of the hardware pins to wherever they are required on the Arduino Zero, so can match the pinout on the Leonardo/Arduboy. But what else do we (I) need to do next to get the UC1701 driver working with the Arduboy2Core? Looking at the contributors on the Arduboy2 repo, I’m hoping for some input from @MLXXXp, @Mr.Blinky and @Pharap and would be very pleased to hear from anyone else with advice.

1 Like

Here’s a video showing what I mean by the blurring (contrast set to 220)

And here’s one where it’s better (contrast set to 255). There’s a shadow under the pixels which isn’t helping, caused by my desk lamp.

  1. Download a copy of Arduboy2.
  2. Empty out any function that uses assembly and put a // TODO: Implement this comment in its place
    • If it needs a return value, just return 0 or false or some suitable placeholder
  3. Empty out any function that depends on pins and put a // TODO: Implement this comment in its place
    • If it needs a return value, just return 0 or false or some suitable placeholder
  4. Write down all the functions you blanked out
  5. Implement the LEDs
  6. Implement the buttons
  7. Start implementing the screen
    • You can stick to using u8g2 and hiding it away if it’s fast enough, but ideally you probably want to have full control over the screen
  8. Anything that was implemented in assembly but doesn’t involve IO pins, have a look at my Pokitto version to see if you can ‘borrow’ a version from there
  9. Keep going until you get stuck, then either write your problems down and ask about them later or ask as you encounter them
  10. Start worrying about audio
    • Hopefully EEPROM and Serial work ‘out of the box’

This more or less how I built the Pokitto version, except instead of implementing the LEDs first I used the print functions for testing that everything was working as intended.

(If anything is missing from the SAMD implementation of the Arduino library then that’s possibly going to be a bit more complicated to fix. Not the actual implementing of the things, but where to put them in the code to avoid breaking games and making sure the licences are all being respected.)

1 Like

I call this hardware procrastination. Do something with HW to put off what needs to be done in SW. So, @filmote I got my screen back, and this time it’s got moving pixels!

Notes to self: ItsyBitsyM0 bootloader puts I2C on the SERCOM that the Arduino M0 uses for SPI. Using SW SPI for now but need to check out swapping the bootloader over at some point and, tbh, writing one especially for Arduboy SAMD21G18A.

Its looking pretty good. How fast is that screen? How many FPS can you crank it up to??

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