initial commit

trunk
alexis 2022-08-15 18:17:16 -06:00
commit c9d065fe72
20 changed files with 1077 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
test_midi
# Ignore list for: c
*.d
*.o
*.obj
*.elf
*.map
*.gch
*.pch
*.lib
*.a
*.so
*.out
*.su
*.bin
*.eep
*.hex
*.lss
build
*.disas

Binary file not shown.

73
avrstuff/avlr.h Normal file
View File

@ -0,0 +1,73 @@
// intellectual property is bullshit. bgdc
//
// AVL's avR stuff
// Rev 2022-08
#ifndef AVLR_H
#define AVLR_H
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#include <avr/io.h>
// --- PIN ACCESS MACROS -------------------------------------------------------
// These provide quick access to port pins. Port pins should be defined in a
// pin table header as 8-bit numbers, with the port offset in the high nibble
// and the pin number in the low nibble. ("Port offset" here is the number of
// (sizeof PORT_t) between &PORTA and &PORTn)
//
// Bear in mind that pins are not currently bounds checked. For best results,
// use PINDEF() instead of hard-coding.
#ifdef PORTA
/// Compute the pin value given a port and pin number (for example,
/// PINDEF(PORTB, 5)).
#define PINDEF(port, pin) (((&(port) - &PORTA) << 4) | ((pin) & 0xF))
/// Retrieve the PORT for a given pin.
#define PORT_FOR(npin) ((&PORTA)[((npin) & 0xF0) >> 4])
/// Retrieve the VPORT for a given pin.
#define VPORT_FOR(npin) ((&VPORTA)[((npin) & 0xF0) >> 4])
/// Retrieve the PINCTRL register for a given pin.
#define PINCTRL_FOR(npin) ( \
(&PORT_FOR(npin).PIN0CTRL)[(npin) & 0x07] )
/// Return the bit for a given pin (1 << pinnumber)
#define BIT_FOR(npin) (1u << ((npin) & 0x07))
/// Return the inverted bitmask for a given pin
#define NBIT_FOR(npin) (uint8_t)(~(1u << ((npin) & 0x07)))
/// Set a pin low. Does not change drive.
#define PIN_CLR(npin) (VPORT_FOR(npin).OUT &= NBIT_FOR(npin))
/// Set a pin high. Does not change drive.
#define PIN_SET(npin) (VPORT_FOR(npin).OUT |= BIT_FOR(npin))
/// Set a pin's value
#define PIN_WRITE(npin, val) do { if (val) PIN_SET(npin); else PIN_CLR(npin); } while (0)
/// Return whether a pin is low.
#define PIN_IS_LOW(npin) (!(VPORT_FOR(npin).IN & BIT_FOR(npin)))
/// Return whether a pin is high.
#define PIN_IS_HIGH(npin) (!!(VPORT_FOR(npin).IN & BIT_FOR(npin)))
/// Set a pin driven.
#define PIN_DRIVE(npin) (VPORT_FOR(npin).DIR |= BIT_FOR(npin))
/// Set a pin un-driven.
#define PIN_RELEASE(npin) (VPORT_FOR(npin).DIR &= NBIT_FOR(npin))
/// Set a pin driven and give it a value
#define PIN_DRIVE_TO(npin, val) do { \
PIN_WRITE(npin, val); \
VPORT_FOR(npin).DIR |= BIT_FOR(npin); } while (0)
#endif // defined(PORTA)
#endif // !defined(AVLR_H)

68
avrstuff/bufserial.hpp Normal file
View File

