add V203 midi

This commit is contained in:
Kizarm 2024-05-09 17:52:29 +02:00
parent 135eb6065d
commit a8fb67d610
32 changed files with 15025 additions and 4 deletions

9
.gitignore vendored
View file

@ -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
View 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
View 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

File diff suppressed because it is too large Load diff

View 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
View 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

View 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
View 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_

View 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);
}

View 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 (;;);
}

View 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);
}

View 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

View 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
View 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 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
View 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
View 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
View 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
View 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
View 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_

View 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
View 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 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

Binary file not shown.

BIN
V203/midi/mid/Elise.mid Normal file

Binary file not shown.

BIN
V203/midi/mid/Straz.mid Normal file

Binary file not shown.

Binary file not shown.

99
V203/midi/midiplayer.cpp Normal file
View 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
View 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
View 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
View 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);
}

File diff suppressed because it is too large Load diff

69
V203/midi/tone.cpp Normal file
View 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
View 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