Go Down

Topic: Simulating Nintendo 3DS touchscreen input (Read 1 time) previous topic - next topic

Kyohack

Apr 15, 2015, 09:36 pm Last Edit: Apr 15, 2015, 10:57 pm by Kyohack
Hi! I've been working on a project that involves porting over some code from a Teensy device onto my arduino. The original project was designed to allow a Teensy microcontroller to control a Nintendo 3DS by simulating button inputs and touchscreen control. Here's the link to the source code and schematics that I am using as a reference:
https://github.com/dekuNukem/3xtDS

I have button inputs working perfectly, but I am having trouble simulating touchscreen input. Basically I've unplugged the touchscreen digitizer of my Nintendo 3DS and have put my arduino in its place. I am not sure if my schematic has an issue or if the arduino's PWM frequency limitations are the problem.

Here's an example of a touchscreen tap that I'm trying to simulate:


Here's the useful section of the Teensy code that I ported:
Code: [Select]
#include <stdint.h>
#include <stdio.h>
#define TOUCH_SCREEN_X_PIN 23
#define TOUCH_SCREEN_Y_PIN A14
#define TOUCH_SCREEN_Y_SENSE_PIN 19
#define DAC_9_BIT_1V8 279

void setup()
{
    analogWriteResolution(9);
analogReadResolution(10);
// PWM frequency at 90KHz
analogWriteFrequency(TOUCH_SCREEN_X_PIN, 90000);
    pinMode(TOUCH_SCREEN_X_PIN, OUTPUT);
pinMode(TOUCH_SCREEN_Y_PIN, OUTPUT);
    analogWrite(TOUCH_SCREEN_X_PIN, 0);
    disable_touch_screen();
}

void touch_screen_click(uint16_t x, uint16_t y, int16_t duration_ms)
{
if(x <= 0 || y <= 0 || x > 320 || y > 240)
return;
int16_t x_potential = ((double)x / 320) * DAC_9_BIT_1V8;
int16_t y_potential = ((double)y / 240) * DAC_9_BIT_1V8;,

analogWrite(TOUCH_SCREEN_X_PIN, x_potential);
enable_touch_screen();
int32_t start = millis();
while(millis() - start < duration_ms)
{
// wait until Y+ pin is in output mode
while(analogRead(TOUCH_SCREEN_Y_SENSE_PIN) < 512);
delayMicroseconds(490);
// now Y+ pin is in input mode, write the value
analogWrite(TOUCH_SCREEN_Y_PIN, y_potential);
delayMicroseconds(180);
// pull Y+ down again to keep interrupt going
enable_touch_screen();
}
disable_touch_screen();
analogWrite(TOUCH_SCREEN_X_PIN, 0);
delay(50);
}


void disable_touch_screen()
{
analogWrite(TOUCH_SCREEN_Y_PIN, DAC_9_BIT_1V8);
}

// pull Y+ pin low to initialize 3DS's touch interrupt
void enable_touch_screen()
{
analogWrite(TOUCH_SCREEN_Y_PIN, 0);
}


And here's the code for arduino:
Code: [Select]

// I've included the setPwmFrequency() function listed on the arduino site, but I'm omitting it here for clarity reasons.

// The main change between the Teensy and arduino is that the teensy uses software to lower the PWM voltage from teensy's native 3.3v down to the nintendo's 1.8v, while I have instead used a voltage divider on the PWM output of the arduino.

// X+ pin of touchscreen.
int touchX = 9;

// Y+ pin of touchscreen.
int touchY = 10;

// Pin to monitor arduino's output of Y+
int senseY = 11;

void setup() {
  // Change pins 9 and 10 to have 31,250 hz PWM frequency. This is the highest I can set it.
  // Teensy originally set their frequency to 90khz.
  // My difference in frequencies MIGHT be the cause of my problems.
  // I picked pins 9 and 10 because they won't affect timing for millis() and delay.
  setPwmFrequency(9, 1);
  setPwmFrequency(10, 1);
  
  // Original teensy code set analog write resolution to 9bits.
  // This was necessary because the nintendo touchscreen is 320x240, which is greater than 255 for an 8bit resolution.
  // However, I don't mind this minor accuracy loss! arduino's analog resolution is accurate enough for me.
  pinMode(touchX, OUTPUT);
  pinMode(touchY, OUTPUT);
  analogWrite(touchX, 0);
  disable_touch_screen();
}

void touch_screen_click(unsigned int x, unsigned int y, unsigned int duration_ms) {
  // Calculate PWM value to write  based on the xy touchscreen coordinates we want.
  unsigned int x_potential = ((double)x / 320) * 255;
  unsigned int y_potential = ((double)y / 240) * 255;

  // Start outputting the x value.
  analogWrite(touchX, x_potential);

  // Pull Y+ to ground.
  enable_touch_screen();

  unsigned int start = millis();

  // Keep touch screen pressed for tap duration.
  while(millis() - start < duration_ms) {
    // wait until Y+ pin is in output mode
    // original teensy code checked for 512, which was half of the max value 1024.
    // we're outputting 1.8v with our circuit, but reading analog at 5v
    // so the value we check for is half of this max value: 1.8/5*255=92
    // I COULD have used arduino's Aref, but math is faster than soldering
    while(analogRead(senseY) < 46)
    delayMicroseconds(490);
    
    // now Y+ pin is in output mode
    analogWrite(touchY, y_potential);
    delayMicroseconds(180);
    
    // pull Y+ down again to keep interrupt going
    enable_touch_screen();
  }
  disable_touch_screen();
  analogWrite(touchX, 0);
  delay(50);
}

void disable_touch_screen() {
  analogWrite(touchY, 255);
}

// pull Y+ pin low to initialize 3DS's touch interrupt
void enable_touch_screen() {
  analogWrite(touchY, 0);
}

// the loop routine runs over and over again forever:
void loop() {

delay(800);
// tap at 150x150 for 100ms.
touch_screen_click(150, 150, 100);

delay(800);

// tap at 50x50 for 100ms.
touch_screen_click(50, 50, 100);
}


Here's my schematic that I'm using. It's pretty simple, just a voltage divider and DAC.



What are your thoughts? In my code, I have tried using a 100 microsecond delay instead of waiting for the Y sense pin. I have also tried placing some diodes between the capacitors and ground (I am using ceramic capacitors which are bidirectional).

Why do you think this is failing, compared to the teensy implementation? Is it indeed the arduino's PWM frequency limitations as I suspected, or do you think there's an issue with my code or schematic?

This is a pretty generic touchscreen that I'm trying to simulate input for, so let me know if you have any other ideas on how I might deal with it.

Go Up