@ -0,0 +1,68 @@
// intellectual property is bullshit bgdc
#ifndef BUFSERIAL_HPP
#define BUFSERIAL_HPP
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#include <avrstuff/serial.hpp>
#include <avrstuff/ringbuf.hpp>
// Buffered interrupt-driven wrapper around a serial port. Transmit and receive
// are both optional - specify their buffer sizes as zero to omit.
template<unsigned Txsize, unsigned Rxsize>
class bufserial: public serial
{
public:
virtual ~bufserial() = default;
bufserial(serial & sp)
: m_serial(&sp)
{
m_serial->rxcie(true);
}
// Receive Complete interrupt handler. Must be called from the relevant
// vector if receive buffering is used.
void rxc()
{
int c = m_serial->recv();
m_rxbuf.push(c); // TODO what if it overflows?
}
// Data Register Empty interrupt handler. Must be called from the
// relevant vector if transmit buffering is used.
void dre()
{
int c;
bool has_c = m_txbuf.pop(c);
if (has_c)
m_serial->send(c);
else
m_serial->dreie(false);
}
virtual int recv() override
{
int c;
if (m_rxbuf.pop(c))
return c;
else
return EOF;
}
virtual void send(int b) override
{
m_txbuf.push_wait(b);
m_serial->dreie(true);
}
private:
serial * m_serial;
ringbuf<int, Txsize> m_txbuf;
ringbuf<int, Rxsize> m_rxbuf;
};
#endif // !defined(BUFSERIAL_HPP)

88
avrstuff/ringbuf.hpp Normal file
View File

@ -0,0 +1,88 @@
// intellectual property is bullshit bgdc
#ifndef RINGBUF_HPP
#define RINGBUF_HPP
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
// Single producer, single consumer ring buffer. Size is limited to 255 to
// permit volatile access to counters. Sizes one less than a power of two
// provide the most efficient implementation.
//
// If used with more than one producer or consumer, access must be locked.
// Otherwise, threadsafe.
template<typename T, unsigned Size>
class ringbuf
{
public:
ringbuf() : m_head(0), m_tail(0) {}
// Push into the buffer or return immediately.
//
// Return: whether the value was pushed
bool push(T const & value)
{
if (full())
return false;
else
{
m_buffer[m_tail] = value;
m_tail = next(m_tail);
return true;
}
}
// Push into the buffer, waiting until ready.
void push_wait(T const & value)
{
while (!push(value));
}
// Pop out of the buffer or return immediately.
//
// Return: whether a value was popped
bool pop(T & value)
{
if (empty())
return false;
else
{
value = m_buffer[m_head];
m_head = next(m_head);
return true;
}
}
// Pop out of the buffer, waiting until ready.
void pop_wait(T & value)
{
while (!pop(value));
}
// Return whether empty
bool empty() const
{
return m_head == m_tail;
}
// Return whether full
bool full() const
{
return m_head == next(m_tail);
}
private:
uint8_t m_head;
uint8_t m_tail;
T m_buffer[Size + 1];
// Return the next index
static constexpr uint8_t next(uint8_t n)
{
return (n + 1) % (Size + 1);
}
};
#endif // !defined(RINGBUF_HPP)

57
avrstuff/serial.hpp Normal file
View File

@ -0,0 +1,57 @@
// intellectual property is bullshit bgdc
#ifndef SERIAL_HPP
#define SERIAL_HPP
#include <stdbool.h>
#include <stdio.h>
#include <stddef.h>
#include <inttypes.h>
// Generic serial interface class.
class serial
{
public:
// Receive one byte. Returns EOF immediately if none is available. May
// return one of this class's additional error values (FERR, PERR,
// OVERFLOW). If the serial peripheral is configured to work with
// larger units than a byte, the return value may exceed 255. If it is
// necessary to manually clear the Receive Complete flag, the
// implementation should do this here.
virtual int recv() = 0;
// Transmit one byte (or larger chunk, as above). Undefined behavior
// for send(EOF).
virtual void send(int b) = 0;
// Set whether the Receive Complete interrupt is enabled
virtual void rxcie(bool en) { (void) en; }
// Set whether the Transmit Complete interrupt is enabled
virtual void txcie(bool en) { (void) en; }
// Set whether the Data Register Empty interrupt is enabled
virtual void dreie(bool en) { (void) en; }
virtual ~serial() = default;
// Transmit a block of data.
void send(uint8_t const * data, size_t len)
{
for (size_t i = 0; i < len; i++)
send((int)(uint8_t) data[i]);
}
// Transmit a string.
void send(char const * s)
{
for (size_t i = 0; s[i]; i++)
send((int)(uint8_t) s[i]);
}
static constexpr int FERR = -2;
static constexpr int PERR = -3;
static constexpr int OVERFLOW = -4;
};
#endif // !defined(SERIAL_HPP)

171
avrstuff/serial_avrdx.hpp Normal file
View File

