Compare commits

...

16 Commits

Author SHA1 Message Date
Gergely Nagy 6db44b88ba
wip: ploopyenc
2 years ago
Gergely Nagy 0d9550a5cb
wip
2 years ago
Gergely Nagy 9fbb820638
Revert "wip: adns + kaleidoscope, ~"
2 years ago
Gergely Nagy d5e75e2ed7
wip: adns + kaleidoscope, ~
2 years ago
Gergely Nagy 7939dc015a
wip
2 years ago
Gergely Nagy 0926aba479
wip: adns working
2 years ago
Gergely Nagy 31e4a54e3c
wip
2 years ago
Gergely Nagy d81cdaeb77
wip
2 years ago
Gergely Nagy 4f98813695
wip
2 years ago
Gergely Nagy 6033b05cba
wip
2 years ago
Gergely Nagy f87ff8ae18
wip: ploopymini flash.sh
2 years ago
Gergely Nagy 4c10038dbb
wip: adns5050 testing in the ploopy sketch
2 years ago
Gergely Nagy 63d96c0059
wip: adns5050 driver
2 years ago
Gergely Nagy 2464ef59db
hardware: Initial support for the Ploopy Trackball Mini
2 years ago
Gergely Nagy 0944fc3b1e
driver::keyscanner: New driver: ATmegaDirect
2 years ago
Gergely Nagy 4ef189c0b1
device/avr: Add `NO_PIN` to `pins_and_ports.h`
2 years ago

