1
0
Fork 0

Added Dichotemy Keyboard, updated docs for Pointing Device (#1817)

* Added Dichotemy Keyboard, updated docs for Pointing Device

* Updated readme

* Updated mouse report pointer in pointing_device.c
This commit is contained in:
Snipeye 2017-10-10 11:07:59 -06:00 committed by Jack Humbert
parent 20031ab982
commit cfd118d158
9 changed files with 929 additions and 1 deletions

View file

@ -21,6 +21,7 @@
* [Leader Key](feature_leader_key.md) * [Leader Key](feature_leader_key.md)
* [Macros](macros.md) * [Macros](macros.md)
* [Mouse keys](mouse_keys.md) * [Mouse keys](mouse_keys.md)
* [Pointing Device](feature_pointing_device.md)
* [PS2 Mouse](feature_ps2_mouse.md) * [PS2 Mouse](feature_ps2_mouse.md)
* [Space Cadet](space_cadet_shift.md) * [Space Cadet](space_cadet_shift.md)
* [Tap Dance](tap_dance.md) * [Tap Dance](tap_dance.md)

View file

@ -0,0 +1,87 @@
/*
Copyright 2012 Jun Wako <wakojun@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIG_H
#define CONFIG_H
#include "config_common.h"
/* USB Device descriptor parameter */
#define VENDOR_ID 0xFEED
#define PRODUCT_ID 0xACC7
#define DEVICE_VER 0x0001
#define MANUFACTURER unknown
#define PRODUCT Dichotemy
#define DESCRIPTION q.m.k. keyboard firmware for Dichotemy
/* key matrix size */
#define MATRIX_ROWS 5
#define MATRIX_COLS 12
/* define if matrix has ghost */
//#define MATRIX_HAS_GHOST
/* number of backlight levels */
//#define BACKLIGHT_LEVELS 3
#define ONESHOT_TIMEOUT 500
/* key combination for command */
#define IS_COMMAND() ( \
keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \
)
/*
* Feature disable options
* These options are also useful to firmware size reduction.
*/
#define PREVENT_STUCK_MODIFIERS
/* disable debug print */
//#define NO_DEBUG
/* disable print */
//#define NO_PRINT
/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT
//#define NO_ACTION_MACRO
//#define NO_ACTION_FUNCTION
//UART settings for communication with the RF microcontroller
#define SERIAL_UART_BAUD 1000000
#define SERIAL_UART_DATA UDR1
#define SERIAL_UART_UBRR (F_CPU / (16UL * SERIAL_UART_BAUD) - 1)
#define SERIAL_UART_TXD_READY (UCSR1A & _BV(UDRE1))
#define SERIAL_UART_RXD_PRESENT (UCSR1A & _BV(RXC1))
#define SERIAL_UART_INIT() do { \
/* baud rate */ \
UBRR1L = SERIAL_UART_UBRR; \
/* baud rate */ \
UBRR1H = SERIAL_UART_UBRR >> 8; \
/* enable TX and RX */ \
UCSR1B = _BV(TXEN1) | _BV(RXEN1); \
/* 8-bit data */ \
UCSR1C = _BV(UCSZ11) | _BV(UCSZ10); \
} while(0)
#endif

View file

