initcommit osclsk prototype

This commit is contained in:
Ellen Arvidsson 2025-06-13 12:51:54 +02:00
commit 4ade3a5f00
19 changed files with 1780 additions and 0 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
**/build/
tags
*.swp
*.swo

120
daisy/Drum/Drum.cpp Normal file
View file

@ -0,0 +1,120 @@
#include "daisy_seed.h"
#include "daisysp.h"
// Use the daisy namespace to prevent having to type
// daisy:: before all libdaisy functions
using namespace daisy;
using namespace daisysp;
// Declare a DaisySeed object called hardware
DaisySeed hardware;
Oscillator osc;
WhiteNoise noise;
AdEnv kickVolEnv, kickPitchEnv, snareEnv;
Switch kick, snare;
void AudioCallback(AudioHandle::InterleavingInputBuffer in,
AudioHandle::InterleavingOutputBuffer out,
size_t size)
{
float osc_out, noise_out, snr_env_out, kck_env_out, sig;
//Get rid of any bouncing
snare.Debounce();
kick.Debounce();
//If you press the kick button...
if(kick.RisingEdge())
{
//Trigger both envelopes!
kickVolEnv.Trigger();
kickPitchEnv.Trigger();
}
//If press the snare button trigger its envelope
if(snare.RisingEdge())
{
snareEnv.Trigger();
}
//Prepare the audio block
for(size_t i = 0; i < size; i += 2)
{
//Get the next volume samples
snr_env_out = snareEnv.Process();
kck_env_out = kickVolEnv.Process();
//Apply the pitch envelope to the kick
osc.SetFreq(kickPitchEnv.Process());
//Set the kick volume to the envelope's output
osc.SetAmp(kck_env_out);
//Process the next oscillator sample
osc_out = osc.Process();
//Get the next snare sample
noise_out = noise.Process();
//Set the sample to the correct volume
noise_out *= snr_env_out;
//Mix the two signals at half volume
sig = .5 * noise_out + .5 * osc_out;
//Set the left and right outputs to the mixed signals
out[i] = sig;
out[i + 1] = sig;
}
}
int main(void)
{
// Configure and Initialize the Daisy Seed
// These are separate to allow reconfiguration of any of the internal
// components before initialization.
hardware.Configure();
hardware.Init();
hardware.SetAudioBlockSize(4);
float samplerate = hardware.AudioSampleRate();
//Initialize oscillator for kickdrum
osc.Init(samplerate);
osc.SetWaveform(Oscillator::WAVE_TRI);
osc.SetAmp(1);
//Initialize noise
noise.Init();
//Initialize envelopes, this one's for the snare amplitude
snareEnv.Init(samplerate);
snareEnv.SetTime(ADENV_SEG_ATTACK, .01);
snareEnv.SetTime(ADENV_SEG_DECAY, .2);
snareEnv.SetMax(1);
snareEnv.SetMin(0);
//This envelope will control the kick oscillator's pitch
//Note that this envelope is much faster than the volume
kickPitchEnv.Init(samplerate);
kickPitchEnv.SetTime(ADENV_SEG_ATTACK, .01);
kickPitchEnv.SetTime(ADENV_SEG_DECAY, .05);
kickPitchEnv.SetMax(400);
kickPitchEnv.SetMin(50);
//This one will control the kick's volume
kickVolEnv.Init(samplerate);
kickVolEnv.SetTime(ADENV_SEG_ATTACK, .01);
kickVolEnv.SetTime(ADENV_SEG_DECAY, 1);
kickVolEnv.SetMax(1);
kickVolEnv.SetMin(0);
//Initialize the kick and snare buttons on pins 27 and 28
//The callback rate is samplerate / blocksize (48)
snare.Init(hardware.GetPin(27), samplerate / 48.f);
kick.Init(hardware.GetPin(28), samplerate / 48.f);
//Start calling the callback function
hardware.StartAudio(AudioCallback);
// Loop forever
for(;;) {}
}

7
daisy/Drum/Makefile Normal file
View file

@ -0,0 +1,7 @@
# Project Name
TARGET = Drum
# Sources
CPP_SOURCES = Drum.cpp
include ../../make/daisy.mk

25
daisy/Drum/README.md Normal file
View file