@ -0,0 +1,171 @@
// intellectual property is bullshit bgdc
#ifndef SERIAL_AVRDX_HPP
#define SERIAL_AVRDX_HPP
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#include <avrstuff/serial.hpp>
// Serial interface class for AVR-Dx USART. Does not provide initialization,
// the user should do this first. If using a character size of 9 bits, 9BITH
// (high byte first) must be used.
//
// NUsart: number of the usart
// Chsize: number of bits per character
template<int NUsart, int Chsize>
class serial_avrdx : public serial
{
public:
virtual ~serial_avrdx() = default;
virtual int recv() override
{
uint8_t hi = Usart()->RXDATAH;
uint8_t lo = Usart()->RXDATAL;
uint8_t flags = hi & (USART_PERR_bm | USART_FERR_bm
| USART_BUFOVF_bm | USART_RXCIF_bm);
if (flags & USART_FERR_bm)
return serial::FERR;
else if (flags & USART_PERR_bm)
return serial::PERR;
else if (flags & USART_BUFOVF_bm)
return serial::OVERFLOW;
else if (flags == USART_RXCIF_bm && Chsize > 8)
return ((hi & 0x01) << 8) | lo;
else if (flags == USART_RXCIF_bm && Chsize <= 8)
return lo;
else
return EOF;
}
virtual void send(int b) override
{
while (!(Usart()->STATUS & USART_DREIF_bm));
if (Chsize > 8)
Usart()->TXDATAH = (b & 0x100) ? 0x1 : 0;
Usart()->TXDATAL = (uint8_t) b;
}
virtual void rxcie(bool en) override
{
if (en)
Usart()->CTRLA |= USART_RXCIE_bm;
else
Usart()->CTRLA &= ~USART_RXCIE_bm;
}
virtual void txcie(bool en) override
{
if (en)
Usart()->CTRLA |= USART_TXCIE_bm;
else
Usart()->CTRLA &= ~USART_TXCIE_bm;
}
virtual void dreie(bool en) override
{
if (en)
Usart()->CTRLA |= USART_DREIE_bm;
else
Usart()->CTRLA &= ~USART_DREIE_bm;
}
// Set the baud rate. This function is templated so it can emit
// compile-time warnings if the baud rate cannot be set adequately.
template<uint32_t Baud>
void set_baud()
{
constexpr uint16_t breg_normal =
64.0 * F_CPU / (16.0 * Baud) + 0.5;
constexpr uint16_t breg_2x =
64.0 * F_CPU / (8.0 * Baud) + 0.5;
constexpr double baud_normal =
64.0 * F_CPU / (16.0 * breg_normal);
constexpr double baud_2x =
64.0 * F_CPU / (8.0 * breg_2x);
constexpr double error_normal = baud_normal / Baud;
constexpr double error_2x = baud_2x / Baud;
constexpr bool valid_normal =
error_normal >= 0.99 && error_normal <= 1.01;
constexpr bool valid_2x =
error_2x >= 0.99 && error_2x <= 1.01;
static_assert(valid_normal || valid_2x,
"Baud rate out of range, cannot achieve tolerance");
if (valid_normal)
{
Usart()->BAUD = breg_normal;
Usart()->CTRLB = (Usart()->CTRLB & ~USART_RXMODE_gm)
| USART_RXMODE_NORMAL_gc;
}
else
{
Usart()->BAUD = breg_2x;
Usart()->CTRLB = (Usart()->CTRLB & ~USART_RXMODE_gm)
| USART_RXMODE_CLK2X_gc;
}
}
// Stop the USART. Interrupts and the transceiver are disabled. Other
// settings may be lost, so it should be restarted with start().
void stop()
{
Usart()->CTRLA = 0;
Usart()->CTRLB &= ~(USART_RXEN_bm | USART_TXEN_bm);
}
// Start the USART. You should first set the baud rate with set_baud()
void start(bool rxen, bool txen, char parity, bool twostop)
{
Usart()->CTRLA = 0;
Usart()->CTRLC =
USART_CMODE_ASYNCHRONOUS_gc |
(parity == 'E' ? USART_PMODE_EVEN_gc :
parity == 'O' ? USART_PMODE_ODD_gc :
USART_PMODE_DISABLED_gc) |
(twostop ? USART_SBMODE_bm : 0) |
(Chsize == 9 ? USART_CHSIZE_9BITH_gc :
Chsize == 8 ? USART_CHSIZE_8BIT_gc :
Chsize == 7 ? USART_CHSIZE_7BIT_gc :
Chsize == 6 ? USART_CHSIZE_6BIT_gc :
USART_CHSIZE_5BIT_gc);
Usart()->CTRLB |=
(rxen ? USART_RXEN_bm : 0) |
(txen ? USART_TXEN_bm : 0);
}
USART_t * Usart() const {
return
#ifdef USART0
NUsart == 0 ? &USART0 :
#endif
#ifdef USART1
NUsart == 1 ? &USART1 :
#endif
#ifdef USART2
NUsart == 2 ? &USART2 :
#endif
#ifdef USART3
NUsart == 3 ? &USART3 :
#endif
#ifdef USART4
NUsart == 4 ? &USART4 :
#endif
#ifdef USART5
NUsart == 5 ? &USART5 :
#endif
NULL;
}
};
#endif // !defined(SERIAL_AVRDX_HPP)