@ -0,0 +1,86 @@
#include "dichotemy.h"
#include "pointing_device.h"
#include "report.h"
void uart_init(void) {
SERIAL_UART_INIT();
}
void pointing_device_task(void){
report_mouse_t currentReport = {};
SERIAL_UART_INIT();
uint32_t timeout = 0;
//the m character requests the RF slave to send the mouse report
SERIAL_UART_DATA = 'm';
//trust the external inputs completely, erase old data
uint8_t uart_data[5] = {0};
//there are 10 bytes corresponding to 10 columns, and an end byte
for (uint8_t i = 0; i < 5; i++) {
//wait for the serial data, timeout if it's been too long
//this only happened in testing with a loose wire, but does no
//harm to leave it in here
while(!SERIAL_UART_RXD_PRESENT){
timeout++;
if (timeout > 10000){
break;
}
}
uart_data[i] = SERIAL_UART_DATA;
}
//check for the end packet, bits 1-4 are movement and scroll
//but bit 5 has bits 0-3 for the scroll button state
//(1000 if pressed, 0000 if not) and bits 4-7 are always 1
//We can use this to verify the report sent properly.
if (uart_data[4] == 0x0F || uart_data[4] == 0x8F)
{
currentReport = pointing_device_get_report();
//shifting and transferring the info to the mouse report varaible
//mouseReport.x = 127 max -127 min
currentReport.x = uart_data[0];
//mouseReport.y = 127 max -127 min
currentReport.y = uart_data[1];
//mouseReport.v = 127 max -127 min (scroll vertical)
currentReport.v = uart_data[2];
//mouseReport.h = 127 max -127 min (scroll horizontal)
currentReport.h = uart_data[3];
//mouseReport.buttons = 0x31 max (bitmask for mouse buttons 1-5) 0x00 min
//mouse buttons 1 and 2 are handled by the keymap, but not 3
if (uart_data[4] == 0x0F) { //then 3 is not pressed
currentReport.buttons &= ~MOUSE_BTN3; //MOUSE_BTN3 is def in report.h
} else { //3 must be pressed
currentReport.buttons |= MOUSE_BTN3;
}
pointing_device_set_report(currentReport);
}
pointing_device_send();
}
void led_init(void) {
DDRD |= (1<<1);
PORTD |= (1<<1);
DDRF |= (1<<4) | (1<<5);
PORTF |= (1<<4) | (1<<5);
}
void matrix_init_kb(void) {
// put your keyboard start-up code here
// runs once when the firmware starts up
matrix_init_user();
uart_init();
led_init();
}
void matrix_scan_kb(void) {
// put your looping keyboard code here
// runs every cycle (a lot)
matrix_scan_user();
}
void led_set_kb(uint8_t usb_led) {
}

View file

@ -0,0 +1,67 @@
#ifndef DICHOTEMY_H
#define DICHOTEMY_H
#include "quantum.h"
#include "matrix.h"
#include "backlight.h"
#include <stddef.h>
#define red_led_off PORTF |= (1<<5)
#define red_led_on PORTF &= ~(1<<5)
#define blu_led_off PORTF |= (1<<4)
#define blu_led_on PORTF &= ~(1<<4)
#define grn_led_off PORTD |= (1<<1)
#define grn_led_on PORTD &= ~(1<<1)
#define set_led_off red_led_off; grn_led_off; blu_led_off
#define set_led_red red_led_on; grn_led_off; blu_led_off
#define set_led_blue red_led_off; grn_led_off; blu_led_on
#define set_led_green red_led_off; grn_led_on; blu_led_off
#define set_led_yellow red_led_on; grn_led_on; blu_led_off
#define set_led_magenta red_led_on; grn_led_off; blu_led_on
#define set_led_cyan red_led_off; grn_led_on; blu_led_on
#define set_led_white red_led_on; grn_led_on; blu_led_on
/*
#define LED_B 5
#define LED_R 6
#define LED_G 7
#define all_leds_off PORTF &= ~(1<<LED_B) & ~(1<<LED_R) & ~(1<<LED_G)
#define red_led_on PORTF |= (1<<LED_R)
#define red_led_off PORTF &= ~(1<<LED_R)
#define grn_led_on PORTF |= (1<<LED_G)
#define grn_led_off PORTF &= ~(1<<LED_G)
#define blu_led_on PORTF |= (1<<LED_B)
#define blu_led_off PORTF &= ~(1<<LED_B)
#define set_led_off PORTF &= ~(1<<LED_B) & ~(1<<LED_R) & ~(1<<LED_G)
#define set_led_red PORTF = PORTF & ~(1<<LED_B) & ~(1<<LED_G) | (1<<LED_R)
#define set_led_blue PORTF = PORTF & ~(1<<LED_G) & ~(1<<LED_R) | (1<<LED_B)
#define set_led_green PORTF = PORTF & ~(1<<LED_B) & ~(1<<LED_R) | (1<<LED_G)
#define set_led_yellow PORTF = PORTF & ~(1<<LED_B) | (1<<LED_R) | (1<<LED_G)
#define set_led_magenta PORTF = PORTF & ~(1<<LED_G) | (1<<LED_R) | (1<<LED_B)
#define set_led_cyan PORTF = PORTF & ~(1<<LED_R) | (1<<LED_B) | (1<<LED_G)
#define set_led_white PORTF |= (1<<LED_B) | (1<<LED_R) | (1<<LED_G)
*/
// This a shortcut to help you visually see your layout.
// The first section contains all of the arguements
// The second converts the arguments into a two-dimensional array
#define KEYMAP( \
k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0A, k0B, \
k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1A, k1B, \
k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2A, k2B, \
k33, k34, k35, k36, k37, k38, \
k43, k44, k45, k46, k47, k48 \
) \
{ \
{ k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0A, k0B }, \
{ k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1A, k1B }, \
{ k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2A, k2B }, \
{ KC_NO, KC_NO, KC_NO, k33, k34, k35, k36, k37, k38, KC_NO, KC_NO, KC_NO }, \
{ KC_NO, KC_NO, KC_NO, k43, k44, k45, k46, k47, k48, KC_NO, KC_NO, KC_NO } \
}
#endif

