large update

This commit is contained in:
2024-02-04 09:59:54 -06:00
parent a3aec45452
commit 266537ff33
14 changed files with 580 additions and 4 deletions

File diff suppressed because one or more lines are too long

14
src/ActionId.hpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because one or more lines are too long