Skip to content

Commit

Permalink
New i2c implementation, support up to 4 faderbanks with teletype 5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Dewb committed Sep 22, 2023
1 parent de9dd48 commit 2803d9f
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 103 deletions.
2 changes: 1 addition & 1 deletion firmware/mock_hardware/common/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ int i2c_leader_rx(uint8_t addr, uint8_t* data, uint8_t l)
{
if (l == 2)
{
uint16_t value = hardware_iiGetFollowerData(addr << 8 | last_port);
uint16_t value = hardware_iiGetFollowerData(addr, last_port);
data[0] = value >> 8;
data[1] = value & 0xFF;
}
Expand Down
22 changes: 14 additions & 8 deletions firmware/mock_hardware/mock_hardware_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,22 +238,28 @@ void hardware_setScreenBuffer(uint8_t* buf)
screenBuffer = buf;
}

// TODO: make this generic for all followers somehow
uint16_t faderbank[16];
// TODO: make this generic to support more follower types
uint16_t faderbank[64];

void hardware_iiUpdateFollowerData(uint16_t key, uint16_t data)
void hardware_iiUpdateFollowerData(uint8_t device, uint8_t param, uint16_t data)
{
if (key >> 8 == 0x34)
// For now, only support 16 params each on devices 0x34-0x37
uint8_t offset = device - 0x34;
if (offset < 4 && param < 16)
{
faderbank[key & 0xFF] = data;
uint8_t index = offset * 16 + param;
faderbank[index] = data;
}
}

uint16_t hardware_iiGetFollowerData(uint16_t key)
uint16_t hardware_iiGetFollowerData(uint8_t device, uint8_t param)
{
if (key >> 8 == 0x34)
// For now, only support 16 params each on devices 0x34-0x37
uint8_t offset = device - 0x34;
if (offset < 4 && param < 16)
{
return faderbank[key & 0xFF];
uint8_t index = offset * 16 + param;
return faderbank[index];
}
return 0;
}
Expand Down
4 changes: 2 additions & 2 deletions firmware/mock_hardware/mock_hardware_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ MOCK_API(void, hidConnect, ());
MOCK_API(void, hidDisconnect, ());
MOCK_API(void, hidMessage, (uint8_t key, uint8_t mod, bool held, bool release));

MOCK_API(void, iiUpdateFollowerData, (uint16_t key, uint16_t data));
MOCK_API(uint16_t, iiGetFollowerData, (uint16_t key));
MOCK_API(void, iiUpdateFollowerData, (uint8_t device, uint8_t param, uint16_t data));
MOCK_API(uint16_t, iiGetFollowerData, (uint8_t device, uint8_t param));
MOCK_API(bool, iiPushMessage, (uint8_t addr, uint8_t* data, uint8_t length));
MOCK_API(bool, iiPopMessage, (uint8_t* addr, uint8_t* data, uint8_t* length));

Expand Down
4 changes: 2 additions & 2 deletions src/common/core/FirmwareManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,11 +459,11 @@ void FirmwareManager::hidMessage(uint8_t key, uint8_t mod, bool held, bool relea
}
}

void FirmwareManager::iiUpdateFollowerData(uint16_t key, uint16_t value)
void FirmwareManager::iiUpdateFollowerData(uint8_t device, uint8_t param, uint16_t value)
{
if (impl)
{
impl->fw_fn_hardware_iiUpdateFollowerData(key, value);
impl->fw_fn_hardware_iiUpdateFollowerData(device, param, value);
}
}

Expand Down
83 changes: 45 additions & 38 deletions src/common/core/iiBus.cpp
Original file line number Diff line number Diff line change
@@ -1,55 +1,62 @@
#include "iiBus.h"
#include "IIBus.h"
#include "LibAVR32Module.hpp"

iiFollowerData_t iiBus::FollowerData;
#define FADERBANK_II_MAX_VALUE 16383