View file

@ -0,0 +1,394 @@
// this is the style you want to emulate.
// This is the canonical layout file for the Quantum project. If you want to add another keyboard,
#include "dichotemy.h"
#include "report.h"
#include "pointing_device.h"
// Each layer gets a name for readability, which is then used in the keymap matrix below.
// The underscores don't mean anything - you can have a layer called STUFF or any other name.
// Layer names don't all need to be of the same length, obviously, and you can also skip them
// entirely and just use numbers.
enum dichotemy_layers
{
_BS,
_SF,
_NM,
_NS,
_MS
};
#define LONGPRESS_COUNT 4
enum dichotemy_keycodes
{
CK_1G = SAFE_RANGE,
CK_BSPE,
CK_QE,
CK_TE, //these 4 CK_XXXX keys are special "alternate long-press" keys controlled with unique timers. Make sure you understand them before you mess with them.
NS_HYPH,
NS_EQU,
NUMKEY,
SFTKEY,
MOUSE,
MS_BTN1,
MS_BTN2
//MS_BTN3
};
// Macro definitions for readability
enum dichotemy_macros
{
VOLU,
VOLD,
ESCM
};
#define LONGPRESS_DELAY 150
#define MAX_TOGGLE_LENGTH 300
#define TAPPING_TOGGLE 1
// Fillers to make layering more clear
#define _______ KC_TRNS
#define XXXXXXX KC_NO
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_BS] = { /* Base layout, nearly qwerty but with modifications because it's not a full keyboard. Obviously. */
{CK_TE, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC },
{NUMKEY, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, CK_QE },
{SFTKEY, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, MOUSE },
{XXXXXXX, XXXXXXX, XXXXXXX, KC_LCTL, KC_LALT, KC_LGUI, KC_RGUI, KC_RALT, KC_RCTL, XXXXXXX, XXXXXXX, XXXXXXX },
{XXXXXXX, XXXXXXX, XXXXXXX, KC_LBRC, KC_LPRN, KC_QUOT, KC_SPC, KC_RPRN, KC_RBRC, XXXXXXX, XXXXXXX, XXXXXXX }
},
[_SF] = { /* Shifted layout, small changes (because angle brackets have been moved to thumb cluster buttons) */
{_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ },
{_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ },
{_______, _______, _______, _______, _______, _______, _______, _______, NS_HYPH, KC_UNDS, _______, _______ },
{XXXXXXX, XXXXXXX, XXXXXXX, _______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, XXXXXXX },
{XXXXXXX, XXXXXXX, XXXXXXX, _______, KC_LABK, _______, _______, KC_RABK, _______, XXXXXXX, XXXXXXX, XXXXXXX }
},
[_NM] = { /* Number layout, basically the main function layer */
{_______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, _______ },
{_______, CK_1G, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, CK_BSPE },
{_______, KC_F11, KC_F12, KC_F13, KC_F14, KC_F15, KC_F16, KC_F17, KC_F18, KC_F19, KC_F20, _______ },
{XXXXXXX, XXXXXXX, XXXXXXX, _______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, XXXXXXX },
{XXXXXXX, XXXXXXX, XXXXXXX, _______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, XXXXXXX }
},
[_NS] = { /* Shifted number/function layout, for per-key control. Only active when shift is held, and number is toggled or held */
{_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ },
{_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PLUS, NS_EQU, _______ },
{_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ },
{XXXXXXX, XXXXXXX, XXXXXXX, _______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, XXXXXXX },
{XXXXXXX, XXXXXXX, XXXXXXX, _______, _______, _______, _______, _______, _______, XXXXXXX, XXXXXXX, XXXXXXX }
},
[_MS] = { /* Mouse layer, including buttons for clicking. */
{_______, _______, _______, _______, _______, _______, KC_VOLU, KC_HOME, KC_PGUP, _______, _______, _______ },
{_______, _______, _______, _______, _______, _______, _______, MS_BTN1, MS_BTN2, _______, _______, _______ },
{_______, _______, _______, _______, _______, _______, KC_VOLD, KC_END, KC_PGDN, _______, _______, _______ },
{XXXXXXX, XXXXXXX, XXXXXXX, _______, _______, _______, _______, KC_UP, _______, XXXXXXX, XXXXXXX, XXXXXXX },
{XXXXXXX, XXXXXXX, XXXXXXX, _______, _______, _______, KC_LEFT, KC_DOWN, KC_RGHT, XXXXXXX, XXXXXXX, XXXXXXX }
}
};
const uint16_t PROGMEM fn_actions[] = {
};
static uint16_t special_timers[LONGPRESS_COUNT] = {0xFFFF,0xFFFF,0xFFFF,0xFFFF};
static bool special_key_states[LONGPRESS_COUNT] = {0,0,0,0};
static uint16_t shift_timer;
static uint16_t num_timer;
static uint16_t mouse_timer;
static bool shift_singular_key = false;
static bool number_singular_key = false;
static bool mouse_singular_key = false;
static bool shift_held = false;
static bool shift_suspended = false;
report_mouse_t currentReport = {};
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
//uint8_t layer;
//layer = biton32(layer_state); // get the current layer //Or don't, I didn't use it.
//custom layer handling for tri_layer,
switch (keycode) {
case NUMKEY:
if (record->event.pressed) {
num_timer = timer_read();
number_singular_key = true;
layer_invert(_NM);
} else {
if (timer_elapsed(num_timer) < MAX_TOGGLE_LENGTH && number_singular_key) {
//do nothing, the layer has already been inverted
} else {
layer_invert(_NM);
}
}
update_tri_layer(_NM, _SF, _NS);
return false;
break;
//SHIFT is handled as LSHIFT in the general case - 'toggle' shoudl activate caps, while the layer is only active when shift is held.
case SFTKEY:
if (record->event.pressed) {
shift_held = true;
shift_suspended = false;
shift_timer = timer_read();
shift_singular_key = true;
layer_on(_SF);
register_code(KC_LSFT);
} else {
shift_held = false;
if (timer_elapsed(shift_timer) < MAX_TOGGLE_LENGTH && shift_singular_key) {
//this was basically a toggle, so activate/deactivate caps lock.
SEND_STRING(SS_TAP(X_CAPSLOCK));
}
layer_off(_SF);
unregister_code(KC_LSFT);
}
update_tri_layer(_NM, _SF, _NS);
return false;
break;
//MOUSE layer needs to be handled the same way as NUMKEY, but differently from shift
case MOUSE:
if (record->event.pressed) {
mouse_timer = timer_read();
mouse_singular_key = true;
layer_invert(_MS);
} else {
if (timer_elapsed(mouse_timer) < MAX_TOGGLE_LENGTH && number_singular_key){
//do nothing, it was a toggle (and it's already been toggled)
} else {
layer_invert(_MS);
}
}
return false;
break;
//Custom macros for strange keys with different long-tap behavior
case CK_1G:
if (shift_held && shift_suspended){
register_code(KC_LSFT);
shift_suspended = false;
}
shift_singular_key = false;
number_singular_key = false;
mouse_singular_key = false;
if (record->event.pressed) {
special_timers[CK_1G-SAFE_RANGE] = timer_read();
} else {
if (special_key_states[CK_1G-SAFE_RANGE]){
//key was activated after longpress_delay, need to close those keycodes
special_key_states[CK_1G-SAFE_RANGE] = 0;
unregister_code(KC_GRAVE);
} else {
//key was not activated, return macro activating proper, pre-long-tap key
SEND_STRING(SS_TAP(X_1));
}
special_timers[CK_1G-SAFE_RANGE] = 0xFFFF;
}
break;
case CK_BSPE:
if (shift_held && shift_suspended){
register_code(KC_LSFT);
shift_suspended = false;
}
shift_singular_key = false;
number_singular_key = false;
mouse_singular_key = false;
if (record->event.pressed) {
special_timers[CK_BSPE-SAFE_RANGE] = timer_read();
} else {
if (special_key_states[CK_BSPE-SAFE_RANGE]){
//key was activated after longpress_delay, need to close those keycodes
special_key_states[CK_BSPE-SAFE_RANGE] = 0;
unregister_code(KC_ENTER);
} else {
//key was not activated, return macro activating proper, pre-long-tap key
SEND_STRING(SS_TAP(X_BSLASH));
}
special_timers[CK_BSPE-SAFE_RANGE] = 0xFFFF;
}
break;
case CK_QE:
if (shift_held && shift_suspended){
register_code(KC_LSFT);
shift_suspended = false;
}
shift_singular_key = false;
number_singular_key = false;
mouse_singular_key = false;
if (record->event.pressed) {
special_timers[CK_QE-SAFE_RANGE] = timer_read();
} else {
if (special_key_states[CK_QE-SAFE_RANGE]){
//key was activated after longpress_delay, need to close those keycodes
special_key_states[CK_QE-SAFE_RANGE] = 0;
unregister_code(KC_ENTER);
} else {
//key was not activated, return macro activating proper, pre-long-tap key
SEND_STRING(SS_TAP(X_QUOTE));
}
special_timers[CK_QE-SAFE_RANGE] = 0xFFFF;
}
break;
case CK_TE:
if (shift_held && shift_suspended){
register_code(KC_LSFT);
shift_suspended = false;
}
if (record->event.pressed) {
special_timers[CK_TE-SAFE_RANGE] = timer_read();
} else {
if (special_key_states[CK_TE-SAFE_RANGE]){
//key was activated after longpress_delay, need to close those keycodes
special_key_states[CK_TE-SAFE_RANGE] = 0;
unregister_code(KC_ENTER);
} else {
//key was not activated, return macro activating proper, pre-long-tap key
SEND_STRING(SS_TAP(X_TAB));
}
special_timers[CK_TE-SAFE_RANGE] = 0xFFFF;
}
break;
//No-shift keys, they unregister the KC_LSFT code so they can send
//unshifted values - but they don't change the bool. if any other
//key is pressed and the bool is set, KC_LSFT is registered again.
case NS_HYPH:
if (record->event.pressed) {
shift_suspended = true;
unregister_code(KC_LSFT);
register_code(KC_MINS);
} else {
unregister_code(KC_MINS);
if (shift_held && shift_suspended){
register_code(KC_LSFT);
shift_suspended = false;
}
}
break;
case NS_EQU:
if (record->event.pressed) {
shift_suspended = true;
unregister_code(KC_LSFT);
register_code(KC_EQUAL);
} else {
unregister_code(KC_EQUAL);
if (shift_held && shift_suspended){
register_code(KC_LSFT);
shift_suspended = false;
}
}
break;
//mouse buttons, for 1-3, to update the mouse report:
case MS_BTN1:
currentReport = pointing_device_get_report();
if (record->event.pressed) {
if (shift_held && shift_suspended){
register_code(KC_LSFT);
shift_suspended = false;
}
//update mouse report here
currentReport.buttons |= MOUSE_BTN1; //MOUSE_BTN1 is a const defined in report.h
} else {
//update mouse report here
currentReport.buttons &= ~MOUSE_BTN1;
}
pointing_device_set_report(currentReport);
break;
case MS_BTN2:
currentReport = pointing_device_get_report();
if (record->event.pressed) {
if (shift_held && shift_suspended){
register_code(KC_LSFT);
shift_suspended = false;
}
//update mouse report here
currentReport.buttons |= MOUSE_BTN2; //MOUSE_BTN2 is a const defined in report.h
} else {
//update mouse report here
}
pointing_device_set_report(currentReport);
break;
//there is a case for button 3, but that's handled in dichotemy.c, and this is being
//disabled to avoid any conflict.
/*case MS_BTN3:
currentReport = pointing_device_get_report();
if (record->event.pressed) {
if (shift_held && shift_suspended){
register_code(KC_LSFT);
shift_suspended = false;
}
//update mouse report here
currentReport.buttons |= MOUSE_BTN3; //MOUSE_BTN2 is a const defined in report.h
} else {
//update mouse report here
}
pointing_device_set_report(currentReport);
break;*/
//If any other key was pressed during the layer mod hold period,
//then the layer mod was used momentarily, and should block latching
//Additionally, if NS_ keys are in use, then shift may be held (but is
//disabled for the unshifted keycodes to be send. Check the bool and
//register shift as necessary.
default:
if (shift_held){
register_code(KC_LSFT);
}
shift_singular_key = false;
number_singular_key = false;
mouse_singular_key = false;
break;
}
return true;
};
void matrix_scan_user(void) {
uint8_t layer = biton32(layer_state);
for (uint8_t i = 0; i<LONGPRESS_COUNT; i++){
if (timer_elapsed(special_timers[i]) >= LONGPRESS_DELAY && !special_key_states[i]){
switch (i + SAFE_RANGE){
case CK_1G:
register_code(KC_GRAVE);
break;
case CK_BSPE:
register_code(KC_ENTER);
break;
case CK_QE:
register_code(KC_ENTER);
break;
case CK_TE:
register_code(KC_ESCAPE);
break;
}
special_key_states[i] = 1;
}
}
switch (layer) {
case _BS:
set_led_off;
break;
case _NM:
set_led_blue;
break;
case _SF:
set_led_red;
break;
case _NS:
set_led_green;
break;
default:
break;
}
};

