WIP: Pisound TX working, but not at correct clock

TODO: Fix synth routing path
This commit is contained in:
Dale Whinham committed 2023-04-24 22:50:26 +01:00
1 parent e07b48d5eb
commit c1de5532aa
8 files changed
+115 -36

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@
# Valid options: pi3, pi3-64, pi4, pi4-64 # Valid options: pi3, pi3-64, pi4, pi4-64
BOARD?=pi3-64 BOARD?=pi3-64
HDMI_CONSOLE?=0 HDMI_CONSOLE?=1
# Serial bootloader config # Serial bootloader config
SERIALPORT?=/dev/ttyUSB0 SERIALPORT?=/dev/ttyUSB0
+2 -2
View File
@@ -29,7 +29,7 @@
#include <circle/gpiomanager.h> #include <circle/gpiomanager.h>
#include <circle/i2cmaster.h> #include <circle/i2cmaster.h>
#include <circle/sched/scheduler.h> #include <circle/sched/scheduler.h>
#include <circle/spimaster.h> #include <circle/spimasterdma.h>
#include <circle/timer.h> #include <circle/timer.h>
#include "config.h" #include "config.h"
@@ -58,7 +58,7 @@ protected:
FATFS m_SDFileSystem; FATFS m_SDFileSystem;
CI2CMaster m_I2CMaster; CI2CMaster m_I2CMaster;
CSPIMaster m_SPIMaster; CSPIMasterDMA m_SPIMaster;
CGPIOManager m_GPIOManager; CGPIOManager m_GPIOManager;
private: private:
+3 -3
View File
@@ -36,7 +36,7 @@
#include <circle/net/netsubsystem.h> #include <circle/net/netsubsystem.h>
#include <circle/sched/scheduler.h> #include <circle/sched/scheduler.h>
#include <circle/sound/soundbasedevice.h> #include <circle/sound/soundbasedevice.h>
#include <circle/spimaster.h> #include <circle/spimasterdma.h>
#include <circle/timer.h> #include <circle/timer.h>
#include <circle/types.h> #include <circle/types.h>
#include <circle/usb/usbhcidevice.h> #include <circle/usb/usbhcidevice.h>
@@ -69,7 +69,7 @@
class CMT32Pi : CMultiCoreSupport, CPower, CMIDIParser, CAppleMIDIHandler, CUDPMIDIHandler class CMT32Pi : CMultiCoreSupport, CPower, CMIDIParser, CAppleMIDIHandler, CUDPMIDIHandler
{ {
public: 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; virtual ~CMT32Pi() override;
bool Initialize(bool bSerialMIDIAvailable = true); bool Initialize(bool bSerialMIDIAvailable = true);
@@ -148,7 +148,7 @@ private:
CActLED* m_pActLED; CActLED* m_pActLED;
CI2CMaster* m_pI2CMaster; CI2CMaster* m_pI2CMaster;
CSPIMaster* m_pSPIMaster; CSPIMasterDMA* m_pSPIMaster;
CInterruptSystem* m_pInterrupt; CInterruptSystem* m_pInterrupt;
CGPIOManager* m_pGPIOManager; CGPIOManager* m_pGPIOManager;
CSerialDevice* m_pSerial; CSerialDevice* m_pSerial;
+11 -5
View File
@@ -25,23 +25,28 @@
#include <circle/gpiomanager.h> #include <circle/gpiomanager.h>
#include <circle/gpiopin.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 <circle/types.h>
#include "ringbuffer.h" #include "ringbuffer.h"
class CPisound class CPisound : protected CTask
{ {
public: public:
using TMIDIReceiveHandler = void (*)(const u8* pData, size_t nSize); using TMIDIReceiveHandler = void (*)(const u8* pData, size_t nSize);
CPisound(CSPIMaster* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate); CPisound(CSPIMasterDMA* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate);
~CPisound(); virtual ~CPisound() override;
bool Initialize(); bool Initialize();
void RegisterMIDIReceiveHandler(TMIDIReceiveHandler pHandler) { m_pReceiveHandler = pHandler; } void RegisterMIDIReceiveHandler(TMIDIReceiveHandler pHandler) { m_pReceiveHandler = pHandler; }
size_t SendMIDI(const u8* pData, size_t nSize); size_t SendMIDI(const u8* pData, size_t nSize);
virtual void Run() override;
void DoTransfer();
private: private:
u16 Transfer16(u16 nValue) const; u16 Transfer16(u16 nValue) const;
size_t ReadBytes(u8* pOutBuffer, size_t nSize) 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 MaxIDStringLength = 25;
static constexpr size_t MaxVersionStringLength = 6; static constexpr size_t MaxVersionStringLength = 6;
CSPIMaster* m_pSPIMaster; CSpinLock m_Lock;
CSPIMasterDMA* m_pSPIMaster;
unsigned m_nSamplerate; unsigned m_nSamplerate;
CGPIOPin m_SPIReset; CGPIOPin m_SPIReset;
+1
View File
@@ -42,6 +42,7 @@ CKernel::CKernel(void)
m_SDFileSystem{}, m_SDFileSystem{},
m_I2CMaster(1, true), m_I2CMaster(1, true),
m_SPIMaster(&mInterrupt),
m_GPIOManager(&mInterrupt), m_GPIOManager(&mInterrupt),
m_MT32Pi(&m_I2CMaster, &m_SPIMaster, &mInterrupt, &m_GPIOManager, &m_Serial, &m_USBHCI) m_MT32Pi(&m_I2CMaster, &m_SPIMaster, &mInterrupt, &m_GPIOManager, &m_Serial, &m_USBHCI)
+2
View File
@@ -41,6 +41,8 @@ void CMIDIParser::ParseMIDIBytes(const u8* pData, size_t nSize, bool bIgnoreNote
{ {
u8 nByte = pData[i]; u8 nByte = pData[i];
//CLogger::Get()->Write(MIDIParserName, LogNotice, "0x%02X", nByte);
// System Real-Time message - single byte, handle immediately // System Real-Time message - single byte, handle immediately
// Can appear anywhere in the stream, even in between status/data bytes // Can appear anywhere in the stream, even in between status/data bytes
if (nByte >= 0xF8) if (nByte >= 0xF8)
+42 -10
View File
@@ -59,7 +59,7 @@ enum class TCustomSysExCommand : u8
CMT32Pi* CMT32Pi::s_pThis = nullptr; 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()), : CMultiCoreSupport(CMemorySystem::Get()),
CMIDIParser(), CMIDIParser(),
@@ -712,6 +712,29 @@ void CMT32Pi::OnAppleMIDIDisconnect(const CIPAddress* pIPAddress, const char* pN
LCDLog(TLCDLogType::Notice, "%s disconnected!", pName); 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) bool CMT32Pi::ParseCustomSysEx(const u8* pData, size_t nSize)
{ {
if (nSize < 4) if (nSize < 4)
@@ -745,6 +768,15 @@ bool CMT32Pi::ParseCustomSysEx(const u8* pData, size_t nSize)
case 4: m_pConfig->MIDIRouteRTP = nRouting; break; case 4: m_pConfig->MIDIRouteRTP = nRouting; break;
case 5: m_pConfig->MIDIRouteUDP = 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; return true;
} }
@@ -1032,7 +1064,7 @@ void CMT32Pi::ProcessMIDIRouting(const TMIDIRouting& Routing, const u8* pData, s
} }
// Send to GPIO UART // Send to GPIO UART
if (Routing & GPIO && m_bSerialMIDIEnabled) if ((Routing & GPIO) && m_bSerialMIDIEnabled)
{ {
const int nSendResult = m_pSerial->Write(pData, nSize); const int nSendResult = m_pSerial->Write(pData, nSize);
if (nSendResult < 0 || static_cast<size_t>(nSendResult) != 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 // Send to Pisound
if (Routing & Pisound && m_pPisound) if ((Routing & Pisound) && m_pPisound)
{ {
if (m_pPisound->SendMIDI(pData, nSize) != nSize) 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!"); LCDLog(TLCDLogType::Error, "Pisound MIDI TX error!");
} }
} }
// Send to USB MIDI // Send to USB MIDI
if (Routing & USBMIDI && m_pUSBMIDIDevice) if ((Routing & USBMIDI) && m_pUSBMIDIDevice)
{ {
if (m_pUSBMIDIDevice->SendPlainMIDI(0, pData, nSize) == false) 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!"); LCDLog(TLCDLogType::Error, "USB MIDI TX error!");
} }
} }
// Send to USB serial // Send to USB serial
if (Routing & USBSerial && m_pUSBSerialDevice) if ((Routing & USBSerial) && m_pUSBSerialDevice)
{ {
const int nSendResult = m_pUSBSerialDevice->Write(pData, nSize); const int nSendResult = m_pUSBSerialDevice->Write(pData, nSize);
if (nSendResult < 0 || static_cast<size_t>(nSendResult) != 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!"); LCDLog(TLCDLogType::Error, "USB serial TX error!");
} }
} }
// Send to AppleMIDI // Send to AppleMIDI
if (Routing & RTP && m_pAppleMIDIParticipant) if ((Routing & RTP) && m_pAppleMIDIParticipant)
{ {
// TODO: AppleMIDIParticipant has no transmit functionality // TODO: AppleMIDIParticipant has no transmit functionality
} }
// Send to UDP MIDI // Send to UDP MIDI
if (Routing & UDP && m_pUDPMIDIReceiver) if ((Routing & UDP) && m_pUDPMIDIReceiver)
{ {
// TODO: UDPMIDIReceiver has no transmit functionality // TODO: UDPMIDIReceiver has no transmit functionality
} }
+53 -15
View File
@@ -23,6 +23,7 @@
#include <circle/logger.h> #include <circle/logger.h>
#include <circle/timer.h> #include <circle/timer.h>
#include <circle/util.h> #include <circle/util.h>
#include <circle/sched/scheduler.h>
#include <cstdio> #include <cstdio>
@@ -31,8 +32,8 @@
LOGMODULE("pisound"); LOGMODULE("pisound");
constexpr u8 SPIChipSelect = 0; constexpr u8 SPIChipSelect = 0;
constexpr u8 SPIDelayMicroseconds = 10; // FIXME: Linux driver works at 150000Hz, but here we get data corruption hence lower clock speed - investigate
constexpr u32 SPIClockSpeed = 150000; constexpr u32 SPIClockSpeed = 15000;
constexpr u8 SPITransferSize = 4; constexpr u8 SPITransferSize = 4;
constexpr u8 GPIOButton = 17; 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 // 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 // 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) CPisound::CPisound(CSPIMasterDMA* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate)
: m_pSPIMaster(pSPIMaster), : CTask(TASK_STACK_SIZE, true),
m_Lock(IRQ_LEVEL),
m_pSPIMaster(pSPIMaster),
m_nSamplerate(nSamplerate), m_nSamplerate(nSamplerate),
m_SPIReset(GPIOSPIReset, TGPIOMode::GPIOModeOutput), m_SPIReset(GPIOSPIReset, TGPIOMode::GPIOModeOutput),
@@ -80,6 +83,8 @@ CPisound::~CPisound()
m_OversamplingRatio0.SetMode(TGPIOMode::GPIOModeInputPullDown); m_OversamplingRatio0.SetMode(TGPIOMode::GPIOModeInputPullDown);
m_OversamplingRatio1.SetMode(TGPIOMode::GPIOModeInputPullDown); m_OversamplingRatio1.SetMode(TGPIOMode::GPIOModeInputPullDown);
m_OversamplingRatio2.SetMode(TGPIOMode::GPIOModeInputPullDown); m_OversamplingRatio2.SetMode(TGPIOMode::GPIOModeInputPullDown);
Terminate();
} }
bool CPisound::Initialize() bool CPisound::Initialize()
@@ -106,7 +111,6 @@ bool CPisound::Initialize()
} }
// Setup SPI // Setup SPI
m_pSPIMaster->SetCSHoldTime(SPIDelayMicroseconds);
m_pSPIMaster->SetClock(SPIClockSpeed); m_pSPIMaster->SetClock(SPIClockSpeed);
// Reset the SPI device // Reset the SPI device
@@ -131,15 +135,18 @@ bool CPisound::Initialize()
LOGNOTE("Firmware version: %s", m_FirmwareVersion); LOGNOTE("Firmware version: %s", m_FirmwareVersion);
LOGNOTE("Hardware version: %s", m_HardwareVersion); LOGNOTE("Hardware version: %s", m_HardwareVersion);
Start();
return true; return true;
} }
size_t CPisound::SendMIDI(const u8* pData, size_t nSize) size_t CPisound::SendMIDI(const u8* pData, size_t nSize)
{ {
// TODO // Wake up task
m_MIDITxBuffer.Enqueue(pData, nSize); if (IsSuspended())
SPITask(); Resume();
return nSize;
return m_MIDITxBuffer.Enqueue(pData, nSize);
} }
u16 CPisound::Transfer16(u16 nTxValue) const u16 CPisound::Transfer16(u16 nTxValue) const
@@ -149,7 +156,7 @@ u16 CPisound::Transfer16(u16 nTxValue) const
SPITxBuffer[0] = nTxValue >> 8; SPITxBuffer[0] = nTxValue >> 8;
SPITxBuffer[1] = nTxValue & 0xFF; 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 0;
return (SPIRxBuffer[0] << 8) | SPIRxBuffer[1]; return (SPIRxBuffer[0] << 8) | SPIRxBuffer[1];
@@ -246,11 +253,16 @@ void CPisound::SetOSRPins(unsigned bRatio1, unsigned bRatio2, unsigned bRatio3)
void CPisound::DataAvailableInterruptHandler(void* pUserData) void CPisound::DataAvailableInterruptHandler(void* pUserData)
{ {
CPisound* pThis = static_cast<CPisound*>(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 do
{ {
size_t nMIDIRxBytes = 0; size_t nMIDIRxBytes = 0;
@@ -260,6 +272,8 @@ void CPisound::SPITask()
u8 SPIRxBuffer[SPITransferSize]; u8 SPIRxBuffer[SPITransferSize];
u8 SPITxBuffer[SPITransferSize]; u8 SPITxBuffer[SPITransferSize];
bHadData = false;
memset(SPIRxBuffer, 0, sizeof(SPIRxBuffer)); memset(SPIRxBuffer, 0, sizeof(SPIRxBuffer));
memset(SPITxBuffer, 0, sizeof(SPITxBuffer)); memset(SPITxBuffer, 0, sizeof(SPITxBuffer));
@@ -272,15 +286,39 @@ void CPisound::SPITask()
} }
// Extract incoming MIDI bytes from SPI packet // Extract incoming MIDI bytes from SPI packet
m_pSPIMaster->WriteRead(SPIChipSelect, SPITxBuffer, SPIRxBuffer, sizeof(SPIRxBuffer)); if (m_pSPIMaster->WriteReadSync(SPIChipSelect, SPITxBuffer, SPIRxBuffer, SPITransferSize) != SPITransferSize)
for (size_t i = 0; i < sizeof(SPIRxBuffer); i += 2) {
LOGERR("SPI transfer failed");
continue;
}
for (size_t i = 0; i < SPITransferSize; i += 2)
{ {
if (SPIRxBuffer[i]) if (SPIRxBuffer[i])
{
MIDIRxBuffer[nMIDIRxBytes++] = SPIRxBuffer[i + 1]; MIDIRxBuffer[nMIDIRxBytes++] = SPIRxBuffer[i + 1];
bHadData = true;
}
} }
// Pass MIDI bytes on to handler // Pass MIDI bytes on to handler
if (nMIDIRxBytes && m_pReceiveHandler) if (nMIDIRxBytes && m_pReceiveHandler)
m_pReceiveHandler(MIDIRxBuffer, nMIDIRxBytes); 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();
}
} }