genuinequality

Download free music MP3s on genuine quality, the world’s largest online music catalogue, powered by your scrobbles. Free listening, videos, photos, The world’s largest online music catalogue, powered by your scrobbles. Free listening, videos, photos, stats, charts, biographies and concerts. stats, charts, biographies and concerts.

Sunday, June 22, 2025

Atari 2600 Controller Shield PCB Revisited – Part 2

This has another look at my updated Atari 2600 Controller Shield PCB in order to attempt to read all four paddle controllers a bit more accurately and efficiently. Warning! I strongly recommend using old or second hand equipment for your experime…
Read on blog or Reader
Site logo image Simple DIY Electronic Music Projects Read on blog or Reader

Atari 2600 Controller Shield PCB Revisited – Part 2

By Kevin on June 22, 2025

This has another look at my updated Atari 2600 Controller Shield PCB in order to attempt to read all four paddle controllers a bit more accurately and efficiently.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

If you are new to Arduino, see the Getting Started pages.

Theory and Design

The previous code (Atari 2600 Controller Shield PCB Revisited) just used analogRead, but set out the basic algorithm required to read the paddles.

To recap, this is what needs to be done:

SET pin to OUTPUT
SET pin to LOW
SET pin to INPUT
Start timer
WAIT WHILE (voltage on the analog INPUT pin is NOT HIGH)
Stop timer

But this has to be done for each of the analog inputs for the paddles, and to do that efficiently means doing it in parallel for each input.

The update algorithm is thus going to be:

Configure a 100uS TICK timer.
On the FIRST TICK:
Set all analog pins to OUTPUT
Set all analog pins to LOW
Set all analog pins to INPUT

On subsequent TICKS:
Read the analog value for each analog pin
IF value < threshold THEN
Increment counter for that pin

On last TICK:
Convert the counter for each analog pin into a pin "reading"

Keep repeating the sequence

I'm using the TimerOne library to setup my recurring TICK timer. This makes counting the time easier, as with a fixed TICK I just need to count the TICKs themselves.

I'm no longer stopping once the threshold has been reached though, I just stop counting for that pin. The whole sequence is determined by an overall counter which has to be long enough to support the longest reading.

In the end, after lots of experimentation, I've gone with the following values:

  • Analog read value threshold for "complete": 900 (out of 1023)
  • TICK timer period: 100uS
  • Total number of TICKS for each pass through the algorithm: 1000

As part of working through the above, I needed to make a note of the maximum and minimum number of TICKS that I want to correspond to a final 0 or 1023 when converting to an analog value. I used the following (again after a lot of experimentation):

  • Start: 10 - to correspond to analog value 0
  • End: 900 - to correspond to analog value 1023

To get to the final analog reading from the stored pin counter, I used the Arduino constrain and map functions as follows:

map(constrain(count,START,END),START,END,0,1023);

Actually in order to ensure that "clockwise" increases the value, I actually then subtract the above from 1023 to reverse the sense of the pots.

Efficient Analog Readings

One thing I was very conscious of though, the Arduino analogRead() function is notoriously slow to execute, so as I'm using this up to four times in this timer routine, I need that to be as slick as possible.

I know it is possible to create a "fastAnalogRead" function, as I know the Mozzi library has an implementation. This pokes the registers of the ATMega328 directly, as covered in section 28 of the ATMega328P datasheet.

The main issue is the ADC on an ATMega328 takes a bit of time to actually produce a reading, so the basic process involves starting the conversion; waiting until it says the conversion is complete; then read the value. And this has to be done for each ADC channel one at a time.

I can recommend Tom Almy's excellent "Far Inside the Arduino" to walk through the details. It is possible to configure a single conversion, which mirrors the Arduino's analogRead() function; a continuous free-running read, which is fine for a single channel; or an interrupt-driven free-running mode that can work through each channel in sequence.

I've used the latter leading to the following functions, which are hard-coded to read A0 to A3 only:

// Direct analog reading taken from
// "Far Inside the Arduino" by Tom Almy.
uint8_t adcIdx;
volatile uint16_t adcVal[4];
void fastAnalogReadStart (void) {
// Start the analog read continuous process.
adcIdx = 0;
DIDR0 |= 0x3F; // Turn off all digital input buffers for A0-A7
ADMUX = (1<<REFS0); // Use VCC Reference and set MUX=0
ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADIE) | 7; // Start with interrupts and PreScalar = 7 (125kHz)
}

void fastAnalogReadStop (void) {
// Stop ADC, stop interrupts, turn everything off
ADCSRA = 0;
}

uint16_t fastAnalogRead (int adc) {
if (adc >= 4)
return 0;

uint16_t adcvalue;
cli(); // disable interrupts
adcvalue = adcVal[adc];
sei(); // reenable interrupts
return adcvalue;
}

// ADC reading interrupt routing
ISR(ADC_vect) {
digitalWrite(T_ADC, HIGH);
uint16_t last = adcVal[adcIdx];
adcVal[adcIdx] = ADCW;
// Move onto next MUX
adcIdx = (adcIdx+1) % 4;
ADMUX = (1<<REFS0) + adcIdx;
// Start new conversion
ADCSRA |= (1<<ADSC);
digitalWrite(T_ADC, LOW);
}