View file

@ -0,0 +1,178 @@
/*
Copyright 2012 Jun Wako
Copyright 2014 Jack Humbert
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdbool.h>
#if defined(__AVR__)
#include <avr/io.h>
#endif
#include "wait.h"
#include "print.h"
#include "debug.h"
#include "util.h"
#include "matrix.h"
#include "timer.h"
#if (MATRIX_COLS <= 8)
# define print_matrix_header() print("\nr/c 01234567\n")
# define print_matrix_row(row) print_bin_reverse8(matrix_get_row(row))
# define matrix_bitpop(i) bitpop(matrix[i])
# define ROW_SHIFTER ((uint8_t)1)
#elif (MATRIX_COLS <= 16)
# define print_matrix_header() print("\nr/c 0123456789ABCDEF\n")
# define print_matrix_row(row) print_bin_reverse16(matrix_get_row(row))
# define matrix_bitpop(i) bitpop16(matrix[i])
# define ROW_SHIFTER ((uint16_t)1)
#elif (MATRIX_COLS <= 32)
# define print_matrix_header() print("\nr/c 0123456789ABCDEF0123456789ABCDEF\n")
# define print_matrix_row(row) print_bin_reverse32(matrix_get_row(row))
# define matrix_bitpop(i) bitpop32(matrix[i])
# define ROW_SHIFTER ((uint32_t)1)
#endif
#define MAIN_ROWMASK 0xFFF0;
#define LOWER_ROWMASK 0x1F80;
/* matrix state(1:on, 0:off) */
static matrix_row_t matrix[MATRIX_ROWS];
__attribute__ ((weak))
void matrix_init_quantum(void) {
matrix_init_kb();
}
__attribute__ ((weak))
void matrix_scan_quantum(void) {
matrix_scan_kb();
}
__attribute__ ((weak))
void matrix_init_kb(void) {
matrix_init_user();
}
__attribute__ ((weak))
void matrix_scan_kb(void) {
matrix_scan_user();
}
__attribute__ ((weak))
void matrix_init_user(void) {
}
__attribute__ ((weak))
void matrix_scan_user(void) {
}
inline
uint8_t matrix_rows(void) {
return MATRIX_ROWS;
}
inline
uint8_t matrix_cols(void) {
return MATRIX_COLS;
}
void matrix_init(void) {
matrix_init_quantum();
}
uint8_t matrix_scan(void)
{
SERIAL_UART_INIT();
uint32_t timeout = 0;
//the s character requests the RF slave to send the matrix
SERIAL_UART_DATA = 's';
//trust the external keystates entirely, erase the last data
uint8_t uart_data[7] = {0};
//there are 10 bytes corresponding to 10 columns, and an end byte
for (uint8_t i = 0; i < 7; i++) {
//wait for the serial data, timeout if it's been too long
//this only happened in testing with a loose wire, but does no
//harm to leave it in here
while(!SERIAL_UART_RXD_PRESENT){
timeout++;
if (timeout > 10000){
break;
}
}
uart_data[i] = SERIAL_UART_DATA;
}
//check for the end packet, the key state bytes use the LSBs, so 0xE0
//will only show up here if the correct bytes were recieved
if (uart_data[6] == 0x96) { //this is an arbitrary binary checksum (10010110)
//shifting and transferring the keystates to the QMK matrix variable
//bits 1-12 are row 1, 13-24 are row 2, 25-36 are row 3,
//bits 37-42 are row 4 (only 6 wide, 1-3 are 0, and 10-12 are 0)
//bits 43-48 are row 5 (same as row 4)
/* ASSUMING MSB FIRST */
matrix[0] = (((uint16_t) uart_data[0] << 8) | ((uint16_t) uart_data[1])) & MAIN_ROWMASK;
matrix[1] = ((uint16_t) uart_data[1] << 12) | ((uint16_t) uart_data[2] << 4);
matrix[2] = (((uint16_t) uart_data[3] << 8) | ((uint16_t) uart_data[4])) & MAIN_ROWMASK;
matrix[3] = (((uint16_t) uart_data[4] << 9) | ((uint16_t) uart_data[5] << 1)) & LOWER_ROWMASK;
matrix[4] = ((uint16_t) uart_data[5] << 7) & LOWER_ROWMASK;
/* OK, TURNS OUT THAT WAS A BAD ASSUMPTION */
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
//I've unpacked these into the mirror image of what QMK expects them to be, so...
matrix[i] = ((matrix[i] * 0x0802LU & 0x22110LU) | (matrix[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
//bithack mirror! Doesn't make any sense, but works - and efficiently.
}
}
matrix_scan_quantum();
return 1;
}
inline
bool matrix_is_on(uint8_t row, uint8_t col)
{
return (matrix[row] & ((matrix_row_t)1<col));
}
inline
matrix_row_t matrix_get_row(uint8_t row)
{
return matrix[row];
}
void matrix_print(void)
{
print_matrix_header();
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
phex(row); print(": ");
print_matrix_row(row);
print("\n");
}
}
uint8_t matrix_key_count(void)
{
uint8_t count = 0;
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
count += matrix_bitpop(i);
}
return count;
}