@ -0,0 +1,25 @@
# Drum
## Author
Ben Sergentanis
## Description
Simple drumset with whitenoise snare and oscillator kick.
[Source Code](https://github.com/electro-smith/DaisyExamples/tree/master/seed/Drum)
## Controls
Press one button to trigger the kick, and the other to trigger the snare.
## Breadboard
[Drum_bb.png](https://raw.githubusercontent.com/electro-smith/DaisyExamples/master/seed/Drum/resources/Drum_bb.png)
## Schematic
[Drum_schem.png](https://raw.githubusercontent.com/electro-smith/DaisyExamples/master/seed/Drum/resources/Drum_schem.png)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

34
daisy/blnk/Blink.cpp Normal file
View file

@ -0,0 +1,34 @@
#include "daisy_seed.h"
// Use the daisy namespace to prevent having to type
// daisy:: before all libdaisy functions
using namespace daisy;
// Declare a DaisySeed object called hardware
DaisySeed hardware;
int main(void)
{
// Declare a variable to store the state we want to set for the LED.
bool led_state;
led_state = true;
// Configure and Initialize the Daisy Seed
// These are separate to allow reconfiguration of any of the internal
// components before initialization.
hardware.Configure();
hardware.Init();
// Loop forever
for(;;)
{
// Set the onboard LED
hardware.SetLed(led_state);
// Toggle the LED state for the next time around.
led_state = !led_state;
// Wait 500ms
System::Delay(500);
}
}

7
daisy/blnk/Makefile Normal file
View file

@ -0,0 +1,7 @@
# Project Name
TARGET = Blink
# Sources
CPP_SOURCES = Blink.cpp
include ../../make/daisy.mk

11
daisy/blnk/README.md Normal file
View file

@ -0,0 +1,11 @@
# Blink
## Author
Shensley
## Description
Blinks the Seed's onboard LED at a constant rate.
[Source Code](https://github.com/electro-smith/DaisyExamples/tree/master/seed/Blink)

7
daisy/scope/Makefile Normal file
View file

@ -0,0 +1,7 @@
# Project Name
TARGET = scope
# Sources
CPP_SOURCES = scope.cpp tft.cpp osclsk.cpp
include ../../make/daisy.mk

41
daisy/scope/audioc.hpp Normal file
View file

@ -0,0 +1,41 @@
#ifndef _AUDIOC_HPP
#define _AUDIOC_HPP
#define AUDIOC_DEBUG
#ifdef AUDIOC_DEBUG
#define AUDIOC_PRECOND(cond) \
do { \
if (! (cond)) { \
AUDIOC_LOG("AC_PRECOND(" #cond ") @ %s: line %u failed\n", \
__FILE__, __LINE__); \
} \
} while (0)
#define AUDIOC_POSTCOND(cond) \
do { \
if (! (cond)) { \
AUDIOC_LOG("AC_POSTCOND(" #cond ") @ %s: line %u failed\n", \
__FILE__, __LINE__); \
} \
} while (0)
#define AUDIOC_ASSERT(cond) \
do { \
if (! (cond)) { \
AUDIOC_LOG("AC_ASSERT(" #cond ") @ %s: line %u failed\n", \
__FILE__, __LINE__); \
} \
} while (0)
#else
#define AUDIOC_ASSERT(cond)
#endif /* ifdef AUDIOC_DEBUG */
#define AC_ASSERT AUDIOC_ASSERT
#define AC_PRECOND AUDIOC_PRECOND
#define AC_POSTCOND AUDIOC_POSTCOND
#define AC_LOG AUDIOC_LOG
#endif /* ifndef _AUDIOC_HPP */

368
daisy/scope/osclsk.cpp Normal file
View file

@ -0,0 +1,368 @@
#include "audioc.hpp"
#include "scope.hpp"
#include "osclsk.hpp"
#define _HUUUGE_FLOAT (std::numeric_limits<float>::infinity())
int
osclsk_scope::init(uint8_t *dma_buf, size_t dma_sz)
{
AC_PRECOND(dma_buf != NULL && dma_sz > 0);
if (tft.init(dma_buf, dma_sz) != 0)
return (-1);
fg = tft_color(0, 0xff, 0);
bg = tft_color(0, 0, 0);
if (init_block() != 0)
return (-1);
if (init_screen() != 0)
return (-1);
AC_ASSERT(st == osc_state::RENDER || st == osc_state::RENDER_DONE);
return (0);
}
#define OSCLSK_WAIT_READY_INTERVAL 100
int
osclsk_scope::wait_ready(int ms)
{
int time;
switch (state()) {
case osclsk_scope::osc_state::INIT:
AC_LOG("scope.state == INIT");
break;
case osclsk_scope::osc_state::TRIGGER:
AC_LOG("scope.state == TRIGGER");
break;
case osclsk_scope::osc_state::SAMPLE:
AC_LOG("scope.state == SAMPLE");
break;
case osclsk_scope::osc_state::SAMPLE_DONE:
AC_LOG("scope.state == SAMPLE_DONE");
break;
case osclsk_scope::osc_state::RENDER:
AC_LOG("scope.state == RENDER");
break;
case osclsk_scope::osc_state::RENDER_DONE:
AC_LOG("scope.state == RENDER_DONE");
break;
case osclsk_scope::osc_state::READY:
AC_LOG("scope.state == READY");
break;
default:
AC_LOG("scope.state invalid");
break;
}
time = 0;
while (true) {
if (prep() == 0 || time > ms)
break;
System::Delay(OSCLSK_WAIT_READY_INTERVAL);
time += OSCLSK_WAIT_READY_INTERVAL;
}
if (st != osc_state::READY)
return (-1);
return (0);
}
int
osclsk_scope::prep(void)
{
switch (st) {
case osc_state::READY:
return (0);
case osc_state::RENDER_DONE:
block_fill = 0;
block_fill_nsamp = 0;
st = osc_state::READY;
return (0);
default:
return (-1);
}
}
int
osclsk_scope::trig(void)
{
switch (st) {
case osc_state::READY:
trig_lkb_idx = 0;
trig_lkb_idx_nsamp = 0;
trig_num = 0;
trig_state = osc_trig_state::READY;
st = osc_state::TRIGGER;
return (0);
default:
return (-1);
}
}
int
osclsk_scope::init_block(void)
{
block_fill = 0;
block_fill_nsamp = 0;
st = osc_state::SAMPLE;
for (block_fill = 0; block_fill < OSCLSK_BLOCK_LEN; block_fill++) {
block_y_max[block_fill] = 0.f;
block_y_min[block_fill] = 0.f;
}
st = osc_state::SAMPLE_DONE;
AUDIOC_ASSERT(block_fill == OSCLSK_BLOCK_LEN);
return (0);
}
int
osclsk_scope::init_screen(void)
{
return (render_block());
}
void
osclsk_scope::sample(const float *sig, size_t num)
{
size_t i;
switch (st) {
case osc_state::SAMPLE:
for (i = 0; i < num && block_fill < OSCLSK_BLOCK_LEN; i++) {
if (block_fill_nsamp == 0) {
block_y_max[block_fill] = -_HUUUGE_FLOAT;
block_y_min[block_fill] = _HUUUGE_FLOAT;
}
block_y_max[block_fill] = std::max(
block_y_max[block_fill], sig[i]);
block_y_min[block_fill] = std::min(
block_y_min[block_fill], sig[i]);
/* block_fill_nsamp increments and wraps.
* wrapping increments block_fill */
block_fill_nsamp++;
if (block_fill_nsamp >= OSCLSK_RATE_DIV) {
block_fill++;
block_fill_nsamp = 0;
}
}
if (block_fill >= OSCLSK_BLOCK_LEN)
st = osc_state::SAMPLE_DONE;
break;
case osc_state::TRIGGER:
sample_trigger(sig, num);
break;
default:
break;
}
}
bool
osclsk_scope::is_triggering(float sample)
{
switch (trig_mode) {
case osc_trig_mode::RISING_EDGE:
return (sample > trig_margin);
default:
return (false);
}
}
void
osclsk_scope::trig_lkb_wr_block(void)
{
size_t lkb_i, n;
lkb_i = trig_lkb_idx > trig_lkb_amount ?
trig_lkb_idx - trig_lkb_amount :
OSCLSK_TRIG_LOOKBACK + trig_lkb_idx - trig_lkb_amount;
for (n = 0; n < trig_lkb_amount; n++) {
block_y_max[block_fill] = trig_lkb_y_max[lkb_i];
block_y_min[block_fill] = trig_lkb_y_min[lkb_i];
block_fill++;
lkb_i = (lkb_i + 1) & (OSCLSK_TRIG_LOOKBACK - 1);
}
/* we wanna resume sampling from right after the lookback data,
* so we write partial data points to block as well */
block_y_max[block_fill] = trig_lkb_y_max[trig_lkb_idx];
block_y_min[block_fill] = trig_lkb_y_min[trig_lkb_idx];
block_fill_nsamp = trig_lkb_idx_nsamp;
}
void
osclsk_scope::sample_trigger(const float *sig, size_t num)
{
size_t i;
AC_PRECOND(st == osc_state::TRIGGER);
/* sample data in lookback buffers */
for (i = 0; i < num; i++) {
AC_ASSERT(trig_lkb_idx < OSCLSK_TRIG_LOOKBACK);
if (trig_lkb_idx_nsamp == 0) {
trig_lkb_y_max[trig_lkb_idx] = -_HUUUGE_FLOAT;
trig_lkb_y_min[trig_lkb_idx] = _HUUUGE_FLOAT;
}
trig_lkb_y_max[trig_lkb_idx] = std::max(
trig_lkb_y_max[trig_lkb_idx], sig[i]);
trig_lkb_y_min[trig_lkb_idx] = std::min(
trig_lkb_y_min[trig_lkb_idx], sig[i]);
trig_lkb_idx_nsamp++;
if (trig_lkb_idx_nsamp >= OSCLSK_RATE_DIV) {
trig_lkb_idx++;
trig_lkb_idx_nsamp = 0;
}
trig_lkb_idx =
trig_lkb_idx & (OSCLSK_TRIG_LOOKBACK - 1);
}
/* do triggering checks */
for (i = 0; i < num; i++) {
switch (trig_state) {
case osc_trig_state::READY:
if (! is_triggering(sig[i])) {
trig_state = osc_trig_state::NO_TRIG;
}
break;
case osc_trig_state::NO_TRIG:
if (is_triggering(sig[i])) {
trig_num = 0;
trig_state = osc_trig_state::TRIG;
}
break;
case osc_trig_state::TRIG:
if (is_triggering(sig[i])) {
if (++trig_num >= trig_num_req) {
trig_state =
osc_trig_state::TRIGGERED;
}
} else {
trig_state = osc_trig_state::NO_TRIG;
}
break;
default:
break;
}
}
/* transfer lookback to actual block if triggered */
switch (trig_state) {
case osc_trig_state::TRIGGERED:
trig_lkb_wr_block();
st = osc_state::SAMPLE;
break;
default:
break;
}
}
osclsk_scope::osc_state
osclsk_scope::state(void)
{
return (st);
}
#define OSCLSK_Y_MAX_PX (OSCLSK_SCREEN_YSZ - 1)
void
osclsk_scope::render_finish_tft_cb(void *ctx)
{
osclsk_scope *osc;
osc = (osclsk_scope *)ctx;
if (osc->st != osc_state::RENDER)
return;
osc->st = osc_state::RENDER_DONE;
}
int
osclsk_scope::render_block(void)
{
int res;
size_t render_idx;
float block_norm = 1.f;
if (st != osc_state::SAMPLE_DONE)
return (-1);
st = osc_state::RENDER;
for (render_idx = 0; render_idx < OSCLSK_BLOCK_LEN; render_idx++) {
block_norm = std::max( std::abs(block_y_max[render_idx]),
block_norm);
block_norm = std::max( std::abs(block_y_min[render_idx]),
block_norm);
}
for (render_idx = 0; render_idx < OSCLSK_BLOCK_LEN; render_idx++) {
block_y_max[render_idx] = block_y_max[render_idx] / block_norm;
block_y_min[render_idx] = block_y_min[render_idx] / block_norm;
}
for (render_idx = 0; render_idx < OSCLSK_BLOCK_LEN; render_idx++) {
screen_px_y_max[render_idx] = 1u + (uint16_t)(
OSCLSK_Y_MAX_PX * (.5f + block_y_max[render_idx]));
screen_px_y_min[render_idx] = (uint16_t)(
OSCLSK_Y_MAX_PX * (.5f + block_y_min[render_idx]));
}
res = -1;
tft.async_on_finish(render_finish_tft_cb, this);
tft.async_start();
for (render_idx = 0; render_idx < OSCLSK_BLOCK_LEN; render_idx++) {
res = tft.async_hline_row_bg_fill(&bg, &fg,
screen_px_y_min[render_idx], screen_px_y_max[render_idx],
render_idx);
if (res == -1)
break;
}
tft.async_end();
return (res);
}
void
osclsk_scope::set_color(osc_cc which, const tft_color *what)
{
switch (which) {
case osc_cc::FOREGROUND:
fg = *what;
break;
case osc_cc::BACKGROUND:
bg = *what;
break;
default:
break;
}
}

115
daisy/scope/osclsk.hpp Normal file
View file

@ -0,0 +1,115 @@
#ifndef _OSCILLOSK_HPP
#define _OSCILLOSK_HPP
#include "daisy.h"
#include "tft.hpp"
using namespace daisy;
#define OSCLSK_SCREEN_XSZ ILI9341_TFTHEIGHT
#define OSCLSK_SCREEN_YSZ ILI9341_TFTWIDTH
#define OSCLSK_RATE_DIV 32
#define OSCLSK_BLOCK_LEN OSCLSK_SCREEN_XSZ
#define OSCLSK_TRIG_LOOKBACK 0x100
#define OSCLSK_TFT_CMD_BUF_NSPLIT 4
#define OSCLSK_CMDS_LEN 8 * OSCLSK_SCREEN_XSZ
struct osclsk_scope {
enum class osc_cc {
FOREGROUND,
BACKGROUND
};
enum class osc_state {
INIT,
TRIGGER,
SAMPLE,
SAMPLE_DONE,
RENDER,
RENDER_DONE,
READY
};
enum class osc_trig_mode {
RISING_EDGE,
FALLING_EDGE,
ABS_THRESHOLD,
};
enum class osc_trig_state {
INIT,
READY,
NO_TRIG,
TRIG,
TRIGGERED
};
volatile osc_state st;
float block_y_max[OSCLSK_BLOCK_LEN];
float block_y_min[OSCLSK_BLOCK_LEN];
size_t block_fill;
int block_fill_nsamp;
float trig_lkb_y_max[OSCLSK_TRIG_LOOKBACK];
float trig_lkb_y_min[OSCLSK_TRIG_LOOKBACK];
size_t trig_lkb_idx;
int trig_lkb_idx_nsamp;
size_t trig_lkb_amount;
float trig_margin;
size_t trig_num;
size_t trig_num_req;
osc_trig_state trig_state;
osc_trig_mode trig_mode;
uint16_t rate_div;
tft_color fg, bg;
tft_driver_ili9341 tft;
uint16_t screen_px_y_max[OSCLSK_SCREEN_XSZ];
uint16_t screen_px_y_min[OSCLSK_SCREEN_XSZ];
osclsk_scope(void) : st(osc_state::INIT),
block_fill(0), block_fill_nsamp(0),
trig_lkb_idx(0), trig_lkb_idx_nsamp(0),
trig_lkb_amount(2),
trig_margin(.05f), trig_num(0),
trig_num_req(2),
trig_state(osc_trig_state::INIT),
trig_mode(osc_trig_mode::RISING_EDGE) {};
int init(uint8_t *dma_buf, size_t dma_sz);
int init_block(void);
int init_screen(void);
osc_state state(void);
void set_color(osc_cc which, const tft_color *to_what);
void sample(const float *sig, size_t num);
void sample_trigger(const float *sig, size_t num);
int wait_ready(int ms);
int prep(void);
int trig(void);
bool is_triggering(float sample);
void trig_lkb_wr_block(void);
int render_block(void);
static void render_finish_tft_cb(void *ctx);
};
#endif /* _OSCILLOSK_HPP */

202
daisy/scope/scope.cpp Normal file
View file

@ -0,0 +1,202 @@
#include "daisy_seed.h"
#include "daisysp.h"
#include "scope.hpp"
#include "osclsk.hpp"
using namespace daisy;
using namespace daisysp;
#define DMA_AREA_SIZE (3*(1 << 16))
#define SCOPE_RING_BUF_SIZE 1024
#define AUDIO_BLOCK_SIZE 2
/* declarations */
DaisySeed daisy_hw;
CpuLoadMeter load_meter;
osclsk_scope scope;
uint8_t DMA_BUFFER_MEM_SECTION dma_area[DMA_AREA_SIZE];
static void audio_cb(AudioHandle::InputBuffer in,
AudioHandle::OutputBuffer out, size_t sz);
Oscillator osc;
WhiteNoise noise;
AdEnv kickVolEnv, kickPitchEnv, snareEnv;
Switch kick, snare;
RingBuffer<float, SCOPE_RING_BUF_SIZE> scope_in;
static void
audio_cb(AudioHandle::InputBuffer in,
AudioHandle::OutputBuffer out, size_t sz)
{
float osc_out, noise_out, snr_env_out, kck_env_out;
float sig[AUDIO_BLOCK_SIZE];
load_meter.OnBlockStart();
//Get rid of any bouncing
snare.Debounce();
kick.Debounce();
//If you press the kick button...
if(kick.RisingEdge())
{
//Trigger both envelopes!
kickVolEnv.Trigger();
kickPitchEnv.Trigger();
}
//If press the snare button trigger its envelope
if(snare.RisingEdge())
{
snareEnv.Trigger();
}
//Prepare the audio block
for(size_t i = 0; i < sz; i++)
{
//Get the next volume samples
snr_env_out = snareEnv.Process();
kck_env_out = kickVolEnv.Process();
//Apply the pitch envelope to the kick
osc.SetFreq(kickPitchEnv.Process());
//Set the kick volume to the envelope's output
osc.SetAmp(kck_env_out);
//Process the next oscillator sample
osc_out = osc.Process();
//Get the next snare sample
noise_out = noise.Process();
//Set the sample to the correct volume
noise_out *= snr_env_out;
//Mix the two signals at half volume
sig[i] = .5 * noise_out + .5 * osc_out;
}
for (size_t i = 0; i < sz; i++) {
out[0][i] = sig[i];
out[1][i] = sig[i];
}
scope_in.Overwrite(sig, sz);
load_meter.OnBlockEnd();
}
static void
setup_drums(void)
{
float samplerate;
/* kick n snare */
samplerate = daisy_hw.AudioSampleRate();
//Initialize oscillator for kickdrum
osc.Init(samplerate);
osc.SetWaveform(Oscillator::WAVE_TRI);
osc.SetAmp(1);
//Initialize noise
noise.Init();
//Initialize envelopes, this one's for the snare amplitude
snareEnv.Init(samplerate);
snareEnv.SetTime(ADENV_SEG_ATTACK, .01);
snareEnv.SetTime(ADENV_SEG_DECAY, .2);
snareEnv.SetMax(1);
snareEnv.SetMin(0);
//This envelope will control the kick oscillator's pitch
//Note that this envelope is much faster than the volume
kickPitchEnv.Init(samplerate);
kickPitchEnv.SetTime(ADENV_SEG_ATTACK, .01);
kickPitchEnv.SetTime(ADENV_SEG_DECAY, .05);
kickPitchEnv.SetMax(400);
kickPitchEnv.SetMin(50);
//This one will control the kick's volume
kickVolEnv.Init(samplerate);
kickVolEnv.SetTime(ADENV_SEG_ATTACK, .01);
kickVolEnv.SetTime(ADENV_SEG_DECAY, 1);
kickVolEnv.SetMax(1);
kickVolEnv.SetMin(0);
//Initialize the kick and snare buttons on pins 27 and 28
//The callback rate is samplerate / blocksize (48)
snare.Init(daisy_hw.GetPin(27), samplerate / (float)AUDIO_BLOCK_SIZE);
kick.Init(daisy_hw.GetPin(28), samplerate / (float)AUDIO_BLOCK_SIZE);
}
#define LOAD_METER_TICKS (1 << 16)
int
main(void)
{
size_t read;
float s[AUDIO_BLOCK_SIZE];
int load_tick;
daisy_hw.Configure();
daisy_hw.Init();
daisy_hw.StartLog(false); /* true = wait for usb connection */
daisy_hw.SetAudioBlockSize(AUDIO_BLOCK_SIZE);
load_meter.Init(daisy_hw.AudioSampleRate(), daisy_hw.AudioBlockSize());
load_tick = LOAD_METER_TICKS;
if (scope.init(dma_area, sizeof(dma_area)) == -1)
daisy_hw.PrintLine("scope.init failed");
scope_in.Init();
setup_drums();
daisy_hw.StartAudio(audio_cb);
while (true) {
read = scope_in.readable();
read = (read <= sizeof(s)/sizeof(*s)) ? read :
sizeof(s)/sizeof(*s);
scope_in.ImmediateRead(s, read);
scope.sample(s, read);
switch (scope.state()) {
case osclsk_scope::osc_state::SAMPLE_DONE:
scope.render_block();
break;
case osclsk_scope::osc_state::RENDER_DONE:
scope.prep();
break;
case osclsk_scope::osc_state::READY:
scope.trig();
break;
default:
break;
}
if (--load_tick == 0) {
load_tick = LOAD_METER_TICKS;
// get the current load (smoothed value and peak values)
const float avgLoad = load_meter.GetAvgCpuLoad();
const float maxLoad = load_meter.GetMaxCpuLoad();
const float minLoad = load_meter.GetMinCpuLoad();
// print it to the serial connection (as percentages)
daisy_hw.PrintLine("Processing Load %:");
daisy_hw.PrintLine("Max: " FLT_FMT3, FLT_VAR3(maxLoad * 100.0f));
daisy_hw.PrintLine("Avg: " FLT_FMT3, FLT_VAR3(avgLoad * 100.0f));
daisy_hw.PrintLine("Min: " FLT_FMT3, FLT_VAR3(minLoad * 100.0f));
}
}
}

11
daisy/scope/scope.hpp Normal file
View file

@ -0,0 +1,11 @@
#ifndef _SCOPE_HPP
#define _SCOPE_HPP
#include "daisy_seed.h"
extern daisy::DaisySeed daisy_hw;
#define AUDIOC_LOG (daisy_hw.PrintLine)
#include "audioc.hpp"
#endif /* _SCOPE_HPP */

528
daisy/scope/tft.cpp Normal file
View file

@ -0,0 +1,528 @@
#include "daisy.h"
#include "tft.hpp"
using namespace daisy;
static tft_cmd init_cmds[] = {
tft_cmd(0xEF, {0x03, 0x80, 0x02}),
tft_cmd(0xCF, {0x00, 0xC1, 0x30}),
tft_cmd(0xED, {0x64, 0x03, 0x12, 0x81}),
tft_cmd(0xE8, {0x85, 0x00, 0x78}),
tft_cmd(0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}),
tft_cmd(0xF7, {0x20}),
tft_cmd(0xEA, {0x00, 0x00}),
tft_cmd(ILI9341_PWCTR1, {0x23}), // Power control VRH[5:0]
tft_cmd(ILI9341_PWCTR2, {0x10}), // Power control SAP[2:0];BT[3:0]
tft_cmd(ILI9341_VMCTR1, {0x3e, 0x28}), // VCM control
tft_cmd(ILI9341_VMCTR2, {0x86}), // VCM control2
tft_cmd(ILI9341_MADCTL, {0x08}), // Memory Access Control
tft_cmd(ILI9341_VSCRSADD, {0x00}), // Vertical scroll zero
tft_cmd(ILI9341_PIXFMT, {0x55}), // 16 bit pixel fmt
tft_cmd(ILI9341_FRMCTR1, {0x00, 0x18}),
tft_cmd(ILI9341_DFUNCTR, {0x08, 0x82, 0x27}), // Display Function Control
tft_cmd(0xF2, {0x00}), // 3Gamma Function Disable
tft_cmd(ILI9341_GAMMASET, {0x01}), // Gamma curve selected
tft_cmd(ILI9341_GMCTRP1, {0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00}),
tft_cmd(ILI9341_GMCTRN1, {0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F}),
tft_cmd(ILI9341_SLPOUT, 150), // Exit Sleep
tft_cmd(ILI9341_DISPON, 150), // Display on
};
static tft_cmd RESET_CMD(ILI9341_SWRESET, 150);
int
tft_driver_ili9341::init(uint8_t *dma_buf, size_t dma_sz)
{
AC_PRECOND(_async_stat == async_status::INIT);
SpiHandle::Result res;
dcx_pin = daisy::seed::D12; // Pin(PORTB, 8);
dcx.Init(dcx_pin, GPIO::Mode::OUTPUT, GPIO::Pull::PULLUP,
GPIO::Speed::VERY_HIGH);
spi_conf.datasize = 8;
spi_conf.mode = SpiHandle::Config::Mode::MASTER;
spi_conf.direction = SpiHandle::Config::Direction::TWO_LINES;
spi_conf.clock_polarity = SpiHandle::Config::ClockPolarity::LOW;
spi_conf.clock_phase = SpiHandle::Config::ClockPhase::ONE_EDGE;
spi_conf.baud_prescaler = SpiHandle::Config::BaudPrescaler::PS_2;
spi_conf.periph = SpiHandle::Config::Peripheral::SPI_1;
spi_conf.nss = SpiHandle::Config::NSS::HARD_OUTPUT;
spi_conf.pin_config.nss = daisy::seed::D7; //Pin(PORTG, 10); // D7
spi_conf.pin_config.sclk = daisy::seed::D8; //Pin(PORTG, 11); // D8
spi_conf.pin_config.miso = daisy::seed::D9; // Pin(PORTB, 4); // D9
spi_conf.pin_config.mosi = daisy::seed::D10; //Pin(PORTB, 5); // D10
res = spi.Init(spi_conf);
if (res == SpiHandle::Result::ERR)
return (-1);
daisy_hw.PrintLine("resetting tft");
send_sync(&RESET_CMD);
for (auto cmd : init_cmds) {
daisy_hw.PrintLine("sending tft init command");
send_sync(&cmd);
}
_async_dma_buf = dma_buf;
_async_dma_sz = dma_sz;
_async_stat = async_status::READY;
return (0);
}
void
tft_driver_ili9341::set_addr_window(uint16_t col_st, uint16_t width,
uint16_t row_st, uint16_t height)
{
uint16_t start, end;
start = col_st; end = col_st + width;
tft_cmd col_set(ILI9341_CASET,
{ (uint8_t)((start >> 8) & 0xff), (uint8_t)(start & 0xff),
(uint8_t)((end >> 8) & 0xff), (uint8_t)(end & 0xff) });
start = row_st; end = row_st + height;
tft_cmd row_set(ILI9341_PASET,
{ (uint8_t)((start >> 8) & 0xff), (uint8_t)(start & 0xff),
(uint8_t)((end >> 8) & 0xff), (uint8_t)(end & 0xff) });
send_sync(&col_set);
send_sync(&row_set);
}
void
tft_driver_ili9341::async_on_finish(async_on_finish_f cb, void *ctx)
{
_async_cb_fun = cb;
_async_cb_ctx = ctx;
}
int
tft_driver_ili9341::async_start(void)
{
if (_async_stat != async_status::READY &&
_async_stat != async_status::DONE)
return (-1);
if (_async_cb_fun == NULL)
return (-1);
_async_curr_wr = (tft_dma_cmd *)_async_dma_buf;
_async_curr_rd = _async_curr_wr;
_async_curr_wr->size = 0;
_async_wr = async_wr::START;
_async_stat = async_status::NO_TX;
return (0);
}
int
tft_driver_ili9341::async_end(void)
{
_async_wr = async_wr::END;
async_flush();
return (0);
}
tft_driver_ili9341::async_status
tft_driver_ili9341::async_stat(void)
{
return (_async_stat);
}
void
tft_driver_ili9341::async_flush(void)
{
async_tx();
}
int
tft_driver_ili9341::async_hline_row_bg_fill(tft_color *bg, tft_color *fg,
uint16_t hline_idx_st, uint16_t hline_idx_end, uint16_t row_idx)
{
if (render_dma_buf_hline_row_bg_fill(bg, fg, hline_idx_st,
hline_idx_end, row_idx) != 0) {
return (-1);
}
async_tx_try();
return (0);
}
#define TFT_ASYNC_TRY_TX_MIN 4096
void
tft_driver_ili9341::async_tx_try(void)
{
if (dma_buf_sz_tx_pend() > TFT_ASYNC_TRY_TX_MIN)
return;
async_tx();
}
void
tft_driver_ili9341::async_tx(void)
{
if (_async_wr == async_wr::END && _async_stat == async_status::NO_TX &&
dma_buf_sz_tx_pend() == 0) {
_async_stat = async_status::DONE;
_async_cb_fun((void *)_async_cb_ctx);
return;
}
if (_async_stat != async_status::NO_TX)
return;
AC_ASSERT(_async_curr_rd->size > 0);
_async_stat = async_status::TX;
async_send_raw((const uint8_t *)_async_curr_rd->buf, 1,
async_tx_cmd_scb, async_tx_cmd_ecb, this);
}
void
tft_driver_ili9341::async_tx_cmd_scb(void *ctx)
{
tft_driver_ili9341 *tft;
tft = (tft_driver_ili9341 *)ctx;
AC_ASSERT(tft->_async_stat == async_status::TX);
tft->dcx_low();
}
void
tft_driver_ili9341::async_tx_cmd_ecb(void *ctx, SpiHandle::Result res)
{
tft_driver_ili9341 *tft;
const tft_dma_cmd *cmd;
tft = (tft_driver_ili9341 *)ctx;
cmd = tft->dma_buf_curr_rd();
tft->async_send_raw(&cmd->buf[1], cmd->size, async_tx_param_scb,
async_tx_param_ecb, tft);
}
void
tft_driver_ili9341::async_tx_param_scb(void *ctx)
{
tft_driver_ili9341 *tft;
tft = (tft_driver_ili9341 *)ctx;
tft->dcx_high();
}
void
tft_driver_ili9341::async_tx_param_ecb(void *ctx, SpiHandle::Result res)
{
tft_driver_ili9341 *tft;
const tft_dma_cmd *cmd;
tft = (tft_driver_ili9341 *)ctx;
cmd = tft->dma_buf_next_rd();
AUDIOC_ASSERT(tft->async_stat() == async_status::TX);
if (cmd->size == 0) {
tft->_async_stat = async_status::NO_TX;
if (tft->dma_buf_sz_tx_pend() == 0 &&
tft->_async_wr == async_wr::END) {
tft->_async_stat = async_status::DONE;
tft->_async_cb_fun((void *)tft->_async_cb_ctx);
}
return;
}
tft->async_send_raw(cmd->buf, 1, async_tx_cmd_scb, async_tx_cmd_ecb,
tft);
}
size_t
tft_driver_ili9341::dma_buf_sz_tx_pend(void)
{
return ((size_t)
((uint8_t *)_async_curr_wr - (uint8_t *)_async_curr_rd));
}
size_t
tft_driver_ili9341::dma_buf_sz_used(void)
{
size_t buf_fill;
buf_fill = (size_t)((uint8_t *)_async_curr_wr - _async_dma_buf);
if (_async_curr_wr->size > 0)
buf_fill += sizeof(tft_dma_cmd) + _async_curr_wr->size;
return (buf_fill);
}
tft_dma_cmd *
tft_driver_ili9341::dma_buf_curr_wr(void)
{
return ((tft_dma_cmd *)_async_curr_wr);
}
tft_dma_cmd *
tft_driver_ili9341::dma_buf_next_wr(void)
{
size_t sz;
uint8_t *bytes;
tft_dma_cmd *tmp;
sz = sizeof(tft_dma_cmd) + _async_curr_wr->size;
bytes = (uint8_t *)_async_curr_wr;
tmp = (tft_dma_cmd *)&bytes[sz];
tmp->size = 0;
return ((tft_dma_cmd *)(_async_curr_wr = tmp));
}
const tft_dma_cmd *
tft_driver_ili9341::dma_buf_curr_rd(void)
{
return ((const tft_dma_cmd *)_async_curr_rd);
}
const tft_dma_cmd *
tft_driver_ili9341::dma_buf_next_rd(void)
{
size_t sz;
uint8_t *bytes;
sz = sizeof(tft_dma_cmd) + _async_curr_rd->size;
bytes = (uint8_t *)_async_curr_rd;
return ((const tft_dma_cmd *)
(_async_curr_rd = (tft_dma_cmd *)&bytes[sz]));
}
int
tft_driver_ili9341::render_dma_buf_set_addr_window(uint16_t col_st,
uint16_t col_end, uint16_t row_st, uint16_t row_end)
{
size_t n;
tft_dma_cmd *cmd;
if (_async_dma_sz - dma_buf_sz_used() <
2 * (sizeof(tft_dma_cmd) + sizeof(uint8_t) + 2 * sizeof(uint16_t)))
return (-1);
cmd = dma_buf_curr_wr();
n = 0;
cmd->buf[n++] = ILI9341_CASET;
cmd->buf[n++] = (uint8_t)((col_st >> 8) & 0xff);
cmd->buf[n++] = (uint8_t)(col_st & 0xff);
cmd->buf[n++] = (uint8_t)((col_end >> 8) & 0xff);
cmd->buf[n++] = (uint8_t)(col_end & 0xff);
cmd->size = n;
cmd = dma_buf_next_wr();
n = 0;
cmd->buf[n++] = ILI9341_PASET;
cmd->buf[n++] = (uint8_t)((row_st >> 8) & 0xff);
cmd->buf[n++] = (uint8_t)(row_st & 0xff);
cmd->buf[n++] = (uint8_t)((row_end >> 8) & 0xff);
cmd->buf[n++] = (uint8_t)(row_end & 0xff);
cmd->size = n;
dma_buf_next_wr();
return (0);
}
int
tft_driver_ili9341::render_dma_buf_memwr(const tft_color *col, size_t num)
{
size_t i, n;
uint16_t c16;
tft_dma_cmd *cmd;
if (_async_dma_sz - dma_buf_sz_used() <
sizeof(tft_dma_cmd) + sizeof(uint8_t) + num * sizeof(uint16_t))
return (-1);
cmd = dma_buf_curr_wr();
c16 = col->u16();
i = 0;
cmd->buf[i++] = ILI9341_RAMWR;
for (n = 0; n < num; n++) {
cmd->buf[i++] = (uint8_t)((c16 >> 8) & 0xff);
cmd->buf[i++] = (uint8_t)(c16 & 0xff);
}
cmd->size = i;
dma_buf_next_wr();
return (0);
}
int
tft_driver_ili9341::render_dma_buf_memwr_cont(const tft_color *col, size_t num)
{
size_t i, n;
uint16_t c16;
tft_dma_cmd *cmd;
if (_async_dma_sz - dma_buf_sz_used() <
sizeof(tft_dma_cmd) + sizeof(uint8_t) + num * sizeof(uint16_t))
return (-1);
cmd = dma_buf_curr_wr();
c16 = col->u16();
i = 0;
cmd->buf[i++] = ILI9341_WRCONT;
for (n = 0; n < num; n++) {
cmd->buf[i++] = (uint8_t)((c16 >> 8) & 0xff);
cmd->buf[i++] = (uint8_t)(c16 & 0xff);
}
cmd->size = i;
dma_buf_next_wr();
return (0);
}
int
tft_driver_ili9341::render_dma_buf_hline(const tft_color *c, uint16_t col_s,
uint16_t col_e, uint16_t row)
{
if (render_dma_buf_set_addr_window(col_s, col_e, row, row + 1) == -1)
return (-1);
if (render_dma_buf_memwr(c, (size_t)(col_e - col_s)) == -1)
return (-1);
return (0);
}
int
tft_driver_ili9341::render_dma_buf_hline_row_bg_fill(const tft_color *bg,
const tft_color *fg, uint16_t col_s, uint16_t col_e, uint16_t row)
{
if (col_e < col_s || width() < col_e)
return (-1);
if (render_dma_buf_set_addr_window(0, width(), row, row+1) == -1)
return (-1);
if (render_dma_buf_memwr(bg, (size_t)col_s) == -1)
return (-1);
if (render_dma_buf_memwr_cont(fg, (size_t)(col_e - col_s)) == -1)
return (-1);
if (render_dma_buf_memwr_cont(bg, (size_t)(width() - col_e)) == -1)
return (-1);
return (0);
}
#define TFT_N_PIXELS (ILI9341_TFTWIDTH * ILI9341_TFTHEIGHT)
#define TFT_BLOCK_SZ ((size_t)16386)
int
tft_driver_ili9341::fill_screen(const tft_color *c)
{
uint8_t buf[TFT_BLOCK_SZ];
size_t n, i;
uint16_t c16;
set_addr_window(0, width(), 0, height());
if (send_sync(ILI9341_RAMWR, NULL, 0) == -1)
return (-1);
i = 0;
c16 = c->u16();
for (n = 0; n < sizeof(buf) / sizeof(uint16_t); n++) {
buf[i++] = (uint8_t)((c16 >> 8) & 0xff);
buf[i++] = (uint8_t)(c16 & 0xff);
}
for (n = 0;
n < TFT_N_PIXELS * sizeof(uint16_t);
n += i) {
if (send_raw_sync(buf,
std::min(i, (TFT_N_PIXELS * sizeof(uint16_t)) - n)) == -1)
return (-1);
}
return (0);
}
int
tft_driver_ili9341::send_sync(const tft_cmd *cmd)
{
if (send_sync(cmd->cmd, cmd->param_bytes(), cmd->param_len) == -1)
return (-1);
if (cmd->post_delay > 0)
System::Delay(cmd->post_delay);
return (0);
}
int
tft_driver_ili9341::send_sync(uint8_t cmd, const uint8_t *buf, size_t len)
{
SpiHandle::Result res;
dcx_low();
res = spi.BlockingTransmit(&cmd, 1);
dcx_high();
if (res == SpiHandle::Result::ERR)
return (-1);
if (len == 0)
return (0);
res = spi.BlockingTransmit((uint8_t *)buf, len);
return (res == SpiHandle::Result::ERR ? -1 : 0);
}
int
tft_driver_ili9341::send_raw_sync(const uint8_t *buf, size_t len)
{
size_t block_st, transfer_sz;
SpiHandle::Result res;
res = SpiHandle::Result::OK;
for (block_st = 0; block_st < len; block_st += block_sz()) {
transfer_sz = std::min(block_sz(), len - block_st);
res = spi.BlockingTransmit((uint8_t *)&buf[block_st],
transfer_sz);
}
return (res == SpiHandle::Result::ERR ? -1 : 0);
}

288
daisy/scope/tft.hpp Normal file
View file

@ -0,0 +1,288 @@
#ifndef _TFT_HPP
#define _TFT_HPP
#include "daisy.h"
#include "scope.hpp"
#include "audioc.hpp"
using namespace daisy;
#define ILI9341_TFTWIDTH 240 ///< ILI9341 max TFT width
#define ILI9341_TFTHEIGHT 320 ///< ILI9341 max TFT height
#define ILI9341_NOP 0x00 ///< No-op register
#define ILI9341_SWRESET 0x01 ///< Software reset register
#define ILI9341_RDDID 0x04 ///< Read display identification information
#define ILI9341_RDDST 0x09 ///< Read Display Status
#define ILI9341_SLPIN 0x10 ///< Enter Sleep Mode
#define ILI9341_SLPOUT 0x11 ///< Sleep Out
#define ILI9341_PTLON 0x12 ///< Partial Mode ON
#define ILI9341_NORON 0x13 ///< Normal Display Mode ON
#define ILI9341_RDMODE 0x0A ///< Read Display Power Mode
#define ILI9341_RDMADCTL 0x0B ///< Read Display MADCTL
#define ILI9341_RDPIXFMT 0x0C ///< Read Display Pixel Format
#define ILI9341_RDIMGFMT 0x0D ///< Read Display Image Format
#define ILI9341_RDSELFDIAG 0x0F ///< Read Display Self-Diagnostic Result
#define ILI9341_INVOFF 0x20 ///< Display Inversion OFF
#define ILI9341_INVON 0x21 ///< Display Inversion ON
#define ILI9341_GAMMASET 0x26 ///< Gamma Set
#define ILI9341_DISPOFF 0x28 ///< Display OFF
#define ILI9341_DISPON 0x29 ///< Display ON
#define ILI9341_CASET 0x2A ///< Column Address Set
#define ILI9341_PASET 0x2B ///< Page Address Set
#define ILI9341_RAMWR 0x2C ///< Memory Write
#define ILI9341_RAMRD 0x2E ///< Memory Read
#define ILI9341_PTLAR 0x30 ///< Partial Area
#define ILI9341_VSCRDEF 0x33 ///< Vertical Scrolling Definition
#define ILI9341_MADCTL 0x36 ///< Memory Access Control
#define ILI9341_VSCRSADD 0x37 ///< Vertical Scrolling Start Address
#define ILI9341_PIXFMT 0x3A ///< COLMOD: Pixel Format Set
#define ILI9341_WRCONT 0x3C ///< Continue Memory Write
#define ILI9341_DBRIGHT 0x51
#define ILI9341_FRMCTR1 \
0xB1 ///< Frame Rate Control (In Normal Mode/Full Colors)
#define ILI9341_FRMCTR2 0xB2 ///< Frame Rate Control (In Idle Mode/8 colors)
#define ILI9341_FRMCTR3 \
0xB3 ///< Frame Rate control (In Partial Mode/Full Colors)
#define ILI9341_INVCTR 0xB4 ///< Display Inversion Control
#define ILI9341_DFUNCTR 0xB6 ///< Display Function Control
#define ILI9341_PWCTR1 0xC0 ///< Power Control 1
#define ILI9341_PWCTR2 0xC1 ///< Power Control 2
#define ILI9341_PWCTR3 0xC2 ///< Power Control 3
#define ILI9341_PWCTR4 0xC3 ///< Power Control 4
#define ILI9341_PWCTR5 0xC4 ///< Power Control 5
#define ILI9341_VMCTR1 0xC5 ///< VCOM Control 1
#define ILI9341_VMCTR2 0xC7 ///< VCOM Control 2
#define ILI9341_RDID1 0xDA ///< Read ID 1
#define ILI9341_RDID2 0xDB ///< Read ID 2
#define ILI9341_RDID3 0xDC ///< Read ID 3
#define ILI9341_RDID4 0xDD ///< Read ID 4
#define ILI9341_GMCTRP1 0xE0 ///< Positive Gamma Correction
#define ILI9341_GMCTRN1 0xE1 ///< Negative Gamma Correction
//#define ILI9341_PWCTR6 0xFC
// Color definitions
#define ILI9341_BLACK 0x0000 ///< 0, 0, 0
#define ILI9341_NAVY 0x000F ///< 0, 0, 123
#define ILI9341_DARKGREEN 0x03E0 ///< 0, 125, 0
#define ILI9341_DARKCYAN 0x03EF ///< 0, 125, 123
#define ILI9341_MAROON 0x7800 ///< 123, 0, 0
#define ILI9341_PURPLE 0x780F ///< 123, 0, 123
#define ILI9341_OLIVE 0x7BE0 ///< 123, 125, 0
#define ILI9341_LIGHTGREY 0xC618 ///< 198, 195, 198
#define ILI9341_DARKGREY 0x7BEF ///< 123, 125, 123
#define ILI9341_BLUE 0x001F ///< 0, 0, 255
#define ILI9341_GREEN 0x07E0 ///< 0, 255, 0
#define ILI9341_CYAN 0x07FF ///< 0, 255, 255
#define ILI9341_RED 0xF800 ///< 255, 0, 0
#define ILI9341_MAGENTA 0xF81F ///< 255, 0, 255
#define ILI9341_YELLOW 0xFFE0 ///< 255, 255, 0
#define ILI9341_WHITE 0xFFFF ///< 255, 255, 255
#define ILI9341_ORANGE 0xFD20 ///< 255, 165, 0
#define ILI9341_GREENYELLOW 0xAFE5 ///< 173, 255, 41
#define ILI9341_PINK 0xFC18 ///< 255, 130, 198
struct __attribute__((packed)) tft_color {
uint8_t red;
uint8_t green;
uint8_t blue;
// uint16_t c16;
tft_color() : red(0x00), green(0), blue(0x00) {};
tft_color(uint8_t r, uint8_t g, uint8_t b) :
red(r), green(g), blue(b) {};
inline uint16_t u16(void) const
{
uint16_t ret = 0;
ret |= ((uint16_t)(red & 0xf8) << 8);
ret |= ((uint16_t)(green & 0xfc)) << 5;
ret |= ((uint16_t)blue) >> 3;
return (ret);
}
};
struct tft_dma_cmd {
size_t size;
uint8_t buf[];
};
#define TFT_CMD_PARAMS_MAXLEN 16
struct tft_cmd {
uint8_t cmd;
uint8_t _param_bytes[TFT_CMD_PARAMS_MAXLEN];
size_t param_len;
uint8_t *param_bytes_ext;
int post_delay;
tft_cmd(uint8_t c, std::initializer_list<uint8_t> params,
int dly = -1)
: cmd(c), param_len(params.size()), post_delay(dly)
{
size_t i;
AUDIOC_ASSERT(param_len <= sizeof(_param_bytes));
i = 0;
for (auto par : params) {
_param_bytes[i++] = par;
}
}
tft_cmd(uint8_t c, int dly = -1)
: cmd(c), param_len(0), post_delay(dly) {};
inline const uint8_t *param_bytes(void) const
{
return (_param_bytes);
}
};
#define TFT_DRIVER_ILI9341_NBLOCKS 8
struct tft_driver_ili9341 {
typedef void (*async_on_finish_f)(void *);
enum class async_status {
INIT,
READY,
TX,
NO_TX,
DONE
};
enum class async_wr {
START,
END
};
enum class async_hint {
NONE,
FULL_UPDATE_RENDER,
PARTIAL_UPDATE_RENDER
};
SpiHandle spi;
SpiHandle::Config spi_conf;
Logger<LOGGER_EXTERNAL> *lg;
Pin dcx_pin;
GPIO dcx;
async_hint _async_hint;
volatile async_status _async_stat;
volatile async_wr _async_wr;
volatile async_on_finish_f _async_cb_fun;
volatile void *_async_cb_ctx;
uint8_t *_async_dma_buf;
size_t _async_dma_sz;
volatile tft_dma_cmd *_async_curr_wr;
volatile const tft_dma_cmd *_async_curr_rd;
/* general */
inline uint16_t width(void) { return (ILI9341_TFTWIDTH); }
inline uint16_t height(void) { return (ILI9341_TFTHEIGHT); }
inline size_t block_rows(void) {
return (sizeof(tft_color) * height() /
TFT_DRIVER_ILI9341_NBLOCKS);
}
inline size_t block_sz(void) {
return (sizeof(tft_color) * width() * height() /
TFT_DRIVER_ILI9341_NBLOCKS);
}
tft_driver_ili9341(void) : _async_hint(async_hint::NONE),
_async_stat(async_status::INIT), _async_wr(async_wr::END),
_async_dma_buf(NULL), _async_dma_sz(0),
_async_curr_wr(NULL), _async_curr_rd(NULL) {};
int init(uint8_t *dma_buf, size_t dma_sz);
/* syncronous */
int fill_screen(const tft_color *c);
void set_addr_window(uint16_t col_st, uint16_t width, uint16_t row_st,
uint16_t height);
int send_sync(const tft_cmd *cmd);
int send_sync(uint8_t cmd, const uint8_t *buf, size_t len);
int send_raw_sync(const uint8_t *buf, size_t len);
ssize_t recv_raw_sync(const uint8_t *buf, size_t len);
/* asyncronous */
void async_on_finish(async_on_finish_f cb, void *ctx);
int async_start(void);
int async_end(void);
async_status async_stat(void);
void async_flush(void);
void async_tx(void);
void async_tx_try(void);
int async_cmd_hint(async_hint hint);
int async_hline_row_bg_fill(tft_color *bg, tft_color *fg,
uint16_t hline_idx_st, uint16_t hline_idx_end, uint16_t row_idx);
static void async_tx_cmd_scb(void *ctx);
static void async_tx_cmd_ecb(void *ctx, SpiHandle::Result res);
static void async_tx_param_scb(void *ctx);
static void async_tx_param_ecb(void *ctx, SpiHandle::Result res);
inline int
async_send_raw(const uint8_t *dma_buf, size_t dma_bufsz,
SpiHandle::StartCallbackFunctionPtr scb,
SpiHandle::EndCallbackFunctionPtr cb,
void *ctx) {
return (spi.DmaTransmit((uint8_t *)dma_buf, dma_bufsz, scb,
cb, ctx) == SpiHandle::Result::OK ? 0 : -1);
}
size_t dma_buf_sz_tx_pend(void);
size_t dma_buf_sz_used(void);
tft_dma_cmd *dma_buf_curr_wr(void);
tft_dma_cmd *dma_buf_next_wr(void);
const tft_dma_cmd *dma_buf_curr_rd(void);
const tft_dma_cmd *dma_buf_next_rd(void);
/* rendering */
int render_dma_buf_hline(const tft_color *c, uint16_t col_s,
uint16_t col_e, uint16_t row);
int render_dma_buf_memwr(const tft_color *col, size_t num);
int render_dma_buf_memwr_cont(const tft_color *col, size_t num);
int render_dma_buf_set_addr_window(uint16_t col_st,
uint16_t col_end, uint16_t row_st, uint16_t row_end);
int render_dma_buf_hline_row_bg_fill(const tft_color *bg,
const tft_color *fg, uint16_t col_s, uint16_t col_e, uint16_t row);
/* gpio control */
inline void
dcx_low(void)
{
dcx.Write(0);
}
inline void
dcx_high(void)
{
dcx.Write(1);
}
};
#endif

10
make/daisy.mk Normal file
View file

@ -0,0 +1,10 @@
PROJECT_DIR = /Users/ellen/code/daisy
# Path to libDaisy and DaisySP
LIBDAISY_DIR = $(PROJECT_DIR)/libDaisy
DAISYSP_DIR = $(PROJECT_DIR)/DaisySP
# Core location, and generic makefile.
SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core
include $(SYSTEM_FILES_DIR)/Makefile