Friday, 25 August 2017

1351 Mouse Progress: Detecting when the SID chip drains the POT lines


The following is all from Daniël Mantione, who is doing great work on working out how to emulate a 1351 mouse using an Arduino, which we intend to use to make a solid-state joystick which will also work as a proportional 1351-compatible mouse:

 

The comparator

It turns out that the analog comparator on the Arduino is located on digital pins 6 and 7. It does not share any functionality with the analog ADC pins.
Two pins are necessary: The comparator compares the voltage on both pins, one cannot compare with a voltage specified in software. The internal resistance of the Arduino is incredible, pins configured as inputs have a resistance of many megaohm, so a resistor from 5V to the pin doesn't really work, you need two resistors to create a voltage divider.
I took my box of electronic components and after a bit of searching and experimenting I came up with a 100 kOhm and a 15 kOhm resistor. 100 kOhm from 5V to the pin and 15 kOhm from the pin to ground:

This did result in a voltage of 0.65V on the pin, which is good for our purposes. Now, once the voltage drops below this, we want an interrupt.
This turned out to be relatively easy to code. After a bit of experimentation, I came up with the following Arduino program:
unsigned long sid_measurement_cycles=0;

ISR(ANALOG_COMP_vect) {
sid_measurement_cycles++;
digitalWrite(LED_BUILTIN, HIGH);
delayMicroseconds(20);
digitalWrite(LED_BUILTIN, LOW);
}

void setup() {
ACSR =
(0<<ACD) | // Analog Comparator: Enabled
(0<<ACBG) | // Analog Comparator Bandgap Select: AIN0 is applied to the positive input
(0<<ACO) | // Analog Comparator Output: Off
(1<<ACI) | // Analog Comparator Interrupt Flag: Clear Pending Interrupt
(1<<ACIE) | // Analog Comparator Interrupt: Enabled
(0<<ACIC) | // Analog Comparator Input Capture: Disabled
(1<<ACIS1) | (0<ACIS0); // Analog Comparator Interrupt Mode: Comparator Interrupt on Falling Output Edge

pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
pinMode(6, INPUT); //Avoid interfering with comparator
pinMode(7, INPUT); //Avoid interfering with comparator
Serial.begin(9600);
Serial.println("Start");
}

void loop() {
if (sid_measurement_cycles % 1000 == 1) {
Serial.print("SID measurement cycles: ");
Serial.println(sid_measurement_cycles);
}
}



Basically, the analog comparator needs to be activated, told to trigger when the voltage drops below (rather than above) the reference voltage and told to generate interrupts.
The interrupt service routine is very simple: It increases a counter and then briefly flashes the internal led of the Arduino. The internal led is pin 13, and can be connected to an oscilloscope. Connecting the second input of the oscilloscope to pin 13:



...the result was as follows:




Exactly the intended result. The Arduino serial monitor shows:

Start
SID measurement cycles: 1
SID measurement cycles: 2
SID measurement cycles: 1001
SID measurement cycles: 2001
SID measurement cycles: 2002
SID measurement cycles: 3001
SID measurement cycles: 3002
SID measurement cycles: 4001


This went much smoother than I expected.

Now, the next steps are going to be quite a bit more difficult. First, we need to wait 256 microseconds. This is easy to achieve using busy waiting, but doing so, would mean that the Arduino would spend the majority of its CPU time inside the ISR. This already sounds like bad taste.

Then we need to generate pulses on POTX and POTY. Doing this with busy waiting as well, is possible would make the Arduino spend the vast majority of its time inside the ISR. Might make it unworkable to do anything usefull with the Arduno besides transmitting data.

In other words, we need to do the transmission without involving the CPU. Using the PWM generators of the Arduino would sound a good approach. This involves advanced timer programming: We first need to program a timer to generate an interrupt at exactly the right time, then program two timers to generate PWM pulses.

The Arduino has 1 16-bit timer and 2 8-bit timers. The 16-bit timer allow the timers to count at high frequency (16 MHz) and is therefore most suited for high precision. But there exists only 1 16-bit timer, while we need to generate two pulses.

In other words, I am not completely certain yet what the best approach is. Perhaps I will try first to generate a timer interrupt at the right time and then generate both pulses with busy waiting. That should already result in less than half of the CPU time spent in ISRs. As both pulses need to be generated inside a single loop, the loop body will contain branches and this might have an effect on accuracy as well. But we'll see.


No comments:

Post a Comment