add V203 midi
This commit is contained in:
parent
135eb6065d
commit
a8fb67d610
32 changed files with 15025 additions and 4 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -5,12 +5,13 @@
|
|||
**/build/*
|
||||
*.lst
|
||||
*.bin
|
||||
*.hex
|
||||
*.map
|
||||
*.elf
|
||||
midi/melody.c
|
||||
midi/miditone.c
|
||||
midi/ton/gen
|
||||
midi/ton/miditones
|
||||
*/midi/melody.c
|
||||
*/midi/miditone.c
|
||||
*/midi/ton/gen
|
||||
*/midi/ton/miditones
|
||||
V203/pwm/sin.c
|
||||
V203/gsm/lib/libgsm.a
|
||||
|
||||
|
|
67
V203/midi/Makefile
Normal file
67
V203/midi/Makefile
Normal file
|
@ -0,0 +1,67 @@
|
|||
# ch32v203
|
||||
TARGET?= ch32v203
|
||||
#TARGET?= linux
|
||||
#TOOL ?= gcc
|
||||
TOOL ?= clang
|
||||
|
||||
PRJ = example
|
||||
|
||||
VPATH = . ./$(TARGET)
|
||||
BLD = ./build/
|
||||
DFLAGS = -d
|
||||
LFLAGS = -g
|
||||
LDLIBS =
|
||||
BFLAGS = --strip-unneeded
|
||||
|
||||
CFLAGS = -MMD -Wall -ggdb -fno-exceptions -ffunction-sections -fdata-sections
|
||||
CFLAGS+= -I. -I./common -I./$(TARGET) -DHAVE_CONFIG=1
|
||||
DEL = rm -f
|
||||
|
||||
# zdrojaky
|
||||
OBJS = main.o pcmdma.o
|
||||
OBJS += tone.o midiplayer.o miditone.o melody.o
|
||||
|
||||
include $(TARGET)/$(TOOL).mk
|
||||
BOBJS = $(addprefix $(BLD),$(OBJS))
|
||||
|
||||
all: $(BLD) $(PRJ).elf
|
||||
# ... atd.
|
||||
-include $(BLD)*.d
|
||||
# linker
|
||||
$(PRJ).elf: $(BOBJS)
|
||||
-@echo [LD $(TOOL),$(TARGET)] $@
|
||||
@$(LD) $(LFLAGS) -o $(PRJ).elf $(BOBJS) $(LDLIBS)
|
||||
-@echo "size:"
|
||||
@$(SIZE) $(PRJ).elf
|
||||
-@echo "listing:"
|
||||
$(DUMP) $(DFLAGS) $(PRJ).elf > $(PRJ).lst
|
||||
-@echo "OK."
|
||||
$(COPY) $(BFLAGS) -O binary $(PRJ).elf $(PRJ).bin
|
||||
$(COPY) $(BFLAGS) -O ihex $(PRJ).elf $(PRJ).hex
|
||||
# preloz co je potreba
|
||||
$(BLD)%.o: %.c
|
||||
-@echo [CC $(TOOL),$(TARGET)] $@
|
||||
@$(CC) -c $(CFLAGS) $< -o $@
|
||||
$(BLD)%.o: %.cpp
|
||||
-@echo [CX $(TOOL),$(TARGET)] $@
|
||||
@$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@
|
||||
$(BLD):
|
||||
mkdir $(BLD)
|
||||
flash: $(PRJ).elf
|
||||
minichlink -w $(PRJ).bin flash -b
|
||||
|
||||
miditone.c: ton/gen
|
||||
ton/gen
|
||||
ton/gen: ton/gen.cpp
|
||||
g++ -Os ton/gen.cpp -o ton/gen
|
||||
melody.c: ton/miditones
|
||||
ton/miditones -d -s2 -t12 mid/
|
||||
ton/miditones: ton/miditonesV1.6.c
|
||||
gcc -Os -Wno-pointer-sign -Wno-return-type ton/miditonesV1.6.c -o ton/miditones
|
||||
|
||||
# vycisti
|
||||
clean:
|
||||
$(DEL) $(BLD)* *.lst *.bin *.hex *.elf *.map *~ miditone.c melody.c
|
||||
distclean: clean
|
||||
$(DEL) ton/gen ton/miditones
|
||||
.PHONY: all clean distclean flash
|
16
V203/midi/audio.h
Normal file
16
V203/midi/audio.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static constexpr int AudioSampleRate = 24000;
|
||||
/// Počet generátorů.
|
||||
static constexpr unsigned int maxGens = 12;
|
||||
/// Kladné maximum vzorku.
|
||||
static constexpr int maxValue = 30000;
|
||||
/// Záporné maximum vzorku.
|
||||
static constexpr int minValue = -maxValue;
|
||||
///
|
||||
static constexpr unsigned int maxAmplt = (1U<<27);
|
||||
|
||||
#endif // AUDIO_H
|
12460
V203/midi/ch32v203/CH32V20xxx.h
Normal file
12460
V203/midi/ch32v203/CH32V20xxx.h
Normal file
File diff suppressed because it is too large
Load diff
23
V203/midi/ch32v203/clang.mk
Normal file
23
V203/midi/ch32v203/clang.mk
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Use clang / llvm toolchain
|
||||
#
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
LD = ld.lld
|
||||
SIZE = llvm-size
|
||||
DUMP = riscv64-unknown-elf-objdump
|
||||
COPY = riscv64-unknown-elf-objcopy
|
||||
|
||||
OBJS += startup.o system.o
|
||||
CCPU = -march=rv32imac -mabi=ilp32
|
||||
MCPU = $(CCPU)
|
||||
TRIP = riscv32-unknown-none-elf
|
||||
CFLAGS+= -Oz -flto
|
||||
CFLAGS+= -fmessage-length=0 -fsigned-char -I/usr/include/newlib
|
||||
#CFLAGS+= -fconstexpr-steps=2097152
|
||||
CFLAGS+= --target=$(TRIP) $(MCPU)
|
||||
LFLAGS+= --Map=$(@:%.elf=%.map) --gc-sections
|
||||
# 16-bit instrukce se do toho asi dostanou až na úrovni LLVM linkeru.
|
||||
# Bohužel to není nikde pořádně popsáno.
|
||||
LFLAGS+= -mllvm -mattr=+c
|
||||
LFLAGS+= -lto-O3 -nostdlib
|
||||
LDLIBS+= -L./$(TARGET) -T generated_CH32V203F8P6.ld
|
19
V203/midi/ch32v203/gcc.mk
Normal file
19
V203/midi/ch32v203/gcc.mk
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Use gcc / binutils toolchain
|
||||
PREFIX = riscv64-unknown-elf-
|
||||
CC = $(PREFIX)gcc
|
||||
CXX = $(PREFIX)g++
|
||||
LD = $(PREFIX)gcc
|
||||
SIZE = $(PREFIX)size
|
||||
DUMP = $(PREFIX)objdump
|
||||
COPY = $(PREFIX)objcopy
|
||||
OBJS += startup.o system.o
|
||||
CFLAGS+= -Os
|
||||
CCPU = -march=rv32imac -mabi=ilp32
|
||||
MCPU = $(CCPU)
|
||||
|
||||
CFLAGS+= $(MCPU) -I/usr/include/newlib -fmessage-length=0
|
||||
#CFLAGS+= -msmall-data-limit=8 -mno-save-restore -fsigned-char
|
||||
LFLAGS+= -Wl,--Map=$(@:%.elf=%.map),--gc-sections
|
||||
#LFLAGS+= -Wl,--print-sysroot -- chyba ld ?
|
||||
LFLAGS+= -O3 $(MCPU) -nostartfiles -nostdlib
|
||||
LDLIBS+= -lgcc -L./$(TARGET) -T generated_CH32V203F8P6.ld
|
115
V203/midi/ch32v203/generated_CH32V203F8P6.ld
Normal file
115
V203/midi/ch32v203/generated_CH32V203F8P6.ld
Normal file
|
@ -0,0 +1,115 @@
|
|||
ENTRY( InterruptVector )
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 64K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
|
||||
}
|
||||
SECTIONS
|
||||
{
|
||||
.init :
|
||||
{
|
||||
_sinit = .;
|
||||
. = ALIGN(4);
|
||||
KEEP(*(SORT_NONE(.init)))
|
||||
. = ALIGN(4);
|
||||
_einit = .;
|
||||
} >FLASH AT>FLASH
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.rodata)
|
||||
*(.rodata*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
. = ALIGN(4);
|
||||
} >FLASH AT>FLASH
|
||||
.fini :
|
||||
{
|
||||
KEEP(*(SORT_NONE(.fini)))
|
||||
. = ALIGN(4);
|
||||
} >FLASH AT>FLASH
|
||||
PROVIDE( _etext = . );
|
||||
PROVIDE( _eitcm = . );
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
.ctors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*crtbegin?.o(.ctors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} >FLASH AT>FLASH
|
||||
.dtors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*crtbegin?.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
} >FLASH AT>FLASH
|
||||
.dalign :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_data_vma = .);
|
||||
} >RAM AT>FLASH
|
||||
.dlalign :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_data_lma = .);
|
||||
} >FLASH AT>FLASH
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.gnu.linkonce.r.*)
|
||||
*(.data .data.*)
|
||||
*(.gnu.linkonce.d.*)
|
||||
. = ALIGN(8);
|
||||
PROVIDE( __global_pointer$ = . + 0x800 );
|
||||
*(.sdata .sdata.*)
|
||||
*(.sdata2*)
|
||||
*(.gnu.linkonce.s.*)
|
||||
. = ALIGN(8);
|
||||
*(.srodata.cst16)
|
||||
*(.srodata.cst8)
|
||||
*(.srodata.cst4)
|
||||
*(.srodata.cst2)
|
||||
*(.srodata .srodata.*)
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _edata = .);
|
||||
} >RAM AT>FLASH
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _sbss = .);
|
||||
*(.sbss*)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
*(.bss*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON*)
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _ebss = .);
|
||||
} >RAM AT>FLASH
|
||||
PROVIDE( _end = _ebss);
|
||||
PROVIDE( end = . );
|
||||
PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM));
|
||||
}
|
56
V203/midi/ch32v203/gpio.h
Normal file
56
V203/midi/ch32v203/gpio.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef _GPIO_CLASS_H_
|
||||
#define _GPIO_CLASS_H_
|
||||
#include "CH32V20xxx.h"
|
||||
|
||||
enum GPIO_MODE : uint32_t {
|
||||
GPIO_Speed_In = 0u,
|
||||
GPIO_Speed_10MHz = 1u,
|
||||
GPIO_Speed_2MHz = 2u,
|
||||
GPIO_Speed_50MHz = 3u,
|
||||
};
|
||||
enum GPIO_CNF : uint32_t {
|
||||
GPIO_AI_PPO = 0u,
|
||||
GPIO_FI_ODO = 1u << 2,
|
||||
GPIO_UPDI_MPPO = 2u << 2,
|
||||
GPIO_none_MPDO = 3u << 2,
|
||||
};
|
||||
enum GPIOPuPd_TypeDef {
|
||||
GPIO_PuPd_NOPULL = 0x00,
|
||||
GPIO_PuPd_UP = 0x01,
|
||||
GPIO_PuPd_DOWN = 0x02
|
||||
};
|
||||
|
||||
class GpioClass {
|
||||
GPIOA_Type & port;
|
||||
const uint32_t pin;
|
||||
public:
|
||||
explicit constexpr GpioClass (GPIOA_Type & _port, const uint32_t _pin, const uint32_t _mode = GPIO_AI_PPO | GPIO_Speed_10MHz) noexcept
|
||||
: port(_port), pin(_pin) {
|
||||
/* Zapneme vše, ono je to dost jedno. */
|
||||
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r)->auto {
|
||||
r.B.IOPAEN = SET;
|
||||
r.B.IOPBEN = SET;
|
||||
//r.B.IOPCEN = SET;
|
||||
return r.R;
|
||||
});
|
||||
const uint32_t pos = pin << 2;
|
||||
port.CFGLR.modify([=](GPIOA_Type::CFGLR_DEF & r)->auto {
|
||||
uint32_t t = r.R;
|
||||
t &= ~(0xFu << pos);
|
||||
t |= _mode << pos;
|
||||
return t;
|
||||
});
|
||||
}
|
||||
void operator<< (const bool b) const {
|
||||
port.BSHR.R = b ? 1u << pin : 1u << (pin + 16);
|
||||
}
|
||||
operator bool () const {
|
||||
return port.INDR.R & (1u << pin);
|
||||
}
|
||||
void setPuPd (GPIOPuPd_TypeDef p) {
|
||||
if (p != GPIO_PuPd_UP) return;
|
||||
port.OUTDR.R |= 1u << pin;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _GPIO_CLASS_H_
|
112
V203/midi/ch32v203/pcmdma.cpp
Normal file
112
V203/midi/ch32v203/pcmdma.cpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
#include "system.h"
|
||||
#include "pcmdma.h"
|
||||
#include "gpio.h"
|
||||
|
||||
static PcmDma * pInstance = nullptr;
|
||||
extern "C" void DMA1_Channel5_IRQHandler( void ) __attribute__((interrupt));
|
||||
void DMA1_Channel5_IRQHandler( void ) {
|
||||
DMA1_Type::INTFR_DEF state (DMA1.INTFR);
|
||||
DMA1.INTFCR.R = state.R; // clear all
|
||||
if (!pInstance) return;
|
||||
if (state.B.HTIF5 != RESET) pInstance->send(false);
|
||||
else if (state.B.TCIF5 != RESET) pInstance->send(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize TIM1 for PWM
|
||||
*/
|
||||
static inline void tim1pwm_init () noexcept {
|
||||
// Enable GPIOA and TIM1
|
||||
RCC.APB2PCENR.modify([] (RCC_Type::APB2PCENR_DEF & r) -> auto {
|
||||
r.B.IOPAEN = SET;
|
||||
r.B.TIM1EN = SET;
|
||||
r.B.AFIOEN = SET;
|
||||
return r.R;
|
||||
});
|
||||
AFIO.PCFR.modify([](AFIO_Type::PCFR_DEF & r) -> auto {
|
||||
r.B.TIM1RM = 1u;
|
||||
return r.R;
|
||||
});
|
||||
// PA7 is T1CH1N, PA8 is T1CH1, 10MHz Output alt func, push-pull
|
||||
GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
|
||||
r.B.CNF7 = 2u;
|
||||
r.B.MODE7 = 1u;
|
||||
return r.R;
|
||||
});
|
||||
GPIOA.CFGHR.modify([](GPIOA_Type::CFGHR_DEF & r) -> auto {
|
||||
r.B.CNF8 = 2u;
|
||||
r.B.MODE8 = 1u;
|
||||
return r.R;
|
||||
});
|
||||
// Reset TIM1 to init all regs
|
||||
RCC.APB2PRSTR.B.TIM1RST = SET;
|
||||
RCC.APB2PRSTR.B.TIM1RST = RESET;
|
||||
// CTLR1: default is up, events generated, edge align
|
||||
// SMCFGR: default clk input is CK_INT
|
||||
// Prescaler
|
||||
TIM1.PSC.R = 0u;
|
||||
// Auto Reload - sets period
|
||||
TIM1.ATRLR.R = MAXPWM - 1;
|
||||
|
||||
TIM1.CCER.modify([](TIM1_Type::CCER_DEF & r) -> auto {
|
||||
// Enable CH1N, CH1 output, positive pol
|
||||
r.B.CC1NE = SET;
|
||||
r.B.CC1E = SET;
|
||||
/*
|
||||
r.B.CC1NP = SET; // active Low
|
||||
r.B.CC1P = SET;
|
||||
*/
|
||||
return r.R;
|
||||
});
|
||||
// CH1 Mode is output, PWM1 (CC1S = 00, OC1M = 110)
|
||||
TIM1.CHCTLR1_Output.modify([](TIM1_Type::CHCTLR1_Output_DEF & r) -> auto {
|
||||
r.B.OC1M = 0x6u;
|
||||
return r.R;
|
||||
});
|
||||
// Enable TIM1 outputs
|
||||
TIM1.BDTR.modify([](TIM1_Type::BDTR_DEF & r) -> auto {
|
||||
r.B.MOE = SET;
|
||||
r.B.DTG = 48u; // Dead time 1us
|
||||
return r.R;
|
||||
});
|
||||
|
||||
// Reload immediately + Trigger DMA
|
||||
TIM1.SWEVGR.B.UG = SET;
|
||||
TIM1.DMAINTENR.B.UDE = SET;
|
||||
// Enable TIM1
|
||||
TIM1.CTLR1.B.CEN = SET;
|
||||
}
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
static inline void dma1ch5_init (void * ptr) noexcept {
|
||||
// Enable DMA
|
||||
RCC.AHBPCENR.modify([](RCC_Type::AHBPCENR_DEF & r) -> auto {
|
||||
r.B.SRAMEN = SET;
|
||||
r.B.DMA1EN = SET;
|
||||
return r.R;
|
||||
});
|
||||
// DMA5 can be configured to attach to T1UP
|
||||
// The system can only DMA out at ~2.2MSPS. 2MHz is stable.
|
||||
DMA1.CNTR5 .R = FULL_LEN;
|
||||
DMA1.MADDR5.R = reinterpret_cast<size_t>(ptr);
|
||||
DMA1.PADDR5.R = reinterpret_cast<size_t>(& TIM1.CH1CVR);
|
||||
NVIC.EnableIRQ (DMA1_Channel5_IRQn);
|
||||
DMA1.CFGR5.modify([](DMA1_Type::CFGR5_DEF & r) -> auto {
|
||||
r.B.DIR = SET; // MEM2PERIPHERAL
|
||||
r.B.PL = 3u; // Highest priority.
|
||||
r.B.PSIZE = 1u; // 16-bit peripheral
|
||||
r.B.MSIZE = 1u; // 16-bit memory
|
||||
r.B.MINC = SET; // Increase memory.
|
||||
r.B.CIRC = SET; // Circular mode.
|
||||
r.B.HTIE = SET; // Half-trigger
|
||||
r.B.TCIE = SET; // Whole-trigger
|
||||
// Enable DMA1 ch5
|
||||
r.B.EN = SET;
|
||||
return r.R;
|
||||
});
|
||||
}
|
||||
|
||||
PcmDma::PcmDma() noexcept : pL(buffer), pH(buffer + HALF_LEN), src(nullptr) {
|
||||
pInstance = this;
|
||||
tim1pwm_init ();
|
||||
dma1ch5_init (buffer);
|
||||
}
|
359
V203/midi/ch32v203/startup.cpp
Normal file
359
V203/midi/ch32v203/startup.cpp
Normal file
|
@ -0,0 +1,359 @@
|
|||
#include "CH32V20xxx.h"
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
extern "C" {
|
||||
extern void handle_reset () __attribute__((naked,nothrow,used));
|
||||
extern void user_prog () __attribute__((used));
|
||||
extern int main () __attribute__((used));
|
||||
extern void SystemInit() __attribute__((used));
|
||||
// This is required to allow pure virtual functions to be defined.
|
||||
extern void __cxa_pure_virtual() { while (1); }
|
||||
|
||||
// These magic symbols are provided by the linker.
|
||||
extern uint32_t _sbss;
|
||||
extern uint32_t _ebss;
|
||||
extern uint32_t _data_lma;
|
||||
extern uint32_t _data_vma;
|
||||
extern uint32_t _edata;
|
||||
|
||||
extern void (*__preinit_array_start[]) (void) __attribute__((weak));
|
||||
extern void (*__preinit_array_end[]) (void) __attribute__((weak));
|
||||
extern void (*__init_array_start[]) (void) __attribute__((weak));
|
||||
extern void (*__init_array_end[]) (void) __attribute__((weak));
|
||||
|
||||
static void __init_array () {
|
||||
uint32_t * dst, * end;
|
||||
|
||||
/* Zero fill the bss section */
|
||||
dst = &_sbss;
|
||||
end = &_ebss;
|
||||
while (dst < end) * dst++ = 0U;
|
||||
/* Copy data section from flash to RAM */
|
||||
uint32_t * src;
|
||||
src = &_data_lma;
|
||||
dst = &_data_vma;
|
||||
end = &_edata;
|
||||
if (src != dst) {
|
||||
while (dst < end) * dst++ = * src++;
|
||||
}
|
||||
|
||||
size_t count;
|
||||
/* Pro Cortex-Mx bylo toto zbytečné, lze předpokládat, že je to tak i zde.
|
||||
count = __preinit_array_end - __preinit_array_start;
|
||||
for (unsigned i = 0; i < count; i++) __preinit_array_start[i]();
|
||||
*/
|
||||
count = __init_array_end - __init_array_start;
|
||||
for (unsigned i = 0; i < count; i++) __init_array_start[i]();
|
||||
}
|
||||
// If you don't override a specific handler, it will just spin forever.
|
||||
void DefaultIRQHandler( void ) {
|
||||
// Infinite Loop
|
||||
for (;;);
|
||||
}
|
||||
void NMI_RCC_CSS_IRQHandler( void ) {
|
||||
RCC.INTR.B.CSSC = RESET; // clear the clock security int flag
|
||||
}
|
||||
#define ALIAS(f) __attribute__((nothrow,weak,alias(#f),used))
|
||||
void Ecall_M_Mode_Handler( void ) ALIAS(DefaultIRQHandler);
|
||||
void Ecall_U_Mode_Handler( void ) ALIAS(DefaultIRQHandler);
|
||||
void Break_Point_Handler( void ) ALIAS(DefaultIRQHandler);
|
||||
|
||||
void NMI_Handler( void ) ALIAS(NMI_RCC_CSS_IRQHandler);
|
||||
void HardFault_Handler( void ) ALIAS(DefaultIRQHandler);
|
||||
void SysTick_Handler( void ) ALIAS(DefaultIRQHandler);
|
||||
void SW_Handler( void ) ALIAS(DefaultIRQHandler);
|
||||
|
||||
void WWDG_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void PVD_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TAMPER_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void RTC_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void FLASH_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void RCC_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void EXTI0_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void EXTI1_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void EXTI2_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void EXTI3_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void EXTI4_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel1_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel2_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel3_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel4_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel5_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel6_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel7_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel8_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void ADC1_2_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void USB_HP_CAN1_TX_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void USB_LP_CAN1_RX0_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void CAN1_RX1_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void CAN1_SCE_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void EXTI9_5_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM1_BRK_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM1_UP_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM1_TRG_COM_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM1_CC_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM2_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM3_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM4_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void I2C1_EV_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void I2C1_ER_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void I2C2_EV_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void I2C2_ER_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void SPI1_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void SPI2_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void USART1_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void USART2_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void USART3_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void EXTI15_10_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void RTCAlarm_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void USBWakeUp_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM8_BRK_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM8_UP__IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM8_TRG_COM_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM8_CC_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM5_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void SPI3_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void UART4_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void UART5_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void ETH_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void ETH_WKUP_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void OTG_FS_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void USBHDWakeUp_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void USBHD_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void UART6_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void UART7_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void UART8_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM9_BRK_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM9_UP__IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM9_TRG_COM_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM9_CC_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM10_BRK_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM10_UP__IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM10_TRG_COM_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
void TIM10_CC_IRQHandler (void) ALIAS(DefaultIRQHandler);
|
||||
|
||||
void ETHWakeUp_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void OSC32KCal_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void OSCWakeUp_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
|
||||
|
||||
void Init() __attribute__((used,section(".init")));
|
||||
/* Tohle je patrně nedomyšlené, sekce .vector není definována.
|
||||
void InterruptVector() __attribute__((nothrow,naked,section(".vector"),weak,alias("InterruptVectorDefault")));
|
||||
void InterruptVectorDefault() __attribute__((nothrow,naked,section(".vector")));
|
||||
*/
|
||||
typedef void (*pHandler) (void);
|
||||
extern const pHandler InterruptVector [] __attribute__((section(".text.vector"),aligned(8)));
|
||||
};
|
||||
const pHandler InterruptVector [] = {
|
||||
Init,
|
||||
0,
|
||||
NMI_Handler, /* NMI */
|
||||
HardFault_Handler, /* Hard Fault */
|
||||
0,
|
||||
Ecall_M_Mode_Handler, /* Ecall M Mode */
|
||||
0,0,
|
||||
Ecall_U_Mode_Handler, /* Ecall U Mode */
|
||||
Break_Point_Handler, /* Break Point */
|
||||
0,0,
|
||||
SysTick_Handler, /* SysTick Handler */
|
||||
0,
|
||||
SW_Handler, /* SW Handler */
|
||||
0,
|
||||
/* External Interrupts */
|
||||
WWDG_IRQHandler, /* Window Watchdog */
|
||||
PVD_IRQHandler, /* PVD through EXTI Line detect */
|
||||
TAMPER_IRQHandler, /* TAMPER */
|
||||
RTC_IRQHandler, /* RTC */
|
||||
FLASH_IRQHandler, /* Flash */
|
||||
RCC_IRQHandler, /* RCC */
|
||||
EXTI0_IRQHandler, /* EXTI Line 0 */
|
||||
EXTI1_IRQHandler, /* EXTI Line 1 */
|
||||
EXTI2_IRQHandler, /* EXTI Line 2 */
|
||||
EXTI3_IRQHandler, /* EXTI Line 3 */
|
||||
EXTI4_IRQHandler, /* EXTI Line 4 */
|
||||
DMA1_Channel1_IRQHandler, /* DMA1 Channel 1 */
|
||||
DMA1_Channel2_IRQHandler, /* DMA1 Channel 2 */
|
||||
DMA1_Channel3_IRQHandler, /* DMA1 Channel 3 */
|
||||
DMA1_Channel4_IRQHandler, /* DMA1 Channel 4 */
|
||||
DMA1_Channel5_IRQHandler, /* DMA1 Channel 5 */
|
||||
DMA1_Channel6_IRQHandler, /* DMA1 Channel 6 */
|
||||
DMA1_Channel7_IRQHandler, /* DMA1 Channel 7 */
|
||||
ADC1_2_IRQHandler, /* ADC1_2 */
|
||||
|
||||
USB_HP_CAN1_TX_IRQHandler, /* USB HP and CAN1 TX */
|
||||
USB_LP_CAN1_RX0_IRQHandler, /* USB LP and CAN1RX0 */
|
||||
CAN1_RX1_IRQHandler, /* CAN1 RX1 */
|
||||
CAN1_SCE_IRQHandler, /* CAN1 SCE */
|
||||
|
||||
EXTI9_5_IRQHandler, /* EXTI Line 9..5 */
|
||||
TIM1_BRK_IRQHandler, /* TIM1 Break */
|
||||
TIM1_UP_IRQHandler, /* TIM1 Update */
|
||||
TIM1_TRG_COM_IRQHandler, /* TIM1 Trigger and Commutation */
|
||||
TIM1_CC_IRQHandler, /* TIM1 Capture Compare */
|
||||
TIM2_IRQHandler, /* TIM2 */
|
||||
TIM3_IRQHandler, /* TIM3 */
|
||||
TIM4_IRQHandler, /* TIM4 */
|
||||
I2C1_EV_IRQHandler, /* I2C1 Event */
|
||||
I2C1_ER_IRQHandler, /* I2C1 Error */
|
||||
I2C2_EV_IRQHandler, /* I2C2 Event */
|
||||
I2C2_ER_IRQHandler, /* I2C2 Error */
|
||||
SPI1_IRQHandler, /* SPI1 */
|
||||
SPI2_IRQHandler, /* SPI2 */
|
||||
USART1_IRQHandler, /* USART1 */
|
||||
USART2_IRQHandler, /* USART2 */
|
||||
USART3_IRQHandler, /* USART3 */
|
||||
EXTI15_10_IRQHandler, /* EXTI Line 15..10 */
|
||||
RTCAlarm_IRQHandler, /* RTC Alarm through EXTI Line */
|
||||
|
||||
USBWakeUp_IRQHandler, /* USB Wake up from suspend */
|
||||
USBHD_IRQHandler, /* USBHD Break */
|
||||
|
||||
USBHDWakeUp_IRQHandler, /* USBHD Wake up from suspend */
|
||||
ETH_IRQHandler, /* ETH global */
|
||||
ETHWakeUp_IRQHandler, /* ETH Wake up */
|
||||
0, /* BLE BB */
|
||||
0, /* BLE LLE */
|
||||
TIM5_IRQHandler, /* TIM5 */
|
||||
UART4_IRQHandler, /* UART4 */
|
||||
DMA1_Channel8_IRQHandler, /* DMA1 Channel8 */
|
||||
OSC32KCal_IRQHandler, /* OSC32KCal */
|
||||
OSCWakeUp_IRQHandler, /* OSC Wake Up */
|
||||
|
||||
};
|
||||
#if 0
|
||||
void InterruptVectorDefault() noexcept {
|
||||
asm volatile( R"---(
|
||||
.align 1
|
||||
.option norvc;
|
||||
.word Init
|
||||
|
||||
.word 0
|
||||
.word NMI_Handler /* NMI */
|
||||
.word HardFault_Handler /* Hard Fault */
|
||||
.word 0
|
||||
|
||||
.word Ecall_M_Mode_Handler /* Ecall M Mode */
|
||||
.word 0
|
||||
.word 0
|
||||
.word Ecall_U_Mode_Handler /* Ecall U Mode */
|
||||
.word Break_Point_Handler /* Break Point */
|
||||
.word 0
|
||||
.word 0
|
||||
|
||||
|
||||
.word SysTick_Handler /* SysTick Handler */
|
||||
.word 0
|
||||
.word SW_Handler /* SW Handler */
|
||||
.word 0
|
||||
/* External Interrupts */
|
||||
.word WWDG_IRQHandler /* Window Watchdog */
|
||||
.word PVD_IRQHandler /* PVD through EXTI Line detect */
|
||||
.word TAMPER_IRQHandler /* TAMPER */
|
||||
.word RTC_IRQHandler /* RTC */
|
||||
.word FLASH_IRQHandler /* Flash */
|
||||
.word RCC_IRQHandler /* RCC */
|
||||
.word EXTI0_IRQHandler /* EXTI Line 0 */
|
||||
.word EXTI1_IRQHandler /* EXTI Line 1 */
|
||||
.word EXTI2_IRQHandler /* EXTI Line 2 */
|
||||
.word EXTI3_IRQHandler /* EXTI Line 3 */
|
||||
.word EXTI4_IRQHandler /* EXTI Line 4 */
|
||||
.word DMA1_Channel1_IRQHandler /* DMA1 Channel 1 */
|
||||
.word DMA1_Channel2_IRQHandler /* DMA1 Channel 2 */
|
||||
.word DMA1_Channel3_IRQHandler /* DMA1 Channel 3 */
|
||||
.word DMA1_Channel4_IRQHandler /* DMA1 Channel 4 */
|
||||
.word DMA1_Channel5_IRQHandler /* DMA1 Channel 5 */
|
||||
.word DMA1_Channel6_IRQHandler /* DMA1 Channel 6 */
|
||||
.word DMA1_Channel7_IRQHandler /* DMA1 Channel 7 */
|
||||
.word ADC1_2_IRQHandler /* ADC1_2 */
|
||||
|
||||
.word USB_HP_CAN1_TX_IRQHandler /* USB HP and CAN1 TX */
|
||||
.word USB_LP_CAN1_RX0_IRQHandler /* USB LP and CAN1RX0 */
|
||||
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */
|
||||
.word CAN1_SCE_IRQHandler /* CAN1 SCE */
|
||||
|
||||
.word EXTI9_5_IRQHandler /* EXTI Line 9..5 */
|
||||
.word TIM1_BRK_IRQHandler /* TIM1 Break */
|
||||
.word TIM1_UP_IRQHandler /* TIM1 Update */
|
||||
.word TIM1_TRG_COM_IRQHandler /* TIM1 Trigger and Commutation */
|
||||
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
|
||||
.word TIM2_IRQHandler /* TIM2 */
|
||||
.word TIM3_IRQHandler /* TIM3 */
|
||||
.word TIM4_IRQHandler /* TIM4 */
|
||||
.word I2C1_EV_IRQHandler /* I2C1 Event */
|
||||
.word I2C1_ER_IRQHandler /* I2C1 Error */
|
||||
.word I2C2_EV_IRQHandler /* I2C2 Event */
|
||||
.word I2C2_ER_IRQHandler /* I2C2 Error */
|
||||
.word SPI1_IRQHandler /* SPI1 */
|
||||
.word SPI2_IRQHandler /* SPI2 */
|
||||
.word USART1_IRQHandler /* USART1 */
|
||||
.word USART2_IRQHandler /* USART2 */
|
||||
.word USART3_IRQHandler /* USART3 */
|
||||
.word EXTI15_10_IRQHandler /* EXTI Line 15..10 */
|
||||
.word RTCAlarm_IRQHandler /* RTC Alarm through EXTI Line */
|
||||
|
||||
.word USBWakeUp_IRQHandler /* USB Wake up from suspend */
|
||||
.word USBHD_IRQHandler /* USBHD Break */
|
||||
|
||||
.word USBHDWakeUp_IRQHandler /* USBHD Wake up from suspend */
|
||||
.word ETH_IRQHandler /* ETH global */
|
||||
.word ETHWakeUp_IRQHandler /* ETH Wake up */
|
||||
.word 0 /* BLE BB */
|
||||
.word 0 /* BLE LLE */
|
||||
.word TIM5_IRQHandler /* TIM5 */
|
||||
.word UART4_IRQHandler /* UART4 */
|
||||
.word DMA1_Channel8_IRQHandler /* DMA1 Channel8 */
|
||||
.word OSC32KCal_IRQHandler /* OSC32KCal */
|
||||
.word OSCWakeUp_IRQHandler /* OSC Wake Up */
|
||||
|
||||
)---");
|
||||
}
|
||||
#endif
|
||||
void Init() {
|
||||
asm volatile( R"---(
|
||||
.align 1
|
||||
_start:
|
||||
j handle_reset
|
||||
.rept 12
|
||||
.word 0x00000013
|
||||
.endr
|
||||
|
||||
.word 0x00100073
|
||||
)---");
|
||||
}
|
||||
void handle_reset() noexcept {
|
||||
asm volatile(R"---(
|
||||
.option push
|
||||
.option norelax
|
||||
la gp, __global_pointer$
|
||||
.option pop
|
||||
la sp, _eusrstack
|
||||
)---"
|
||||
#if __GNUC__ > 10
|
||||
".option arch, +zicsr\n"
|
||||
#endif
|
||||
// Setup the interrupt vector, processor status and INTSYSCR.
|
||||
R"---(
|
||||
li t0, 0x1f
|
||||
csrw 0xbc0, t0
|
||||
|
||||
/* Enabled nested and hardware stack */
|
||||
li t0, 0x88
|
||||
csrs mstatus, t0
|
||||
|
||||
la t0, InterruptVector
|
||||
ori t0, t0, 3
|
||||
csrw mtvec, t0
|
||||
|
||||
/* Takhle RISC-V přejde do uživatelského programu. */
|
||||
csrw mepc, %[user]
|
||||
mret
|
||||
)---"
|
||||
: : [user]"r"(user_prog)/*, "InterruptVector" (InterruptVector)*/
|
||||
: "t0", "memory" );
|
||||
}
|
||||
void user_prog () {
|
||||
SystemInit ();
|
||||
__init_array();
|
||||
main();
|
||||
for (;;);
|
||||
}
|
31
V203/midi/ch32v203/system.cpp
Normal file
31
V203/midi/ch32v203/system.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "CH32V20xxx.h"
|
||||
extern "C" void SystemInit ();
|
||||
enum CLKSRC : uint32_t {
|
||||
CLK_HSI = 0u,
|
||||
CLK_HSE,
|
||||
CLK_PLL,
|
||||
};
|
||||
// HSE i HSI mají frekvenci 8 MHz
|
||||
void SystemInit(void) {
|
||||
/// TODO
|
||||
EXTEND.EXTEND_CTR.B.PLL_HSI_PRE = SET; // HSI used for PLL, not divided
|
||||
RCC.CFGR0.modify([](RCC_Type::CFGR0_DEF & r) -> uint32_t {
|
||||
r.B.PLLMUL = 0xFu; // 8x18 = 144
|
||||
r.B.PPRE1 = 4u; // 100: HCLK divided by 2 (PB1)
|
||||
return r.R; // HB, PB2 not divided
|
||||
});
|
||||
RCC.CTLR.modify([](RCC_Type::CTLR_DEF & r) -> uint32_t {
|
||||
r.B.HSITRIM = 0x10u;
|
||||
r.B.HSION = SET;
|
||||
//r.B.HSEBYP = SET;
|
||||
r.B.CSSON = SET; // Enable clock security system
|
||||
r.B.PLLON = SET;
|
||||
return r.R;
|
||||
});
|
||||
|
||||
RCC.INTR.R = 0x009F0000u; // clear interrupts
|
||||
while (RCC.CTLR.B.PLLRDY == RESET);
|
||||
// USE PLL
|
||||
RCC.CFGR0.B.SW = CLK_PLL ;
|
||||
while (RCC.CFGR0.B.SWS != CLK_PLL);
|
||||
}
|
84
V203/midi/ch32v203/system.h
Normal file
84
V203/midi/ch32v203/system.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
#ifndef SYSTEM_H
|
||||
#define SYSTEM_H
|
||||
#include "CH32V20xxx.h"
|
||||
struct NVIC_Type {
|
||||
__I uint32_t ISR[8];
|
||||
__I uint32_t IPR[8];
|
||||
__IO uint32_t ITHRESDR;
|
||||
__IO uint32_t RESERVED;
|
||||
__IO uint32_t CFGR;
|
||||
__I uint32_t GISR;
|
||||
__IO uint8_t VTFIDR[4];
|
||||
uint8_t RESERVED0[12];
|
||||
__IO uint32_t VTFADDR[4];
|
||||
uint8_t RESERVED1[0x90];
|
||||
__O uint32_t IENR[8];
|
||||
uint8_t RESERVED2[0x60];
|
||||
__O uint32_t IRER[8];
|
||||
uint8_t RESERVED3[0x60];
|
||||
__O uint32_t IPSR[8];
|
||||
uint8_t RESERVED4[0x60];
|
||||
__O uint32_t IPRR[8];
|
||||
uint8_t RESERVED5[0x60];
|
||||
__IO uint32_t IACTR[8];
|
||||
uint8_t RESERVED6[0xE0];
|
||||
__IO uint8_t IPRIOR[256];
|
||||
uint8_t RESERVED7[0x810];
|
||||
__IO uint32_t SCTLR;
|
||||
void EnableIRQ (IRQn IRQ) {
|
||||
IENR [((uint32_t)(IRQ) >> 5)] = (1 << ((uint32_t)(IRQ) & 0x1F));
|
||||
}
|
||||
void DisableIRQ (IRQn IRQ) {
|
||||
IRER [((uint32_t)(IRQ) >> 5)] = (1 << ((uint32_t)(IRQ) & 0x1F));
|
||||
}
|
||||
void SetPriority(IRQn IRQ, uint8_t priority) {
|
||||
IPRIOR[(uint32_t)(IRQ)] = priority;
|
||||
}
|
||||
|
||||
};
|
||||
static NVIC_Type & NVIC = * reinterpret_cast<NVIC_Type * const> (0xE000E000);
|
||||
struct SysTick_Type {
|
||||
union CTLR_DEF {
|
||||
struct {
|
||||
__IO ONE_BIT STE : 1; //!<[00] System counter enable
|
||||
__IO ONE_BIT STIE : 1; //!<[01] System counter interrupt enable
|
||||
__IO ONE_BIT STCLK : 1; //!<[02] System selects the clock source
|
||||
__IO ONE_BIT STRE : 1; //!<[03] System reload register
|
||||
__IO ONE_BIT MODE : 1; //!<[04] System Mode
|
||||
__IO ONE_BIT INIT : 1; //!<[05] System Initialization update
|
||||
uint32_t UNUSED0 : 25; //!<[06]
|
||||
__IO ONE_BIT SWIE : 1; //!<[31] System software triggered interrupts enable
|
||||
} B;
|
||||
__IO uint32_t R;
|
||||
template<typename F> void modify (F f) volatile {
|
||||
CTLR_DEF r; r.R = R;
|
||||
R = f (r);
|
||||
}
|
||||
};
|
||||
|
||||
__IO CTLR_DEF CTLR ; //!< [1000](04)[0x00000000]
|
||||
__IO uint32_t SR ; //!< [1004](04)[0x00000000]
|
||||
__IO uint32_t CNTL ; //!< [1008](04)[0x00000000]
|
||||
__IO uint32_t CNTH ; //!< [100c](04)[0x00000000]
|
||||
__IO uint32_t CMPLR ; //!< [1010](04)[0x00000000]
|
||||
__IO uint32_t CMPHR ; //!< [1014](04)[0x00000000]
|
||||
|
||||
void Config (const uint32_t ticks) {
|
||||
CNTL = 0u;
|
||||
CNTH = 0u;
|
||||
CMPLR = ticks - 1u;
|
||||
CMPHR = 0u;
|
||||
CTLR.modify ([] (CTLR_DEF & r) -> auto { // TODO ???
|
||||
r.B.STE = SET;
|
||||
r.B.STIE = SET;
|
||||
r.B.STCLK = SET;
|
||||
r.B.STRE = SET;
|
||||
return r.R;
|
||||
});
|
||||
NVIC.EnableIRQ (SysTicK_IRQn);
|
||||
}
|
||||
};
|
||||
static SysTick_Type & SysTick = * reinterpret_cast<SysTick_Type * const> (0xE000F000);
|
||||
static constexpr unsigned SYSTEM_CORE_CLOCK = 144'000'000u;
|
||||
|
||||
#endif // SYSTEM_H
|
74
V203/midi/common/baselayer.h
Normal file
74
V203/midi/common/baselayer.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef BASELAYER_H
|
||||
#define BASELAYER_H
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __arm__
|
||||
#define debug(...)
|
||||
#else // ARCH_CM0
|
||||
#ifdef DEBUG
|
||||
#define debug printf
|
||||
#else // DEBUG
|
||||
#define debug(...)
|
||||
#endif // DEBUG
|
||||
#endif // ARCH_CM0
|
||||
/** @brief Bázová třída pro stack trochu obecnějšího komunikačního protokolu.
|
||||
*
|
||||
* @class BaseLayer
|
||||
* @brief Od této třídy budeme dále odvozovat ostatní.
|
||||
*
|
||||
*/
|
||||
class BaseLayer {
|
||||
public:
|
||||
/** Konstruktor
|
||||
*/
|
||||
explicit constexpr BaseLayer () noexcept : pUp(nullptr), pDown(nullptr) {};
|
||||
/** Virtuální metoda, přesouvající data směrem nahoru, pokud s nimi nechceme dělat něco jiného.
|
||||
@param data ukazatel na pole dat
|
||||
@param len delka dat v bytech
|
||||
@return počet přenesených bytů
|
||||
*/
|
||||
virtual uint32_t Up (const char * data, const uint32_t len) {
|
||||
if (pUp) return pUp->Up (data, len);
|
||||
return 0;
|
||||
};
|
||||
/** Virtuální metoda, přesouvající data směrem dolů, pokud s nimi nechceme dělat něco jiného.
|
||||
@param data ukazatel na pole dat
|
||||
@param len delka dat v bytech
|
||||
@return počet přenesených bytů
|
||||
*/
|
||||
virtual uint32_t Down (const char * data, const uint32_t len) {
|
||||
if (pDown) return pDown->Down (data, len);
|
||||
return len;
|
||||
};
|
||||
/** @brief Zřetězení stacku.
|
||||
* Tohle je vlastně to nejdůležitější. V čistém C by se musely
|
||||
* nastavovat ukazatele na callback funkce, tady je to čitší - pro uživatele neviditelné,
|
||||
* ale je to to samé.
|
||||
@param bl Třída, ležící pod, spodní
|
||||
@return Odkaz na tuto třídu (aby se to dalo řetězit)
|
||||
*/
|
||||
virtual BaseLayer & operator += (BaseLayer & bl) {
|
||||
bl.setUp (this); // ta spodní bude volat při Up tuto třídu
|
||||
setDown (& bl); // a tato třída bude volat při Down tu spodní
|
||||
return * this;
|
||||
};
|
||||
/** Getter pro pDown
|
||||
@return pDown
|
||||
*/
|
||||
BaseLayer * getDown (void) const { return pDown; };
|
||||
protected:
|
||||
/** Lokální setter pro pUp
|
||||
@param p Co budeme do pUp dávat
|
||||
*/
|
||||
void setUp (BaseLayer * p) { pUp = p; };
|
||||
/** Lokální setter pro pDown
|
||||
@param p Co budeme do pDown dávat
|
||||
*/
|
||||
void setDown (BaseLayer * p) { pDown = p; };
|
||||
private:
|
||||
// Ono to je vlastně oboustranně vázaný spojový seznam.
|
||||
BaseLayer * pUp; //!< Ukazatel na třídu, která bude dále volat Up
|
||||
BaseLayer * pDown; //!< Ukazatel na třídu, která bude dále volat Down
|
||||
};
|
||||
|
||||
#endif // BASELAYER_H
|
73
V203/midi/common/fifo.h
Normal file
73
V203/midi/common/fifo.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#ifndef FIFO_H
|
||||
#define FIFO_H
|
||||
/** Typ dbus_w_t je podobně definován jako sig_atomic_t v hlavičce signal.h.
|
||||
* Je to prostě největší typ, ke kterému je "atomický" přístup. V GCC je definováno
|
||||
* __SIG_ATOMIC_TYPE__, šlo by použít, ale je znaménkový.
|
||||
* */
|
||||
#ifdef __SIG_ATOMIC_TYPE__
|
||||
typedef unsigned __SIG_ATOMIC_TYPE__ dbus_w_t;
|
||||
#else
|
||||
typedef unsigned int dbus_w_t; // pro AVR by to měl být uint8_t (šířka datové sběrnice)
|
||||
#endif //__SIG_ATOMIC_TYPE__
|
||||
/// Tahle podivná rekurzívní formule je použita pro validaci délky bufferu.
|
||||
static constexpr bool isValidM (const int N, const dbus_w_t M) {
|
||||
// constexpr má raději rekurzi než cyklus (c++11)
|
||||
return (N > 12) ? false : (((1u << N) == M) ? true : isValidM (N+1, M));
|
||||
}
|
||||
/** @class FIFO
|
||||
* @brief Jednoduchá fronta (kruhový buffer).
|
||||
*
|
||||
* V tomto přikladu je vidět, že synchronizace mezi přerušením a hlavní smyčkou programu
|
||||
* může být tak jednoduchá, že je v podstatě neviditelná. Využívá se toho, že pokud
|
||||
* do kruhového buferu zapisujeme jen z jednoho bodu a čteme také jen z jednoho bodu
|
||||
* (vlákna), zápis probíhá nezávisle pomocí indexu m_head a čtení pomocí m_tail.
|
||||
* Délka dat je dána rozdílem tt. indexů, pokud v průběhu výpočtu délky dojde k přerušení,
|
||||
* v zásadě se nic špatného neděje, maximálně je délka určena špatně a to tak,
|
||||
* že zápis nebo čtení je nutné opakovat. Důležité je, že po výpočtu se nová délka zapíše
|
||||
* do paměti "atomicky". Takže např. pro 8-bit procesor musí být indexy jen 8-bitové.
|
||||
* To není moc velké omezení, protože tyto procesory obvykle mají dost malou RAM, takže
|
||||
* velikost bufferu stejně nebývá být větší než nějakých 64 položek.
|
||||
* Opět nijak nevadí že přijde přerušení při zápisu nebo čtení položky - to se provádí
|
||||
* dříve než změna indexu, zápis a čtení je vždy na jiném místě RAM. Celé je to uděláno
|
||||
* jako šablona, takže je možné řadit do fronty i složitější věci než je pouhý byte.
|
||||
* Druhým parametrem šablony je délka bufferu (aby to šlo konstruovat jako statický objekt),
|
||||
* musí to být mocnina dvou v rozsahu 8 až 4096, default je 64. Mocnina 2 je zvolena proto,
|
||||
* aby se místo zbytku po dělení mohl použít jen bitový and, což je rychlejší.
|
||||
* */
|
||||
template<typename T, const dbus_w_t M = 64> class FIFO {
|
||||
T m_data [M];
|
||||
volatile dbus_w_t m_head; //!< index pro zápis (hlava)
|
||||
volatile dbus_w_t m_tail; //!< index pro čtení (ocas)
|
||||
/// vrací skutečnou délku dostupných dat
|
||||
constexpr dbus_w_t lenght () const { return (M + m_head - m_tail) & (M - 1); };
|
||||
/// zvětší a saturuje index, takže se tento motá v kruhu @param n index
|
||||
void sat_inc (volatile dbus_w_t & n) const { n = (n + 1) & (M - 1); };
|
||||
public:
|
||||
/// Konstruktor
|
||||
explicit constexpr FIFO<T,M> () noexcept {
|
||||
// pro 8-bit architekturu může být byte jako index poměrně malý
|
||||
static_assert (1ul << (8 * sizeof(dbus_w_t) - 1) >= M, "atomic type too small");
|
||||
// a omezíme pro jistotu i delku buferu na nějakou rozumnou delku
|
||||
static_assert (isValidM (3, M), "M must be power of two in range <8,4096> or <8,128> for 8-bit data bus (AVR)");
|
||||
m_head = 0;
|
||||
m_tail = 0;
|
||||
}
|
||||
/// Čtení položky
|
||||
/// @return true, pokud se úspěšně provede
|
||||
const bool Read (T & c) {
|
||||
if (lenght() == 0) return false;
|
||||
c = m_data [m_tail];
|
||||
sat_inc (m_tail);
|
||||
return true;
|
||||
}
|
||||
/// Zápis položky
|
||||
/// @return true, pokud se úspěšně provede
|
||||
const bool Write (const T & c) {
|
||||
if (lenght() >= (M - 1)) return false;
|
||||
m_data [m_head] = c;
|
||||
sat_inc (m_head);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // FIFO_H
|
10
V203/midi/common/oneway.h
Normal file
10
V203/midi/common/oneway.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef ONEWAY_H
|
||||
#define ONEWAY_H
|
||||
#include <stdint.h>
|
||||
/* C++ interface (jako callback v C) */
|
||||
class OneWay {
|
||||
public:
|
||||
virtual unsigned Send (uint16_t * const ptr, const unsigned len) = 0;
|
||||
};
|
||||
|
||||
#endif // ONEWAY_H
|
29
V203/midi/common/pcmdma.h
Normal file
29
V203/midi/common/pcmdma.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef PCMDMA_H
|
||||
#define PCMDMA_H
|
||||
#include "oneway.h"
|
||||
#ifdef HAVE_CONFIG
|
||||
/* Umožní použít externí parametry. */
|
||||
#include "pwmconfig.h"
|
||||
#else
|
||||
static constexpr unsigned HALF_LEN = 0x80u;
|
||||
static constexpr unsigned MAXPWM = 6000u;
|
||||
#endif
|
||||
static constexpr unsigned FULL_LEN = 2u * HALF_LEN;
|
||||
/* Používá TIM1, PWM kanál 1, DMA1 kanál 5, přerušení DMA1_Channel5_IRQHandler */
|
||||
class PcmDma {
|
||||
uint16_t * const pL;
|
||||
uint16_t * const pH;
|
||||
uint16_t buffer [FULL_LEN];
|
||||
OneWay * src;
|
||||
public:
|
||||
explicit PcmDma () noexcept;
|
||||
uint16_t * getBuff () { return buffer; };
|
||||
void attach (OneWay & s) { src = & s; }
|
||||
void send (const bool b) {
|
||||
if (!src) return;
|
||||
if (b) src->Send (pH, HALF_LEN);
|
||||
else src->Send (pL, HALF_LEN);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // PCMDMA_H
|
11
V203/midi/linux/clang.mk
Normal file
11
V203/midi/linux/clang.mk
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Use clang / binutils toolchain
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
LD = clang++
|
||||
SIZE = size
|
||||
DUMP = objdump
|
||||
COPY = objcopy
|
||||
CFLAGS+= -Oz
|
||||
|
||||
LFLAGS+= -Wl,--Map=$(@:%.elf=%.map),--gc-sections
|
||||
LDLIBS+= -lc -lpthread -lasound
|
12
V203/midi/linux/gcc.mk
Normal file
12
V203/midi/linux/gcc.mk
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Use gcc / binutils toolchain
|
||||
PREFIX =
|
||||
CC = $(PREFIX)gcc
|
||||
CXX = $(PREFIX)g++
|
||||
LD = $(PREFIX)g++
|
||||
SIZE = $(PREFIX)size
|
||||
DUMP = $(PREFIX)objdump
|
||||
COPY = $(PREFIX)objcopy
|
||||
CFLAGS+= -Os
|
||||
|
||||
LFLAGS+= -Wl,--Map=$(@:%.elf=%.map),--gc-sections
|
||||
LDLIBS+= -lc -lpthread -lasound
|
37
V203/midi/linux/gpio.h
Normal file
37
V203/midi/linux/gpio.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef _GPIO_CLASS_H_
|
||||
#define _GPIO_CLASS_H_
|
||||
#include <stdint.h>
|
||||
enum GPIO_MODE : uint32_t {
|
||||
GPIO_Speed_In = 0u,
|
||||
GPIO_Speed_10MHz = 1u,
|
||||
GPIO_Speed_2MHz = 2u,
|
||||
GPIO_Speed_50MHz = 3u,
|
||||
};
|
||||
enum GPIO_CNF : uint32_t {
|
||||
GPIO_AI_PPO = 0u,
|
||||
GPIO_FI_ODO = 1u << 2,
|
||||
GPIO_UPDI_MPPO = 2u << 2,
|
||||
GPIO_none_MPDO = 3u << 2,
|
||||
};
|
||||
enum GPIOPuPd_TypeDef {
|
||||
GPIO_PuPd_NOPULL = 0x00,
|
||||
GPIO_PuPd_UP = 0x01,
|
||||
GPIO_PuPd_DOWN = 0x02
|
||||
};
|
||||
|
||||
class GpioClass {
|
||||
//const uint32_t pin;
|
||||
public:
|
||||
explicit constexpr GpioClass (const uint32_t _pin, const uint32_t _mode = GPIO_AI_PPO) noexcept
|
||||
{
|
||||
}
|
||||
void operator<< (const bool b) const {
|
||||
}
|
||||
operator bool () const {
|
||||
return false;
|
||||
}
|
||||
void setPuPd (GPIOPuPd_TypeDef p) {
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _GPIO_CLASS_H_
|
79
V203/midi/linux/pcmdma.cpp
Normal file
79
V203/midi/linux/pcmdma.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "pcmdma.h"
|
||||
#include <pthread.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
struct Sample {
|
||||
short l;
|
||||
short r;
|
||||
}__attribute__((packed));
|
||||
|
||||
static constexpr unsigned BufLen = FULL_LEN;
|
||||
|
||||
static const char *device = "default";
|
||||
static snd_pcm_t *handle;
|
||||
|
||||
static int open_alsa_device (int channels, int srate) {
|
||||
int err;
|
||||
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
|
||||
printf("Playback open error: %s\n", snd_strerror(err));
|
||||
return 0;
|
||||
}
|
||||
if ((err = snd_pcm_set_params(handle,
|
||||
SND_PCM_FORMAT_S16_LE,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||
channels,
|
||||
srate,
|
||||
1,
|
||||
500000)) < 0) { /* 0.5sec */
|
||||
printf("Playback open error: %s\n", snd_strerror(err));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
int alsa_write (const void * buf, int len) {
|
||||
snd_pcm_sframes_t frames;
|
||||
int err = 0;
|
||||
frames = snd_pcm_writei(handle, buf, len);
|
||||
if (frames < 0)
|
||||
frames = snd_pcm_recover(handle, frames, 0);
|
||||
if (frames < 0) {
|
||||
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
|
||||
return 0;
|
||||
}
|
||||
if (frames > 0 && frames < (long)sizeof(buf))
|
||||
printf("Short write (expected %i, wrote %li)\n", len, frames);
|
||||
return len;
|
||||
}
|
||||
void sig_handler (int signum) {
|
||||
printf(" - Received signal %d\n", signum);
|
||||
::exit (0);
|
||||
}
|
||||
|
||||
static constexpr int CC = MAXPWM / 2;
|
||||
|
||||
static pthread_t rc;
|
||||
|
||||
void * WriteHandler (void * data) {
|
||||
printf ("Start thread\n");
|
||||
PcmDma * pA = (PcmDma *) data;
|
||||
Sample buf [BufLen];
|
||||
uint16_t * pbuf = pA->getBuff();
|
||||
for (;;) {
|
||||
pA->send (false);
|
||||
pA->send (true);
|
||||
//printf ("pass\n");
|
||||
for (unsigned i=0; i<BufLen; i++) {
|
||||
const short s = (pbuf [i] - CC) * 10;
|
||||
buf[i].l = s; buf[i].r = s;
|
||||
}
|
||||
alsa_write (buf, BufLen);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PcmDma::PcmDma() noexcept : pL(buffer), pH(buffer + HALF_LEN), src(nullptr) {
|
||||
signal (SIGINT, sig_handler);
|
||||
open_alsa_device(2, 24000);
|
||||
pthread_create (&rc, NULL, WriteHandler, this);
|
||||
}
|
20
V203/midi/main.cpp
Normal file
20
V203/midi/main.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include "midiplayer.h"
|
||||
/* I zde přetrvává problém s optimalizací LTO
|
||||
* v gcc (clang problém nemá). Možná jen někde
|
||||
* chybí volatile, možná je to BUG staršího g++.
|
||||
* Zase tak moc to nevadí.
|
||||
*
|
||||
* Player na tomto čipu má 12 generátorů, lepší
|
||||
* rozlišení PWM, tedy lepší zvuk. A vejde se
|
||||
* tam více melodií.
|
||||
*/
|
||||
static MidiPlayer player;
|
||||
static PcmDma pcm;
|
||||
|
||||
int main (void) {
|
||||
pcm.attach (player);
|
||||
for (;;) {
|
||||
player.pass();
|
||||
}
|
||||
return 0;
|
||||
}
|
BIN
V203/midi/mid/Bugatti.mid
Normal file
BIN
V203/midi/mid/Bugatti.mid
Normal file
Binary file not shown.
BIN
V203/midi/mid/Elise.mid
Normal file
BIN
V203/midi/mid/Elise.mid
Normal file
Binary file not shown.
BIN
V203/midi/mid/Straz.mid
Normal file
BIN
V203/midi/mid/Straz.mid
Normal file
Binary file not shown.
BIN
V203/midi/mid/The-Entertainer.mid
Normal file
BIN
V203/midi/mid/The-Entertainer.mid
Normal file
Binary file not shown.
99
V203/midi/midiplayer.cpp
Normal file
99
V203/midi/midiplayer.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include "midiplayer.h"
|
||||
/**
|
||||
* @file
|
||||
* @brief Jednoduchý přehrávač midi souborů.
|
||||
*
|
||||
* Kompletní midi obsahuje zvukové fonty, které jsou obrovské. Tohle je velice zjednodušené,
|
||||
* takže docela dobře přehrává skladby typu ragtime, orchestrální midi jsou skoro nepoužitelné.
|
||||
* Přesto se to pro jednoduché zvuky může hodit, protože je to poměrně nenáročné na systémové
|
||||
* prostředky. Může to fungovat dokonce i na 8-bitovém uP.
|
||||
* */
|
||||
|
||||
static constexpr unsigned AudioMidiDelay = 24;
|
||||
static constexpr int INPUT_BIT_RANGE = 16;
|
||||
static constexpr unsigned SIGMA_MASK = (1u << (INPUT_BIT_RANGE + 0)) - 1u;
|
||||
static constexpr unsigned SIGNED_OFFEST = (1u << (INPUT_BIT_RANGE - 1));
|
||||
// Předpokládá se na vstupu signed int o šířce INPUT_BIT_RANGE
|
||||
// přičemž 0 na vstupu odpovídá MAXPWM / 2 na výstupu. Vypadá to divně, ale funguje.
|
||||
static unsigned pwm_sd (const int input) {
|
||||
static unsigned sigma = 0; // podstatné je, že proměnná je statická
|
||||
const unsigned sample = (input + SIGNED_OFFEST) * MAXPWM;
|
||||
sigma &= SIGMA_MASK; // v podstatě se odečte hodnota PWM
|
||||
sigma += sample; // integrace prostým součtem
|
||||
return sigma >> INPUT_BIT_RANGE;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/// Konstruktor
|
||||
MidiPlayer::MidiPlayer() noexcept : OneWay(), led (LED_CFG), but (BUT_CFG), passcnt (0u) {
|
||||
index = 0;
|
||||
pause = 0;
|
||||
melody = scores[index++];
|
||||
running = false;
|
||||
but.setPuPd (GPIO_PuPd_UP);
|
||||
}
|
||||
/// Počítá další vzorek
|
||||
short MidiPlayer::nextSample (void) {
|
||||
if (pause) pause -= 1; // Časování tónu
|
||||
else ToneChange(); // Nový tón - MidiPlayer::ToneChange
|
||||
return genSample ();
|
||||
}
|
||||
void MidiPlayer::pass() {
|
||||
const bool b = passcnt & 0x100000;
|
||||
led << b;
|
||||
passcnt += 1u;
|
||||
}
|
||||
|
||||
unsigned MidiPlayer::Send (uint16_t * const ptr, const unsigned len) {
|
||||
const bool b = false; // but;
|
||||
if (!b and !running) running = true;
|
||||
|
||||
if (!running) {
|
||||
for (unsigned n=0; n<len; n++) ptr [n] = MAXPWM >> 1;
|
||||
return len;
|
||||
}
|
||||
|
||||
for (unsigned n=0; n<len; n++) {
|
||||
const short s = nextSample();
|
||||
ptr [n] = pwm_sd (s);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
void MidiPlayer::stop (void) {
|
||||
running = false; // na konci každé melodie stop
|
||||
melody = scores[index++];
|
||||
if (!melody) {
|
||||
index = 0;
|
||||
melody = scores[index++];
|
||||
running = false; // test bez tlačítka
|
||||
}
|
||||
}
|
||||
void MidiPlayer::ToneChange (void) {
|
||||
unsigned char midt;
|
||||
|
||||
for (;;) { // Pro všechny tóny před pauzou
|
||||
unsigned char cmd = * melody++;
|
||||
if (cmd & 0x80) { // event
|
||||
const unsigned geno = cmd & 0x0F;
|
||||
cmd >>= 4;
|
||||
switch (cmd) {
|
||||
case 0x8: // off
|
||||
gens[geno].setMidiOff();
|
||||
break;
|
||||
case 0x9: // on
|
||||
midt = * melody++;
|
||||
gens[geno].setMidiOn (midt);
|
||||
break;
|
||||
default:
|
||||
stop();
|
||||
return; // melodie končí eventem 0xf0
|
||||
}
|
||||
} else { // pause
|
||||
midt = * melody++;
|
||||
// Když to trochu uteče, zase se z toho nestřílí, tak to nechme být.
|
||||
pause = ((unsigned int) cmd << 8) + midt; // v ms
|
||||
pause *= AudioMidiDelay; // ale máme vzorkování cca 24 kHz
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
46
V203/midi/midiplayer.h
Normal file
46
V203/midi/midiplayer.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef DACPLAYER_H
|
||||
#define DACPLAYER_H
|
||||
#include "oneway.h"
|
||||
#include "gpio.h"
|
||||
#include "tone.h"
|
||||
#include "audio.h"
|
||||
#include "pcmdma.h"
|
||||
#include "pwmconfig.h"
|
||||
|
||||
/// Třída, která hraje čistě na pozadí.
|
||||
class MidiPlayer : public OneWay {
|
||||
// Veřejné metody
|
||||
public:
|
||||
/// Konstruktor
|
||||
explicit MidiPlayer () noexcept;
|
||||
unsigned Send (uint16_t * const ptr, const unsigned len) override;
|
||||
void stop ();
|
||||
void pass ();
|
||||
protected:
|
||||
// Chráněné metody
|
||||
/// Obsluha tónu
|
||||
void ToneChange (void);
|
||||
/// Obsluha vzorku
|
||||
short nextSample (void);
|
||||
/// Generuj vzorek pro všechny tóny @return Vzorek
|
||||
short genSample (void) {
|
||||
int res = 0;
|
||||
for (unsigned int i=0; i<maxGens; i++) res += gens[i].step();
|
||||
// Pro jistotu omezíme - předejdeme chrastění
|
||||
if (res > maxValue) res = maxValue;
|
||||
if (res < minValue) res = minValue;
|
||||
return (res);
|
||||
}
|
||||
private:
|
||||
Tone gens[maxGens]; /// Generátory tónů
|
||||
GpioClass led, but;
|
||||
unsigned passcnt;
|
||||
volatile bool running;
|
||||
unsigned char const * melody;
|
||||
unsigned index;
|
||||
volatile int pause;
|
||||
|
||||
};
|
||||
extern "C" const unsigned char * const scores[];
|
||||
|
||||
#endif // DACPLAYER_H
|
17
V203/midi/pwmconfig.h
Normal file
17
V203/midi/pwmconfig.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
static constexpr unsigned HALF_LEN = 0x80u;
|
||||
static constexpr unsigned MAXPWM = 6000u;
|
||||
|
||||
#if __riscv
|
||||
#define LED_CFG GPIOA,0
|
||||
#define BUT_CFG GPIOA,3,(GPIO_Speed_In | GPIO_UPDI_MPPO)
|
||||
#elif __linux__
|
||||
#define LED_CFG 0
|
||||
#define BUT_CFG 3
|
||||
#else
|
||||
#error "bad target"
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_H
|
76
V203/midi/ton/gen.cpp
Normal file
76
V203/midi/ton/gen.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "../audio.h"
|
||||
|
||||
static constexpr int maxTone = (1L<<23) - 1;
|
||||
|
||||
int limit (double tone) {
|
||||
int k = (int) round (tone);
|
||||
if (k > maxTone) k = 0;
|
||||
return k;
|
||||
}
|
||||
int normalize (double val, double scale) {
|
||||
return (int) round (val * scale);
|
||||
}
|
||||
int main (void) {
|
||||
double base, dint;
|
||||
int i,j;
|
||||
|
||||
base = 8.1757989156; // C5 v Hz (http://www.tonalsoft.com/pub/news/pitch-bend.aspx)
|
||||
base *= (double)(1UL << 24) / double (AudioSampleRate);
|
||||
dint = pow(2.0, 1.0 / 12.0);
|
||||
|
||||
FILE* out = fopen ("miditone.c","w");
|
||||
// Tabulka inkrementů pro midi tóny
|
||||
fprintf (out, "const unsigned int midiTones[] = {\n");
|
||||
for (i=0,j=0; i<127; i++) {
|
||||
fprintf (out, "%8d, ", limit (base));
|
||||
if (++j >= 12) {
|
||||
j = 0;
|
||||
fprintf (out, "\n");
|
||||
}
|
||||
base *= dint;
|
||||
}
|
||||
fprintf (out, "%8d };\n\n", limit (base));
|
||||
// Vzorky pro jednu periodu tónu včetně barvy
|
||||
double samples [256], max = 0.0, val;
|
||||
base = M_PI / 128.0;
|
||||
for (i=0; i<256; i++) {
|
||||
val = 0.0;
|
||||
val += 1.0 * sin (1.0 * base * (double) i);
|
||||
// Je dobré přidat nějaké harmonické, jinak je tón chudý
|
||||
val += 0.3 * sin (2.0 * base * (double) i);
|
||||
// 7. harmonická je nepříjemná, zkuste si to.
|
||||
// val += 0.5 * sin (7.0 * base * (double) i);
|
||||
if (val > +max) max = +val;
|
||||
if (val < -max) max = -val;
|
||||
samples [i] = val;
|
||||
}
|
||||
max = (double)(0x1FFF) / max; // normála do 14. bitů
|
||||
// mormalizace a výpis
|
||||
fprintf (out, "const short onePeriod[] = {\n");
|
||||
for (i=0,j=0; i<255; i++) {
|
||||
fprintf (out, "%6d, ", normalize (samples[i], max));
|
||||
if (++j >= 8) {
|
||||
j = 0;
|
||||
fprintf (out, "\n");
|
||||
}
|
||||
base *= dint;
|
||||
}
|
||||
fprintf (out, "%6d };\n\n", normalize (samples[i], max));
|
||||
|
||||
unsigned Attack = maxAmplt;
|
||||
fprintf (out, "const unsigned attackTable[] = {\n");
|
||||
for (i=0,j=0; i<127; i++) {
|
||||
fprintf (out, "0x%08X, ", Attack);
|
||||
if (++j >= 8) {
|
||||
j = 0;
|
||||
fprintf (out, "\n");
|
||||
}
|
||||
Attack -= Attack / 20;
|
||||
}
|
||||
fprintf (out, "0x%08X };\n\n", Attack);
|
||||
|
||||
|
||||
fclose (out);
|
||||
}
|
1000
V203/midi/ton/miditonesV1.6.c
Normal file
1000
V203/midi/ton/miditonesV1.6.c
Normal file
File diff suppressed because it is too large
Load diff
69
V203/midi/tone.cpp
Normal file
69
V203/midi/tone.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include "tone.h"
|
||||
/**
|
||||
* Přidán attack - zmizí rušivé lupání, prodlouží se obsluha tónu.
|
||||
* */
|
||||
|
||||
extern "C" const short onePeriod[];
|
||||
extern "C" const unsigned int midiTones[];
|
||||
extern "C" const unsigned int attackTable[];
|
||||
|
||||
static constexpr unsigned defFall = 16u;
|
||||
static constexpr unsigned maxAttack = 127u;
|
||||
|
||||
Tone::Tone() noexcept {
|
||||
ampl = 0; freq = 0; base = 0; atck = 0;
|
||||
fall = defFall;
|
||||
}
|
||||
|
||||
void Tone::setAmpl (unsigned int a) {
|
||||
ampl = a;
|
||||
}
|
||||
|
||||
void Tone::setFreq (unsigned int f) {
|
||||
freq = f;
|
||||
}
|
||||
|
||||
void Tone::setMidiOn (unsigned int m) {
|
||||
freq = midiTones [m & 0x7F];
|
||||
if (freq) atck = maxAttack;
|
||||
fall = 1;
|
||||
}
|
||||
|
||||
void Tone::setMidiOff (void) {
|
||||
fall = defFall;
|
||||
/*
|
||||
base = 0;
|
||||
freq = 0;
|
||||
*/
|
||||
}
|
||||
|
||||
void Tone::setFall (unsigned int f) {
|
||||
fall = f;
|
||||
}
|
||||
|
||||
int Tone::step (void) {
|
||||
unsigned int k,t;
|
||||
int y;
|
||||
// Spočteme index x pro přístup do tabulky
|
||||
const unsigned x = (base >> 16) & 0xFF;
|
||||
y = onePeriod [x]; // vzorek vezmeme z tabulky
|
||||
|
||||
// k je horní půlka amplitudy
|
||||
k = ampl >> 16;
|
||||
y *= k; // vzorek násobíme amplitudou (tedy tím vrškem)
|
||||
y >>= 12; // a vezmeme jen to, co potřebuje DAC
|
||||
k *= fall; // Konstanta fall určuje rychlost poklesu amplitudy,
|
||||
// čím více, tím je rychlejší. Pokud by bylo 1, pokles je 2^16 vzorků, což už je pomalé.
|
||||
base += freq; // časová základna pro další vzorek
|
||||
|
||||
if (atck) { // přidán attack = náběh amplitudy
|
||||
t = attackTable [atck]; // z tabulky
|
||||
if (t > ampl) ampl = t; // prevence lupání - nemí být skok amplitudy
|
||||
atck -= 1; // dočasovat k nule
|
||||
} else
|
||||
ampl -= k; // exponenciální pokles amplitudy
|
||||
// a je to
|
||||
|
||||
return y;
|
||||
}
|
||||
|
26
V203/midi/tone.h
Normal file
26
V203/midi/tone.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef TONE_H
|
||||
#define TONE_H
|
||||
|
||||
class Tone {
|
||||
public:
|
||||
explicit Tone () noexcept;
|
||||
void setMidiOn (unsigned int m);
|
||||
void setMidiOff (void);
|
||||
void setFreq (unsigned int f);
|
||||
void setAmpl (unsigned int a);
|
||||
void setFall (unsigned int f);
|
||||
int step (void);
|
||||
private:
|
||||
/// Amplituda tónu, interní proměnná
|
||||
unsigned int ampl;
|
||||
/// Exponenciální doběh - čím víc, tím rychlejší (0 = stálý)
|
||||
unsigned int fall;
|
||||
/// Frekvence (normalizovaná)
|
||||
unsigned int freq;
|
||||
/// Přetékající index do tabulky vzorků
|
||||
unsigned int base;
|
||||
/// Attack = index do tabulky attackTable
|
||||
unsigned int atck;
|
||||
};
|
||||
|
||||
#endif // TONE_H
|
Loading…
Reference in a new issue