61
board.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "debugpins.h"
#include "board.hpp"
#include <avr/io.h>
#include <avr/fuse.h>
FUSES = {
.WDTCFG = 0x00,
.BODCFG = ACTIVE_DISABLE_gc,
.OSCCFG = CLKSEL_OSCHF_gc,
.reserved_1 = {0x00, 0x00},
.SYSCFG0 = CRCSRC_NOCRC_gc,
.SYSCFG1 = MVSYSCFG_DUAL_gc,
.CODESIZE = 0x00,
.BOOTSIZE = 0x00
};
void board_pins_init()
{
// We have a lot of analog inputs. Just disable ALL input buffers, then
// go back and enable the ones we care about.
PORTA.PINCONFIG = PORT_ISC_INPUT_DISABLE_gc; // Mirrored to all PORTx
PORTA.PINCTRLUPD = 0xFF;
PORTB.PINCTRLUPD = 0xFF;
PORTC.PINCTRLUPD = 0xFF;
PORTD.PINCTRLUPD = 0xFF;
PORTE.PINCTRLUPD = 0xFF;
PORTF.PINCTRLUPD = 0xFF;
PORTG.PINCTRLUPD = 0xFF;
DEBUGPINS_INIT();
// Drive all the chip selects high
PIN_DRIVE_TO(PIN_MIXER_nCS, 1);
PIN_DRIVE_TO(PIN_LFO_nCS, 1);
PIN_DRIVE_TO(PIN_SD_nCS, 1);
PIN_DRIVE_TO(PIN_nVS, 1);
// Set the alternates and inversions (we invert usbmidi.rx to allow
// detection of cable disconnects - because the opto is ON by default,
// depowering the USB region will generate a break)
//
// USART2 -> PF4,5 and invert PF5
// SPI0 -> PG4-7
PORTMUX.USARTROUTEA = PORTMUX_USART2_ALT1_gc;
PINCTRL_FOR(PIN_USBMIDI_RX) = PORT_INVEN_bm;
PORTMUX.SPIROUTEA = PORTMUX_SPI0_ALT2_gc;
// Set up the clocks
DEBUGPINS_START_INIT_CLOCK();
_PROTECTED_WRITE(CLKCTRL.XOSCHFCTRLA,
CLKCTRL_CSUTHF_4K_gc | CLKCTRL_FRQRANGE_24M_gc
| CLKCTRL_ENABLE_bm);
while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_EXTS_bm));
DEBUGPINS_CLOCK_READY();
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_EXTCLK_gc);
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0); // No prescaler
DEBUGPINS_RUN();
}

7
board.hpp Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "boarddefs.h"
// Initialize pins on startup. Peripherals are not started here.
void board_pins_init();

52
boarddefs.h Normal file
View File