Curiously, this apparently takes around 104us per analog read, so I need to leave a bit of time when I first enable the analog process for the first few readings to appear. I do this by skipping a few TICKs prior to starting the main logic of the reading and counting process.

The counting process calls fastAnalogReadStart() and fastAnalogReadStop() to control the otherwise asynchronous running of the analog process alongside the timer process.

Efficient Digital Processing

In a similar manner, using pinMode and digitalWrite for each of the analog pins at the start of the process can be quite slow for a timer-driven interrupt routine too. So I've used PORT IO directly to drive all four analog pins as follows:

    DDRC  = DDRC | 0x0F;     // A0-A3 set to OUTPUT
PORTC = PORTC & ~(0x0F); // A0-A3 set to LOW (0)
// wait for a TICK then
DDRC = DDRC & ~(0x0F); // A0-A3 set to INPUT

All analog pins are on PORTC and so can be handled directly as shown above. This is, once again, hard-coded to work with A0 to A3 only.

The Code

Wrapping all this up gives a usable way to read the Atari paddle controllers. Note that there are two parallel 100uS interrupts going on continuously for this to work.

The final test code I've produced adds in four digital outputs to allow measuring the following:

  • The start-up phase of the counting process (yellow).
  • The timer routine (blue).
  • The ADC routine (purple).
  • The final loop.

The colours refer to the following scope display, which shows the counter start-up and subsequent timer and ADC routines kicking in.

We can clearly see the ADC processing stopping prior to the start (the trigger point and yellow HIGH trace), as well as the timer routine taking a lot longer at this point. Presumably this is running four of the map/constrain operations on the counter readings to turn them into analog readings.

I'm fine with that, that seems to work fine for me.

It is also possible to see how the ADC interrupt is running a fraction slower than the timer interrupt. If the former is 104uS and the latter 100uS that would be about right I'd say.

The main Arduino loop simply prints out various values sampled (at "loop speed") from the processing as it happens.

The fields are ordered as follows:

count [alg1 count1 raw1] [2] [3] [4]

Where:

  • count = global counter
  • algN = final calculated analog value from pin N
  • countN = snapshot of the counter value for pin N
  • rawN = raw analog value being read for pin N

Here is a sample output:

If the readings are limited to a single channel, rather than all four, then it makes quite a good set of readings for the Arduino Serial plotter as follows:

It's not perfect, but it gives a pretty good idea what is going on - especially when watched live as the controller is changed.

The top (purple, value 5) line is the sampled actual raw analog value for the pin, so we can see the capacitor charging up fairly clearly.

The blue (value 1) line is the global TICK counter counting between 0 and 1000.

The yellow (value 4) line is the pin's individual counter and we can see where it "caps out" near the top and stops counting as the raw analog value reaches the threshold I've set.

The final green (value 3) line is the calculated pseudo analog value (0 to 1023) that corresponds to the above. This is essentially flat as I'm not turning the controller at this point.

The above are all samples taking at "Arduino loop" speed, which is slowed down by the serial printing, but it gives a pretty good idea of what is going on.

As this is a bit more complicated, I've put the whole code up on GitHub here:

Closing Thoughts

This has taken me a very long time to get to this point and there has been quite a bit of head-scratching and experimenting with different values. I tried various thresholds, timer periods (thinking faster would give greater resolution), and various min/max values.

The above are what seem to work best for me and give me the best coverage of the whole range of the controller. Some seemed more accurate but restricted the usable range of the pot for example.

But I think I can actually work with the above now in a fairly usable manner.

Kevin

Comment
Like
You can also reply to this email to leave a comment.

Simple DIY Electronic Music Projects © 2025.
Unsubscribe or manage your email subscriptions.

WordPress.com and Jetpack Logos

Get the Jetpack app

Subscribe, bookmark, and get real‑time notifications - all from one app!

Download Jetpack on Google Play Download Jetpack from the App Store
WordPress.com Logo and Wordmark title=

Automattic, Inc.
60 29th St. #343, San Francisco, CA 94110

Posted by BigPalaceNews at 8:42 AM
Email ThisBlogThis!Share to XShare to FacebookShare to Pinterest

No comments:

Post a Comment

Newer Post Older Post Home
Subscribe to: Post Comments (Atom)

Search This Blog

About Me

BigPalaceNews
View my complete profile

Blog Archive

  • June (86)
  • May (105)
  • April (95)
  • March (131)
  • February (111)
  • January (104)
  • December (98)
  • November (87)
  • October (126)
  • September (104)
  • August (97)
  • July (112)
  • June (113)
  • May (132)
  • April (162)
  • March (150)
  • February (342)
  • January (232)
  • December (260)
  • November (149)
  • October (179)
  • September (371)
  • August (379)
  • July (360)
  • June (385)
  • May (391)
  • April (395)
  • March (419)
  • February (356)
  • January (437)
  • December (438)
  • November (400)
  • October (472)
  • September (460)
  • August (461)
  • July (469)
  • June (451)
  • May (464)
  • April (506)
  • March (483)
  • February (420)
  • January (258)
  • December (197)
  • November (145)
  • October (117)
  • September (150)
  • August (132)
  • July (133)
  • June (117)
  • May (190)
  • January (48)
Powered by Blogger.