Skip to content

Commit 2e3bd16

Browse files
New/updated examples: UART MIDI, WAV Writer, and QSPI Erase Timing (#697)
* adds optional realtime callback, and improved logging to UART MIDI example. * new examples for Realtime use of WAV writer, and a profiling/benchmarking example for QSPI erasures * code style, and naming consistency of WavWriter_Realtime example
1 parent e667533 commit 2e3bd16

7 files changed

Lines changed: 481 additions & 19 deletions

File tree

examples/MIDI_UART_Input/MIDI_UART_Input.cpp

Lines changed: 119 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,97 @@
11
/** Example of setting reading MIDI Input via UART
2-
*
32
*
43
* This can be used with any 5-pin DIN or TRS connector that has been wired up
54
* to one of the UART Rx pins on Daisy.
65
* This will use D14 as the UART 1 Rx pin
76
*
87
* This example will also log incoming messages to the serial port for general MIDI troubleshooting
8+
*
9+
* This includes a demonstration of managing System Realtime messages immediately via
10+
* an optional callback. This can be disabled by setting, `kUseRealtimeCallback` to false.
11+
* in which case all messages while run through the typical MIDI event queue.
912
*/
1013
#include "daisy_seed.h"
1114

1215
/** This prevents us from having to type "daisy::" in front of a lot of things. */
1316
using namespace daisy;
1417

18+
/** Select whether to use the optional system realtime callback or not. */
19+
constexpr const bool kUseRealtimeCallback = true;
20+
21+
/** Indicates from where the MIDI Message was logged */
22+
enum class Source
23+
{
24+
Queue,
25+
RealtimeCb,
26+
Unknown,
27+
};
28+
29+
/** Wrapper type that adds a timestamp and source to the MIDI event. */
30+
struct SourcedMidiEvent
31+
{
32+
MidiEvent message;
33+
Source src;
34+
uint32_t timestamp;
35+
36+
SourcedMidiEvent(MidiEvent e, Source s)
37+
{
38+
message = e;
39+
src = s;
40+
timestamp = System::GetNow();
41+
}
42+
43+
SourcedMidiEvent() : src(Source::Unknown), timestamp(0xffffffff) {}
44+
};
45+
1546
/** Global Hardware access */
1647
DaisySeed hw;
1748
MidiUartHandler midi;
1849

1950
/** FIFO to hold messages as we're ready to print them */
20-
FIFO<MidiEvent, 128> event_log;
51+
FIFO<SourcedMidiEvent, 128> event_log;
52+
53+
54+
/** Helper for getting human readable strings from realtime messages */
55+
const char* GetStringForRealTimeType(SystemRealTimeType type)
56+
{
57+
switch(type)
58+
{
59+
case SystemRealTimeType::TimingClock: return "Timing Clock";
60+
case SystemRealTimeType::SRTUndefined0: return "SRT Undefined0";
61+
case SystemRealTimeType::Start: return "Start";
62+
case SystemRealTimeType::Continue: return "Continue";
63+
case SystemRealTimeType::Stop: return "Stop";
64+
case SystemRealTimeType::SRTUndefined1: return "SRT Undefined1";
65+
case SystemRealTimeType::ActiveSensing: return "ActiveSensing";
66+
case SystemRealTimeType::Reset: return "Reset";
67+
default: return "Unknown Type...";
68+
}
69+
}
70+
71+
/** Helper for getting a string for the source */
72+
const char* GetStringForSource(Source src)
73+
{
74+
switch(src)
75+
{
76+
case Source::Queue: return "Queue";
77+
case Source::RealtimeCb: return "Realtime Callback";
78+
case Source::Unknown:
79+
default: return "Unknown";
80+
}
81+
}
82+
83+
/** Optional callback for synchronous realtime message handling
84+
*
85+
* Typically, you would handle your timing-sensitive behavior
86+
* right here to take advantage of the syncronous handling.
87+
* In this case, we're just going to forward this to the log
88+
* to make sure no messages are missed.
89+
*/
90+
void MidiRealtimeCallback(MidiEvent& event)
91+
{
92+
auto ev = SourcedMidiEvent(event, Source::RealtimeCb);
93+
event_log.PushBack(ev);
94+
}
2195

2296
int main(void)
2397
{
@@ -29,7 +103,14 @@ int main(void)
29103
MidiUartHandler::Config midi_config;
30104
midi.Init(midi_config);
31105

32-
midi.StartReceive();
106+
if(kUseRealtimeCallback)
107+
{
108+
midi.StartReceiveRt(MidiRealtimeCallback);
109+
}
110+
else
111+
{
112+
midi.StartReceive();
113+
}
33114

34115
uint32_t now = System::GetNow();
35116
uint32_t log_time = System::GetNow();
@@ -39,10 +120,10 @@ int main(void)
39120
{
40121
now = System::GetNow();
41122

42-
/** Process MIDI in the background */
123+
/** Monitor MIDI status, and trigger recovery of MIDI Rx if necessary */
43124
midi.Listen();
44125

45-
/** Loop through any MIDI Events */
126+
/** Loop through any MIDI Events in the queue */
46127
while(midi.HasEvents())
47128
{
48129
MidiEvent msg = midi.PopEvent();
@@ -56,16 +137,17 @@ int main(void)
56137
// Do something on Note On events
57138
{
58139
uint8_t bytes[3] = {0x90, 0x00, 0x00};
59-
bytes[1] = msg.data[0];
60-
bytes[2] = msg.data[1];
140+
bytes[1] = msg.data[0];
141+
bytes[2] = msg.data[1];
61142
midi.SendMessage(bytes, 3);
62143
}
63144
break;
64145
default: break;
65146
}
66147

67-
/** Regardless of message, let's add the message data to our queue to output */
68-
event_log.PushBack(msg);
148+
/** Regardless of message, let's add the message data to our queue to output to the log */
149+
auto ev = SourcedMidiEvent(msg, Source::Queue);
150+
event_log.PushBack(ev);
69151
}
70152

71153
/** Now separately, every 5ms we'll print the top message in our queue if there is one */
@@ -74,17 +156,35 @@ int main(void)
74156
log_time = now;
75157
if(!event_log.IsEmpty())
76158
{
77-
auto msg = event_log.PopFront();
78-
char outstr[128];
159+
auto event = event_log.PopFront();
160+
auto msg = event.message;
161+
char outstr[128];
79162
const char* type_str = MidiEvent::GetTypeAsString(msg);
80-
sprintf(outstr,
81-
"time:\t%ld\ttype: %s\tChannel: %d\tData MSB: "
82-
"%d\tData LSB: %d\n",
83-
now,
84-
type_str,
85-
msg.channel,
86-
msg.data[0],
87-
msg.data[1]);
163+
if(msg.type == MidiMessageType::SystemRealTime)
164+
{
165+
const char* src_str = GetStringForSource(event.src);
166+
const char* desc_str
167+
= GetStringForRealTimeType(msg.srt_type);
168+
sprintf(outstr,
169+
"time:\t%ld\ttype: %s - %s\tsource: %s\n",
170+
event.timestamp,
171+
type_str,
172+
desc_str,
173+
src_str);
174+
}
175+
else
176+
{
177+
// Only realtime messages are capable of hitting the realtime
178+
// callback so we do not need to include the source here.
179+
sprintf(outstr,
180+
"time:\t%ld\ttype: %s\tChannel: %d\tData MSB: "
181+
"%d\tData LSB: %d\n",
182+
event.timestamp,
183+
type_str,
184+
msg.channel,
185+
msg.data[0],
186+
msg.data[1]);
187+
}
88188
hw.PrintLine(outstr);
89189
}
90190
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
set(FIRMWARE_NAME QSPI_EraseTiming)
2+
set(FIRMWARE_SOURCES main.cpp)
3+
include(DaisyProject)

examples/QSPI_EraseTiming/Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Project Name
2+
TARGET = QSPI_EraseTiming
3+
4+
# Sources
5+
CPP_SOURCES = main.cpp
6+
7+
# Library Locations
8+
LIBDAISY_DIR = ../..
9+
10+
# Core location, and generic Makefile.
11+
SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core
12+
include $(SYSTEM_FILES_DIR)/Makefile

examples/QSPI_EraseTiming/main.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/** Test program for evaluating QSPI erase performance.
2+
* This program will perform multiple write-erase cycles
3+
* on the first 256kB bytes of the QSPI.
4+
*/
5+
#include "daisy_seed.h"
6+
#include <cmath>
7+
8+
/** This prevents us from having to type "daisy::" in front of a lot of things. */
9+
using namespace daisy;
10+
11+
struct TestProfile
12+
{
13+
uint32_t erase_dur, write_dur;
14+
bool pass;
15+
};
16+
17+
DaisySeed hw;
18+
19+
/** statically allocated workspace for creation/validation of data
20+
* 256kB so we can look at multiple blocks of 64kB if we want.
21+
*/
22+
static const size_t kWorkspaceSize = 0x40000;
23+
static uint8_t workspace[kWorkspaceSize];
24+
25+
TestProfile EraseWriteValidate(QSPIHandle& qspi, size_t test_size)
26+
{
27+
TestProfile res;
28+
29+
// Erase
30+
auto start = System::GetNow();
31+
qspi.Erase(0, test_size);
32+
auto end = System::GetNow();
33+
res.erase_dur = end - start;
34+
35+
// Write
36+
start = System::GetNow();
37+
qspi.Write(0, test_size, workspace);
38+
end = System::GetNow();
39+
res.write_dur = end - start;
40+
41+
// Validate write
42+
dsy_dma_invalidate_cache_for_buffer((uint8_t*)(0x90000000), test_size);
43+
uint8_t* mem = (uint8_t*)(0x90000000);
44+
res.pass = true;
45+
for(size_t i = 0; i < test_size; i++)
46+
{
47+
if(mem[i] != 0x00)
48+
res.pass = false;
49+
}
50+
return res;
51+
}
52+
53+
int main(void)
54+
{
55+
/** Initialize our hardware */
56+
hw.Init();
57+
58+
/** Start Log */
59+
hw.StartLog(true);
60+
// without these delays the printing seems to just get lost before being output?
61+
System::Delay(100);
62+
hw.PrintLine("Beginning Erasure Timing Tests...");
63+
64+
// Naive write data, but since erase sets all bytes 0xff, we'll just flip'em.
65+
std::fill(workspace, workspace + kWorkspaceSize, 0x00);
66+
67+
hw.PrintLine("Test 1: Erase, Write and Validate a single 4kB sector.");
68+
hw.PrintLine("\tstarting...");
69+
auto t1_res = EraseWriteValidate(hw.qspi, 0x1000);
70+
hw.PrintLine("\tErase Time: %dms", t1_res.erase_dur);
71+
hw.PrintLine("\tWrite Time: %dms", t1_res.write_dur);
72+
hw.PrintLine("\tValidated Write: %s", t1_res.pass ? "Pass" : "Fail");
73+
hw.PrintLine("\tfinished.");
74+
75+
hw.PrintLine("Test 2: Erase, Write, and Validate a single 64kB block");
76+
hw.PrintLine("\tstarting...");
77+
auto t2_res = EraseWriteValidate(hw.qspi, 0x10000);
78+
hw.PrintLine("\tErase Time: %dms", t2_res.erase_dur);
79+
hw.PrintLine("\tWrite Time: %dms", t2_res.write_dur);
80+
hw.PrintLine("\tValidated Write: %s", t2_res.pass ? "Pass" : "Fail");
81+
hw.PrintLine("\tfinished.");
82+
83+
hw.PrintLine("Test 3: Erase 256kB in one call (internally 4x 64kB erases)");
84+
size_t test_size = 0x40000;
85+
auto start = System::GetNow();
86+
hw.qspi.Erase(0, test_size);
87+
auto end = System::GetNow();
88+
auto erase_dur = end - start;
89+
hw.PrintLine("\tTotal Erase Time: %dms", erase_dur);
90+
91+
hw.PrintLine("4: Erase 256kB in 4x 64kB blocks/fn calls");
92+
uint32_t t4_durs[4] = {0};
93+
test_size = 0x10000;
94+
start = System::GetNow();
95+
for(size_t i = 0; i < 4; i++)
96+
{
97+
auto istart = System::GetNow();
98+
auto start_addr = 0 + (test_size * i);
99+
hw.qspi.Erase(start_addr, start_addr + test_size);
100+
auto iend = System::GetNow();
101+
t4_durs[i] = iend - istart;
102+
}
103+
end = System::GetNow();
104+
erase_dur = end - start;
105+
hw.PrintLine("\t0: %dms\t1: %dms\t2: %dms\t3: %dms",
106+
t4_durs[0],
107+
t4_durs[1],
108+
t4_durs[2],
109+
t4_durs[3]);
110+
hw.PrintLine("\tTotal Erase Time: %dms", erase_dur);
111+
hw.PrintLine("\tfinished.");
112+
113+
hw.PrintLine("5: Erase 256kB in 64x 4kB blocks/fn calls");
114+
uint32_t t5_durs[64] = {0};
115+
test_size = 0x1000;
116+
start = System::GetNow();
117+
for(size_t i = 0; i < 64; i++)
118+
{
119+
auto istart = System::GetNow();
120+
auto start_addr = 0 + (test_size * i);
121+
hw.qspi.Erase(start_addr, start_addr + test_size);
122+
auto iend = System::GetNow();
123+
t5_durs[i] = iend - istart;
124+
}
125+
end = System::GetNow();
126+
erase_dur = end - start;
127+
128+
for(size_t i = 0; i < 64; i++)
129+
{
130+
hw.Print("\t%d: %dms", i, t5_durs[i]);
131+
if(i % 4 == 3)
132+
hw.Print("\n");
133+
}
134+
135+
hw.PrintLine("\tTotal Erase Time: %dms", erase_dur);
136+
hw.PrintLine("\tfinished.");
137+
138+
hw.PrintLine("Done.");
139+
140+
/** Infinite Loop to Blink when completed. */
141+
while(1)
142+
{
143+
hw.SetLed((System::GetNow() & 511) > 255);
144+
}
145+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
set(FIRMWARE_NAME WavWriter_Realtime)
2+
set(FIRMWARE_SOURCES main.cpp)
3+
include(DaisyProject)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Project Name
2+
TARGET = WavWriter_Realtime
3+
4+
# Sources
5+
CPP_SOURCES = main.cpp
6+
7+
USE_FATFS=1
8+
9+
# Library Locations
10+
LIBDAISY_DIR = ../..
11+
12+
# Core location, and generic Makefile.
13+
SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core
14+
include $(SYSTEM_FILES_DIR)/Makefile

0 commit comments

Comments
 (0)