@ -0,0 +1,52 @@
#pragma once
#include <avrstuff/avlr.h>
#define PIN_XTAL1 PINDEF(PORTA, 0)
#define PIN_XTAL2 PINDEF(PORTA, 1)
#define PIN_PA2 PINDEF(PORTA, 2)
#define PIN_PA3 PINDEF(PORTA, 3)
#define PIN_MIXER_nCS PINDEF(PORTA, 4)
#define PIN_LFO_nCS PINDEF(PORTA, 5)
#define PIN_PA6 PINDEF(PORTA, 6)
#define PIN_PA7 PINDEF(PORTA, 7)
#define PORT_ADDR PORTB /* Entire port */
#define PIN_SD_COPI PINDEF(PORTC, 0)
#define PIN_SD_CIPO PINDEF(PORTC, 1)
#define PIN_SD_SCK PINDEF(PORTC, 2)
#define PIN_SD_nCS PINDEF(PORTC, 3)
#define PIN_DEBUG0 PINDEF(PORTC, 4)
#define PIN_DEBUG1 PINDEF(PORTC, 5)
#define PIN_DEBUG2 PINDEF(PORTC, 6)
#define PIN_DEBUG3 PINDEF(PORTC, 7)
#define PIN_PD0 PINDEF(PORTD, 0)
#define PIN_AIN_TEMP PINDEF(PORTD, 1)
#define PIN_GATE_OUT PINDEF(PORTD, 2)
#define PIN_GATE_IN PINDEF(PORTD, 3)
#define PIN_AIN_MODCV_IN PINDEF(PORTD, 4)
#define PIN_AIN_CV_IN PINDEF(PORTD, 5)
#define PIN_CV_OUT PINDEF(PORTD, 6)
#define PIN_VREFA PINDEF(PORTD, 7)
#define PIN_AIN_NOISE PINDEF(PORTE, 0)
#define PIN_AIN_CAL PINDEF(PORTE, 1)
#define PIN_AIN_CAL_OP PINDEF(PORTE, 2)
#define PIN_AIN_CAL_OPn PINDEF(PORTE, 3)
#define PIN_INTMIDI_TX PINDEF(PORTE, 4)
#define PIN_INTMIDI_RX PINDEF(PORTE, 5)
#define PIN_PE6 PINDEF(PORTE, 6)
#define PIN_AIN_CAL_OP_2 PINDEF(PORTE, 7)
#define PIN_PF0 PINDEF(PORTF, 0)
#define PIN_PF1 PINDEF(PORTF, 1)
#define PIN_PF2 PINDEF(PORTF, 2)
#define PIN_PF3 PINDEF(PORTF, 3)
#define PIN_USBMIDI_TX PINDEF(PORTF, 4)
#define PIN_USBMIDI_RX PINDEF(PORTF, 5)
#define PIN_PF6 PINDEF(PORTF, 6)
#define PIN_MIDI_TX PINDEF(PORTG, 0)
#define PIN_MIDI_RX PINDEF(PORTG, 1)
#define PIN_ENVAUX1 PINDEF(PORTG, 2)
#define PIN_ENVAUX2 PINDEF(PORTG, 3)
#define PIN_COPI PINDEF(PORTG, 4)
#define PIN_CIPO PINDEF(PORTG, 5)
#define PIN_SCK PINDEF(PORTG, 6)
#define PIN_nVS PINDEF(PORTG, 7)

12
build.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
set -e
BDIR=build
meson . "$BDIR" --cross-file meson_cross.txt
for arg in "$@"; do
meson configure -D"$arg" "$BDIR"
done
meson compile -C "$BDIR"

22
debugpins.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "boarddefs.h"
#define DEBUG_PINS_ARE_STARTUP_STATE 1
#if DEBUG_PINS_ARE_STARTUP_STATE
# define DEBUGPINS_INIT() do { \
PIN_DRIVE_TO(PIN_DEBUG0, 0); \
PIN_DRIVE_TO(PIN_DEBUG1, 0); \
PIN_DRIVE_TO(PIN_DEBUG2, 0); \
PIN_DRIVE_TO(PIN_DEBUG3, 0); \
} while (0)
# define DEBUGPINS_START_INIT_CLOCK() PIN_SET(PIN_DEBUG0)
# define DEBUGPINS_CLOCK_READY() PIN_SET(PIN_DEBUG1)
# define DEBUGPINS_RUN() PIN_SET(PIN_DEBUG3)
#endif

1
ioavr128db64.h Symbolic link
View File

@ -0,0 +1 @@
build/atpack/include/avr/ioavr128db64.h

