initial commit
commit
45921994c2
|
@ -0,0 +1 @@
|
|||
.vscode
|
|
@ -0,0 +1,22 @@
|
|||
# DIY Maimai Touch Screen
|
||||
|
||||
Welcome to our DIY Maimai Touch Screen Project! This project is a modification from the original project shared by [HelloworldDk ↗](https://github.com/HelloworldDk/dkmaiproj/blob/main/newmap-llvermtn202212271340/newmap-llvermtn202212271340.ino) and aims to improve the stability and usability of the touch screen.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Before you proceed, we strongly recommend you to read the [original repository ↗](https://github.com/HelloworldDk/dkmaiproj) for setup guide and understanding the basis of this project.
|
||||
|
||||
## Project Improvements
|
||||
|
||||
Our contribution to this project includes:
|
||||
|
||||
1. **Isolated MPR121 Parameters**: We've isolated key MPR121 parameters for easy modification. This will allow users to customize the sensitivity and responsiveness of the touch screen to meet their specific needs.
|
||||
|
||||
2. **Utilized .touched() Method**: We've switched from manually calculating the threshold using raw data to using the .touched() method. This not only improves the speed of the touch screen but also increases its stability by leveraging MPR121's debounce functionality.
|
||||
|
||||
## Conclusion
|
||||
|
||||
We hope you find this project useful and inspiring. Should you have any queries or need further assistance, feel free to raise an issue or contribute to this project.
|
||||
|
||||
Happy Building!
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
#include <stdint.h>
|
||||
|
||||
class touchblock
|
||||
{
|
||||
public:
|
||||
uint8_t mprid;
|
||||
uint8_t portid;
|
||||
int8_t thresOffset;
|
||||
};
|
||||
|
||||
const uint8_t MPR_PADNUMS[] = {8, 9, 9, 8};
|
||||
const uint8_t THREASHOLD = 35;
|
||||
|
||||
touchblock touchmap[34] = {
|
||||
// Group A
|
||||
{3, 4, 0}, // 1
|
||||
{3, 0, 0}, // 2
|
||||
{2, 4, 0}, // 3
|
||||
{2, 0, 0}, // 4
|
||||
{1, 5, 0}, // 5
|
||||
{1, 0, 0}, // 6
|
||||
{0, 4, 0}, // 7
|
||||
{0, 0, 0}, // 8
|
||||
// Group B
|
||||
{3, 5, 0}, // 1
|
||||
{3, 1, 0}, // 2
|
||||
{2, 5, 0}, // 3
|
||||
{2, 1, 0}, // 4
|
||||
{1, 6, 0}, // 5
|
||||
{1, 1, 0}, // 6
|
||||
{0, 5, 0}, // 7
|
||||
{0, 1, 0}, // 8
|
||||
// Group C
|
||||
{2, 8, 0}, // 1
|
||||
{1, 2, 0}, // 2
|
||||
// Group D
|
||||
{3, 6, 0}, // 1
|
||||
{3, 2, 0}, // 2
|
||||
{2, 6, 0}, // 3
|
||||
{2, 2, 0}, // 4
|
||||
{1, 7, 0}, // 5
|
||||
{1, 3, 0}, // 6
|
||||
{0, 6, 0}, // 7
|
||||
{0, 2, 0}, // 8
|
||||
// Group E
|
||||
{3, 7, 0}, // 1
|
||||
{3, 3, 0}, // 2
|
||||
{2, 7, 0}, // 3
|
||||
{2, 3, 0}, // 4
|
||||
{1, 8, 0}, // 5
|
||||
{1, 4, 0}, // 6
|
||||
{0, 7, 0}, // 7
|
||||
{0, 3, 0}, // 8
|
||||
};
|
||||
|
||||
// the release threashold will be set to THREASHOLD - RELEASE_THREASHOLD_OFFSET
|
||||
const uint8_t RELEASE_THREASHOLD_OFFSET = 0;
|
||||
|
||||
// AN3944 recommended values are:
|
||||
// MHDR = 1
|
||||
// NHDR = 1
|
||||
// NCLR = 0
|
||||
// FDLR = 0
|
||||
// MHDF = 1
|
||||
// NHDF = 1
|
||||
// NCLF = 255
|
||||
// FDLF = 2
|
||||
// dk values are:
|
||||
// MHDR = 1
|
||||
// NHDR = 16
|
||||
// NCLR = 4
|
||||
// FDLR = 0
|
||||
// MHDF = 4
|
||||
// NHDF = 1
|
||||
// NCLF = 16
|
||||
// FDLF = 4
|
||||
const uint8_t MHDR = 2;
|
||||
const uint8_t NHDR = 2;
|
||||
const uint8_t NCLR = 4;
|
||||
const uint8_t FDLR = 0;
|
||||
const uint8_t MHDF = 2;
|
||||
const uint8_t NHDF = 2;
|
||||
const uint8_t NCLF = 255;
|
||||
const uint8_t FDLF = 4;
|
||||
|
||||
// range: 0 - 3
|
||||
// 0: 6 samples
|
||||
// 1: 10 samples
|
||||
// 2: 18 samples
|
||||
// 3: 34 samples
|
||||
// aka. first filter averaging samples
|
||||
// the highest and lowest are tossed
|
||||
// so theoretically, the less samples, the system is less prone to noise
|
||||
const uint8_t FIRST_FILTER_ITERATIONS = 0;
|
||||
|
||||
// range: 0 - 7
|
||||
// actual value is 2^(CDT-1) us
|
||||
// will be overriden by autoconfig, unless SCTS is set to 1
|
||||
const uint8_t CHARGE_DISCHARGE_TIME = 1;
|
||||
|
||||
// range: 0 - 3
|
||||
// 0: 4 samples
|
||||
// 1: 6 samples
|
||||
// 2: 10 samples
|
||||
// 3: 18 samples
|
||||
// aka. second filter averaging samples
|
||||
const uint8_t SECOND_FILTER_ITERATIONS = 0;
|
||||
|
||||
// range: 0 - 7
|
||||
// 2^ESI ms of first stage samples are taken into account
|
||||
// all the first stage samples in the period is averaged
|
||||
const uint8_t ELECTRODE_SAMPLE_INTERVAL = 0;
|
||||
|
||||
// range: 0 - 7
|
||||
// when DR + 1 samples are lower than the threshold, the electrode is released
|
||||
const uint8_t RELEASE_DEBOUNCE = 1;
|
||||
|
||||
// range: 0 - 7
|
||||
// when DT + 1 samples are higher than the threshold, the electrode is touched
|
||||
// a debounce value that's too low might cause touches that's too short to be missed
|
||||
const uint8_t TOUCH_DEBOUNCE = 1;
|
||||
// ! the touch response time is debounce * SFI * ESI ms
|
||||
// ! so in the code bellow we set read interval to SFI * ESI ms
|
||||
|
||||
// please refer to AN3889 page 9 for more information
|
||||
const uint8_t AUTO_CONFIG_RETRY = 0;
|
||||
const uint8_t AUTO_CONFIG_BVA = 2;
|
||||
const uint8_t AUTO_CONFIG_ARE = 1;
|
||||
const uint8_t AUTO_CONFIG_ACE = 1;
|
||||
|
||||
// @datasheet page 15 and 18
|
||||
// setting this to 1 disables CDT search
|
||||
// which means it will use a fixed CDT and only search for CDC
|
||||
// this results in a known first stage sample amount
|
||||
// however, through testing, setting this to 1 will greatly degrade signal quality
|
||||
const uint8_t SCTS = 0;
|
||||
|
||||
// those values are suitable for 3.3V, for other voltage systems, please refer to AN3889 page 7 through 8
|
||||
const uint8_t USL = 202;
|
||||
const uint8_t TL = 182;
|
||||
const uint8_t LSL = 131;
|
||||
|
||||
// please refer to page 16 of the datasheet for more information
|
||||
const uint8_t CALIBRATION_LOCK = 0x10;
|
|
@ -0,0 +1,280 @@
|
|||
// original author: HelloworldDk
|
||||
// original file: https://github.com/HelloworldDk/dkmaiproj/blob/main/newmap-llvermtn202212271340/newmap-llvermtn202212271340.ino
|
||||
|
||||
#define SerialDevice Serial
|
||||
#include "Adafruit_MPR121.h"
|
||||
#include "config.h"
|
||||
|
||||
Adafruit_MPR121 mpr[4];
|
||||
uint8_t packet[6];
|
||||
uint8_t len = 0;
|
||||
bool Conditioning = true;
|
||||
uint32_t lastMillis = 0;
|
||||
|
||||
enum
|
||||
{
|
||||
commandRSET = 0x45, // E
|
||||
commandHALT = 0x4C, // L
|
||||
commandSTAT = 0x41, // A
|
||||
commandRatio = 0x72, // r
|
||||
commandSens = 0x6B, // k
|
||||
};
|
||||
|
||||
uint8_t SECOND_FILTER_ITERATION_SAMPLES;
|
||||
uint8_t ELECTRODE_SAMPLE_INTERVAL_MS;
|
||||
|
||||
void setup()
|
||||
{
|
||||
SerialDevice.begin(9600);
|
||||
SerialDevice.setTimeout(0);
|
||||
mpr[0].begin(0x5A, &Wire);
|
||||
mpr[1].begin(0x5B, &Wire);
|
||||
mpr[2].begin(0x5C, &Wire);
|
||||
mpr[3].begin(0x5D, &Wire);
|
||||
Wire.setClock(800000);
|
||||
|
||||
// config conversion
|
||||
|
||||
switch (SECOND_FILTER_ITERATIONS)
|
||||
{
|
||||
case 0:
|
||||
SECOND_FILTER_ITERATION_SAMPLES = 4;
|
||||
break;
|
||||
case 1:
|
||||
SECOND_FILTER_ITERATION_SAMPLES = 6;
|
||||
break;
|
||||
case 2:
|
||||
SECOND_FILTER_ITERATION_SAMPLES = 10;
|
||||
break;
|
||||
case 3:
|
||||
SECOND_FILTER_ITERATION_SAMPLES = 18;
|
||||
break;
|
||||
}
|
||||
|
||||
ELECTRODE_SAMPLE_INTERVAL_MS = 1 << ELECTRODE_SAMPLE_INTERVAL;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// there will only be a new data point every this many milliseconds
|
||||
// so we don't need to check every loop
|
||||
uint16_t currentMillis = millis();
|
||||
if (currentMillis - lastMillis > ELECTRODE_SAMPLE_INTERVAL_MS * SECOND_FILTER_ITERATION_SAMPLES)
|
||||
{
|
||||
lastMillis = currentMillis;
|
||||
Recv();
|
||||
Conditioning ? void() : TouchSend();
|
||||
}
|
||||
}
|
||||
|
||||
void MprSetup(Adafruit_MPR121 cap)
|
||||
{
|
||||
// @datasheet page 19,
|
||||
// Write 0x80 with 0x63 asserts soft reset.
|
||||
// The soft reset does not effect the I2C module, but all others reset the same as POR.
|
||||
cap.writeRegister(MPR121_SOFTRESET, 0x63);
|
||||
|
||||
delay(1);
|
||||
|
||||
// @datasheet page 16
|
||||
// 2bit Cl, 2bit ELEPROX_EN, 4bit ELE_EN
|
||||
// ELE_EN being 0 means disbled
|
||||
cap.writeRegister(MPR121_ECR, 0x0);
|
||||
|
||||
// @AN3891
|
||||
cap.writeRegister(MPR121_MHDR, MHDR);
|
||||
cap.writeRegister(MPR121_NHDR, NHDR);
|
||||
cap.writeRegister(MPR121_NCLR, NCLR);
|
||||
cap.writeRegister(MPR121_FDLR, FDLR);
|
||||
cap.writeRegister(MPR121_MHDF, MHDF);
|
||||
cap.writeRegister(MPR121_NHDF, NHDF);
|
||||
cap.writeRegister(MPR121_NCLF, NCLF);
|
||||
cap.writeRegister(MPR121_FDLF, FDLF);
|
||||
|
||||
// @AN3892 page 7
|
||||
// 1 bit unused, 3 bit DR, 1 bit unused, 3 bit DT
|
||||
// DR is the debounce count for release detection
|
||||
// DT is the debounce count for touch detection
|
||||
// for example, when DR is set to 0b010, the debounce count is 4
|
||||
// which means there must be 4 consecutive samples's delta is less than the threshold to trigger a release
|
||||
// dk's value is 4 << 4 | 2, meaning DR is 4, DT is 2
|
||||
cap.writeRegister(MPR121_DEBOUNCE, RELEASE_DEBOUNCE << 4 | TOUCH_DEBOUNCE);
|
||||
|
||||
// @AN3890 page 2
|
||||
// 2 bit FFI, 6 bit CDC
|
||||
// the default value is 10, meaning
|
||||
// First Filter Iterations is 0b00: 6 samples. toss the hightest and lowest average the rest
|
||||
// Charge Discharge Current is 10: 10uA, which will be overriden by autoconfig
|
||||
// the value set by dk is 16, but it will be overriden by autoconfig, so it doesn't matter
|
||||
cap.writeRegister(MPR121_CONFIG1, FIRST_FILTER_ITERATIONS << 6);
|
||||
|
||||
// @AN3890 page 3
|
||||
// 3 bit CDT, 2 bit SFI, 3 bit ESI
|
||||
// the default value is 0b00011000, meaning
|
||||
// CDT is 0b000: invalid, dk set it to 0b001: 0.5us
|
||||
// the value here is 2^(CDT-2) us
|
||||
// SFI is number of samples to take for second filter
|
||||
// ESI selects the period between samples for second filter
|
||||
// ! the touch response time is DT * SFI * ESI ms
|
||||
// ! and the release response time is DR * SFI * ESI ms
|
||||
// we can set SFI to 0b00 which is 4 samples, and ESI to 0b000, which is 1ms, if DR and DT is set to 4, the response time will be 16ms, approximately one frame
|
||||
cap.writeRegister(MPR121_CONFIG2, CHARGE_DISCHARGE_TIME << 5 | SECOND_FILTER_ITERATIONS << 3 | ELECTRODE_SAMPLE_INTERVAL);
|
||||
|
||||
// @AN3889 page 9
|
||||
// 2 bit AFES, 2 bit RETRY, 2 bit BVA, 1 bit ARE, 1 bit ACE
|
||||
// AFES must match FFI setting in CONFIG1, which is 0b00: 6 samples
|
||||
// RETRY is 0b00, disabled, which is said to be not required in production systems.
|
||||
// BVA should be set to 0b10, which allows the baseline to be updated
|
||||
// 10 is used instead of 11 because this guarantees that the baseline will be lower than the data
|
||||
// ARE should be 1 to enable auto configuration
|
||||
// ACE should be 1 to enable auto reconfiguration
|
||||
// reconfiguration will trigger any time the baseline drifts outside the range set by the USL and the LSL
|
||||
// in conclusion, this register should be set to 0b00001011, the same as described in AN3944 page 4
|
||||
cap.writeRegister(MPR121_AUTOCONFIG0, FIRST_FILTER_ITERATIONS << 6 | AUTO_CONFIG_RETRY << 4 | AUTO_CONFIG_BVA << 2 | AUTO_CONFIG_ARE << 1 | AUTO_CONFIG_ACE);
|
||||
|
||||
// @datasheet page 15 and 18
|
||||
// 1bit SCTS, 4bit empty, 3bit other config that we don't care.
|
||||
// SCTS being 1 means individual electrode CDT(charge/discharge time) search is disabled
|
||||
// "Using only the global CDC and/or global CDT is acceptable where the capacitance values from all 13 channels are similar. If the electrode pattern, size, or even overlay and base material type changes from one channel to another, then using individual CDCx (and CDTx) will have a better result on sensing sensitivity as each electrode is charged up to a point closing to the supply voltage rail so that the highest sensing field is built for each channel."
|
||||
// so we should not disable SCTS.
|
||||
// CHANGED
|
||||
cap.writeRegister(MPR121_AUTOCONFIG1, SCTS << 7);
|
||||
|
||||
// @AN3889 page 7 through 8
|
||||
cap.writeRegister(MPR121_UPLIMIT, USL);
|
||||
cap.writeRegister(MPR121_TARGETLIMIT, TL);
|
||||
cap.writeRegister(MPR121_LOWLIMIT, LSL);
|
||||
|
||||
// @datasheet page 16
|
||||
// 2bit CL, 2bit ELEPROX_EN, 4bit ELE_EN
|
||||
// CL set to 11 means Baseline tracking enabled, initial baseline value is loaded with all 10 bits of the first electrode data value
|
||||
// ELEPROX_EN being 0 means proximity disabled
|
||||
// ELE_EN being a certain number means how many electrodes are enabled
|
||||
// the ELE_EN value set here is irrelevant because we will set it again in STAT command
|
||||
cap.writeRegister(MPR121_ECR, CALIBRATION_LOCK << 6 | 12);
|
||||
}
|
||||
|
||||
void cmd_RSET()
|
||||
{
|
||||
MprSetup(mpr[0]);
|
||||
MprSetup(mpr[1]);
|
||||
MprSetup(mpr[2]);
|
||||
MprSetup(mpr[3]);
|
||||
}
|
||||
void cmd_HALT()
|
||||
{
|
||||
// Start Conditioning Mode
|
||||
mpr[0].writeRegister(MPR121_ECR, 0);
|
||||
mpr[1].writeRegister(MPR121_ECR, 0);
|
||||
mpr[2].writeRegister(MPR121_ECR, 0);
|
||||
mpr[3].writeRegister(MPR121_ECR, 0);
|
||||
Conditioning = true;
|
||||
}
|
||||
|
||||
void cmd_Ratio()
|
||||
{
|
||||
// Set Touch Panel Ratio
|
||||
SerialDevice.write('(');
|
||||
SerialDevice.write(packet + 1, 4);
|
||||
SerialDevice.write(')');
|
||||
}
|
||||
|
||||
void cmd_Sens()
|
||||
{
|
||||
// Set Touch Panel Sensitivity
|
||||
SerialDevice.write('(');
|
||||
SerialDevice.write(packet + 1, 4);
|
||||
SerialDevice.write(')');
|
||||
}
|
||||
|
||||
void cmd_STAT()
|
||||
{
|
||||
// End Conditioning Mode
|
||||
Conditioning = false;
|
||||
|
||||
// setting individual electrode sensitivity
|
||||
for (uint8_t i = 0; i < 34; i++)
|
||||
{
|
||||
uint8_t mprid = touchmap[i].mprid;
|
||||
uint8_t portid = touchmap[i].portid;
|
||||
int8_t thresOffset = touchmap[i].thresOffset;
|
||||
mpr[mprid].writeRegister(MPR121_TOUCHTH_0 + portid * 2, THREASHOLD + thresOffset);
|
||||
mpr[mprid].writeRegister(MPR121_RELEASETH_0 + portid * 2, THREASHOLD - RELEASE_THREASHOLD_OFFSET + thresOffset);
|
||||
}
|
||||
|
||||
// setting ECR to enable electrodes
|
||||
mpr[0].writeRegister(MPR121_ECR, CALIBRATION_LOCK << 6 | MPR_PADNUMS[0]);
|
||||
mpr[1].writeRegister(MPR121_ECR, CALIBRATION_LOCK << 6 | MPR_PADNUMS[1]);
|
||||
mpr[2].writeRegister(MPR121_ECR, CALIBRATION_LOCK << 6 | MPR_PADNUMS[2]);
|
||||
mpr[3].writeRegister(MPR121_ECR, CALIBRATION_LOCK << 6 | MPR_PADNUMS[3]);
|
||||
}
|
||||
|
||||
void Recv()
|
||||
{
|
||||
while (SerialDevice.available())
|
||||
{
|
||||
uint8_t r = SerialDevice.read();
|
||||
if (r == '{')
|
||||
{
|
||||
len = 0;
|
||||
}
|
||||
if (r == '}')
|
||||
{
|
||||
break;
|
||||
}
|
||||
packet[len++] = r;
|
||||
}
|
||||
if (len == 5)
|
||||
{
|
||||
switch (packet[3])
|
||||
{
|
||||
case commandRSET:
|
||||
cmd_RSET();
|
||||
break;
|
||||
case commandHALT:
|
||||
cmd_HALT();
|
||||
break;
|
||||
case commandRatio:
|
||||
cmd_Ratio();
|
||||
break;
|
||||
case commandSens:
|
||||
cmd_Sens();
|
||||
break;
|
||||
case commandSTAT:
|
||||
cmd_STAT();
|
||||
break;
|
||||
}
|
||||
len = 0;
|
||||
memset(packet, 0, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void TouchSend()
|
||||
{
|
||||
// setting up the stage and reading the touched data
|
||||
uint64_t TouchData = 0;
|
||||
uint16_t mprTouchedData[] = {
|
||||
mpr[0].touched(),
|
||||
mpr[1].touched(),
|
||||
mpr[2].touched(),
|
||||
mpr[3].touched(),
|
||||
};
|
||||
|
||||
// mapping the touched data to the correct order
|
||||
for (int8_t i = 33; i >= 0; i--)
|
||||
{
|
||||
TouchData <<= 1;
|
||||
TouchData = (TouchData | ((mprTouchedData[touchmap[i].mprid] >> touchmap[i].portid) & 1));
|
||||
}
|
||||
|
||||
// sending the data
|
||||
uint8_t data[9];
|
||||
data[0] = '(';
|
||||
data[8] = ')';
|
||||
for (uint8_t i = 1; i < 8; i++)
|
||||
{
|
||||
data[i] = (byte)TouchData & B11111;
|
||||
TouchData >>= 5;
|
||||
}
|
||||
SerialDevice.write(data, 9);
|
||||
}
|
Loading…
Reference in New Issue