@ -0,0 +1,48 @@
# This makefile for a Kaleidoscope sketch pulls in all the targets
# required to build the example
ifneq ($(KALEIDOSCOPE_DIR),)
search_path += $(KALEIDOSCOPE_DIR)
endif
ifneq ($(ARDUINO_DIRECTORIES_USER),)
search_path += $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope
endif
ifeq ($(shell uname -s),Darwin)
search_path += $(HOME)/Documents/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope
else
search_path += $(HOME)/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope
endif
sketch_makefile := etc/makefiles/sketch.mk
$(foreach candidate, $(search_path), $(if $(wildcard $(candidate)/$(sketch_makefile)), $(eval ks_dir ?= $(candidate))))
ifneq ($(ks_dir),)
$(info Using Kaleidoscope from $(ks_dir))
export KALEIDOSCOPE_DIR := $(ks_dir)
include $(ks_dir)/$(sketch_makefile)
else
$(info I can't find your Kaleidoscope installation.)
$(info )
$(info I tried looking in:)
$(info )
$(foreach candidate, $(search_path), $(info $(candidate)))
$(info )
$(info The easiest way to fix this is to set the 'KALEIDOSCOPE_DIR' environment)
$(info variable to the location of your Kaleidoscope directory.)
endif
null-target:
$(info You should never see this message)
@:

@ -0,0 +1,120 @@
// -*- mode: c++ -*-
#include <stdint.h> // for uint16_t, uint8_t
#include "pins_and_ports.h" // IWYU pragma: keep
#include <avr/wdt.h>
extern "C" {
#include "adns5050.h"
}
#define BOOTLOADER_RESET_KEY 0xB007B007
static uint32_t reset_key __attribute__((section(".noinit")));
/*
* This function runs before main(), and jumps to the bootloader after a reset
* initiated by .resetDevice().
*/
static void _bootloader_jump_after_watchdog_reset()
__attribute__((used, naked, section(".init3")));
static void _bootloader_jump_after_watchdog_reset() {
if ((MCUSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) {
reset_key = 0;
((void (*)(void))(((FLASHEND + 1L) - 4096) >> 1))();
}
}
void rebootBootloader() {
reset_key = BOOTLOADER_RESET_KEY;
wdt_enable(WDTO_250MS);
while (true) {} // This infinite loop ensures nothing else
// happens before the watchdog reboots us
}
static constexpr uint8_t direct_pins[5] = {PIN_D4, PIN_D2, PIN_E6, PIN_B6, PIN_D7};
static constexpr uint8_t unused_pins[14] = {PIN_B5, PIN_C7, PIN_D0, PIN_D1, PIN_D3, PIN_D5, PIN_D6, PIN_F1, PIN_F3, PIN_F5, PIN_F6, PIN_F7, PIN_F0, PIN_F4};
void mcuSetup() {
wdt_disable();
// disable jtag
MCUCR |= (1 << JTD);
MCUCR |= (1 << JTD);
// disable clock division
CLKPR = (1 << CLKPCE);
CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
}
void buttonSetup() {
// Initialize keyscanner
for (uint8_t i = 0; i < sizeof(unused_pins); i++) {
DDR_OUTPUT(unused_pins[i]);
DISABLE_PULLUP(unused_pins[i]);
}
for (uint8_t i = 0; i < sizeof(direct_pins); i++) {
DDR_INPUT(direct_pins[i]);
ENABLE_PULLUP(direct_pins[i]);
}
}
void setup() {
mcuSetup();
Serial.begin(9600);
buttonSetup();
adns5050_init();
//delay(100);
//adns5050_sync();
adns5050_set_cpi(CPI375);
//adns5050_read_burst();
while (!Serial);
uint8_t pid = adns5050_read_reg(0x00);
uint8_t rid = adns5050_read_reg(0x01);
uint8_t pid2 = adns5050_read_reg(0x3e);
Serial.print("pid=");
Serial.print(pid, HEX);
Serial.print("; rid=");
Serial.print(rid, HEX);
Serial.print("; pid2=");
Serial.println(pid2);
}
void handleSerial() {
if (Serial.available() == 0) return;
uint8_t b = Serial.read();
if (b == 'r') rebootBootloader();
Serial.flush();
}
void loop() {
handleSerial();
#if 1
report_adns5050_t r = adns5050_read_burst();
if (r.dx != 0 || r.dy != 0) {
Serial.println(r.dx);
Serial.println(r.dy);
}
#else
uint8_t dx = adns5050_read_reg(0x03); // DELTA_X
uint8_t dy = adns5050_read_reg(0x04); // DELTA_Y
Serial.print(dx);
Serial.print(",");
Serial.println(dy);
#endif
delay(10);
}

@ -0,0 +1,238 @@
/* Copyright 2021 Colin Lam (Ploopy Corporation)
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
* Copyright 2019 Sunjun Kim
* Copyright 2019 Hiroyuki Okada
*
* 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 "adns5050.h"
#include "Arduino.h"
#include "pins_and_ports.h"
#include <util/delay.h>
#define wait_ms(ms) \
do { \
if (__builtin_constant_p(ms)) { \
_delay_ms(ms); \
} else { \
for (uint16_t i = ms; i > 0; i--) { \
_delay_ms(1); \
} \
} \
} while (0)
#define wait_us(us) \
do { \
if (__builtin_constant_p(us)) { \
_delay_us(us); \
} else { \
for (uint16_t i = us; i > 0; i--) { \
_delay_us(1); \
} \
} \
} while (0)
// Registers
// clang-format off
#define REG_PRODUCT_ID 0x00
#define REG_REVISION_ID 0x01
#define REG_MOTION 0x02
#define REG_DELTA_X 0x03
#define REG_DELTA_Y 0x04
#define REG_SQUAL 0x05
#define REG_SHUTTER_UPPER 0x06
#define REG_SHUTTER_LOWER 0x07
#define REG_MAXIMUM_PIXEL 0x08
#define REG_PIXEL_SUM 0x09
#define REG_MINIMUM_PIXEL 0x0a
#define REG_PIXEL_GRAB 0x0b
#define REG_MOUSE_CONTROL 0x0d
#define REG_MOUSE_CONTROL2 0x19
#define REG_LED_DC_MODE 0x22
#define REG_CHIP_RESET 0x3a
#define REG_PRODUCT_ID2 0x3e
#define REG_INV_REV_ID 0x3f
#define REG_MOTION_BURST 0x63
// clang-format on
void adns5050_init(void) {
// Initialize the ADNS serial pins.
DDR_OUTPUT(ADNS5050_SCLK_PIN);
DDR_OUTPUT(ADNS5050_SDIO_PIN);
DDR_OUTPUT(ADNS5050_CS_PIN);
// reboot the adns.
// if the adns hasn't initialized yet, this is harmless.
adns5050_write_reg(REG_CHIP_RESET, 0x5a);
// wait maximum time before adns is ready.
// this ensures that the adns is actuall ready after reset.
wait_ms(55);
// read a burst from the adns and then discard it.
// gets the adns ready for write commands
// (for example, setting the dpi).
adns5050_read_burst();
}
// Perform a synchronization with the ADNS.
// Just as with the serial protocol, this is used by the slave to send a
// synchronization signal to the master.
void adns5050_sync(void) {
OUTPUT_LOW(ADNS5050_CS_PIN);
wait_us(1);
OUTPUT_HIGH(ADNS5050_CS_PIN);
}
void adns5050_cs_select(void) {
OUTPUT_LOW(ADNS5050_CS_PIN);
}
void adns5050_cs_deselect(void) {
OUTPUT_HIGH(ADNS5050_CS_PIN);
}
uint8_t adns5050_serial_read(void) {
DDR_INPUT(ADNS5050_SDIO_PIN);
DISABLE_PULLUP(ADNS5050_SDIO_PIN);
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; ++i) {
OUTPUT_LOW(ADNS5050_SCLK_PIN);
wait_us(1);
byte = (byte << 1) | READ_PIN(ADNS5050_SDIO_PIN);
OUTPUT_HIGH(ADNS5050_SCLK_PIN);
wait_us(1);
}
return byte;
}
void adns5050_serial_write(uint8_t data) {
DDR_OUTPUT(ADNS5050_SDIO_PIN);
for (int8_t b = 7; b >= 0; b--) {
OUTPUT_LOW(ADNS5050_SCLK_PIN);
if (data & (1 << b))
OUTPUT_HIGH(ADNS5050_SDIO_PIN);
else
OUTPUT_LOW(ADNS5050_SDIO_PIN);
wait_us(2);
OUTPUT_HIGH(ADNS5050_SCLK_PIN);
}
// tSWR. See page 15 of the ADNS spec sheet.
// Technically, this is only necessary if the next operation is an SDIO
// read. This is not guaranteed to be the case, but we're being lazy.
wait_us(4);
// Note that tSWW is never necessary. All write operations require at
// least 32us, which exceeds tSWW, so there's never a need to wait for it.
}
// Read a byte of data from a register on the ADNS.
// Don't forget to use the register map (as defined in the header file).
uint8_t adns5050_read_reg(uint8_t reg_addr) {
adns5050_cs_select();
adns5050_serial_write(reg_addr);
// We don't need a minimum tSRAD here. That's because a 4ms wait time is
// already included in adns5050_serial_write(), so we're good.
// See page 10 and 15 of the ADNS spec sheet.
// wait_us(4);
uint8_t byte = adns5050_serial_read();
// tSRW & tSRR. See page 15 of the ADNS spec sheet.
// Technically, this is only necessary if the next operation is an SDIO
// read or write. This is not guaranteed to be the case.
// Honestly, this wait could probably be removed.
wait_us(1);
adns5050_cs_deselect();
return byte;
}
void adns5050_write_reg(uint8_t reg_addr, uint8_t data) {
adns5050_cs_select();
adns5050_serial_write(0b10000000 | reg_addr);
adns5050_serial_write(data);
adns5050_cs_deselect();
}
report_adns5050_t adns5050_read_burst(void) {
adns5050_cs_select();
report_adns5050_t data;
data.dx = 0;
data.dy = 0;
adns5050_serial_write(REG_MOTION_BURST);
wait_us(4);
// We don't need a minimum tSRAD here. That's because a 4ms wait time is
// already included in adns5050_serial_write(), so we're good.
// See page 10 and 15 of the ADNS spec sheet.
// wait_us(4);
uint8_t x = adns5050_serial_read();
uint8_t y = adns5050_serial_read();
// Burst mode returns a bunch of other shit that we don't really need.
// Setting CS to high ends burst mode early.
adns5050_cs_deselect();
data.dx = convert_twoscomp(x);
data.dy = convert_twoscomp(y);
return data;
}
// Convert a two's complement byte from an unsigned data type into a signed
// data type.
int8_t convert_twoscomp(uint8_t data) {
if ((data & 0x80) == 0x80)
return -128 + (data & 0x7F);
else
return data;
}
// Don't forget to use the definitions for CPI in the header file.
void adns5050_set_cpi(uint16_t cpi) {
uint8_t cpival = constrain((cpi / 125), 0x1, 0xD); // limits to 0--119
adns5050_write_reg(REG_MOUSE_CONTROL2, 0b10000 | cpival);
}
uint16_t adns5050_get_cpi(void) {
uint8_t cpival = adns5050_read_reg(REG_MOUSE_CONTROL2);
return (uint16_t)((cpival & 0b10000) * 125);
}
bool adns5050_check_signature(void) {
uint8_t pid = adns5050_read_reg(REG_PRODUCT_ID);
uint8_t rid = adns5050_read_reg(REG_REVISION_ID);
uint8_t pid2 = adns5050_read_reg(REG_PRODUCT_ID2);
return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);
}

@ -0,0 +1,79 @@
/* Copyright 2021 Colin Lam (Ploopy Corporation)
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
* Copyright 2019 Sunjun Kim
* Copyright 2019 Hiroyuki Okada
*
* 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/>.
*/
#pragma once
#include <stdbool.h>
#include "pins_and_ports.h"
// CPI values
// clang-format off
#define CPI125 0x11
#define CPI250 0x12
#define CPI375 0x13
#define CPI500 0x14
#define CPI625 0x15
#define CPI750 0x16
#define CPI875 0x17
#define CPI1000 0x18
#define CPI1125 0x19
#define CPI1250 0x1a
#define CPI1375 0x1b
// clang-format on
#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
#if 0
// Definitions for the ADNS serial line.
#ifndef ADNS5050_SCLK_PIN
# error "No clock pin defined -- missing ADNS5050_SCLK_PIN"
#endif
#ifndef ADNS5050_SDIO_PIN
# error "No data pin defined -- missing ADNS5050_SDIO_PIN"
#endif
#ifndef ADNS5050_CS_PIN
# error "No chip select pin defined -- missing ADNS5050_CS_PIN"
#endif
#endif
#define ADNS5050_SCLK_PIN PIN_B7
#define ADNS5050_SDIO_PIN PIN_C6
#define ADNS5050_CS_PIN PIN_B4
typedef struct {
int8_t dx;
int8_t dy;
} report_adns5050_t;
// A bunch of functions to implement the ADNS5050-specific serial protocol.
// Note that the "serial.h" driver is insufficient, because it does not
// manually manipulate a serial clock signal.
void adns5050_init(void);
void adns5050_sync(void);
uint8_t adns5050_serial_read(void);
void adns5050_serial_write(uint8_t data);
uint8_t adns5050_read_reg(uint8_t reg_addr);
void adns5050_write_reg(uint8_t reg_addr, uint8_t data);
report_adns5050_t adns5050_read_burst(void);
void adns5050_set_cpi(uint16_t cpi);
uint16_t adns5050_get_cpi(void);
int8_t convert_twoscomp(uint8_t data);
bool adns5050_check_signature(void);

@ -0,0 +1,12 @@
#! /bin/sh
set -e
echo "* Building"
make OUTPUT_PATH=$(pwd)/_build
echo "* Jumping to bootloader..."
DEVICE=/dev/ttyACM1 focus r
sleep 2s
echo "* Flashing..."
dfu-programmer atmega32u4 erase && dfu-programmer atmega32u4 flash _build/*-latest.hex && dfu-programmer atmega32u4 start

@ -0,0 +1,164 @@
/* This file originated in QMK. https://github.com/qmk/qmk_firmware
* quantum/config_common.h 73e634482ea8f57d1f1a5f1e16bc3ffd74f84b8e
*
* Original copyright statement:
*
* Copyright 2015-2018 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/>.
*/
#pragma once
#if !defined(__ASSEMBLER__) && !defined(KALEIDOSCOPE_VIRTUAL_BUILD)
#include <avr/io.h>
#endif
#define PORT_SHIFTER 4 // this may be 4 for all AVR chips
// If you want to add more to this list, reference the PINx definitions in these header
// files: https://github.com/vancegroup-mirrors/avr-libc/tree/master/avr-libc/include/avr
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
#define ADDRESS_BASE 0x00
#define PINB_ADDRESS 0x3
#define PINC_ADDRESS 0x6
#define PIND_ADDRESS 0x9
#define PINE_ADDRESS 0xC
#define PINF_ADDRESS 0xF
#elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__)
#define ADDRESS_BASE 0x00
#define PINB_ADDRESS 0x3
#define PINC_ADDRESS 0x6
#define PIND_ADDRESS 0x9
#elif defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB646__)
#define ADDRESS_BASE 0x00
#define PINA_ADDRESS 0x0
#define PINB_ADDRESS 0x3
#define PINC_ADDRESS 0x6
#define PIND_ADDRESS 0x9
#define PINE_ADDRESS 0xC
#define PINF_ADDRESS 0xF
#elif defined(__AVR_ATmega32A__)
#define ADDRESS_BASE 0x10
#define PIND_ADDRESS 0x0
#define PINC_ADDRESS 0x3
#define PINB_ADDRESS 0x6
#define PINA_ADDRESS 0x9
#elif !defined(KALEIDOSCOPE_VIRTUAL_BUILD)
#error "Pins are not defined"
#endif
/* I/O pins */
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
#define PINDEF(port, pin) ((PIN##port##_ADDRESS << PORT_SHIFTER) | pin)
#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
#define PINDEF(port, pin) 0
#define PORTA
#define PORTB
#define PORTC
#define PORTD
#define PORTE
#define PORTF
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
#ifdef PORTA
#define PIN_A0 PINDEF(A, 0)
#define PIN_A1 PINDEF(A, 1)
#define PIN_A2 PINDEF(A, 2)
#define PIN_A3 PINDEF(A, 3)
#define PIN_A4 PINDEF(A, 4)
#define PIN_A5 PINDEF(A, 5)
#define PIN_A6 PINDEF(A, 6)
#define PIN_A7 PINDEF(A, 7)
#endif
#ifdef PORTB
#define PIN_B0 PINDEF(B, 0)
#define PIN_B1 PINDEF(B, 1)
#define PIN_B2 PINDEF(B, 2)
#define PIN_B3 PINDEF(B, 3)
#define PIN_B4 PINDEF(B, 4)
#define PIN_B5 PINDEF(B, 5)
#define PIN_B6 PINDEF(B, 6)
#define PIN_B7 PINDEF(B, 7)
#endif
#ifdef PORTC
#define PIN_C0 PINDEF(C, 0)
#define PIN_C1 PINDEF(C, 1)
#define PIN_C2 PINDEF(C, 2)
#define PIN_C3 PINDEF(C, 3)
#define PIN_C4 PINDEF(C, 4)
#define PIN_C5 PINDEF(C, 5)
#define PIN_C6 PINDEF(C, 6)
#define PIN_C7 PINDEF(C, 7)
#endif
#ifdef PORTD
#define PIN_D0 PINDEF(D, 0)
#define PIN_D1 PINDEF(D, 1)
#define PIN_D2 PINDEF(D, 2)
#define PIN_D3 PINDEF(D, 3)
#define PIN_D4 PINDEF(D, 4)
#define PIN_D5 PINDEF(D, 5)
#define PIN_D6 PINDEF(D, 6)
#define PIN_D7 PINDEF(D, 7)
#endif
#ifdef PORTE
#define PIN_E0 PINDEF(E, 0)
#define PIN_E1 PINDEF(E, 1)
#define PIN_E2 PINDEF(E, 2)
#define PIN_E3 PINDEF(E, 3)
#define PIN_E4 PINDEF(E, 4)
#define PIN_E5 PINDEF(E, 5)
#define PIN_E6 PINDEF(E, 6)
#define PIN_E7 PINDEF(E, 7)
#endif
#ifdef PORTF
#define PIN_F0 PINDEF(F, 0)
#define PIN_F1 PINDEF(F, 1)
#define PIN_F2 PINDEF(F, 2)
#define PIN_F3 PINDEF(F, 3)
#define PIN_F4 PINDEF(F, 4)
#define PIN_F5 PINDEF(F, 5)
#define PIN_F6 PINDEF(F, 6)
#define PIN_F7 PINDEF(F, 7)
#endif
/* converting pins to ports */
enum { PIN_OFFSET,
DDR_OFFSET,
PORT_OFFSET };
#define PIN_ADDRESS_MASK 0xF
#define PIN_ADDRESS(p, offset) _SFR_IO8(ADDRESS_BASE + (p >> PORT_SHIFTER) + offset)
#define PIN_REG_FOR_PIN(pin) PIN_ADDRESS(pin, PIN_OFFSET)
#define DDR_REG_FOR_PIN(pin) PIN_ADDRESS(pin, DDR_OFFSET)
#define PORT_REG_FOR_PIN(pin) PIN_ADDRESS(pin, PORT_OFFSET)
#define PIN_NUM_FOR_PIN(pin) (pin & PIN_ADDRESS_MASK)
#define PIN_MASK_FOR_PIN(pin) _BV(PIN_NUM_FOR_PIN(pin))
#define DDR_INPUT(pin) (DDR_REG_FOR_PIN(pin) &= ~(PIN_MASK_FOR_PIN(pin)))
#define DDR_OUTPUT(pin) (DDR_REG_FOR_PIN(pin) |= (PIN_MASK_FOR_PIN(pin)))
#define ENABLE_PULLUP(pin) (PORT_REG_FOR_PIN(pin) |= (PIN_MASK_FOR_PIN(pin)))
#define DISABLE_PULLUP(pin) (PORT_REG_FOR_PIN(pin) &= ~(PIN_MASK_FOR_PIN(pin)))
#define OUTPUT_HIGH(pin) (PORT_REG_FOR_PIN(pin) |= (PIN_MASK_FOR_PIN(pin)))
#define OUTPUT_LOW(pin) (PORT_REG_FOR_PIN(pin) &= ~(PIN_MASK_FOR_PIN(pin)))
#define OUTPUT_TOGGLE(pin) (PORT_REG_FOR_PIN(pin) ^= (PIN_MASK_FOR_PIN(pin)))
#define READ_PIN(pin) (!!(PIN_REG_FOR_PIN(pin) & PIN_MASK_FOR_PIN(pin)))
#define NO_PIN (uint8_t)(~0)

@ -0,0 +1,6 @@
{
"cpu": {
"fqbn": "keyboardio:avr:ploopy_mini",
"port": ""
}
}

@ -0,0 +1,48 @@
# This makefile for a Kaleidoscope sketch pulls in all the targets
# required to build the example
ifneq ($(KALEIDOSCOPE_DIR),)
search_path += $(KALEIDOSCOPE_DIR)
endif
ifneq ($(ARDUINO_DIRECTORIES_USER),)
search_path += $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope
endif
ifeq ($(shell uname -s),Darwin)
search_path += $(HOME)/Documents/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope
else
search_path += $(HOME)/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope
endif
sketch_makefile := etc/makefiles/sketch.mk
$(foreach candidate, $(search_path), $(if $(wildcard $(candidate)/$(sketch_makefile)), $(eval ks_dir ?= $(candidate))))
ifneq ($(ks_dir),)
$(info Using Kaleidoscope from $(ks_dir))
export KALEIDOSCOPE_DIR := $(ks_dir)
include $(ks_dir)/$(sketch_makefile)
else
$(info I can't find your Kaleidoscope installation.)
$(info )
$(info I tried looking in:)
$(info )
$(foreach candidate, $(search_path), $(info $(candidate)))
$(info )
$(info The easiest way to fix this is to set the 'KALEIDOSCOPE_DIR' environment)
$(info variable to the location of your Kaleidoscope directory.)
endif
null-target:
$(info You should never see this message)
@:

@ -0,0 +1,65 @@
// -*- mode: c++ -*-
#include "Kaleidoscope.h"
#include "Kaleidoscope-FocusSerial.h"
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.h"
#include "Kaleidoscope-MouseKeys.h"
#include "Kaleidoscope-Macros.h"
KEYMAPS(
[0] = KEYMAP(M(0), Key_B, Key_C, Key_D, Key_E, Key_NoKey),
);
#define OPT_ENC1 PIN_F0
#define OPT_ENC2 PIN_F4
#define OPT_ENC1_MUX 0
#define OPT_ENC2_MUX 4
void handleEncoder() {
uint16_t p1 = analogRead(OPT_ENC1_MUX);
uint16_t p2 = analogRead(OPT_ENC2_MUX);
if (p1 != 0 || p2 != 0) {
Focus.send("p1=", p1, "p2=", p2, "\n");
}
}
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (!keyToggledOn(event.state)) {
return MACRO_NONE;
}
switch (macro_id) {
case 0:
handleEncoder();
break;
}
return MACRO_NONE;
}
KALEIDOSCOPE_INIT_PLUGINS(
Focus,
EEPROMSettings,
EEPROMKeymap,
MouseKeys,
Macros
);
void setup() {
Kaleidoscope.setup();
EEPROMKeymap.setup(5);
DDR_INPUT(OPT_ENC1);
ENABLE_PULLUP(OPT_ENC1);
DDR_INPUT(OPT_ENC2);
ENABLE_PULLUP(OPT_ENC2);
}
void loop() {
Kaleidoscope.loop();
handleEncoder();
delay(10);
}

@ -0,0 +1,12 @@
#! /bin/sh
set -e
echo "* Building"
make OUTPUT_PATH=$(pwd)/_build
echo "* Jumping to bootloader..."
DEVICE=/dev/ttyACM1 focus device.reset
sleep 2s
echo "* Flashing..."
dfu-programmer atmega32u4 erase && dfu-programmer atmega32u4 flash _build/*-latest.hex && dfu-programmer atmega32u4 start

@ -0,0 +1,6 @@
{
"cpu": {
"fqbn": "keyboardio:avr:ploopy_mini",
"port": ""
}
}

@ -0,0 +1,48 @@
# This makefile for a Kaleidoscope sketch pulls in all the targets
# required to build the example
ifneq ($(KALEIDOSCOPE_DIR),)
search_path += $(KALEIDOSCOPE_DIR)
endif
ifneq ($(ARDUINO_DIRECTORIES_USER),)
search_path += $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope
endif
ifeq ($(shell uname -s),Darwin)
search_path += $(HOME)/Documents/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope
else
search_path += $(HOME)/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope
endif
sketch_makefile := etc/makefiles/sketch.mk
$(foreach candidate, $(search_path), $(if $(wildcard $(candidate)/$(sketch_makefile)), $(eval ks_dir ?= $(candidate))))
ifneq ($(ks_dir),)
$(info Using Kaleidoscope from $(ks_dir))
export KALEIDOSCOPE_DIR := $(ks_dir)
include $(ks_dir)/$(sketch_makefile)
else
$(info I can't find your Kaleidoscope installation.)
$(info )
$(info I tried looking in:)
$(info )
$(foreach candidate, $(search_path), $(info $(candidate)))
$(info )
$(info The easiest way to fix this is to set the 'KALEIDOSCOPE_DIR' environment)
$(info variable to the location of your Kaleidoscope directory.)
endif
null-target:
$(info You should never see this message)
@:

@ -0,0 +1,100 @@
// -*- mode: c++ -*-
#include "Kaleidoscope.h"
#include "Kaleidoscope-FocusSerial.h"
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.h"
#include "Kaleidoscope-MouseKeys.h"
#include "Kaleidoscope-Macros.h"
#include "kaleidoscope/driver/sensor/ADNS5050.h"
struct PTBMProps : kaleidoscope::driver::sensor::ADNS5050BaseProps {
struct pin {
static constexpr uint8_t SCLK = PIN_B7;
static constexpr uint8_t SDIO = PIN_C6;
static constexpr uint8_t NCS = PIN_B4;
};
};
typedef kaleidoscope::driver::sensor::ADNS5050<PTBMProps> SensorT;
SensorT Sensor;
KEYMAPS(
[0] = KEYMAP(M(0), M(1), M(2), Key_D, Key_E, Key_NoKey),
);
void deltaX() {
uint8_t dx = Sensor.readRegister(Sensor.Register::DELTA_X);
Serial.print("dx = ");
Serial.println(dx);
}
void deltaY() {
uint8_t dy = Sensor.readRegister(Sensor.Register::DELTA_Y);
Serial.print("dy = ");
Serial.println(dy);
}
void getPID() {
uint8_t pid = Sensor.readRegister(Sensor.Register::PRODUCT_ID);
uint8_t rid = Sensor.readRegister(Sensor.Register::REVISION_ID);
uint8_t pid2 = Sensor.readRegister(Sensor.Register::PRODUCT_ID2);
Focus.send("pid=", pid, "rid=", rid, "pid2=", pid2, "\n");
}
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (!keyToggledOn(event.state)) {
return MACRO_NONE;
}
switch (macro_id) {
case 0:
deltaX();
break;
case 1:
deltaY();
break;
case 2:
getPID();
break;
}
return MACRO_NONE;
}
KALEIDOSCOPE_INIT_PLUGINS(
Focus,
EEPROMSettings,
EEPROMKeymap,
MouseKeys,
Macros
);
void setup() {
Kaleidoscope.setup();
EEPROMKeymap.setup(5);
Sensor.setup();
}
void loop() {
SensorT::Report report;
Kaleidoscope.loop();
report = Sensor.readReport();
if (report.dx != -1) {
Serial.print("dx = ");
Serial.print(report.dx);
Serial.print(" ");
}
if (report.dy != -1) {
Serial.print("dy = ");
Serial.print(report.dy);
}
if (report.dy != -1 || report.dx != -1) {
Serial.println();
}
}

@ -0,0 +1,12 @@
#! /bin/sh
set -e
echo "* Building"
make OUTPUT_PATH=$(pwd)/_build >/dev/null 2>LOG
echo "* Jumping to bootloader..."
DEVICE=/dev/ttyACM1 focus device.reset
sleep 2s
echo "* Flashing..."
dfu-programmer atmega32u4 erase && dfu-programmer atmega32u4 flash _build/PloopyMini-latest.hex && dfu-programmer atmega32u4 start

@ -0,0 +1,6 @@
{
"cpu": {
"fqbn": "keyboardio:avr:ploopy_mini",
"port": ""
}
}

@ -0,0 +1,7 @@
name=Kaleidoscope-Hardware-Ploopy-Trackball-Mini
version=0.0.0
sentence=Ploopy Trackball Mini hardware support for Kaleidoscope
maintainer=Kaleidoscope's Developers <jesse@keyboard.io>
url=https://github.com/keyboardio/Kaleidoscope
author=Keyboardio
paragraph=

@ -0,0 +1,21 @@
/* -*- mode: c++ -*-
* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc
*
* 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 3 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/>.
*/
#pragma once
#include "kaleidoscope/device/ploopy/TrackballMini.h" // IWYU pragma: export

@ -0,0 +1,67 @@
/* -*- mode: c++ -*-
* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc
*
* 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 3 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 KALEIDOSCOPE_VIRTUAL_BUILD
#ifdef ARDUINO_AVR_PLOOPY_MINI
#include "kaleidoscope/Runtime.h"
#include "kaleidoscope/driver/keyscanner/Base_Impl.h"
// Here, we set up aliases to the device's KeyScanner and KeyScannerProps
// in the global namespace within the scope of this file. We'll use these
// aliases to simplify some template initialization code below.
using KeyScannerProps = typename kaleidoscope::device::ploopy::TrackballMiniProps::KeyScannerProps;
using KeyScanner = typename kaleidoscope::device::ploopy::TrackballMiniProps::KeyScanner;
namespace kaleidoscope {
namespace device {
namespace ploopy {
// `KeyScannerProps` here refers to the alias set up above. We do not need to
// prefix the `matrix_rows` and `matrix_columns` names within the array
// declaration, because those are resolved within the context of the class, so
// the `matrix_rows` in `KeyScannerProps::matrix_row_pins[matrix_rows]` gets
// resolved as `KeyScannerProps::matrix_rows`.
const uint8_t KeyScannerProps::matrix_rows;
const uint8_t KeyScannerProps::matrix_columns;
constexpr uint8_t KeyScannerProps::direct_pins[];
constexpr uint8_t KeyScannerProps::unused_pins[];
// `KeyScanner` here refers to the alias set up above, just like in the
// `KeyScannerProps` case above.
template<>
KeyScanner::pin_state_t KeyScanner::pin_state_ = {};
// We set up the TIMER1 interrupt vector here. Due to dependency reasons, this
// cannot be in a header-only driver, and must be placed here.
//
// Timer1 is responsible for setting a property on the KeyScanner, which will
// tell it to do a scan. We use this to make sure that scans happen at roughly
// the intervals we want. We do the scan outside of the interrupt scope for
// practical reasons: guarding every codepath against interrupts that can be
// reached from the scan is far too tedious, for very little gain.
ISR(TIMER1_OVF_vect) {
Runtime.device().keyScanner().do_scan_ = true;
}
} // namespace ploop
} // namespace device
} // namespace kaleidoscope
#endif
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD

@ -0,0 +1,84 @@
/* -*- mode: c++ -*-
* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc
*
* 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 3 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/>.
*/
#pragma once
#ifdef ARDUINO_AVR_PLOOPY_MINI
#include <Arduino.h>
#define KALEIDOSCOPE_BOOTLOADER_FLIP_WORKAROUND 1
#include "kaleidoscope/device/ATmega32U4Keyboard.h"
#include "kaleidoscope/driver/bootloader/avr/FLIP.h"
#include "kaleidoscope/driver/keyscanner/ATmegaDirect.h"
namespace kaleidoscope {
namespace device {
namespace ploopy {
struct TrackballMiniProps : kaleidoscope::device::ATmega32U4KeyboardProps {
struct MCUProps : public kaleidoscope::driver::mcu::ATmega32U4Props {
static constexpr bool disable_jtag = true;
static constexpr bool disable_clock_division = true;
};
typedef kaleidoscope::driver::mcu::ATmega32U4<MCUProps> MCU;
struct KeyScannerProps : public kaleidoscope::driver::keyscanner::ATmegaDirectProps {
static constexpr uint8_t matrix_rows = 1;
static constexpr uint8_t matrix_columns = 6;
typedef MatrixAddr<matrix_rows, matrix_columns> KeyAddr;
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
static constexpr uint8_t direct_pins[5] = {PIN_D4, PIN_D2, PIN_E6, PIN_B6, PIN_D7};
static constexpr uint8_t unused_pins[12] = {PIN_B5, PIN_C7, PIN_D0, PIN_D1, PIN_D3, PIN_D5, PIN_D6, PIN_F1, PIN_F3, PIN_F5, PIN_F6, PIN_F7};
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
};
typedef kaleidoscope::driver::keyscanner::ATmegaDirect<KeyScannerProps> KeyScanner;
typedef kaleidoscope::driver::bootloader::avr::FLIP Bootloader;
static constexpr const char *short_name = "ploopy_mini";
};
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
class TrackballMini : public kaleidoscope::device::ATmega32U4Keyboard<TrackballMiniProps> {};
#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
/* Device definition omitted for virtual device builds.
* We need to forward declare the device name, though, as there are
* some legacy extern references to boards whose definition
* depends on this.
*/
class TrackballMini;
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
// clang-format off
#define PER_KEY_DATA(dflt, P1, P2, P3, P4, P5, SENSOR) \
P1, P2, P3, P4, P5, SENSOR
// clang-format on
} // namespace keyboardio
} // namespace device
EXPORT_DEVICE(kaleidoscope::device::ploopy::TrackballMini)
} // namespace kaleidoscope
#endif

@ -160,3 +160,5 @@ enum { PIN_OFFSET,
#define OUTPUT_TOGGLE(pin) (PORT_REG_FOR_PIN(pin) ^= (PIN_MASK_FOR_PIN(pin)))
#define READ_PIN(pin) (!!(PIN_REG_FOR_PIN(pin) & PIN_MASK_FOR_PIN(pin)))
#define NO_PIN (uint8_t)(~0)

@ -0,0 +1,205 @@
/* -*- mode: c++ -*-
* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc
*
* 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 3 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/>.
*/
#pragma once
#include <stdint.h> // for uint16_t, uint8_t
#include "kaleidoscope/device/avr/pins_and_ports.h" // IWYU pragma: keep
#include "kaleidoscope/driver/keyscanner/Base.h" // for BaseProps
#include "kaleidoscope/driver/keyscanner/None.h" // for None
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
#include <avr/wdt.h>
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
namespace kaleidoscope {
namespace driver {
namespace keyscanner {
struct ATmegaDirectProps : public kaleidoscope::driver::keyscanner::BaseProps {
static const uint16_t keyscan_interval = 1500;
typedef uint16_t PinState;
/*
* The following lines declares empty arrays, and both must be shadowed by the
* descendant keyscanner description class.
*/
static constexpr uint8_t direct_pins[] = {};
static constexpr uint8_t unused_pins[] = {};
};
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
template<typename _KeyScannerProps>
class ATmegaDirect : kaleidoscope::driver::keyscanner::Base<_KeyScannerProps> {
private:
typedef ATmegaDirect<_KeyScannerProps> ThisType;
public:
void setup() {
static_assert(
sizeof(_KeyScannerProps::direct_pins) > 0,
"The key scanner description has an empty array of direct pins.");
static_assert(
_KeyScannerProps::matrix_rows == 1,
"ATmegaDirect requires matrix_rows to be 1.");
for (uint8_t i = 0; i < sizeof(_KeyScannerProps::unused_pins); i++) {
DDR_OUTPUT(_KeyScannerProps::unused_pins[i]);
DISABLE_PULLUP(_KeyScannerProps::unused_pins[i]);
}
for (uint8_t i = 0; i < sizeof(_KeyScannerProps::direct_pins); i++) {
DDR_INPUT(_KeyScannerProps::direct_pins[i]);
ENABLE_PULLUP(_KeyScannerProps::direct_pins[i]);
}
setScanCycleTime(_KeyScannerProps::keyscan_interval);
}
/* setScanCycleTime takes a value of between 0 and 8192. This corresponds
* (roughly) to the number of microseconds to wait between scanning the key
* matrix. Our debouncing algorithm does four checks before deciding that a
* result is valid. Most normal mechanical switches specify a 5ms debounce
* period. On an ATMega32U4, 1700 gets you about 5ms of debouncing.
*
* Because keycanning is triggered by an interrupt but not run in that
* interrupt, the actual amount of time between scans is prone to a little bit
* of jitter.
*/
void setScanCycleTime(uint16_t c) {
TCCR1B = _BV(WGM13);
TCCR1A = 0;
const uint32_t cycles = (F_CPU / 2000000) * c;
ICR1 = cycles;
TCCR1B = _BV(WGM13) | _BV(CS10);
TIMSK1 = _BV(TOIE1);
}
void readMatrix() {
typename _KeyScannerProps::PinState hot_pins = readPins();
typename _KeyScannerProps::PinState debounced_changes =
debounce(hot_pins, &(pin_state_.debouncer));
if (debounced_changes) {
pin_state_.current = pin_state_.debouncer.debounced_state;
}
}
void scanMatrix() {
if (do_scan_) {
do_scan_ = false;
readMatrix();
}
actOnMatrixScan();
}
void actOnMatrixScan() {
for (uint8_t pin = 0; pin < sizeof(_KeyScannerProps::direct_pins); pin++) {
uint8_t keyState = (bitRead(pin_state_.previous, pin) << 0) |
(bitRead(pin_state_.current, pin) << 1);
if (keyState) {
ThisType::handleKeyswitchEvent(Key_NoKey, typename _KeyScannerProps::KeyAddr(0, pin), keyState);
}
}
pin_state_.previous = pin_state_.current;
}
uint8_t pressedKeyswitchCount() {
return __builtin_popcount(pin_state_.current);
}
bool isKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) {
return (bitRead(pin_state_.current, key_addr.col()) != 0);
}
uint8_t previousPressedKeyswitchCount() {
return __builtin_popcount(pin_state_.previous);
}
bool wasKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) {
return (bitRead(pin_state_.previous, key_addr.col()) != 0);
}
bool do_scan_;
protected:
/*
each of these variables are storing the state for a row of keys
so for key 0, the counter is represented by db0[0] and db1[0]
and the state in debounced_state[0].
*/
struct debounce_t {
typename _KeyScannerProps::PinState db0; // counter bit 0
typename _KeyScannerProps::PinState db1; // counter bit 1
typename _KeyScannerProps::PinState debounced_state; // debounced state
};
struct pin_state_t {
typename _KeyScannerProps::PinState previous;
typename _KeyScannerProps::PinState current;
debounce_t debouncer;
};
private:
typedef _KeyScannerProps KeyScannerProps_;
static pin_state_t pin_state_;
typename _KeyScannerProps::PinState
readPins() {
typename _KeyScannerProps::PinState hot_pins = 0;
for (uint8_t i = 0; i < sizeof(_KeyScannerProps::direct_pins); i++) {
hot_pins |= (!READ_PIN(_KeyScannerProps::direct_pins[i]) << i);
}
return hot_pins;
}
static inline typename _KeyScannerProps::PinState debounce(
typename _KeyScannerProps::PinState sample, debounce_t *debouncer) {
typename _KeyScannerProps::PinState delta, changes;
// Use xor to detect changes from last stable state:
// if a key has changed, it's bit will be 1, otherwise 0
delta = sample ^ debouncer->debounced_state;
// Increment counters and reset any unchanged bits:
// increment bit 1 for all changed keys
debouncer->db1 = ((debouncer->db1) ^ (debouncer->db0)) & delta;
// increment bit 0 for all changed keys
debouncer->db0 = ~(debouncer->db0) & delta;
// Calculate returned change set: if delta is still true
// and the counter has wrapped back to 0, the key is changed.
changes = ~(~delta | (debouncer->db0) | (debouncer->db1));
// Update state: in this case use xor to flip any bit that is true in changes.
debouncer->debounced_state ^= changes;
return changes;
}
};
#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
template<typename _KeyScannerProps>
class ATmegaDirect : public keyscanner::None {};
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
} // namespace keyscanner
} // namespace driver
} // namespace kaleidoscope

@ -0,0 +1,224 @@
/* -*- mode: c++ -*-
* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc
*
* 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 3 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/>.
*/
#pragma once
#if defined(__AVR__)
#include <stdint.h> // for uint8_t
#include "kaleidoscope/device/avr/pins_and_ports.h"
namespace kaleidoscope {
namespace driver {
namespace sensor {
struct ADNS5050BaseProps {
struct pin {
static constexpr uint8_t SCLK = NO_PIN;
static constexpr uint8_t SDIO = NO_PIN;
static constexpr uint8_t NCS = NO_PIN;
};
};
template <typename _Props>
class ADNS5050 {
public:
ADNS5050() {}
typedef struct Report {
int8_t dx;
int8_t dy;
};
enum class CPI {
CPI125 = 0x11,
CPI250 = 0x12,
CPI375 = 0x13,
CPI500 = 0x14,
CPI625 = 0x15,
CPI750 = 0x16,
CPI875 = 0x17,
CPI1000 = 0x18,
CPI1125 = 0x19,
CPI1250 = 0x1a,
CPI1375 = 0x1b
};
void setup() {
static_assert(_Props::pin::SCLK != NO_PIN, "No clock pin specified!");
static_assert(_Props::pin::SDIO != NO_PIN, "No IO pin specified!");
static_assert(_Props::pin::NCS != NO_PIN, "No chip select pin specified!");
DDR_OUTPUT(_Props::pin::SCLK);
DDR_OUTPUT(_Props::pin::SDIO);
DDR_OUTPUT(_Props::pin::NCS);
// 0x5a is chip reset (datasheet page 26)
writeRegister(Register::CHIP_RESET, 0x5a);
delay(55);
// read an initial report from the sensor, and discard it.
// readReport();
}
void sync() {
OUTPUT_LOW(_Props::pin::NCS);
delayMicroseconds(1);
OUTPUT_HIGH(_Props::pin::NCS);
}
Report readReport() {
Report report;
OUTPUT_LOW(_Props::pin::NCS);
serialWrite(Register::MOTION_BURST);
uint8_t x = serialRead();
uint8_t y = serialRead();
OUTPUT_HIGH(_Props::pin::NCS);
report.dx = convertTwosComp(x);
report.dy = convertTwosComp(y);
return report;
}
void setCPI(CPI cpi) {
uint8_t cpival = cpi / 125;
if (cpival < 0x01) {
cpival = 0x1;
}
if (cpival > 0x0d) {
cpival = 0x0d;
}
writeRegister(Register::MOUSE_CONTROL2, 0b10000 | cpival);
}
CPI getCPI() {
// not implemented yet
return CPI::CPI125;
}
bool checkSignature() {
uint8_t rid = readRegister(Register::REVISION_ID);
uint8_t pid = readRegister(Register::PRODUCT_ID);
uint8_t pid2 = readRegister(Register::PRODUCT_ID2);
return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);
}
public:
struct Timeout {
static constexpr uint16_t WAKEUP = 55000;
static constexpr uint16_t SWW = 30;
static constexpr uint16_t SWR = 20;
static constexpr uint16_t SRW = 1;
static constexpr uint16_t SRR = 1;
static constexpr uint16_t SRAD = 4;
};
enum class Register {
PRODUCT_ID = 0x00,
REVISION_ID = 0x01,
MOTION = 0x02,
DELTA_X = 0x03,
DELTA_Y = 0x04,
MOUSE_CONTROL2 = 0x19,
CHIP_RESET = 0x3a,
PRODUCT_ID2 = 0x3e,
MOTION_BURST = 0x63
};
int8_t convertTwosComp(uint8_t data) {
if ((data & 0x80) == 0x80)
return -128 + (data & 0x7F);
else
return data;
}
uint8_t serialRead() {
DDR_INPUT(_Props::pin::SDIO);
delayMicroseconds(1);
uint8_t byte;
for (uint8_t i = 0; i < 8; i++) {
OUTPUT_LOW(_Props::pin::SCLK);
delayMicroseconds(1);
byte = (byte << 1) | READ_PIN(_Props::pin::SDIO);
OUTPUT_HIGH(_Props::pin::SCLK);
delayMicroseconds(1);
}
return byte;
}
void serialWrite(uint8_t data) {
DDR_OUTPUT(_Props::pin::SDIO);
delayMicroseconds(1);
for (int8_t i = 7; i >= 0; i--) {
OUTPUT_LOW(_Props::pin::SCLK);
delayMicroseconds(1);
if (data && (1 << i)) {
OUTPUT_HIGH(_Props::pin::SDIO);
} else {
OUTPUT_LOW(_Props::pin::SDIO);
}
delayMicroseconds(2);
OUTPUT_HIGH(_Props::pin::SCLK);
delayMicroseconds(1);
}
delayMicroseconds(4);
}
void serialWrite(Register reg) {
serialWrite((uint8_t) reg);
}
uint8_t readRegister(Register reg) {
OUTPUT_LOW(_Props::pin::NCS);
serialWrite(reg);
uint8_t byte = serialRead();
delayMicroseconds(1);
OUTPUT_HIGH(_Props::pin::NCS);
return byte;
}
void writeRegister(Register reg, uint8_t data) {
OUTPUT_LOW(_Props::pin::NCS);
serialWrite(0b10000000 | (uint8_t)reg);
serialWrite(data);
OUTPUT_HIGH(_Props::pin::NCS);
}
};
} // namespace sensor
} // namespace driver
} // namespace kaleidoscope
#endif // define(__AVR__)
Loading…
Cancel
Save