47
main.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "board.hpp"
#include <avrstuff/serial_avrdx.hpp>
#include <avrstuff/bufserial.hpp>
#include <avr/interrupt.h>
#include <assert.h>
static serial_avrdx<2, 8> usbmidi_usart;
static bufserial<63, 63> usbmidi_buf(usbmidi_usart);
// Plan: main pulls from an event queue. Events are generated from the MIDI
// receive vectors as well as from other sources (e.g. timers to implement
// portamento), in close to raw MIDI format via a struct. "Proprietary"
// commands are sent as a SysEx message with an ASCII command string. There is
// one global SysEx buffer per MIDI receiver, so a second message may not be
// sent until the acknowledge is received.
int main(void)
{
board_pins_init();
usbmidi_usart.set_baud<31250>();
usbmidi_usart.start(true, true, 'N', false);
sei();
for (;;)
{
int c = usbmidi_buf.recv();
if (c >= 0)
usbmidi_buf.send(c);
}
}
void operator delete(void * p, unsigned int sz)
{
assert(false && "ouch!");
for(;;);
}
ISR(USART2_DRE_vect)
{
usbmidi_buf.dre();
}
ISR(USART2_RXC_vect)
{
usbmidi_buf.rxc();
}

81
meson.build Normal file
View File

@ -0,0 +1,81 @@
project('poly1', 'cpp', default_options: ['optimization=2'])
atpack_zip = 'Microchip.AVR-Dx_DFP.2.1.152.atpack'
dependencies = []
local_headers = ['.']
sources = [
'main.cpp',
'board.cpp',
]
# debug build
if get_option('buildtype') == 'debug'
add_project_arguments(['-DDEBUG=1', '-ggdb3'], language: ['c', 'cpp'])
else
add_project_arguments(['-ggdb3'], language: ['c', 'cpp'])
endif
objcopy = find_program('objcopy')
avrdude = find_program('avrdude', required: false, disabler: true)
atpack_path = meson.build_root() / 'atpack'
sysroot = atpack_path / 'gcc/dev' / host_machine.cpu()
add_project_arguments(['-B', sysroot], language: ['c', 'cpp'])
add_project_link_arguments(['-B', sysroot], language: ['c', 'cpp'])
add_project_arguments(['-mmcu=' + host_machine.cpu()], language: ['c', 'cpp'])
add_project_link_arguments(['-mmcu=' + host_machine.cpu()], language: ['c', 'cpp'])
add_project_arguments(['-DF_CPU=' + get_option('cpu_freq').to_string()], language: ['c', 'cpp'])
add_project_arguments(['-I' + atpack_path / 'include'], language: ['c', 'cpp'])
_includes = local_headers
_incl_dirs = include_directories(_includes)
atpack = custom_target(
'atpack',
command: [ 'unzip', '@INPUT@', '-d', '@OUTPUT@' ],
input: atpack_zip,
output: 'atpack',
)
main = executable(
meson.project_name() + '.elf',
sources,
atpack,
include_directories: _incl_dirs,
dependencies: dependencies,
)
main_hex = custom_target(
meson.project_name() + '.hex',
command: [
objcopy,
'-O', 'ihex',
'@INPUT@', '@OUTPUT@'
],
input: main,
output: meson.project_name() + '.hex',
build_by_default: true
)
if avrdude.found()
# creates an empty file named 'flash' to get around meson's inability
# to have a target create no outputs.
#
# TODO i don't think this is necessary...
flash = custom_target(
'flash',
build_always_stale: true,
command: [
avrdude, '-p', host_machine.cpu(),
'-c', get_option('programmer'),
'-U', 'flash:w:@INPUT@:i'
],
input: main_hex,
capture: true,
output: 'flash'
)
endif

22
meson_cross.txt Normal file
View File

@ -0,0 +1,22 @@
[properties]
sys_root = '.atpack/gcc/dev/avr128db64'
[constants]
arch = 'avr'
[host_machine]
system = 'baremetal'
endian = 'little'
cpu_family = 'avr'
cpu = 'avr128db64'
[binaries]
c = arch + '-gcc'
cpp = arch + '-g++'
strip = arch + '-strip'
objcopy = arch + '-objcopy'
[built-in options]
c_args = []
cpp_args = []
c_link_args = []

14
meson_options.txt Normal file
View File