View file

@ -0,0 +1,33 @@
Dichotemy Keyboard Firmware
======================
These configuration files were based off the Mitosis keyboard. This keyboard uses a completely different 'matrix scan' system to other keyboards, it relies on an external nRF51822 microcontroller maintaining a matrix of keystates received from the keyboard halves - it also receives mouse pointer information from the keyboard halves, which is implemented through a new feature, "Pointing Device". The matrix.c file contains the code to poll the external microcontroller for the key matrix, and the keymap.c file contains similar code to obtain the mouse report. As long as the relavant functions in these files are not changed, all other QMK features are supported.
Build log of the keyboard can be found [here](http://google.com)
Hardware design files can be found [here](http://google.com)
Firmware for the nordic MCUs can be found [here](http://google.com)
## Quantum MK Firmware
For the full Quantum feature list, see [the parent readme](/).
## Building
Download or clone the whole firmware and navigate to the keyboards/atreus folder. Once your dev env is setup, you'll be able to type `make` to generate your .hex - you can then use `make dfu` to program your PCB once you hit the reset button.
Depending on which keymap you would like to use, you will have to compile slightly differently.
### Default
To build with the default keymap, simply run `make default`.
### Other Keymaps
Several version of keymap are available in advance but you are recommended to define your favorite layout yourself. To define your own keymap create file named `<name>.c` and see keymap document (you can find in top readme.md) and existent keymap files.
To build the firmware binary hex file with a keymap just do `make` with a keymap like this:
```
$ make [default|jack|<name>]
```
Keymaps follow the format **__\<name\>.c__** and are stored in the `keymaps` folder.

View file

@ -0,0 +1,82 @@
OPT_DEFS += -DDICHOTEMY_PROMICRO
OPT_DEFS += -DCATERINA_BOOTLOADER
DICHOTEMY_UPLOAD_COMMAND = while [ ! -r $(USB) ]; do sleep 1; done; \
avrdude -p $(MCU) -c avr109 -U flash:w:$(TARGET).hex -P $(USB)
# # project specific files
SRC = matrix.c
# MCU name
#MCU = at90usb1287
MCU = atmega32u4
# Processor frequency.
# This will define a symbol, F_CPU, in all source code files equal to the
# processor frequency in Hz. You can then use this symbol in your source code to
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
# automatically to create a 32-bit value in your source code.
#
# This will be an integer division of F_USB below, as it is sourced by
# F_USB after it has run through any CPU prescalers. Note that this value
# does not *change* the processor frequency - it should merely be updated to
# reflect the processor speed set externally so that the code can use accurate
# software delays.
F_CPU = 16000000
#
# LUFA specific
#
# Target architecture (see library "Board Types" documentation).
ARCH = AVR8
# Input clock frequency.
# This will define a symbol, F_USB, in all source code files equal to the
# input clock frequency (before any prescaling is performed) in Hz. This value may
# differ from F_CPU if prescaling is used on the latter, and is required as the
# raw input clock is fed directly to the PLL sections of the AVR for high speed
# clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
# at the end, this will be done automatically to create a 32-bit value in your
# source code.
#
# If no clock division is performed on the input clock inside the AVR (via the
# CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
F_USB = $(F_CPU)
# Interrupt driven control endpoint task(+60)
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
# Boot Section Size in *bytes*
# Teensy halfKay 512
# Teensy++ halfKay 1024
# Atmel DFU loader 4096
# LUFA bootloader 4096
# USBaspLoader 2048
OPT_DEFS += -DBOOTLOADER_SIZE=4096
# Build Options
# comment out to disable the options.
#
#BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration(+1000)
#MOUSEKEY_ENABLE = yes # Mouse keys(+4700)
POINTING_DEVICE_ENABLE = yes # Generic Pointer, not as big as mouse keys hopefully.
EXTRAKEY_ENABLE = yes # Audio control and System control(+450)
CONSOLE_ENABLE = yes # Console for debug(+400)
COMMAND_ENABLE = yes # Commands for debug and configuration
CUSTOM_MATRIX = yes # Remote matrix from the wireless bridge
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
# SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend
NKRO_ENABLE = yes # USB Nkey Rollover - not yet supported in LUFA
# BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality
# MIDI_ENABLE = YES # MIDI controls
UNICODE_ENABLE = YES # Unicode
# BLUETOOTH_ENABLE = yes # Enable Bluetooth with the Adafruit EZ-Key HID
USB = /dev/ttyACM0
#upload: build
# $(DICHOTEMY_UPLOAD_COMMAND)

View file

@ -33,7 +33,7 @@ void pointing_device_init(void){
__attribute__ ((weak)) __attribute__ ((weak))
void pointing_device_send(void){ void pointing_device_send(void){
//If you need to do other things, like debugging, this is the place to do it. //If you need to do other things, like debugging, this is the place to do it.
host_mouse_send(mouseReport); host_mouse_send(&mouseReport);
//send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device //send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device
mouseReport.x = 0; mouseReport.x = 0;
mouseReport.y = 0; mouseReport.y = 0;