void iiBus::Initialize()
{
for (int device = 0x34; device <= 0x37; device++)
{
for (int fader = 0; fader < 16; fader++)
{
iiBus::FollowerData.emplace(std::make_pair((device << 8) | fader, 0));
}
}
}
extern rack::plugin::Model* modelFaderbank;

iiDevice::iiDevice(rack::Module* module)
: _module(module)
IIBus::IIBus(LibAVR32Module* leader)
: leader(leader)
{
if (_module)
{
_module->rightExpander.producerMessage = new iiCommand();
_module->leftExpander.producerMessage = new iiCommand();
}
}

void iiDevice::setAddress(uint8_t address)
bool IIBus::isFollower(rack::Module* module)
{
_address = address;
// Only faderbanks participate in II right now
return module != nullptr && module->model == modelFaderbank;
}

void iiDevice::updateFollowerData(uint8_t id, uint16_t data)
void IIBus::step()
{
const auto record = iiBus::FollowerData.find((_address << 8) | id);
if (record != iiBus::FollowerData.end())
if (leader == nullptr)
{
record->second.store(data, std::memory_order_relaxed);
return;
}
}

void iiDevice::transmit(const iiCommand& msg)
{
if (_module)
// let's tentatively define a bus as an unbroken chain of II-supporting
// modules directly attached to either the left or right of the leader.

// scan the "bus" for eligible modules, starting to the leader's left
std::vector<rack::Module*> followers;
auto module = leader->getLeftExpander().module;
while (isFollower(module))
{
if (_module->rightExpander.producerMessage)
{
*(reinterpret_cast<iiCommand*>(_module->rightExpander.producerMessage)) = msg;
_module->rightExpander.messageFlipRequested = true;
}
if (_module->leftExpander.producerMessage)
followers.push_back(module);
module = module->getLeftExpander().module;
}

// flip the order, so we can prioritize left-to-right
if (followers.size() > 1)
{
std::reverse(followers.begin(), followers.end());
}

// scan to the leader's right
module = leader->getRightExpander().module;
while (isFollower(module))
{
followers.push_back(module);
module = module->getRightExpander().module;
}

// gather params from all followers
for (size_t follower = 0; follower < std::min(static_cast<size_t>(4), followers.size()); follower++)
{
for (uint8_t fader = 0; fader < std::min(16, followers[follower]->getNumParams()); fader++)
{
*(reinterpret_cast<iiCommand*>(_module->leftExpander.producerMessage)) = msg;
_module->leftExpander.messageFlipRequested = true;
float voltage = followers[follower]->params[fader].getValue();
uint16_t value = static_cast<uint16_t>(voltage / 10.0 * FADERBANK_II_MAX_VALUE);
leader->firmware.iiUpdateFollowerData(follower + 0x34, fader, value);
}
}
}
}
50 changes: 17 additions & 33 deletions src/common/core/iiBus.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,24 @@
#include <unordered_map>
#include <atomic>

#define MAX_II_DATA_BYTES 8

// i2c devices cannot wait on each other. Even if the Rack engine is using multiple threads,
// a leader and follower may be executing on the same thread.
// Since the leader will block waiting on a request for data from a follower, we need to
// have the data ready in advance. Therefore all followers must continuously update a data
// structure accessible to the leader with all data the leader could possible request.

// cheapo concurrent data structure for time-travelling follower data
typedef std::unordered_map<uint16_t, std::atomic<uint16_t>> iiFollowerData_t;

struct iiBus {
static void Initialize();
static iiFollowerData_t FollowerData;
};

// TODO: send commands from follower over Rack expander bus
struct iiCommand
struct LibAVR32Module;

// Notes on the work-in-progress implementation of II (monome ecosystem i2c):
//
// An i2c module cannot wait for a response to messages. Even if the Rack engine is
// using multiple threads, a leader and follower may be executing on the same thread.
//
// Since the leader will block waiting on a request for data from a follower, we need to
// have the data ready in advance. Therefore the bus must continuously maintain a data
// structure accessible to the leader with all data the leader could possibly request.