@ -0,0 +1,14 @@
option(
'cpu_freq',
type: 'integer',
min: 0,
value: 20000000,
description: 'Nominal clock speed'
)
option(
'programmer',
type: 'array',
value: ['usbtiny'], #['arduino', '-P', '/dev/ttyACM0'],
description: 'AVR Programmer to flash the MCU with.'
)

97
midi.hpp Normal file
View File

@ -0,0 +1,97 @@
// intellectual property is bullshit bgdc
#ifndef MIDI_H
#define MIDI_H
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
class midix;
// == MIDI COMMANDS ===========================================================
// All midi commands use values >= 0x80 as the high bit marks the start of a
// packet. We use values < 0x80 as internal events so all may share the same
// queue.
struct midi_message {
midix * source;
uint8_t command;
union {
uint8_t arguments[2];
uint8_t * content;
};
};
#define MIDI_CMD_HAS_CHAN(cmd) ((cmd) >= 0x80 && (cmd) <= 0xEF)
#define MIDI_CMD_CHAN(cmd) ((cmd) & 0x0F)
#define MIDI_CMD(cmd) (MIDI_CMD_HAS_CHAN(cmd) ? ((cmd) & 0xF0) : (cmd))
// Internal command events
#define MIDI_ICMD_NOP 0x00
#define MIDI_ICMD_SYSEX 0x01 // sysex text via midi_message.content,
// NUL-terminated
#define MIDI_ICMD_SYSEX_OVERFLOW 0x02
// MIDI events
#define MIDI_CMD_NOTE_OFF 0x80 // key velocity
#define MIDI_CMD_NOTE_ON 0x90 // key velocity
#define MIDI_CMD_AFTERTOUCH 0xA0 // key touch
#define MIDI_CMD_CC 0xB0 // cc# value
#define MIDI_CMD_PATCH 0xC0 // inst# ?
#define MIDI_CMD_PRESSURE 0xD0 // pres -n/a-
#define MIDI_CMD_PITCHBEND 0xE0 // lsb msb
#define MIDI_CMD_START_SYSEX 0xF0
#define MIDI_CMD_TIMECODE_QFRM 0xF1
#define MIDI_CMD_SONG_POS_PTR 0xF2
#define MIDI_CMD_SONG_SELECT 0xF3
#define MIDI_CMD_TUNE_REQ 0xF6
#define MIDI_CMD_END_SYSEX 0xF7
#define MIDI_CMD_TIMING_CLK 0xF8
#define MIDI_CMD_START 0xFA
#define MIDI_CMD_CONTINUE 0xFB
#define MIDI_CMD_STOP 0xFC
#define MIDI_CMD_ACTIVESENSE 0xFE
#define MIDI_CMD_SYSRESET 0xFF
// Convert a single MIDI argument to a signed int
static inline int midi_signed(uint8_t arg)
{
uint16_t v = arg;
if (arg & 0x40)
v |= 0xFF80;
return (int16_t) v;
}
// Convert two MIDI arguments to a signed int
static inline int midi_double_signed(uint8_t lsb, uint8_t msb)
{
uint16_t v = lsb | (msb << 7);
if (v & 0x2000)
v |= 0xC000;
return (int16_t) v;
}
// Return the number of arguments expected for a command
static inline int midi_num_args(uint8_t cmd)
{
cmd = MIDI_CMD(cmd);
switch (cmd)
{
case MIDI_CMD_NOTE_OFF: return 2;
case MIDI_CMD_NOTE_ON: return 2;
case MIDI_CMD_AFTERTOUCH: return 2;
case MIDI_CMD_CC: return 2;
case MIDI_CMD_PATCH: return 2;
case MIDI_CMD_PRESSURE: return 1;
case MIDI_CMD_PITCHBEND: return 2;
default: return 0;
}
}
#endif // !defined(MIDI_H)

85
midix.hpp Normal file
View File

