You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
6.1 KiB
215 lines
6.1 KiB
4 years ago
|
/* kaleidoscope::driver::keyboardio::Model01Side
|
||
|
* Copyright (C) 2015-2020 Keyboard.io, Inc
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
* SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
|
||
|
|
||
|
#include <Arduino.h>
|
||
|
#include "Model01Side.h"
|
||
|
|
||
|
extern "C" {
|
||
|
#include "kaleidoscope/device/keyboardio/twi.h"
|
||
|
}
|
||
|
|
||
|
#include "kaleidoscope/driver/color/GammaCorrection.h"
|
||
|
|
||
|
namespace kaleidoscope {
|
||
|
namespace driver {
|
||
|
namespace keyboardio {
|
||
|
|
||
|
#define SCANNER_I2C_ADDR_BASE 0x58
|
||
|
#define ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||
|
|
||
|
uint8_t twi_uninitialized = 1;
|
||
|
|
||
|
Model01Side::Model01Side(byte setAd01) {
|
||
|
ad01 = setAd01;
|
||
|
addr = SCANNER_I2C_ADDR_BASE | ad01;
|
||
|
if (twi_uninitialized--) {
|
||
|
twi_init();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Returns the relative controller addresss. The expected range is 0-3
|
||
|
uint8_t Model01Side::controllerAddress() {
|
||
|
return ad01;
|
||
|
}
|
||
|
|
||
|
// Sets the keyscan interval. We currently do three reads.
|
||
|
// before declaring a key event debounced.
|
||
|
//
|
||
|
// Takes an integer value representing a counter.
|
||
|
//
|
||
|
// 0 - 0.1-0.25ms
|
||
|
// 1 - 0.125ms
|
||
|
// 10 - 0.35ms
|
||
|
// 25 - 0.8ms
|
||
|
// 50 - 1.6ms
|
||
|
// 100 - 3.15ms
|
||
|
//
|
||
|
// You should think of this as the _minimum_ keyscan interval.
|
||
|
// LED updates can cause a bit of jitter.
|
||
|
//
|
||
|
// returns the Wire.endTransmission code (0 = success)
|
||
|
// https://www.arduino.cc/en/Reference/WireEndTransmission
|
||
|
byte Model01Side::setKeyscanInterval(byte delay) {
|
||
|
uint8_t data[] = {TWI_CMD_KEYSCAN_INTERVAL, delay};
|
||
|
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// returns -1 on error, otherwise returns the scanner version integer
|
||
|
int Model01Side::readVersion() {
|
||
|
return readRegister(TWI_CMD_VERSION);
|
||
|
}
|
||
|
|
||
|
// returns -1 on error, otherwise returns the scanner keyscan interval
|
||
|
int Model01Side::readKeyscanInterval() {
|
||
|
return readRegister(TWI_CMD_KEYSCAN_INTERVAL);
|
||
|
}
|
||
|
|
||
|
|
||
|
// returns -1 on error, otherwise returns the LED SPI Frequncy
|
||
|
int Model01Side::readLEDSPIFrequency() {
|
||
|
return readRegister(TWI_CMD_LED_SPI_FREQUENCY);
|
||
|
}
|
||
|
|
||
|
// Set the LED SPI Frequency. See wire-protocol-constants.h for
|
||
|
// values.
|
||
|
//
|
||
|
// returns the Wire.endTransmission code (0 = success)
|
||
|
// https://www.arduino.cc/en/Reference/WireEndTransmission
|
||
|
byte Model01Side::setLEDSPIFrequency(byte frequency) {
|
||
|
uint8_t data[] = {TWI_CMD_LED_SPI_FREQUENCY, frequency};
|
||
|
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int Model01Side::readRegister(uint8_t cmd) {
|
||
|
|
||
|
byte return_value = 0;
|
||
|
|
||
|
uint8_t data[] = {cmd};
|
||
|
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0);
|
||
|
|
||
|
|
||
|
|
||
|
delayMicroseconds(15); // We may be able to drop this in the future
|
||
|
// but will need to verify with correctly
|
||
|
// sized pull-ups on both the left and right
|
||
|
// hands' i2c SDA and SCL lines
|
||
|
|
||
|
uint8_t rxBuffer[1];
|
||
|
|
||
|
// perform blocking read into buffer
|
||
|
uint8_t read = twi_readFrom(addr, rxBuffer, ELEMENTS(rxBuffer), true);
|
||
|
if (read > 0) {
|
||
|
return rxBuffer[0];
|
||
|
} else {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
// gives information on the key that was just pressed or released.
|
||
|
bool Model01Side::readKeys() {
|
||
|
|
||
|
uint8_t rxBuffer[5];
|
||
|
|
||
|
// perform blocking read into buffer
|
||
|
uint8_t read = twi_readFrom(addr, rxBuffer, ELEMENTS(rxBuffer), true);
|
||
|
if (rxBuffer[0] == TWI_REPLY_KEYDATA) {
|
||
|
keyData.rows[0] = rxBuffer[1];
|
||
|
keyData.rows[1] = rxBuffer[2];
|
||
|
keyData.rows[2] = rxBuffer[3];
|
||
|
keyData.rows[3] = rxBuffer[4];
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
keydata_t Model01Side::getKeyData() {
|
||
|
return keyData;
|
||
|
}
|
||
|
|
||
|
void Model01Side::sendLEDData() {
|
||
|
sendLEDBank(nextLEDBank++);
|
||
|
if (nextLEDBank == LED_BANKS) {
|
||
|
nextLEDBank = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto constexpr gamma8 = kaleidoscope::driver::color::gamma_correction;
|
||
|
|
||
|
void Model01Side::sendLEDBank(byte bank) {
|
||
|
uint8_t data[LED_BYTES_PER_BANK + 1];
|
||
|
data[0] = TWI_CMD_LED_BASE + bank;
|
||
|
for (uint8_t i = 0 ; i < LED_BYTES_PER_BANK; i++) {
|
||
|
/* While the ATTiny controller does have a global brightness command, it is
|
||
|
* limited to 32 levels, and those aren't nicely spread out either. For this
|
||
|
* reason, we're doing our own brightness adjustment on this side, because
|
||
|
* that results in a considerably smoother curve. */
|
||
|
uint8_t c = ledData.bytes[bank][i];
|
||
|
if (c > brightness_adjustment_)
|
||
|
c -= brightness_adjustment_;
|
||
|
else
|
||
|
c = 0;
|
||
|
|
||
|
data[i + 1] = pgm_read_byte(&gamma8[c]);
|
||
|
}
|
||
|
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0);
|
||
|
}
|
||
|
|
||
|
void Model01Side::setAllLEDsTo(cRGB color) {
|
||
|
uint8_t data[] = {TWI_CMD_LED_SET_ALL_TO,
|
||
|
pgm_read_byte(&gamma8[color.b]),
|
||
|
pgm_read_byte(&gamma8[color.g]),
|
||
|
pgm_read_byte(&gamma8[color.r])
|
||
|
};
|
||
|
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0);
|
||
|
}
|
||
|
|
||
|
void Model01Side::setOneLEDTo(byte led, cRGB color) {
|
||
|
uint8_t data[] = {TWI_CMD_LED_SET_ONE_TO,
|
||
|
led,
|
||
|
pgm_read_byte(&gamma8[color.b]),
|
||
|
pgm_read_byte(&gamma8[color.g]),
|
||
|
pgm_read_byte(&gamma8[color.r])
|
||
|
};
|
||
|
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0);
|
||
|
|
||
|
}
|
||
|
|
||
|
} // namespace keyboardio
|
||
|
} // namespace driver
|
||
|
} // namespace kaleidoscope
|
||
|
|
||
|
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
|