File neotrellis.h¶
File List > dev > neotrellis.h
Go to the documentation of this file
Source Code¶
#pragma once
#ifndef DSY_NEO_TRELLIS_H
#define DSY_NEO_TRELLIS_H
#include "dev/neopixel.h"
#define NEO_TRELLIS_ADDR 0x2E
#define NEO_TRELLIS_NEOPIX_PIN 3
#define NEO_TRELLIS_NUM_ROWS 4
#define NEO_TRELLIS_NUM_COLS 4
#define NEO_TRELLIS_NUM_KEYS (NEO_TRELLIS_NUM_ROWS * NEO_TRELLIS_NUM_COLS)
#define NEO_TRELLIS_MAX_CALLBACKS 32
#define NEO_TRELLIS_KEY(x) (((x) / 4) * 8 + ((x) % 4))
#define NEO_TRELLIS_SEESAW_KEY(x) (((x) / 8) * 4 + ((x) % 8))
#define NEO_TRELLIS_X(k) ((k) % 4)
#define NEO_TRELLIS_Y(k) ((k) / 4)
#define NEO_TRELLIS_XY(x, y) ((y)*NEO_TRELLIS_NUM_ROWS + (x))
namespace daisy
{
class NeoTrellisI2CTransport
{
public:
NeoTrellisI2CTransport() {}
~NeoTrellisI2CTransport() {}
struct Config
{
I2CHandle::Config::Peripheral periph;
I2CHandle::Config::Speed speed;
Pin scl;
Pin sda;
uint8_t address;
Config()
{
address = NEO_TRELLIS_ADDR;
periph = I2CHandle::Config::Peripheral::I2C_1;
speed = I2CHandle::Config::Speed::I2C_400KHZ;
scl = Pin(PORTB, 8);
sda = Pin(PORTB, 9);
}
};
inline void Init(Config config)
{
config_ = config;
I2CHandle::Config i2c_config;
i2c_config.mode = I2CHandle::Config::Mode::I2C_MASTER;
i2c_config.periph = config.periph;
i2c_config.speed = config.speed;
i2c_config.pin_config.scl = config.scl;
i2c_config.pin_config.sda = config.sda;
error_ |= I2CHandle::Result::OK != i2c_.Init(i2c_config);
}
void Write(uint8_t *data, uint16_t size)
{
error_ |= I2CHandle::Result::OK
!= i2c_.TransmitBlocking(config_.address, data, size, 10);
}
void Read(uint8_t *data, uint16_t size)
{
error_ |= I2CHandle::Result::OK
!= i2c_.ReceiveBlocking(config_.address, data, size, 10);
}
void ReadLen(uint8_t reg_high,
uint8_t reg_low,
uint8_t *buff,
uint16_t size,
int delay)
{
uint8_t reg[2] = {reg_high, reg_low};
Write(reg, 2);
System::DelayUs(delay);
Read(buff, size);
}
void Write8(uint8_t reg_high, uint8_t reg_low, uint8_t value)
{
uint8_t buffer[3];
buffer[0] = reg_high;
buffer[1] = reg_low;
buffer[2] = value;
Write(buffer, 3);
}
uint8_t Read8(uint8_t reg_high, uint8_t reg_low, int delay)
{
uint8_t buffer;
ReadLen(reg_high, reg_low, &buffer, 1, delay);
return buffer;
}
bool GetError()
{
bool tmp = error_;
error_ = false;
return tmp;
}
private:
I2CHandle i2c_;
Config config_;
// true if error has occured since last check
bool error_;
};
template <typename Transport>
class NeoTrellis
{
public:
NeoTrellis() {}
~NeoTrellis() {}
enum ModuleBaseAddress
{
SEESAW_STATUS_BASE = 0x00,
SEESAW_GPIO_BASE = 0x01,
SEESAW_SERCOM0_BASE = 0x02,
SEESAW_TIMER_BASE = 0x08,
SEESAW_ADC_BASE = 0x09,
SEESAW_DAC_BASE = 0x0A,
SEESAW_INTERRUPT_BASE = 0x0B,
SEESAW_DAP_BASE = 0x0C,
SEESAW_EEPROM_BASE = 0x0D,
SEESAW_NEOPIXEL_BASE = 0x0E,
SEESAW_TOUCH_BASE = 0x0F,
SEESAW_KEYPAD_BASE = 0x10,
SEESAW_ENCODER_BASE = 0x11,
SEESAW_SPECTRUM_BASE = 0x12,
};
enum KeypadFuncAddRegs
{
SEESAW_KEYPAD_STATUS = 0x00,
SEESAW_KEYPAD_EVENT = 0x01,
SEESAW_KEYPAD_INTENSET = 0x02,
SEESAW_KEYPAD_INTENCLR = 0x03,
SEESAW_KEYPAD_COUNT = 0x04,
SEESAW_KEYPAD_FIFO = 0x10,
};
enum StatusFuncAddRegs
{
SEESAW_STATUS_HW_ID = 0x01,
SEESAW_STATUS_VERSION = 0x02,
SEESAW_STATUS_OPTIONS = 0x03,
SEESAW_STATUS_TEMP = 0x04,
SEESAW_STATUS_SWRST = 0x7F,
};
union keyEventRaw
{
struct
{
uint8_t EDGE : 2;
uint8_t NUM : 6;
} bit;
uint8_t reg;
};
union keyEvent
{
struct Bit
{
uint8_t EDGE : 2;
uint16_t NUM : 14;
} bit;
uint16_t reg;
};
union keyState
{
struct
{
uint8_t STATE : 1;
uint8_t ACTIVE : 4;
} bit;
uint8_t reg;
};
enum KeypadEdge
{
HIGH = 0,
LOW,
FALLING,
RISING,
};
struct Config
{
typename Transport::Config transport_config;
NeoPixelI2C::Config pixels_conf;
Config() {}
};
enum Result
{
OK = 0,
ERR
};
typedef void (*TrellisCallback)(keyEvent evt); //< Trellis Callback typedef
Result Init(Config config)
{
config_ = config;
// init neopixels
if(pixels.Init(config_.pixels_conf) == NeoPixelI2C::Result::ERR)
{
return ERR;
}
transport_.Init(config_.transport_config);
// 10 ms delay
System::Delay(10);
EnableKeypadInterrupt();
return GetTransportError();
}
void Write8(uint8_t reg_high, uint8_t reg_low, uint8_t value)
{
return transport_.Write8(reg_high, reg_low, value);
}
uint8_t Read8(uint8_t reg_high, uint8_t reg_low, int delay)
{
return transport_.Read8(reg_high, reg_low, delay);
}
void ReadLen(uint8_t reg_high,
uint8_t reg_low,
uint8_t *buff,
uint8_t len,
int delay)
{
transport_.ReadLen(reg_high, reg_low, buff, len, delay);
}
Result GetTransportError() { return transport_.GetError() ? ERR : OK; }
void SWReset()
{
return Write8(SEESAW_STATUS_BASE, SEESAW_STATUS_SWRST, 0xFF);
}
void ActivateKey(uint8_t x, uint8_t y, uint8_t edge, bool enable)
{
int xkey = NEO_TRELLIS_X(x);
int ykey
= NEO_TRELLIS_Y(y % NEO_TRELLIS_NUM_ROWS * NEO_TRELLIS_NUM_COLS);
SetKeypadEvent(
NEO_TRELLIS_KEY(NEO_TRELLIS_XY(xkey, ykey)), edge, enable);
}
void Process(bool polling = true)
{
uint8_t count = GetKeypadCount();
System::DelayUs(500);
if(count > 0)
{
if(polling)
count = count + 2;
keyEventRaw e[count];
ReadKeypad(e, count);
for(int i = 0; i < count; i++)
{
e[i].bit.NUM = NEO_TRELLIS_SEESAW_KEY(e[i].bit.NUM);
if(e[i].bit.NUM < NEO_TRELLIS_NUM_KEYS)
{
keyEvent evt = {e[i].bit.EDGE, e[i].bit.NUM};
state_[evt.bit.NUM]
= evt.bit.EDGE == HIGH || evt.bit.EDGE == RISING;
rising_[evt.bit.NUM] = evt.bit.EDGE == RISING;
falling_[evt.bit.NUM] = evt.bit.EDGE == FALLING;
// call any callbacks associated with the key
if(_callbacks[e[i].bit.NUM] != NULL)
{
_callbacks[e[i].bit.NUM](evt);
}
}
}
}
}
bool GetState(uint8_t idx)
{
if(idx < NEO_TRELLIS_NUM_KEYS)
{
return state_[idx];
}
return false;
}
bool GetRising(uint8_t idx)
{
if(idx < NEO_TRELLIS_NUM_KEYS)
{
bool tmp = rising_[idx];
rising_[idx] = false;
return tmp;
}
return false;
}
bool GetFalling(uint8_t idx)
{
if(idx < NEO_TRELLIS_NUM_KEYS)
{
bool tmp = falling_[idx];
falling_[idx] = false;
return tmp;
}
return false;
}
void ReadKeypad(keyEventRaw *buf, uint8_t count)
{
ReadLen(SEESAW_KEYPAD_BASE,
SEESAW_KEYPAD_FIFO,
(uint8_t *)buf,
count,
1000);
}
uint8_t GetKeypadCount()
{
return Read8(SEESAW_KEYPAD_BASE, SEESAW_KEYPAD_COUNT, 500);
}
void SetKeypadEvent(uint8_t key, uint8_t edge, bool enable)
{
keyState ks;
ks.bit.STATE = enable;
ks.bit.ACTIVE = (1 << edge);
uint8_t cmd[] = {SEESAW_KEYPAD_BASE, SEESAW_KEYPAD_EVENT, key, ks.reg};
transport_.Write(cmd, 4);
}
void EnableKeypadInterrupt()
{
Write8(SEESAW_KEYPAD_BASE, SEESAW_KEYPAD_INTENSET, 0x01);
}
void RegisterCallback(uint8_t x, uint8_t y, TrellisCallback (*cb)(keyEvent))
{
int xkey = NEO_TRELLIS_X(x);
int ykey
= NEO_TRELLIS_Y(y % NEO_TRELLIS_NUM_ROWS * NEO_TRELLIS_NUM_COLS);
_callbacks[NEO_TRELLIS_XY(xkey, ykey)] = cb;
}
void UnregisterCallback(uint8_t x, uint8_t y)
{
int xkey = NEO_TRELLIS_X(x);
int ykey
= NEO_TRELLIS_Y(y % NEO_TRELLIS_NUM_ROWS * NEO_TRELLIS_NUM_COLS);
_callbacks[NEO_TRELLIS_XY(xkey, ykey)] = NULL;
}
NeoPixelI2C pixels;
private:
Config config_;
Transport transport_;
bool state_[NEO_TRELLIS_NUM_KEYS];
bool rising_[NEO_TRELLIS_NUM_KEYS];
bool falling_[NEO_TRELLIS_NUM_KEYS];
TrellisCallback (*_callbacks[NEO_TRELLIS_NUM_KEYS])(
keyEvent);
}; // namespace daisy
using NeoTrellisI2C = NeoTrellis<NeoTrellisI2CTransport>;
} // namespace daisy
#endif