Saturday, June 2, 2018

Finishing touches on LL530 v1.00 firmware

I'm currently cramming to get the first version of the firmware for my LL530 project out, so I can ship the first few ordered boards. (YAY!).

Quick backstory...

LL530 is a board with an Atmega32u4 (Arduino Leonardo/Pro Micro) and a few connectors on it so that you have a USB interface for Amiga/Atari ST/Atari mice, Atari controllers, and Amiga 500, 1000, 2000, 3000 keyboards.  Yes it even supports weird Atari controllers like Basic Programming's "Keyboard Controller" and Star Raiders' Video Touchpad, as well as the Indy 500 "Driving" controller that they made look identical to Paddles,... which are also supported.

Yes, there are other versions of portions of this, but I wanted more flexibility. I want to be able to just connect to the device, tell it that i have a mouse plugged in now, and it will just work... and in the future, auto-configuring the ports without even connecting... but i digress...

Yeah. I wrote a program in VCS Basic Programming via the Stella
emulator on my Mac.  Yeah. it's as cumbersome as it looks, but I'm
still freaking impressed that BASIC is even possible... it means only
one thing.. Warren Robinett is a god among men!
I'm getting off the subject here... okay...

Hey, Scott... don't forget the reason for this post...

Oh right.. Thank you Document McSubheading for reminding me.

No Prob..... now get on with it!

RIGHT.  So I'm trying to get all of the non-keyboard stuff working in one firmware.  I have a decent shell interface that lets you do all sorts of configuration stuff of the two ports, which I call "Port A" and "Port B".  I have a framework where you can pick the input device (joystick, mouse, paddle, etc) and then pick what it sends out to the host computer (mouse movements, joystick movements, various keyboard configurations.)

Now, the Arduino framework is kind of notorious for being slow in some respects.  That's the price you pay for ease of use.  One of the slowest is the port pin reads and writes.  For example:

digitalWrite( Pin3, HIGH )

 is a function that sets Arduino Digital Pin 3 as a HIGH value.  So internally, it looks at "Pin3", and figures out where on the ATmega microprocessor that maps to... The digital/analog pins from the Arduino are abstractions of the ATmega's actual ports.  for example digital pin 3 might actually be "PORTD" bit 5.  This function makes it so that beginners don't need to know that stuff... and what's more, all arduinos have digital pins 0, 1,2, 3, etc but generally map to different ATmega ports/pins... it might be PORTD bit 5 on a 328P, but it might be PORTG bit2 on a 32u4.  

Too much info.  TL;DR: digitalWrite() digitalRead() are an order of magnitude slower than just reading PORTG and masking off bit 2, which is near instantaneous.  (I should note that analog reads are slow, regardless, as the micro needs time to make the reading.)

I noticed this on the Eastman Theater sign project. I was using digitalWrite() calls to bit-bang out the data to the sign's shift register/LED drivers.  I was getting roughly 5 frames per second, when driving all 240 columns of 8 pixels...  When I switched it to direct bit writes, it went well over 60 FPS!

Mouse support....

I have all of the other controllers working, at least somewhat, and I've had a USB-Mouse interface using a 32u4/Pro Micro on my RaspberryPi/Amibian system for years now, so I knew it would work... but i was surprised when I finally got to working on the mouse device reading to find it wasn't working.  It was getting readings, but it was missing some... it was missing a lot of them.

To read a joystick, you only need to know "is it up now?"  "is it left now?"... not very time critical between readings.  However, for reading a mouse, you need to read all of the "gray code" sequence state numbers that it generates.  As it rotates left-right (the up-down axis is identical), it sends out, over two bits of the connector, a sequence of digits.

00 ⇨ 01  11  10  00 ⇨ 01  11 ⇨ ....etc
Moving left

00 ⇨ 10  11  01  00 ⇨ 10  11 ⇨ ....etc
Moving right

The mouse generates these regardless of you reading them. 

There was some slowdown in reading the mouse, so i was missing some of the readings... so instead of getting 00, 01, 11, 10, 00, 01, 11, ... I was reading 00, ... 11, 10, 00, .. 11, .. .. 01, ... 00,  which generated bad mouse movements, since those are sometimes recognized as left, sometimes as right, sometimes as completely invalid changes.

So it must've been the port reading?  Right?  That was my first thought too.  This thought was cemented further, when I tried out some stripped down firmware, which worked, but once i read from all three possible buttons, it would fail again.  I thought maybe the arduino framework was doing an analog read on one of those ports or something. I wrote some code to profile read/write times, and found that it was just as fast as expected.  (I had switched the port IO for this project to direct reads, without the framework long ago.)  (sidenote: Still not sure why commenting out button 3 had any affect... weird.)

So that wasn't it.

I eventually moved some simple mouse reading/generation code as a dedicated loop, which worked.. 

Next, I re-enabled my big switch statement that checks the input types, and checks the output types and does the right thing... to my surprise, it still worked.

I re-enabled some of the other polling routines... some are timing based, so they use the millis() function which of course has some overhead... and surprisingly, everything still worked.

Then I re-enabled the serial shell interface... and things broke... quickly!

But... the serial interface wasn't doing ANYTHING?!  what?!  All it's doing is first doing a
if( !Serial ) return;
The 32u4/Leonardo/Pro Micro will have major issues if you try to send out Serial data before it's done initializing that HID device.  This call just checks to see if the port is active.. I littered my code with them before Serial.print() blocks to prevent the thing from bricking again...  But even when the shell is active, i do a standard
if( Serial.available()) { .... }
to check if the user typed anything... then it pretty much returns...

The problem is... my assumptions were WRONG.

I threw some of these calls into my profiler, which is basically this kind of thing
startTime = millis();for( x=0 ; x<1000 ; x++ ) {    /* do the thing to test in here */}endTime = millis();
then it prints out (endTime - startTime)/1000, and you get the average time per loop.

if( Serial.available() ) { /* do nothing */ }

This took roughly 0ms per loop, so it's probably just checking a bit inernally...

if( !Serial ) { /* do nothing */ }
I thought this did the same sort of thing... Nope.  No matter what I did, 10ms.  Every time this is called, it blocks for 10ms.  No wonder that my mouse polling was failing! it was missing tons of gray code sequence states since the thing was almost constantly blocking in the "!Serial" check.


It's feeling better now. :D

Anyway... Here's some more info on the project in general:

Links for the project are in flux right now; once I get the first version of the firmware packaged up, i'll be setting up a proper website/hub for it with full documentation of the project.  Until then, here are some links:

Testing the system is kinda fun!  Although that 7800 controller is pretty horrible.

No comments:

Post a Comment