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.