struct IIBus
{
// 7-bit I2C address
uint8_t address;
uint8_t data[MAX_II_DATA_BYTES];
uint8_t length;
};

struct iiDevice
{
iiDevice(rack::Module* module);
IIBus(LibAVR32Module* leader);
bool isFollower(rack::Module* module);

void setAddress(uint8_t address);
void updateFollowerData(uint8_t id, uint16_t data);
void transmit(const iiCommand& msg);
void step();

protected:
rack::Module* _module;
uint8_t _address;
};
LibAVR32Module* leader;
};
5 changes: 0 additions & 5 deletions src/faderbank/FaderbankModule.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#include "FaderbankModule.hpp"

#define FADERBANK_II_MAX_VALUE 16383

struct FBFaderParam : rack::engine::ParamQuantity
{
std::string getDisplayValueString() override
Expand Down Expand Up @@ -37,9 +35,6 @@ void FaderbankModule::process(const ProcessArgs& args)
for (unsigned i = 0; i < NUM_FADERS; i++)
{
outputs[i].setVoltage(params[i].getValue());

// float iiValue = params[i].getValue() / 10.0 * FADERBANK_II_MAX_VALUE;
// _iiDevice.updateFollowerData(i, static_cast<uint16_t>(iiValue));
}
}

Expand Down
1 change: 0 additions & 1 deletion src/faderbank/FaderbankModule.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once
#include "rack.hpp"
#include "iiBus.h"

#include <map>

Expand Down
3 changes: 2 additions & 1 deletion src/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using namespace rack;

Plugin* pluginInstance;
Model* modelFaderbank;

void init(Plugin* p)
{
Expand All @@ -33,7 +34,7 @@ void init(Plugin* p)
Model* modelGrid64 = createModel<VirtualGridModuleTemplate<8, 8>, VirtualGridWidgetTemplate<8, 8>>("grid64");
Model* modelGrid256 = createModel<VirtualGridModuleTemplate<16, 16>, VirtualGridWidgetTemplate<16, 16>>("grid256");

Model* modelFaderbank = createModel<FaderbankModule, FaderbankWidget>("faderbank");
modelFaderbank = createModel<FaderbankModule, FaderbankWidget>("faderbank");

p->addModel(modelWhiteWhale);
p->addModel(modelMeadowphysics);
Expand Down
14 changes: 4 additions & 10 deletions src/teletype/TeletypeModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct TTParamQuantity : rack::engine::ParamQuantity

TeletypeModule::TeletypeModule()
: LibAVR32Module("teletype", "teletype4")
, _iiDevice(this)
, iiBus(this)
, screenBuffer{}
{
// initialize screen with "engine stopped" message
Expand Down Expand Up @@ -89,15 +89,9 @@ void TeletypeModule::processInputs(const ProcessArgs& args)
firmware.setGPIO(A00 + i, inputTriggers[i].isHigh());
}

// for (const auto& [key, value] : iiBus::FollowerData)
// {
// firmware.iiUpdateFollowerData(key, value.load(std::memory_order_relaxed));
// }

// iiCommand msg;
// while (firmware.iiPopMessage(&msg.address, msg.data, &msg.length)) {
// _iiDevice.transmit(msg);
// }
// process II follower-to-leader input
// no support for leader-to-follower commands right now
iiBus.step();
}

void TeletypeModule::processOutputs(const ProcessArgs& args)
Expand Down
4 changes: 2 additions & 2 deletions src/teletype/TeletypeModule.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "LibAVR32Module.hpp"
#include "iiBus.h"
#include "IIBus.h"

#include "rack.hpp"

Expand Down Expand Up @@ -64,7 +64,7 @@ struct TeletypeModule : LibAVR32Module
virtual uint8_t* getScreenBuffer() override { return screenBuffer; }

protected:
iiDevice _iiDevice;
IIBus iiBus;

uint8_t screenBuffer[128 * 64];
};

0 comments on commit 2803d9f

Please sign in to comment.