@ -0,0 +1,85 @@
// intellectual property is bullshit bgdc
#ifndef MIDIX_HPP
#define MIDIX_HPP
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#include <assert.h>
#include <avrstuff/serial.hpp>
#include "midi.hpp"
#define MIDIX_SYSEX_LEN 64
// MIDI transceiver
class midix {
public:
midix(serial & serport)
: m_serport(serport)
, m_sysex_buf_avail(true)
, m_message_pos(0)
, m_receiving_sysex(false)
{
m_message.source = this;
m_message.command = MIDI_ICMD_NOP;
}
// Receive Complete interrupt. Call within the serial port's RXC ISR.
void rxc()
{
int c = m_serport.recv();
if (c < 0)
{
m_message.command = MIDI_ICMD_NOP;
m_message_pos = 0;
}
else if (c >= 0x80)
{
m_message.command = (uint8_t) c;
m_message_pos = 1;
}
else
{
if (m_message_pos <= sizeof(m_message.arguments))
{
m_message.arguments[m_message_pos - 1] = c;
m_message_pos++;
}
if (m_message_pos == midi_num_args(m_message.command))
{
// TODO if MIDI_CMD_START_SYSEX, begin
// receiving the SysEx message into the buffer
// and convert into MIDI_ICMD_SYSEX. If the
// buffer is currently held, push a
// MIDI_ICMD_SYSEX_OVERFLOW instead.
// TODO push message onto queue
}
}
}
// Release the sysex buffer
void sysex_release_buffer(uint8_t * buf)
{
assert(!m_sysex_buf_avail);
assert(buf == &m_sysex_buf[0]);
buf[0] = 0;
m_sysex_buf_avail = true;
}
private:
serial & m_serport;
uint8_t m_sysex_buf[MIDIX_SYSEX_LEN];
bool m_sysex_buf_avail;
public: // XXX
struct midi_message m_message;
size_t m_message_pos;
bool m_receiving_sysex;
};
#endif // !defined(MIDIX_HPP)

98
test_midi.cpp Normal file
View File

@ -0,0 +1,98 @@
// intellectual property is bullshit bgdc
#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#include <assert.h>
#include <vector>
#include <avrstuff/serial.hpp>
#include "midix.hpp"
#include "midi.hpp"
#include <avrstuff/ringbuf.hpp>
class serial_test: public serial
{
public:
virtual int recv() override
{
uint8_t byte;
if (m_data.pop(byte))
return byte;
else
return EOF;
}
virtual void send(int b) override
{
(void) b;
}
// Put data into the internal buffer
void put(uint8_t const * data, size_t len)
{
for (size_t i = 0; i < len; i++)
assert(m_data.push(data[i]));
}
private:
ringbuf<uint8_t, 255> m_data;
};
void print_midi(struct midi_message * m)
{
uint8_t cmd = MIDI_CMD(m->command);
uint8_t chan = MIDI_CMD_CHAN(m->command);
switch (cmd)
{
case MIDI_ICMD_NOP: printf("I NOP "); break;
case MIDI_ICMD_SYSEX: printf("I SYSEX "); break;
case MIDI_ICMD_SYSEX_OVERFLOW: printf("I SYSEX_OVERFLOW"); break;
case MIDI_CMD_NOTE_OFF: printf("NOTE_OFF [%X]", chan); break;
case MIDI_CMD_NOTE_ON: printf("NOTE_ON [%X]", chan); break;
case MIDI_CMD_AFTERTOUCH: printf("AFTERTOUCH [%X]", chan); break;
case MIDI_CMD_CC: printf("CC [%X]", chan); break;
case MIDI_CMD_PATCH: printf("PATCH [%X]", chan); break;
case MIDI_CMD_PRESSURE: printf("PRESSURE [%X]", chan); break;
case MIDI_CMD_PITCHBEND: printf("PITCHBEND [%X]", chan); break;
case MIDI_CMD_START_SYSEX: printf("START_SYSEX "); break;
case MIDI_CMD_TUNE_REQ: printf("TUNE_REQ "); break;
case MIDI_CMD_END_SYSEX: printf("END_SYSEX "); break;
case MIDI_CMD_SYSRESET: printf("SYSRESET "); break;
}
if (cmd == MIDI_ICMD_SYSEX)
{
printf("%s", m->content);
}
else
{
for (int i = 0; i < midi_num_args(cmd); i++)
{
printf(" %02x", m->arguments[i]);
}
}
printf("\n");
}
int main(int argc, char ** argv)
{
serial_test serport;
midix m(serport);
for (int i = 0; i < argc; i++)
{
unsigned x = 0;
sscanf(argv[i], "%X", &x);
uint8_t b = x;
serport.put(&b, 1);
m.rxc();
}
print_midi(&m.m_message);
return 0;
}