initcommit osclsk prototype
This commit is contained in:
commit
4ade3a5f00
19 changed files with 1780 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
**/build/
|
||||
|
||||
tags
|
||||
|
||||
*.swp
|
||||
*.swo
|
120
daisy/Drum/Drum.cpp
Normal file
120
daisy/Drum/Drum.cpp
Normal 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
7
daisy/Drum/Makefile
Normal 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
25
daisy/Drum/README.md
Normal 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)
|
||||
|
BIN
daisy/Drum/resources/Drum.fzz
Normal file
BIN
daisy/Drum/resources/Drum.fzz
Normal file
Binary file not shown.
BIN
daisy/Drum/resources/Drum_bb.png
Normal file
BIN
daisy/Drum/resources/Drum_bb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 270 KiB |
BIN
daisy/Drum/resources/Drum_schem.png
Normal file
BIN
daisy/Drum/resources/Drum_schem.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
34
daisy/blnk/Blink.cpp
Normal file
34
daisy/blnk/Blink.cpp
Normal 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
7
daisy/blnk/Makefile
Normal 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
11
daisy/blnk/README.md
Normal 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
7
daisy/scope/Makefile
Normal 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
41
daisy/scope/audioc.hpp
Normal 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
368
daisy/scope/osclsk.cpp
Normal 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
115
daisy/scope/osclsk.hpp
Normal 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
202
daisy/scope/scope.cpp
Normal 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
11
daisy/scope/scope.hpp
Normal 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
528
daisy/scope/tft.cpp
Normal 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
288
daisy/scope/tft.hpp
Normal 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
10
make/daisy.mk
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue