WIP: Pisound TX working, but not at correct clock

TODO: Fix synth routing path
This commit is contained in:
Dale Whinham
2023-02-02 22:38:30 +00:00
parent e07b48d5eb
commit c1de5532aa
8 changed files with 115 additions and 36 deletions

View File

@@ -4,7 +4,7 @@
# Valid options: pi3, pi3-64, pi4, pi4-64
BOARD?=pi3-64
HDMI_CONSOLE?=0
HDMI_CONSOLE?=1
# Serial bootloader config
SERIALPORT?=/dev/ttyUSB0

View File

@@ -29,7 +29,7 @@
#include <circle/gpiomanager.h>
#include <circle/i2cmaster.h>
#include <circle/sched/scheduler.h>
#include <circle/spimaster.h>
#include <circle/spimasterdma.h>
#include <circle/timer.h>
#include "config.h"
@@ -58,7 +58,7 @@ protected:
FATFS m_SDFileSystem;
CI2CMaster m_I2CMaster;
CSPIMaster m_SPIMaster;
CSPIMasterDMA m_SPIMaster;
CGPIOManager m_GPIOManager;
private:

View File

@@ -36,7 +36,7 @@
#include <circle/net/netsubsystem.h>
#include <circle/sched/scheduler.h>
#include <circle/sound/soundbasedevice.h>
#include <circle/spimaster.h>
#include <circle/spimasterdma.h>
#include <circle/timer.h>
#include <circle/types.h>
#include <circle/usb/usbhcidevice.h>
@@ -69,7 +69,7 @@
class CMT32Pi : CMultiCoreSupport, CPower, CMIDIParser, CAppleMIDIHandler, CUDPMIDIHandler
{
public:
CMT32Pi(CI2CMaster* pI2CMaster, CSPIMaster* pSPIMaster, CInterruptSystem* pInterrupt, CGPIOManager* pGPIOManager, CSerialDevice* pSerialDevice, CUSBHCIDevice* pUSBHCI);
CMT32Pi(CI2CMaster* pI2CMaster, CSPIMasterDMA* pSPIMaster, CInterruptSystem* pInterrupt, CGPIOManager* pGPIOManager, CSerialDevice* pSerialDevice, CUSBHCIDevice* pUSBHCI);
virtual ~CMT32Pi() override;
bool Initialize(bool bSerialMIDIAvailable = true);
@@ -148,7 +148,7 @@ private:
CActLED* m_pActLED;
CI2CMaster* m_pI2CMaster;
CSPIMaster* m_pSPIMaster;
CSPIMasterDMA* m_pSPIMaster;
CInterruptSystem* m_pInterrupt;
CGPIOManager* m_pGPIOManager;
CSerialDevice* m_pSerial;

View File

@@ -25,23 +25,28 @@
#include <circle/gpiomanager.h>
#include <circle/gpiopin.h>
#include <circle/spimaster.h>
#include <circle/sched/task.h>
#include <circle/spimasterdma.h>
#include <circle/spinlock.h>
#include <circle/types.h>
#include "ringbuffer.h"
class CPisound
class CPisound : protected CTask
{
public:
using TMIDIReceiveHandler = void (*)(const u8* pData, size_t nSize);
CPisound(CSPIMaster* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate);
~CPisound();
CPisound(CSPIMasterDMA* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate);
virtual ~CPisound() override;
bool Initialize();
void RegisterMIDIReceiveHandler(TMIDIReceiveHandler pHandler) { m_pReceiveHandler = pHandler; }
size_t SendMIDI(const u8* pData, size_t nSize);
virtual void Run() override;
void DoTransfer();
private:
u16 Transfer16(u16 nValue) const;
size_t ReadBytes(u8* pOutBuffer, size_t nSize) const;
@@ -53,7 +58,8 @@ private:
static constexpr size_t MaxIDStringLength = 25;
static constexpr size_t MaxVersionStringLength = 6;
CSPIMaster* m_pSPIMaster;
CSpinLock m_Lock;
CSPIMasterDMA* m_pSPIMaster;
unsigned m_nSamplerate;
CGPIOPin m_SPIReset;

View File

@@ -42,6 +42,7 @@ CKernel::CKernel(void)
m_SDFileSystem{},
m_I2CMaster(1, true),
m_SPIMaster(&mInterrupt),
m_GPIOManager(&mInterrupt),
m_MT32Pi(&m_I2CMaster, &m_SPIMaster, &mInterrupt, &m_GPIOManager, &m_Serial, &m_USBHCI)

View File

@@ -41,6 +41,8 @@ void CMIDIParser::ParseMIDIBytes(const u8* pData, size_t nSize, bool bIgnoreNote
{
u8 nByte = pData[i];
//CLogger::Get()->Write(MIDIParserName, LogNotice, "0x%02X", nByte);
// System Real-Time message - single byte, handle immediately
// Can appear anywhere in the stream, even in between status/data bytes
if (nByte >= 0xF8)

View File

@@ -59,7 +59,7 @@ enum class TCustomSysExCommand : u8
CMT32Pi* CMT32Pi::s_pThis = nullptr;
CMT32Pi::CMT32Pi(CI2CMaster* pI2CMaster, CSPIMaster* pSPIMaster, CInterruptSystem* pInterrupt, CGPIOManager* pGPIOManager, CSerialDevice* pSerialDevice, CUSBHCIDevice* pUSBHCI)
CMT32Pi::CMT32Pi(CI2CMaster* pI2CMaster, CSPIMasterDMA* pSPIMaster, CInterruptSystem* pInterrupt, CGPIOManager* pGPIOManager, CSerialDevice* pSerialDevice, CUSBHCIDevice* pUSBHCI)
: CMultiCoreSupport(CMemorySystem::Get()),
CMIDIParser(),
@@ -712,6 +712,29 @@ void CMT32Pi::OnAppleMIDIDisconnect(const CIPAddress* pIPAddress, const char* pN
LCDLog(TLCDLogType::Notice, "%s disconnected!", pName);
}
void PrintRouting(const char* pDevice, u8 nRouting)
{
char Buffer[256];
sprintf(Buffer, " - %-10s ->", pDevice);
if (nRouting == TMIDIRoutingDest::None)
{
strcat(Buffer, " none");
LOGNOTE(Buffer);
return;
}
if (nRouting & TMIDIRoutingDest::Synth) strncat(Buffer, " synth", sizeof(Buffer));
if (nRouting & TMIDIRoutingDest::GPIO) strncat(Buffer, " gpio", sizeof(Buffer));
if (nRouting & TMIDIRoutingDest::Pisound) strncat(Buffer, " pisound", sizeof(Buffer));
if (nRouting & TMIDIRoutingDest::USBMIDI) strncat(Buffer, " usb_midi", sizeof(Buffer));
if (nRouting & TMIDIRoutingDest::USBSerial) strncat(Buffer, " usb_serial", sizeof(Buffer));
if (nRouting & TMIDIRoutingDest::RTP) strncat(Buffer, " rtp", sizeof(Buffer));
if (nRouting & TMIDIRoutingDest::UDP) strncat(Buffer, " udp", sizeof(Buffer));
LOGNOTE(Buffer);
}
bool CMT32Pi::ParseCustomSysEx(const u8* pData, size_t nSize)
{
if (nSize < 4)
@@ -745,6 +768,15 @@ bool CMT32Pi::ParseCustomSysEx(const u8* pData, size_t nSize)
case 4: m_pConfig->MIDIRouteRTP = nRouting; break;
case 5: m_pConfig->MIDIRouteUDP = nRouting; break;
}
LOGNOTE("MIDI routing setup:");
PrintRouting("gpio", m_pConfig->MIDIRouteGPIO);
PrintRouting("usb_midi", m_pConfig->MIDIRouteUSBMIDI);
PrintRouting("usb_serial", m_pConfig->MIDIRouteUSBSerial);
PrintRouting("pisound", m_pConfig->MIDIRoutePisound);
PrintRouting("rtp", m_pConfig->MIDIRouteRTP);
PrintRouting("udp", m_pConfig->MIDIRouteUDP);
return true;
}
@@ -1032,7 +1064,7 @@ void CMT32Pi::ProcessMIDIRouting(const TMIDIRouting& Routing, const u8* pData, s
}
// Send to GPIO UART
if (Routing & GPIO && m_bSerialMIDIEnabled)
if ((Routing & GPIO) && m_bSerialMIDIEnabled)
{
const int nSendResult = m_pSerial->Write(pData, nSize);
if (nSendResult < 0 || static_cast<size_t>(nSendResult) != nSize)
@@ -1043,44 +1075,44 @@ void CMT32Pi::ProcessMIDIRouting(const TMIDIRouting& Routing, const u8* pData, s
}
// Send to Pisound
if (Routing & Pisound && m_pPisound)
if ((Routing & Pisound) && m_pPisound)
{
if (m_pPisound->SendMIDI(pData, nSize) != nSize)
{
m_pLogger->Write(MT32PiName, LogError, "Pisound MIDI TX error");
LOGERR("Pisound MIDI TX error");
LCDLog(TLCDLogType::Error, "Pisound MIDI TX error!");
}
}
// Send to USB MIDI
if (Routing & USBMIDI && m_pUSBMIDIDevice)
if ((Routing & USBMIDI) && m_pUSBMIDIDevice)
{
if (m_pUSBMIDIDevice->SendPlainMIDI(0, pData, nSize) == false)
{
m_pLogger->Write(MT32PiName, LogError, "USB MIDI TX error");
LOGERR("USB MIDI TX error");
LCDLog(TLCDLogType::Error, "USB MIDI TX error!");
}
}
// Send to USB serial
if (Routing & USBSerial && m_pUSBSerialDevice)
if ((Routing & USBSerial) && m_pUSBSerialDevice)
{
const int nSendResult = m_pUSBSerialDevice->Write(pData, nSize);
if (nSendResult < 0 || static_cast<size_t>(nSendResult) != nSize)
{
m_pLogger->Write(MT32PiName, LogError, "USB serial TX error %d (wanted to send %d bytes)", nSendResult, nSize);
LOGERR("USB serial TX error %d (wanted to send %d bytes)", nSendResult, nSize);
LCDLog(TLCDLogType::Error, "USB serial TX error!");
}
}
// Send to AppleMIDI
if (Routing & RTP && m_pAppleMIDIParticipant)
if ((Routing & RTP) && m_pAppleMIDIParticipant)
{
// TODO: AppleMIDIParticipant has no transmit functionality
}
// Send to UDP MIDI
if (Routing & UDP && m_pUDPMIDIReceiver)
if ((Routing & UDP) && m_pUDPMIDIReceiver)
{
// TODO: UDPMIDIReceiver has no transmit functionality
}

View File

@@ -23,6 +23,7 @@
#include <circle/logger.h>
#include <circle/timer.h>
#include <circle/util.h>
#include <circle/sched/scheduler.h>
#include <cstdio>
@@ -31,8 +32,8 @@
LOGMODULE("pisound");
constexpr u8 SPIChipSelect = 0;
constexpr u8 SPIDelayMicroseconds = 10;
constexpr u32 SPIClockSpeed = 150000;
// FIXME: Linux driver works at 150000Hz, but here we get data corruption hence lower clock speed - investigate
constexpr u32 SPIClockSpeed = 15000;
constexpr u8 SPITransferSize = 4;
constexpr u8 GPIOButton = 17;
@@ -48,8 +49,10 @@ constexpr u8 GPIOSPIDataAvailable = 25;
// Based on: https://github.com/raspberrypi/linux/blob/rpi-5.4.y/sound/soc/bcm/pisound.c
// https://github.com/raspberrypi/linux/blob/rpi-5.4.y/arch/arm/boot/dts/overlays/pisound-overlay.dts
CPisound::CPisound(CSPIMaster* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate)
: m_pSPIMaster(pSPIMaster),
CPisound::CPisound(CSPIMasterDMA* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate)
: CTask(TASK_STACK_SIZE, true),
m_Lock(IRQ_LEVEL),
m_pSPIMaster(pSPIMaster),
m_nSamplerate(nSamplerate),
m_SPIReset(GPIOSPIReset, TGPIOMode::GPIOModeOutput),
@@ -80,6 +83,8 @@ CPisound::~CPisound()
m_OversamplingRatio0.SetMode(TGPIOMode::GPIOModeInputPullDown);
m_OversamplingRatio1.SetMode(TGPIOMode::GPIOModeInputPullDown);
m_OversamplingRatio2.SetMode(TGPIOMode::GPIOModeInputPullDown);
Terminate();
}
bool CPisound::Initialize()
@@ -106,7 +111,6 @@ bool CPisound::Initialize()
}
// Setup SPI
m_pSPIMaster->SetCSHoldTime(SPIDelayMicroseconds);
m_pSPIMaster->SetClock(SPIClockSpeed);
// Reset the SPI device
@@ -131,15 +135,18 @@ bool CPisound::Initialize()
LOGNOTE("Firmware version: %s", m_FirmwareVersion);
LOGNOTE("Hardware version: %s", m_HardwareVersion);
Start();
return true;
}
size_t CPisound::SendMIDI(const u8* pData, size_t nSize)
{
// TODO
m_MIDITxBuffer.Enqueue(pData, nSize);
SPITask();
return nSize;
// Wake up task
if (IsSuspended())
Resume();
return m_MIDITxBuffer.Enqueue(pData, nSize);
}
u16 CPisound::Transfer16(u16 nTxValue) const
@@ -149,7 +156,7 @@ u16 CPisound::Transfer16(u16 nTxValue) const
SPITxBuffer[0] = nTxValue >> 8;
SPITxBuffer[1] = nTxValue & 0xFF;
if (m_pSPIMaster->WriteRead(SPIChipSelect, SPITxBuffer, SPIRxBuffer, sizeof(SPIRxBuffer)) < 0)
if (m_pSPIMaster->WriteReadSync(SPIChipSelect, SPITxBuffer, SPIRxBuffer, sizeof(SPIRxBuffer)) < 0)
return 0;
return (SPIRxBuffer[0] << 8) | SPIRxBuffer[1];
@@ -246,11 +253,16 @@ void CPisound::SetOSRPins(unsigned bRatio1, unsigned bRatio2, unsigned bRatio3)
void CPisound::DataAvailableInterruptHandler(void* pUserData)
{
CPisound* pThis = static_cast<CPisound*>(pUserData);
pThis->SPITask();
if (pThis->IsSuspended())
pThis->Resume();
}
void CPisound::SPITask()
void CPisound::DoTransfer()
{
bool bHadData = false;
m_Lock.Acquire();
do
{
size_t nMIDIRxBytes = 0;
@@ -260,6 +272,8 @@ void CPisound::SPITask()
u8 SPIRxBuffer[SPITransferSize];
u8 SPITxBuffer[SPITransferSize];
bHadData = false;
memset(SPIRxBuffer, 0, sizeof(SPIRxBuffer));
memset(SPITxBuffer, 0, sizeof(SPITxBuffer));
@@ -272,15 +286,39 @@ void CPisound::SPITask()
}
// Extract incoming MIDI bytes from SPI packet
m_pSPIMaster->WriteRead(SPIChipSelect, SPITxBuffer, SPIRxBuffer, sizeof(SPIRxBuffer));
for (size_t i = 0; i < sizeof(SPIRxBuffer); i += 2)
if (m_pSPIMaster->WriteReadSync(SPIChipSelect, SPITxBuffer, SPIRxBuffer, SPITransferSize) != SPITransferSize)
{
LOGERR("SPI transfer failed");
continue;
}
for (size_t i = 0; i < SPITransferSize; i += 2)
{
if (SPIRxBuffer[i])
{
MIDIRxBuffer[nMIDIRxBytes++] = SPIRxBuffer[i + 1];
bHadData = true;
}
}
// Pass MIDI bytes on to handler
if (nMIDIRxBytes && m_pReceiveHandler)
m_pReceiveHandler(MIDIRxBuffer, nMIDIRxBytes);
} while (m_DataAvailable.Read() == HIGH || !m_MIDITxBuffer.IsEmpty());
} while (bHadData || !m_MIDITxBuffer.IsEmpty() || m_DataAvailable.Read() == HIGH);
m_Lock.Release();
}
void CPisound::Run()
{
CScheduler* const pScheduler = CScheduler::Get();
while (true)
{
DoTransfer();
Suspend();
// Allow other tasks to run
pScheduler->Yield();
}
}