mirror of
https://github.com/Ikatono/stream_pad_arduino.git
synced 2026-01-14 13:27:41 -06:00
large update
This commit is contained in:
6
src/200hz300hz_sr10khz_dur8sec.hpp
Normal file
6
src/200hz300hz_sr10khz_dur8sec.hpp
Normal file
File diff suppressed because one or more lines are too long
14
src/ActionId.hpp
Normal file
14
src/ActionId.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef H_C91B532FD0D0486EAE4B48A326F9CA8C
|
||||
#define H_C91B532FD0D0486EAE4B48A326F9CA8C
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Configuration
|
||||
{
|
||||
struct ActionId
|
||||
{
|
||||
uint32_t value;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //H_C91B532FD0D0486EAE4B48A326F9CA8C
|
||||
30
src/Audio.cpp
Normal file
30
src/Audio.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "Audio.hpp"
|
||||
|
||||
//extern "C"
|
||||
void audio_interrupt_handler()
|
||||
{
|
||||
//api seems to require I check channels 1 at a time
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
if (dma_irqn_get_channel_status(_BaseAudio::IRQ_NUM, i))
|
||||
{
|
||||
auto aud = _BaseAudio::audio_callback_items[i];
|
||||
if (aud)
|
||||
aud->perform_interrupt();
|
||||
dma_irqn_acknowledge_channel(_BaseAudio::IRQ_NUM, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _BaseAudio::enable_handler()
|
||||
{
|
||||
static bool handler_enabled = false;
|
||||
if (!handler_enabled)
|
||||
{
|
||||
handler_enabled = true;
|
||||
irq_add_shared_handler(IRQ_NUM, audio_interrupt_handler, 200);
|
||||
irq_set_enabled(IRQ_NUM, true);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<_BaseAudio*, 12> _BaseAudio::audio_callback_items;
|
||||
165
src/Audio.hpp
Normal file
165
src/Audio.hpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#ifndef H_DB5EAFBD40EE4ACDADB973F02E1A4AF1
|
||||
#define H_DB5EAFBD40EE4ACDADB973F02E1A4AF1
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/irq.h>
|
||||
|
||||
//extern "C"
|
||||
void audio_interrupt_handler();
|
||||
|
||||
//allows audio classes of different sizes to share interrupt
|
||||
class _BaseAudio
|
||||
{
|
||||
public:
|
||||
using interrupt_t = void(*)(uint8_t* write_to, uint8_t* dont_write_to, size_t size);
|
||||
static constexpr float sys_tick_freq = 133'000'000;
|
||||
static constexpr unsigned int IRQ_NUM = DMA_IRQ_1;
|
||||
virtual void perform_interrupt() = 0;
|
||||
static std::array<_BaseAudio*, 12> audio_callback_items;
|
||||
|
||||
protected:
|
||||
friend void audio_interrupt_handler();
|
||||
static void enable_handler();
|
||||
};
|
||||
|
||||
template <std::size_t buffer_size>
|
||||
class Audio : public _BaseAudio
|
||||
{
|
||||
public:
|
||||
//claims and configures dma resources
|
||||
bool init(void* write_address)
|
||||
{
|
||||
deinit();
|
||||
dma_channel_a = dma_claim_unused_channel(false);
|
||||
if (dma_channel_a < 0)
|
||||
return false;
|
||||
audio_callback_items[dma_channel_a] = this;
|
||||
dma_channel_b = dma_claim_unused_channel(false);
|
||||
if (dma_channel_b < 0)
|
||||
{
|
||||
dma_channel_unclaim(dma_channel_a);
|
||||
audio_callback_items[dma_channel_a] = nullptr;
|
||||
dma_channel_a = -1;
|
||||
return false;
|
||||
}
|
||||
audio_callback_items[dma_channel_b] = this;
|
||||
|
||||
dma_channel_config dconf;
|
||||
channel_config_set_read_increment(&dconf, true);
|
||||
channel_config_set_write_increment(&dconf, false);
|
||||
timer = dma_claim_unused_timer(false);
|
||||
if (timer < 0)
|
||||
{
|
||||
dma_channel_unclaim(dma_channel_a);
|
||||
dma_channel_unclaim(dma_channel_b);
|
||||
audio_callback_items[dma_channel_a] = audio_callback_items[dma_channel_b] = nullptr;
|
||||
dma_channel_a = dma_channel_b = -1;
|
||||
return false;
|
||||
}
|
||||
channel_config_set_dreq(&dconf, dma_get_timer_dreq(timer));
|
||||
channel_config_set_transfer_data_size(&dconf, DMA_SIZE_8);
|
||||
|
||||
//create a second config so the triggers can point to each other
|
||||
dma_channel_config dconf_b = dconf;
|
||||
channel_config_set_chain_to(&dconf, dma_channel_b);
|
||||
channel_config_set_chain_to(&dconf, dma_channel_a);
|
||||
|
||||
dma_channel_configure(dma_channel_a, &dconf, write_address, bankA.data(), buffer_size, false);
|
||||
dma_channel_configure(dma_channel_b, &dconf_b, write_address, bankB.data(), buffer_size, false);
|
||||
|
||||
enable_handler();
|
||||
dma_irqn_set_channel_enabled(IRQ_NUM, dma_channel_a, true);
|
||||
dma_irqn_set_channel_enabled(IRQ_NUM, dma_channel_b, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
void deinit()
|
||||
{
|
||||
if (dma_channel_a >= 0)
|
||||
{
|
||||
dma_channel_cleanup(dma_channel_a);
|
||||
dma_channel_unclaim(dma_channel_a);
|
||||
dma_irqn_set_channel_enabled(IRQ_NUM, dma_channel_a, false);
|
||||
audio_callback_items[dma_channel_a] = nullptr;
|
||||
dma_channel_a = -1;
|
||||
}
|
||||
if (dma_channel_b >= 0)
|
||||
{
|
||||
dma_channel_cleanup(dma_channel_b);
|
||||
dma_channel_unclaim(dma_channel_b);
|
||||
dma_irqn_set_channel_enabled(IRQ_NUM, dma_channel_a, false);
|
||||
audio_callback_items[dma_channel_b] = nullptr;
|
||||
dma_channel_b = -1;
|
||||
}
|
||||
if (timer >= 0)
|
||||
{
|
||||
dma_timer_unclaim(timer);
|
||||
timer = -1;
|
||||
}
|
||||
}
|
||||
bool is_initialized() const
|
||||
{
|
||||
return dma_channel_a >= 0 && dma_channel_b >= 0;
|
||||
}
|
||||
std::array<uint8_t, buffer_size>& get_inactive_buffer()
|
||||
{
|
||||
return bankA_active ? bankB : bankA;
|
||||
}
|
||||
std::array<uint8_t, buffer_size>& get_active_buffer()
|
||||
{
|
||||
return bankA_active ? bankA : bankB;
|
||||
}
|
||||
//this callback is responsible for loading data into the unused buffer
|
||||
//(or triggering it to happen outside of interrupt context)
|
||||
void set_callback(interrupt_t new_callback)
|
||||
{
|
||||
callback = new_callback;
|
||||
}
|
||||
//need an algorithm for this
|
||||
// void set_sample_rate(float sample_rate)
|
||||
// {
|
||||
// float ratio = sys_tick_freq / sample_rate;
|
||||
|
||||
// }
|
||||
void set_sample_fraction(uint16_t num, uint16_t den)
|
||||
{
|
||||
if (timer >= 0)
|
||||
dma_timer_set_fraction(timer, num, den);
|
||||
}
|
||||
void start()
|
||||
{
|
||||
dma_channel_start(dma_channel_a);
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_active_channel() const
|
||||
{
|
||||
return bankA_active ? dma_channel_a : dma_channel_b;
|
||||
}
|
||||
int get_inactive_channel() const
|
||||
{
|
||||
return bankA_active ? dma_channel_b : dma_channel_a;
|
||||
}
|
||||
void perform_interrupt() override
|
||||
{
|
||||
bankA_active = !bankA_active;
|
||||
dma_channel_set_read_addr(get_inactive_channel(), get_inactive_buffer().data(), false);
|
||||
auto& just_finished = get_inactive_buffer();
|
||||
auto& now_active = get_active_buffer();
|
||||
if (callback)
|
||||
callback(just_finished.data(), now_active.data(), buffer_size);
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<uint8_t, buffer_size> bankA;
|
||||
std::array<uint8_t, buffer_size> bankB;
|
||||
int dma_channel_a = -1;
|
||||
int dma_channel_b = -1;
|
||||
int timer = -1;
|
||||
bool bankA_active = true;
|
||||
interrupt_t callback;
|
||||
};
|
||||
|
||||
#endif //H_DB5EAFBD40EE4ACDADB973F02E1A4AF1
|
||||
66
src/Button.hpp
Normal file
66
src/Button.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef H_B4D7EE1A3D57466AA444F3FC912641B9
|
||||
#define H_B4D7EE1A3D57466AA444F3FC912641B9
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "Pixel.hpp"
|
||||
#include "ActionId.hpp"
|
||||
#include "ConfigError.hpp"
|
||||
|
||||
namespace Configuration
|
||||
{
|
||||
class Page;
|
||||
|
||||
class Button
|
||||
{
|
||||
friend class Page;
|
||||
|
||||
//text on screen when holding the button
|
||||
std::string hold_text;
|
||||
//series of keycodes pressed in order then all released
|
||||
std::vector<uint8_t> keycodes;
|
||||
//color of this button
|
||||
Pixel color;
|
||||
//if set, make this page active after press
|
||||
Page *goto_page;
|
||||
//for custom actions, this tells the host what to do
|
||||
ActionId action_id;
|
||||
public:
|
||||
inline Button()
|
||||
{
|
||||
|
||||
}
|
||||
inline Button(const Button& other)
|
||||
{
|
||||
hold_text = other.hold_text;
|
||||
keycodes = other.keycodes;
|
||||
color = other.color;
|
||||
goto_page = other.goto_page;
|
||||
action_id = other.action_id;
|
||||
}
|
||||
inline Button& operator=(const Button& other)
|
||||
{
|
||||
hold_text = other.hold_text;
|
||||
keycodes = other.keycodes;
|
||||
color = other.color;
|
||||
goto_page = other.goto_page;
|
||||
action_id = other.action_id;
|
||||
return *this;
|
||||
}
|
||||
inline Button(Button&& other)
|
||||
{
|
||||
hold_text = std::move(other.hold_text);
|
||||
keycodes = std::move(other.keycodes);
|
||||
color = other.color;
|
||||
goto_page = other.goto_page;
|
||||
action_id = other.action_id;
|
||||
}
|
||||
inline ConfigError load()
|
||||
{
|
||||
return ConfigError::None;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //H_B4D7EE1A3D57466AA444F3FC912641B9
|
||||
9
src/Config.cpp
Normal file
9
src/Config.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "Config.hpp"
|
||||
|
||||
namespace Configuration
|
||||
{
|
||||
void Config::append(const Page& new_page)
|
||||
{
|
||||
pages.push_back(new_page);
|
||||
}
|
||||
}
|
||||
26
src/Config.hpp
Normal file
26
src/Config.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef H_AE095F749CEA4FC29AC38902BC5FD5D1
|
||||
#define H_AE095F749CEA4FC29AC38902BC5FD5D1
|
||||
|
||||
#include <vector>
|
||||
#include <ArduinoJson.h>
|
||||
#include "Page.hpp"
|
||||
#include "ConfigError.hpp"
|
||||
|
||||
namespace Configuration
|
||||
{
|
||||
|
||||
class Config
|
||||
{
|
||||
std::vector<Page> pages;
|
||||
public:
|
||||
Page& page(size_t index)
|
||||
{
|
||||
return pages[index];
|
||||
}
|
||||
void append(const Page& new_page);
|
||||
ConfigError load(JsonDocument);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //H_AE095F749CEA4FC29AC38902BC5FD5D1
|
||||
16
src/ConfigError.hpp
Normal file
16
src/ConfigError.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef H_BA0C3ED91D9B4CAAB2D8C6FBCD1A1C07
|
||||
#define H_BA0C3ED91D9B4CAAB2D8C6FBCD1A1C07
|
||||
|
||||
namespace Configuration
|
||||
{
|
||||
enum class ConfigError
|
||||
{
|
||||
None = 0,
|
||||
deserialization_error = 1,
|
||||
serialization_error = 2,
|
||||
};
|
||||
//prefered over using error = ConfigError::None directly in case other non-error values are added
|
||||
bool is_error(ConfigError error);
|
||||
}
|
||||
|
||||
#endif //H_BA0C3ED91D9B4CAAB2D8C6FBCD1A1C07
|
||||
12
src/Page.cpp
Normal file
12
src/Page.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "Page.hpp"
|
||||
|
||||
namespace Configuration
|
||||
{
|
||||
std::array<Pixel, 12> Page::led_rgb;
|
||||
|
||||
void Page::update_leds()
|
||||
{
|
||||
for (int i = 0; i < 12; i++)
|
||||
led_rgb[i] = buttons[i].color;
|
||||
}
|
||||
}
|
||||
32
src/Page.hpp
Normal file
32
src/Page.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef H_1AAB801F01BA49D0BB2AAB917C307E50
|
||||
#define H_1AAB801F01BA49D0BB2AAB917C307E50
|
||||
|
||||
#include <array>
|
||||
#include "Button.hpp"
|
||||
#include "ConfigError.hpp"
|
||||
|
||||
namespace Configuration
|
||||
{
|
||||
class Page
|
||||
{
|
||||
static std::array<Pixel, 12> led_rgb;
|
||||
std::array<Button, 12> buttons;
|
||||
std::string display_text;
|
||||
public:
|
||||
inline Page() { }
|
||||
inline Page(const Page& other)
|
||||
{
|
||||
buttons = other.buttons;
|
||||
display_text = other.display_text;
|
||||
}
|
||||
inline Page(Page&& other) : buttons(std::move(other.buttons)) { }
|
||||
ConfigError load();
|
||||
void update_leds();
|
||||
inline Button& button(std::size_t index)
|
||||
{
|
||||
return buttons[index];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //H_1AAB801F01BA49D0BB2AAB917C307E50
|
||||
27
src/Pixel.hpp
Normal file
27
src/Pixel.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef H_FF2FF9A6BC114C719F44441B6FD238C2
|
||||
#define H_FF2FF9A6BC114C719F44441B6FD238C2
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Configuration
|
||||
{
|
||||
struct Pixel
|
||||
{
|
||||
uint32_t value;
|
||||
//should check for endianness I think
|
||||
//but there is no fully compliant way to test at compile time
|
||||
#if defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
uint8_t& green() { return *(reinterpret_cast<uint8_t*>(value)+0); }
|
||||
uint8_t& red() { return *(reinterpret_cast<uint8_t*>(value)+1); }
|
||||
uint8_t& blue() { return *(reinterpret_cast<uint8_t*>(value)+2); }
|
||||
uint8_t& white() { return *(reinterpret_cast<uint8_t*>(value)+3); }
|
||||
#else
|
||||
uint8_t& green() { return *(reinterpret_cast<uint8_t*>(value)+3); }
|
||||
uint8_t& red() { return *(reinterpret_cast<uint8_t*>(value)+2); }
|
||||
uint8_t& blue() { return *(reinterpret_cast<uint8_t*>(value)+1); }
|
||||
uint8_t& white() { return *(reinterpret_cast<uint8_t*>(value)+0); }
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif //H_FF2FF9A6BC114C719F44441B6FD238C2
|
||||
31
src/pwm.pio
Normal file
31
src/pwm.pio
Normal file
@@ -0,0 +1,31 @@
|
||||
;
|
||||
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
; Side-set pin 0 is used for PWM output
|
||||
|
||||
.program pwm
|
||||
.side_set 1 opt
|
||||
|
||||
pull noblock side 0 ; Pull from FIFO to OSR if available, else copy X to OSR.
|
||||
mov x, osr ; Copy most-recently-pulled value back to scratch X
|
||||
mov y, isr ; ISR contains PWM period. Y used as counter.
|
||||
countloop:
|
||||
jmp x!=y noset ; Set pin high if X == Y, keep the two paths length matched
|
||||
jmp skip side 1
|
||||
noset:
|
||||
nop ; Single dummy cycle to keep the two paths the same length
|
||||
skip:
|
||||
jmp y-- countloop ; Loop until Y hits 0, then pull a fresh PWM value from FIFO
|
||||
|
||||
% c-sdk {
|
||||
static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
pio_sm_config c = pwm_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
%}
|
||||
3
src/sine_8khz.hpp
Normal file
3
src/sine_8khz.hpp
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user