Initial upload of the Physical Flick keyboard files.

This commit is contained in:
yamaguchi-am
2016-04-01 00:04:23 +09:00
parent 45d465b9ce
commit a7241d1900
5 changed files with 298 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
#include <SPI.h>
#include "flick_keyboard.h"
// Number of A/D converter ICs.
const int kNumAdcIc = 3;
const int kCsPins[kNumAdcIc] = {10, 9, 8};
const int kButtonPins[12] = {
// If you use Arduino Nano version 2, use A6 and A7 instead of A0 and A1.
// (which are actually port C, thus used as digital input)
A0, A1, A2, A3, A4, A5,
7, 6, 5, 4, 3, 2
};
FlickKeyboard keyboard;
void setup() {
Serial.begin(115200);
for (size_t i = 0; i < kNumAdcIc; i++) {
pinMode(kCsPins[i], OUTPUT);
digitalWrite(kCsPins[i], HIGH);
}
for (size_t i = 0; i < 12; i++) {
pinMode(kButtonPins[i], INPUT_PULLUP);
}
SPI.begin();
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
}
void SelectChip(uint8_t id) {
if (id > kNumAdcIc) {
return;
}
for (size_t i = 0; i < kNumAdcIc; i++) {
digitalWrite(kCsPins[i], HIGH);
}
digitalWrite(kCsPins[id], LOW);
}
void DeselectChips() {
for (size_t i = 0; i < kNumAdcIc; i++) {
digitalWrite(kCsPins[i], HIGH);
}
}
// Fetch a ADC result of a specified channel from a MCP3208.
int16_t ReadMcp3208Adc(uint8_t chipId, uint8_t channel) {
SelectChip(chipId);
// MCP3208 receives only 5 bits for start.
// Since the SPI library only supports bytewise tramsmission,
// we send dummy (high-level) bits before the start bit.
// Send data :
// 3 dummy bits (000)
// start bit (1)
// select single-end (1)
// 3 address bits: d2, d1, d0
SPI.transfer(0x18 | channel);
uint16_t b0 = SPI.transfer(0x00);
uint16_t b1 = SPI.transfer(0x00);
DeselectChips();
// The first output bit is high-Z (sampling period).
// The second bit is always 0.
// Then the ADC value follows in MSB first format.
// Therefore the first received byte contains 6 bits from MSB,
// and the second byte contains the remainder.
return ((b0 & 0x3f) << 6) | ((b1 & 0xfc) >> 2);
}
void ReadSwitches(bool* button) {
for (size_t i = 0; i < 12; i++) {
button[i] = (digitalRead(kButtonPins[i]) == LOW);
}
}
void ReadVolumes(uint16_t* data) {
byte inByte = 0;
for (uint8_t j = 0; j < kNumAdcIc; j++) {
for (uint8_t i = 0; i < 8; i++) {
data[j * 8 + i] = ReadMcp3208Adc(j, i);
}
}
}
// Passes through any serial input to the output.
// This mode can be used to configure RN-42 using serial terminal
// connected to Arduino.
void EchoBackMode() {
while (true) {
if (Serial.available()) {
Serial.print((char)Serial.read());
}
}
}
void loop() {
if (Serial.available() && Serial.read() == '!') {
EchoBackMode();
}
SensorData keys;
ReadVolumes(keys.axes);
ReadSwitches(keys.button);
int nOutputs;
const char* outputs[COLS];
keyboard.ProcessSensorData(keys, COLS, outputs, &nOutputs);
for (size_t i = 0; i < nOutputs; i++) {
Serial.print(outputs[i]);
}
}

View File

@@ -0,0 +1,67 @@
#include "flick_keyboard.h"
#include <stdint.h>
#define ADC_BITS 12
void FlickKeyboard::ProcessSensorData(const SensorData& data, size_t maxKeyNum,
const char** outputStrings, int* nOutputs) {
const char* characters[COLS][ROWS] = {
{"a", "i", "u", "e", "o"},
{"ka", "ki", "ku", "ke", "ko"},
{"sa", "si", "su", "se", "so"},
{"ta", "ti", "tu", "te", "to"},
{"na", "ni", "nu", "ne", "no"},
{"ha", "hi", "hu", "he", "ho"},
{"ma", "mi", "mu", "me", "mo"},
{"ya", "(", "yu", ")", "yo"},
{"ra", "ri", "ru", "re", "ro"},
{"\n", "\b", "", " ", ""},
{"wa", "wo", "nn", "-", "~"},
{",", ".", "?", "!", "..."}
};
*nOutputs = 0;
for (size_t i = 0; i < COLS; i++) {
Direction s = ConvertToFlickState(data.axes[i * 2 + 1],
data.axes[i * 2], data.button[i]);
if (lastState[i] == NONE && s != NONE) {
if (*nOutputs < maxKeyNum) {
outputStrings[(*nOutputs)++] = characters[i][s];
}
}
lastState[i] = s;
}
}
FlickKeyboard::FlickKeyboard() {
for (size_t i = 0; i < COLS; i++) {
lastState[i] = NONE;
}
}
Direction FlickKeyboard::ConvertToFlickState(
int16_t x, int16_t y, bool buttonPressed) {
const int16_t adScale = 1 << ADC_BITS;
const int16_t adCenter = adScale / 2;
const int16_t threshold = adScale * 0.4;
int16_t diffX = x - adCenter;
int16_t diffY = y - adCenter;
if (buttonPressed) {
return CENTER;
}
if (abs(diffX) > abs(diffY)) {
if (diffX < -threshold) {
return LEFT;
} else if (diffX > threshold) {
return RIGHT;
}
} else {
if (diffY < -threshold) {
return UP;
} else if (diffY > threshold) {
return DOWN;
}
}
return NONE;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
#define ROWS 5
#define COLS 12 // Equals to the number of the keys.
enum Direction {
CENTER, LEFT, UP, RIGHT, DOWN, NONE
};
struct SensorData {
uint16_t axes[COLS * 2];
bool button[COLS];
};
class FlickKeyboard {
private:
Direction lastState[COLS];
Direction ConvertToFlickState(int16_t x, int16_t y,
bool buttonPressed);
public:
FlickKeyboard();
void ProcessSensorData(const SensorData& data, size_t maxKeyNum,
const char** outputStrings, int* nOutputs);
};