add midi
This commit is contained in:
parent
f00b7884f7
commit
c3d1d2706a
30 changed files with 2781 additions and 0 deletions
65
V203F6P6/midi/Makefile
Normal file
65
V203F6P6/midi/Makefile
Normal file
|
@ -0,0 +1,65 @@
|
|||
# ch32v203
|
||||
TARGET?= ch32v203
|
||||
TOOL ?= gcc
|
||||
#TOOL ?= clang
|
||||
|
||||
PRJ = example
|
||||
|
||||
VPATH = . ./$(TARGET)
|
||||
BLD = ./build/
|
||||
DFLAGS = -d
|
||||
LFLAGS = -g
|
||||
LDLIBS = -L./$(TARGET)/usbd -lusbd
|
||||
BFLAGS = --strip-unneeded
|
||||
|
||||
CFLAGS = -MMD -Wall -ggdb -fno-exceptions -ffunction-sections -fdata-sections
|
||||
CFLAGS+= -I. -I./common -I./$(TARGET) -I./$(TARGET)/usbd
|
||||
DEL = rm -f
|
||||
|
||||
# zdrojaky
|
||||
OBJS = main.o hack.o pwmclass.o
|
||||
OBJS += tone.o midiplayer.o miditone.o
|
||||
OBJS += spiblocked.o norflash.o
|
||||
OBJS += intelhex.o linkprotocol.o usb_desc.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
|
||||
|
||||
# vycisti
|
||||
clean:
|
||||
$(DEL) $(BLD)* *.lst *.bin *.hex *.elf *.map *~ miditone.c
|
||||
distclean: clean
|
||||
$(DEL) ton/gen
|
||||
cd ./img && make distclean
|
||||
.PHONY: all clean distclean flash
|
16
V203F6P6/midi/audio.h
Normal file
16
V203F6P6/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
|
39
V203F6P6/midi/cache.h
Normal file
39
V203F6P6/midi/cache.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef _CACHE_H_DEF
|
||||
#define _CACHE_H_DEF
|
||||
#include "norflash.h"
|
||||
|
||||
template<typename T, const unsigned N> class Cache {
|
||||
T data [N];
|
||||
unsigned begin_address;
|
||||
unsigned ofset_current;
|
||||
NorFlash nor;
|
||||
public:
|
||||
explicit Cache () noexcept : begin_address(0u), ofset_current(0u), nor() {}
|
||||
Cache & operator= (const unsigned addr) {
|
||||
begin_address = addr;
|
||||
ofset_current = 0u;
|
||||
reload ();
|
||||
return * this;
|
||||
}
|
||||
operator bool () { if (begin_address == 0u) return false; return true; }
|
||||
T * operator++ (int) {
|
||||
check_validity ();
|
||||
T * tmp = data + ofset_current;
|
||||
ofset_current += 1;
|
||||
return tmp;
|
||||
}
|
||||
protected:
|
||||
void check_validity () {
|
||||
if (ofset_current >= N) {
|
||||
begin_address += N * sizeof(T);
|
||||
reload ();
|
||||
ofset_current = 0u;
|
||||
}
|
||||
}
|
||||
void reload () {
|
||||
unsigned char * ptr = reinterpret_cast<unsigned char *>(data);
|
||||
nor.ReadBlock (begin_address, ptr, N * sizeof(T));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _CACHE_H_DEF
|
1
V203F6P6/midi/ch32v203
Symbolic link
1
V203F6P6/midi/ch32v203
Symbolic link
|
@ -0,0 +1 @@
|
|||
../ch32v203/
|
1
V203F6P6/midi/common
Symbolic link
1
V203F6P6/midi/common
Symbolic link
|
@ -0,0 +1 @@
|
|||
../common/
|
112
V203F6P6/midi/endien.h
Normal file
112
V203F6P6/midi/endien.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
#ifndef ENDIEN_H
|
||||
#define ENDIEN_H
|
||||
#include <stdint.h>
|
||||
/**
|
||||
@file
|
||||
@brief Pořadí bytů ve slově.
|
||||
|
||||
*/
|
||||
#if __GNUC__ < 4 // Compiler test
|
||||
#error "Zkontroluj nastaveni"
|
||||
#endif
|
||||
// Pro 16.bit musíme struktury pakovat
|
||||
#define PACKSTRUCT __attribute__((packed))
|
||||
|
||||
#if __thumb__
|
||||
// Specificky a krátce pro danou architektutu a pokud chceme mít úplnou kontrolu.
|
||||
static inline uint32_t swab32 (uint32_t p) {
|
||||
register uint32_t t;
|
||||
asm volatile ("rev %0, %1":"=r"(t):"0"(p));
|
||||
return t;
|
||||
}
|
||||
#else //__thumb__
|
||||
// Obecně
|
||||
static inline uint32_t swab32 (uint32_t p) {
|
||||
// return __builtin_bswap32 (p); // I takto to jde, ale pro názornost:
|
||||
return ((p & 0x000000ff) << 24)
|
||||
| ((p & 0x0000ff00) << 8)
|
||||
| ((p & 0x00ff0000) >> 8)
|
||||
| ((p & 0xff000000) >> 24);
|
||||
}
|
||||
#endif //__thumb__
|
||||
// Tohle nebudeme ani specifikovat pro ARM
|
||||
static inline uint16_t swab16 (uint16_t p) {
|
||||
return (p << 8) | (p >> 8);
|
||||
}
|
||||
// Nedělá nic a ani nijak neotravuje - pouze pro kompatibilitu
|
||||
static inline uint32_t nosw32 (uint32_t p) {
|
||||
return p;
|
||||
}
|
||||
static inline uint16_t nosw16 (uint16_t p) {
|
||||
return p;
|
||||
}
|
||||
// Specificky podle pořadí bytů - LE je pro ARM i X86 normální.
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define cpu_to_be32 swab32
|
||||
#define be32_to_cpu swab32
|
||||
#define cpu_to_le32 nosw32
|
||||
#define le32_to_cpu nosw32
|
||||
|
||||
#define cpu_to_be16 swab16
|
||||
#define be16_to_cpu swab16
|
||||
#define cpu_to_le16 nosw16
|
||||
#define le16_to_cpu nosw16
|
||||
#else
|
||||
#define cpu_to_be32 nosw32
|
||||
#define be32_to_cpu nosw32
|
||||
#define cpu_to_le32 swab32
|
||||
#define le32_to_cpu swab32
|
||||
|
||||
#define cpu_to_be16 nosw16
|
||||
#define be16_to_cpu nosw16
|
||||
#define cpu_to_le16 swab16
|
||||
#define le16_to_cpu swab16
|
||||
#endif
|
||||
/**
|
||||
@class ui32be
|
||||
@brief C++ 32.bit data v BIG Endien.
|
||||
@class ui32le
|
||||
@brief C++ 32.bit data v LITTLE Endien.
|
||||
@class ui16be
|
||||
@brief C++ 16.bit data v BIG Endien.
|
||||
@class ui16le
|
||||
@brief C++ 16.bit data v LITTLE Endien.
|
||||
*/
|
||||
class ui32be {
|
||||
public:
|
||||
/// Nastavení hodnoty
|
||||
/// @par p uint32_t hodnota
|
||||
void set (uint32_t p) {num = cpu_to_be32 (p); };
|
||||
/// Zjištění hodnoty
|
||||
/// @return uint32_t hodnota
|
||||
uint32_t get (void) {return be32_to_cpu (num); };
|
||||
private:
|
||||
/// Vlastní data uint32_t - k nim se přímo nedostaneme, jsou privátní
|
||||
uint32_t num;
|
||||
}PACKSTRUCT;
|
||||
|
||||
class ui32le {
|
||||
public:
|
||||
void set (uint32_t p) {num = cpu_to_le32 (p); };
|
||||
uint32_t get (void) {return le32_to_cpu (num); };
|
||||
private:
|
||||
uint32_t num;
|
||||
}PACKSTRUCT;
|
||||
/* *******************************************************/
|
||||
class ui16be {
|
||||
public:
|
||||
void set (uint16_t p) {num = cpu_to_be16 (p); };
|
||||
uint16_t get (void) {return be16_to_cpu (num); };
|
||||
private:
|
||||
uint16_t num;
|
||||
}PACKSTRUCT;
|
||||
|
||||
class ui16le {
|
||||
public:
|
||||
void set (uint16_t p) {num = cpu_to_le16 (p); };
|
||||
uint16_t get (void) {return le16_to_cpu (num); };
|
||||
private:
|
||||
uint16_t num;
|
||||
}PACKSTRUCT;
|
||||
|
||||
#endif // ENDIEN_H
|
21
V203F6P6/midi/hack.c
Normal file
21
V203F6P6/midi/hack.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
size_t strlen (const char *s) {
|
||||
size_t l = 0;
|
||||
while (*s++) l++;
|
||||
return l;
|
||||
}
|
||||
void *memcpy (void *dest, const void *src, size_t n) {
|
||||
const char *s = (const char *) src;
|
||||
char *d = (char *) dest;
|
||||
int i;
|
||||
for (i=0; i<n; i++) d[i] = s[i];
|
||||
return dest;
|
||||
}
|
||||
void *memset (void *s, int c, size_t n) {
|
||||
char *p = (char *) s;
|
||||
int i;
|
||||
for (i=0; i<n; i++) p[i] = c;
|
||||
return s;
|
||||
}
|
61
V203F6P6/midi/helpers.h
Normal file
61
V203F6P6/midi/helpers.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
#ifndef HELPERS_H
|
||||
#define HELPERS_H
|
||||
#include <stdint.h>
|
||||
#include "norflash.h"
|
||||
#include "fifo.h"
|
||||
#include "baselayer.h"
|
||||
|
||||
class MemoryBase {
|
||||
NorFlash flash;
|
||||
public:
|
||||
explicit MemoryBase () noexcept : flash() {}
|
||||
uint32_t Write (const uint32_t addr, const void * ptr, const uint32_t len) {
|
||||
return flash.WriteBlock(addr, reinterpret_cast<const uint8_t*>(ptr), len);
|
||||
}
|
||||
uint32_t Read (const uint32_t addr, void * ptr, const uint32_t len) {
|
||||
return flash.ReadBlock(addr, reinterpret_cast<uint8_t*> (ptr), len);
|
||||
}
|
||||
void Erase (const uint32_t blok) {
|
||||
/* Flash používá 24-bitovou adresu počátku bloku, zde máme k dispozici
|
||||
* jen 16-bitový offset. Takže to jen natáhneme 256 krát. Software
|
||||
* tomu musí odpovídat.
|
||||
* */
|
||||
flash.EraseSector(blok << 8);
|
||||
}
|
||||
};
|
||||
|
||||
class CdcCmd : public BaseLayer {
|
||||
static constexpr int maxlen = 0x80;
|
||||
FIFO<char, maxlen> ring;
|
||||
char line [maxlen];
|
||||
unsigned index;
|
||||
public:
|
||||
explicit CdcCmd () noexcept : BaseLayer(), ring(), index(0u) {}
|
||||
uint32_t Up(const char * data, const uint32_t len) override {
|
||||
for (unsigned n=0u; n<len; n++) ring.Write (data [n]);
|
||||
return len;
|
||||
}
|
||||
void SendString (const char * ptr, const uint32_t len) {
|
||||
unsigned ofs = 0u, res = len;
|
||||
while (res) {
|
||||
const unsigned chunk = res > 0x20u ? 0x20u : res;
|
||||
const unsigned n = Down (ptr + ofs, chunk);
|
||||
ofs += n;
|
||||
res -= n;
|
||||
}
|
||||
}
|
||||
char * GetLine (unsigned & len) {
|
||||
char c;
|
||||
while (ring.Read(c)) {
|
||||
line [index++] = c;
|
||||
if (c == '\n') {
|
||||
len = index;
|
||||
index = 0u;
|
||||
return line;
|
||||
}
|
||||
}
|
||||
len = 0u;
|
||||
return line;
|
||||
}
|
||||
};
|
||||
#endif // HELPERS_H
|
31
V203F6P6/midi/img/Makefile
Normal file
31
V203F6P6/midi/img/Makefile
Normal file
|
@ -0,0 +1,31 @@
|
|||
PR = image
|
||||
VPATH = .
|
||||
CXX = g++
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Os -I..
|
||||
MFLAGS = -o $(PR)
|
||||
LFLAGS =
|
||||
|
||||
all: data.bin $(PR)
|
||||
|
||||
OBJECTS = main.o
|
||||
# fakticky není potřeba
|
||||
$(PR): $(OBJECTS)
|
||||
$(CXX) $(MFLAGS) $(OBJECTS) $(LFLAGS)
|
||||
clean:
|
||||
rm -f *.o *~ data.elf data.map $(PR) extdata.c
|
||||
distclean: clean
|
||||
rm -f data.bin miditones melody.c
|
||||
%.o: %.cpp
|
||||
$(CXX) -std=c++14 -c $(CFLAGS) -o $@ $<
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
data.elf: melody.c
|
||||
riscv64-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -Wall -Os -I.. -fdata-sections -Wl,-Map=data.map,--gc-sections -nostdlib -nostartfiles -o data.elf melody.c -L. -T script.ld
|
||||
data.bin: data.elf
|
||||
riscv64-unknown-elf-objcopy -O binary data.elf data.bin
|
||||
melody.c: miditones
|
||||
./miditones -d -s2 -t12 ../mid/
|
||||
miditones: miditonesV1.6.c
|
||||
gcc -Os -Wno-pointer-sign -Wno-return-type miditonesV1.6.c -o miditones
|
||||
.PHONY: all clean
|
69
V203F6P6/midi/img/main.cpp
Normal file
69
V203F6P6/midi/img/main.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
/* Jedna z možností jak dostat data do externí flash. Sice to poněkud
|
||||
* nabobtná (na začátku je zbytečně celá struktura SayedTexts + padding),
|
||||
* ale funguje to.
|
||||
* 1. zdroják melofy.c se přeloží a pomocí linker skriptu se vytvoří elf
|
||||
* 2. z elf se udělá binárka, příp. hex soubor standardním binutils
|
||||
* 3. binárka se tímto přečte a do extdata.c se extrahují potřebné informace.
|
||||
* extdata.c pak obsahuje adresy meloddií potřebné pro čtení.
|
||||
* Nezávisí to na tom, zda se binárka vytváří na 64. nebo 32. bit stroji. */
|
||||
class Reader {
|
||||
unsigned char * file_data;
|
||||
int file_size;
|
||||
public:
|
||||
explicit Reader() : file_data(nullptr), file_size(0) {}
|
||||
~Reader() {
|
||||
if (file_data) delete [] file_data;
|
||||
}
|
||||
int process (const char * filename);
|
||||
protected:
|
||||
void check ();
|
||||
void generate();
|
||||
};
|
||||
int Reader::process(const char * filename) {
|
||||
struct stat statbuf;
|
||||
int r = ::stat(filename, &statbuf);
|
||||
if (r < 0) return -1;
|
||||
if (file_data) delete [] file_data;
|
||||
file_data = new unsigned char [statbuf.st_size];
|
||||
FILE * in_file = fopen(filename, "r");
|
||||
r = fread (file_data, 1, statbuf.st_size, in_file);
|
||||
file_size = r;
|
||||
printf("readen %d bytes (%g 0x1000 blocks)\n", file_size, double(file_size)/4096.0);
|
||||
if (!in_file) return -1;
|
||||
fclose(in_file);
|
||||
check(); // kontrola - mělo by sedět to co je v data.bin a přeložené melody.c
|
||||
generate(); // generuje C-soubor s adresami a počty rámců
|
||||
return 0;
|
||||
}
|
||||
void Reader::check() {
|
||||
if (!file_size) return;
|
||||
}
|
||||
// Jen toto je podstatné - vytvoření extdata.c
|
||||
static const char * prefix = R"---(/* GENERATED FILE DO NOT EDIT */
|
||||
const unsigned scores [] = {)---";
|
||||
|
||||
void Reader::generate() {
|
||||
FILE * out = fopen("extdata.c","w");
|
||||
fprintf(out, "%s", prefix);
|
||||
|
||||
uint32_t * ptr = reinterpret_cast<uint32_t*>(file_data);
|
||||
for (unsigned n=0; ; n++) {
|
||||
if ((n%8) == 0) fprintf(out, "\n");
|
||||
const size_t e = ptr[n];
|
||||
fprintf(out, " 0x%06lXu,", e);
|
||||
if (e == 0lu) break;
|
||||
}
|
||||
fprintf(out, "\n};\n");
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
/// main ()
|
||||
int main (int argc, char *argv[]) {
|
||||
Reader read;
|
||||
return read.process("data.bin");
|
||||
}
|
1000
V203F6P6/midi/img/miditonesV1.6.c
Normal file
1000
V203F6P6/midi/img/miditonesV1.6.c
Normal file
File diff suppressed because it is too large
Load diff
40
V203F6P6/midi/img/script.ld
Normal file
40
V203F6P6/midi/img/script.ld
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*OUTPUT_FORMAT (binary) možnosti zjistíme příkazem "objdump -i" (lze i ihex) */
|
||||
|
||||
ENTRY (scores) /* odtud se začne (start po resetu) */
|
||||
/* popis pamětí */
|
||||
MEMORY {
|
||||
ram (RWX) : ORIGIN = 0x00, LENGTH = 0x40000
|
||||
}
|
||||
/* popis sekcí */
|
||||
SECTIONS {
|
||||
.fixed : { /* výstupní sekce v rom - nazveme např. .fixed */
|
||||
. = ALIGN (8); /* zarovnání příští sekce na 4 byty (celkem zbytečné) */
|
||||
KEEP(*(.rodata.scores));
|
||||
*(.rodata*) /* .rodata* - stejně jako .text*, ale pro const proměnné (data) (-fdata-sections) */
|
||||
. = ALIGN (8);
|
||||
*(.text*) /* gcc pojmenovává sekce s kódem jako .text, pokud je použito -ffunction-sections,
|
||||
pak bude každá funkce v jiné sekci s názvy .text.jmeno_funkce, proto ta * na konci */
|
||||
. = ALIGN (8);
|
||||
*(.data*) /* .data* - stejně jako .text*, ale pro proměnné (data inicializovaná) */
|
||||
. = ALIGN (8);
|
||||
*(.bss*) /* .bss* - stejně jako .text*, ale pro proměnné (data inicializovaná na 0) */
|
||||
*(COMMON*)
|
||||
*(.note.*)
|
||||
. = ALIGN (4096);
|
||||
relocate_end = .; /* konec dat v sekci .relocate - podle toho se např. nějak určí hodnota SP */
|
||||
end = .;
|
||||
} > ram /* tady je řečeno, že to má být v ram */
|
||||
/DISCARD/ : {
|
||||
*(.rela*)
|
||||
*(.dynamic)
|
||||
*(.eh*)
|
||||
*(.debug*)
|
||||
*(.comment*)
|
||||
*(.interp)
|
||||
*(.dynsym)
|
||||
*(.dynstr)
|
||||
*(.hash)
|
||||
*(.gnu.hash)
|
||||
*(.header)
|
||||
} : phdr
|
||||
}
|
251
V203F6P6/midi/intelhex.cpp
Normal file
251
V203F6P6/midi/intelhex.cpp
Normal file
|
@ -0,0 +1,251 @@
|
|||
#include "endien.h"
|
||||
#include "intelhex.h"
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
extern "C" void *memcpy (void *dest, const void *src, size_t n);
|
||||
extern "C" size_t strlen (const char *s);
|
||||
|
||||
static uint8_t hexval (const char c) {
|
||||
if ('0' <= c && c <= '9')
|
||||
return (uint8_t) c - '0';
|
||||
else if ('a' <= c && c <= 'f')
|
||||
return (uint8_t) c - 'a' + 10;
|
||||
else return 0;
|
||||
}
|
||||
static unsigned to_hex_str (char * str, const uint8_t * data, const unsigned len) {
|
||||
unsigned result = 0;
|
||||
const char * hexstr = "0123456789ABCDEF";
|
||||
for (unsigned n=0; n<len; n++) {
|
||||
const uint8_t c = data [n];
|
||||
str [result++] = hexstr [c >> 4];
|
||||
str [result++] = hexstr [c & 0xF];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static uint8_t chk_sum (const uint8_t * data, const unsigned len) {
|
||||
uint8_t sum = 0;
|
||||
for (unsigned n=0; n<len; n++) sum += data [n];
|
||||
return sum;
|
||||
}
|
||||
|
||||
union IhxHeader {
|
||||
struct {
|
||||
uint8_t reclen;
|
||||
ui16be load;
|
||||
RowTypes rectyp;
|
||||
}__attribute__((packed));
|
||||
uint8_t bytes [4];
|
||||
};
|
||||
union UlbaAddr {
|
||||
ui16be addr;
|
||||
uint8_t bytes [2];
|
||||
};
|
||||
union EipAddr {
|
||||
ui32be addr;
|
||||
uint8_t bytes [4];
|
||||
};
|
||||
|
||||
IntelHex::IntelHex() noexcept : address(0), offset(0), lenght(0), total_lenght(0), ackofs(0) {
|
||||
}
|
||||
|
||||
RowTypes IntelHex::parseRecord (const char * data, const unsigned int len) {
|
||||
//printf ("parseRecord: %d %s\r\n", len, data);
|
||||
if (data[0] != (uint8_t) ':') return badRecord;
|
||||
uint8_t tmp [(len >> 1) + 1];
|
||||
uint8_t val = 0;
|
||||
unsigned k = 0;
|
||||
for (unsigned n=1; n<len; n++) {
|
||||
const uint8_t c = data [n];
|
||||
if (c == '\r') break;
|
||||
if (c == '\n') break;
|
||||
val *= 0x10;
|
||||
val += hexval (c | 0x20);
|
||||
if ((n & 1) == 0) {
|
||||
tmp [k++] = val;
|
||||
val = 0;
|
||||
}
|
||||
}
|
||||
k -= 1;
|
||||
uint8_t chk = 0;
|
||||
for (unsigned n=0; n<k; n++) chk += tmp [n];
|
||||
chk = 0 - chk;
|
||||
// kontrolní součet
|
||||
if (chk != tmp[k]) return badRecord;
|
||||
// chybná délka
|
||||
if (k < sizeof (IhxHeader)) return badRecord;
|
||||
IhxHeader hdr;
|
||||
memcpy (hdr.bytes, tmp, sizeof(IhxHeader));
|
||||
// chybná délka
|
||||
if (k != (hdr.reclen + sizeof(IhxHeader))) return badRecord;
|
||||
//printf ("%d: %d=%d, %d, %02X=%02X\n", len, k, hdr.reclen+4, hdr.rectyp, chk, tmp[k]);
|
||||
UlbaAddr a1; EipAddr a2;
|
||||
RowTypes t = hdr.rectyp;
|
||||
lenght = hdr.reclen;
|
||||
const uint8_t * start = tmp + sizeof(IhxHeader);
|
||||
uint32_t tmpadr;
|
||||
// uint16_t old_ofs = offset;
|
||||
switch (t) {
|
||||
case dataRecord:
|
||||
memcpy (chunk, start, lenght);
|
||||
offset = hdr.load.get();
|
||||
// if (old_ofs != offset) printf ("Posun 0x%04X-0x%04X\n", old_ofs, offset);
|
||||
total_lenght = offset + lenght;
|
||||
break;
|
||||
case elaRecord:
|
||||
total_lenght = 0;
|
||||
memcpy (a1.bytes, start, lenght);
|
||||
address = (uint32_t) a1.addr.get() << 16;
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
case slaRecord:
|
||||
memcpy (a2.bytes, start, lenght);
|
||||
address = a2.addr.get();
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
case ackRecord:
|
||||
case nakRecord:
|
||||
ackofs = hdr.load.get();
|
||||
break;
|
||||
case eofRecord:
|
||||
case reqRecord:
|
||||
case ersRecord:
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
// Tyhle 2 rekordy nějak fungují, ale je to divné. Bohužel třeba objcopy
|
||||
// je na nízkých adresách z nějakého důvodu používá. Možná je v tom chyba.
|
||||
case esaRecord:
|
||||
total_lenght = 0;
|
||||
memcpy (a1.bytes, start, lenght);
|
||||
address = (uint32_t) a1.addr.get() << 4;
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
case ssaRecord:
|
||||
memcpy (a2.bytes, start, lenght);
|
||||
tmpadr = a2.addr.get();
|
||||
address = (tmpadr >> 12); // CS:IP ? čerti vědí, jak je to myšleno
|
||||
offset = hdr.load.get();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
bool IntelHex::CompareAckOffsset() {
|
||||
//printf ("ofs: %04X=%04X\n", ackofs, offset);
|
||||
if (ackofs == offset) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void IntelHex::getAddress (uint32_t & adr) {
|
||||
adr = address;
|
||||
}
|
||||
void IntelHex::getLenght (uint32_t & len) {
|
||||
len = total_lenght;
|
||||
}
|
||||
void IntelHex::getOffset (uint32_t & ofs) {
|
||||
ofs = offset;
|
||||
}
|
||||
void IntelHex::WriteData (uint8_t * data) {
|
||||
memcpy (data + offset, chunk, lenght);
|
||||
offset += lenght;
|
||||
}
|
||||
uint8_t * IntelHex::getDataRow (uint32_t & ofs, uint32_t & len) {
|
||||
ofs = offset;
|
||||
len = lenght;
|
||||
return chunk;
|
||||
}
|
||||
void IntelHex::AddOffset (uint32_t len) {
|
||||
offset += len;
|
||||
}
|
||||
|
||||
uint32_t IntelHex::ElaRecord (char * string, const uint32_t adr) {
|
||||
offset = 0;
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
UlbaAddr lba;
|
||||
IhxHeader hdr;
|
||||
hdr.load.set(0);
|
||||
hdr.reclen = sizeof (lba);
|
||||
hdr.rectyp = elaRecord;
|
||||
lba.addr.set (adr >> 16);
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk += chk_sum (lba.bytes, sizeof(UlbaAddr ));
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
result += to_hex_str (string + result, lba.bytes, sizeof(UlbaAddr ));
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
uint32_t IntelHex::SlaRecord (char* string, const uint32_t adr) {
|
||||
offset = 0;
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
EipAddr lba;
|
||||
IhxHeader hdr;
|
||||
hdr.load.set(0);
|
||||
hdr.reclen = sizeof (lba);
|
||||
hdr.rectyp = slaRecord;
|
||||
lba.addr.set (adr);
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk += chk_sum (lba.bytes, sizeof(EipAddr ));
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
result += to_hex_str (string + result, lba.bytes, sizeof(EipAddr ));
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t IntelHex::DataRecord (char * string, const uint8_t * data, const uint32_t len) {
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
IhxHeader hdr;
|
||||
hdr.load.set (offset);
|
||||
hdr.reclen = len;
|
||||
hdr.rectyp = dataRecord;
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk += chk_sum (data, len);
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
if (len) result += to_hex_str (string + result, data, len);
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t IntelHex::EofRecord (char * string) {
|
||||
offset = 0;
|
||||
const char * eof = ":00000001FF\r\n";
|
||||
const uint32_t result = strlen (eof);
|
||||
memcpy (string, eof, result);
|
||||
string [result] = '\0';
|
||||
return result;
|
||||
}
|
||||
uint32_t IntelHex::BTxRecord (char * string, RowTypes t) {
|
||||
uint32_t result = 0;
|
||||
string [result++] = ':';
|
||||
IhxHeader hdr;
|
||||
hdr.load.set (offset);
|
||||
hdr.reclen = 0;
|
||||
hdr.rectyp = t;
|
||||
uint8_t chk = 0;
|
||||
chk += chk_sum (hdr.bytes, sizeof(IhxHeader));
|
||||
chk = 0 - chk;
|
||||
result += to_hex_str (string + result, hdr.bytes, sizeof(IhxHeader));
|
||||
result += to_hex_str (string + result, & chk, 1);
|
||||
string [result++] = '\r';
|
||||
string [result++] = '\n';
|
||||
string [result] = '\0';
|
||||
//offset += len;
|
||||
return result;
|
||||
}
|
138
V203F6P6/midi/intelhex.h
Normal file
138
V203F6P6/midi/intelhex.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
#ifndef INTELHEX_H
|
||||
#define INTELHEX_H
|
||||
#include <stdint.h>
|
||||
|
||||
/// Maximální délka rekordu
|
||||
static const unsigned MaxHexRecord = 0x80u;
|
||||
|
||||
enum RowTypes : uint8_t {
|
||||
// defined by Intel
|
||||
dataRecord = 0, //!< datový paket
|
||||
eofRecord, //!< konec dat
|
||||
esaRecord, //!< X86/16.bit CS/IP počáteční adresa
|
||||
ssaRecord, //!< X86/16.bit CS/IP startovací adresa
|
||||
elaRecord, //!< počáteční adresa 32-bit (horních 16.bit)
|
||||
slaRecord, //!< startovací adresa 32-bit
|
||||
// ... defined by BT protocol
|
||||
// všechny budou používat jen offset pro přenos dat.
|
||||
ackRecord, //!< offset - představuje offset předchozích dat
|
||||
nakRecord, //!< stejně jako předchozí
|
||||
reqRecord, //!< Request for data - offset značí odkud vzhledem k počáteční adrese
|
||||
ersRecord, //!< Erase - po blocích, offset je číslo bloku (závisí na typu flash)
|
||||
badRecord, //!< použito pro detekci chyb
|
||||
};
|
||||
/// @enum RowTypes
|
||||
/// Enumerace datových typů Intel-hex formátu
|
||||
|
||||
/**
|
||||
* @class IntelHex
|
||||
* @brief Hepler pro parsování a vytváření paketů Intel HEX formátu.
|
||||
*
|
||||
* Prvních 6 typů (#RowTypes) je definováno ve specifikaci např.
|
||||
* <a href="https://cs.wikipedia.org/wiki/Intel_HEX">zde</a>. A protože je tam dost volného
|
||||
* místa pro rozšíření, jsou zde dodefinovány další typy použité pro komunikaci. Je to tak
|
||||
* jednodušší než vymýšlet nějaký vlastní formát pro potvrzování a jiné. Protokol je znakový,
|
||||
* poměrně robustní, na druhou stranu má dost velku režii.
|
||||
*
|
||||
* Ukázalo sem že úzkým hrdlem komunikace je čekání na potvrzování, takže se režie poněkud zastírá,
|
||||
* ale potvrzování být musí pokud to má trochu spolehlivě fungovat.
|
||||
* */
|
||||
|
||||
class IntelHex {
|
||||
public:
|
||||
explicit IntelHex() noexcept;
|
||||
/**
|
||||
* @brief Základní funkce parseru
|
||||
*
|
||||
* @param data vstupní řádek (paket)
|
||||
* @param len jeho délka (např. strlen(data))
|
||||
* @return RowTypes - to se pak zpracovává dál v nadřazené části např. LinkProtocol
|
||||
*/
|
||||
RowTypes parseRecord (const char * data, const unsigned len);
|
||||
/**
|
||||
* @brief Nastavení adresy
|
||||
*
|
||||
* Buď počáteční nebo starovací.
|
||||
*
|
||||
* @param adr odkaz na proměnnou, kam bude adresa nastavena
|
||||
*/
|
||||
void getAddress (uint32_t & adr);
|
||||
/**
|
||||
* @brief Nastavení délky
|
||||
* @param len odkaz na proměnnou, kam bude délka nastavena
|
||||
*/
|
||||
void getLenght (uint32_t & len);
|
||||
/**
|
||||
* @brief Nastavení ofsetu
|
||||
* @param ofs odkaz na proměnnou, kam bude ofset nastaven
|
||||
*/
|
||||
void getOffset (uint32_t & ofs);
|
||||
/**
|
||||
* @brief Zápis dat z příchozího dataRecord
|
||||
*
|
||||
* @param data kam se zapíší
|
||||
*/
|
||||
void WriteData (uint8_t * data);
|
||||
/**
|
||||
* @brief Posun ofsetu kvůli udržení konzistence
|
||||
*
|
||||
* @param len o kolik se má posunout
|
||||
*/
|
||||
void AddOffset (uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Vytvoření dataRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param data pure data rekordu
|
||||
* @param len a jejich délka
|
||||
* @return uint32_t kolik bylo zapsáno do string
|
||||
*/
|
||||
uint32_t DataRecord (char * string, const uint8_t * data, const uint32_t len);
|
||||
/**
|
||||
* @brief Vytvoření elaRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param adr počáteční adresa
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t ElaRecord (char * string, const uint32_t adr);
|
||||
/**
|
||||
* @brief Vytvoření slaRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param adr startovací adresa
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t SlaRecord (char * string, const uint32_t adr);
|
||||
/**
|
||||
* @brief Vytvoření eofRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t EofRecord (char * string);
|
||||
|
||||
/**
|
||||
* @brief Vytvoření BT rekordů ackRecord, nakRecord, reqRecord, ersRecord
|
||||
*
|
||||
* @param string kam se record zapíše
|
||||
* @param t definuj typ rekordu
|
||||
* @return uint32_t kolik znaků bylo zapsáno do string
|
||||
*/
|
||||
uint32_t BTxRecord (char * string, RowTypes t = ackRecord);
|
||||
/// pomocná funkce
|
||||
bool CompareAckOffsset ();
|
||||
/// pomocná funkce
|
||||
uint8_t * getDataRow (uint32_t & ofs, uint32_t & len);
|
||||
protected:
|
||||
private:
|
||||
uint32_t address;
|
||||
uint32_t offset;
|
||||
uint32_t lenght;
|
||||
uint32_t total_lenght;
|
||||
uint32_t ackofs;
|
||||
uint8_t chunk [MaxHexRecord];
|
||||
};
|
||||
|
||||
#endif // INTELHEX_H
|
76
V203F6P6/midi/linkprotocol.cpp
Normal file
76
V203F6P6/midi/linkprotocol.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include "helpers.h"
|
||||
#include "linkprotocol.h"
|
||||
|
||||
#define trace(...)
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
extern "C" size_t strlen (const char *s);
|
||||
|
||||
LinkProtocol::LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept : cdccmd(g), ihx(), driver(b),
|
||||
begin_addr(0), start_addr(0), lenght (0) {
|
||||
}
|
||||
void LinkProtocol::ParseLine (const char * line) {
|
||||
const unsigned readen = strlen (line);
|
||||
RowTypes t = ihx.parseRecord (line, readen);
|
||||
switch (t) {
|
||||
case dataRecord: AcquireDataRecord (); break;
|
||||
case elaRecord: AcquireElaRecord (); break;
|
||||
case slaRecord: AcquireSlaRecord (); break;
|
||||
case eofRecord: AcquireEofRecord (); break;
|
||||
case reqRecord: AcquireReqRecord (); break;
|
||||
case ersRecord: AcquireErsRecord (); break;
|
||||
default:
|
||||
trace ("BAD record 0x%02X\r\n", t);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
void LinkProtocol::AcquireDataRecord() {
|
||||
uint32_t ofs, len;
|
||||
uint8_t * ptr = ihx.getDataRow (ofs, len);
|
||||
uint32_t res = driver.Write (begin_addr + ofs, ptr, len);
|
||||
if (res) {
|
||||
SendAck();
|
||||
ihx.AddOffset (res); // posun offsetu az po ack
|
||||
return;
|
||||
}
|
||||
SendAck (false);
|
||||
}
|
||||
// První paket, který musí přijít při každé akci.
|
||||
void LinkProtocol::AcquireElaRecord() {
|
||||
ihx.getAddress (begin_addr);
|
||||
trace("Begin = 0x%08X\r\n", begin_addr);
|
||||
SendAck();
|
||||
}
|
||||
typedef void (*pHandler) (void);
|
||||
void LinkProtocol::AcquireSlaRecord() {
|
||||
ihx.getAddress (start_addr);
|
||||
trace("Start = 0x%08X\r\n", start_addr);
|
||||
}
|
||||
void LinkProtocol::AcquireEofRecord() {
|
||||
ihx.getLenght (lenght);
|
||||
trace ("Lenght = %d\r\n", lenght);
|
||||
SendAck();
|
||||
}
|
||||
void LinkProtocol::AcquireReqRecord() {
|
||||
static constexpr unsigned chunk_size = 0x20;
|
||||
uint8_t data [chunk_size];
|
||||
uint32_t res, len, ofs;
|
||||
len = chunk_size;
|
||||
ihx.getOffset (ofs);
|
||||
//trace ("AcquireReqRecord: %04X, len=%d\r\n", ofs, len);
|
||||
res = driver.Read (begin_addr + ofs, data, len);
|
||||
res = ihx.DataRecord(strbuf, data, res);
|
||||
cdccmd.SendString (strbuf, res);
|
||||
}
|
||||
void LinkProtocol::AcquireErsRecord() {
|
||||
uint32_t block;
|
||||
ihx.getOffset (block);
|
||||
driver.Erase (block);
|
||||
SendAck();
|
||||
}
|
||||
|
||||
void LinkProtocol::SendAck (bool ok) {
|
||||
RowTypes t = ok ? ackRecord : nakRecord;
|
||||
const uint32_t r = ihx.BTxRecord (strbuf, t);
|
||||
cdccmd.SendString (strbuf, r);
|
||||
}
|
55
V203F6P6/midi/linkprotocol.h
Normal file
55
V203F6P6/midi/linkprotocol.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef LINKPROTOCOL_H
|
||||
#define LINKPROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "intelhex.h"
|
||||
|
||||
class CdcCmd;
|
||||
class MemoryBase;
|
||||
/**
|
||||
* @class LinkProtocol
|
||||
* @brief Protokol po CDC.
|
||||
*
|
||||
* Vychází z formátu IntelHex. komunikaci začíná PC, modul odpovídá. První co musí PC (vždy na začátku konunikace !) poslat
|
||||
* je paket ElaRecord (4), který určuje počáteční adresu - podle ní modul vybere potřebný ovladač paměti. Formy komunikace :
|
||||
* -# Mazání obsahu flash. Probíhá po jednotlivých blocích.
|
||||
* - PC -> MODUL : ersRecord (v poli offset číslo bloku relativně k počátku typu paměti)
|
||||
* - MODUL -> PC : ackRecord (v poli offset číslo bloku relativně k počátku typu paměti)
|
||||
* -# Zápis dat do paměti.
|
||||
* - PC -> MODUL : dataRecord (max. 32 bytů)
|
||||
* - MODUL -> PC : ackRecord nebo nakRecord pokud je cosi špatně
|
||||
* -# Čtení dat z paměti.
|
||||
* - PC -> MODUL : reqRecord (v poli offset odkud chci číst)
|
||||
* - MODUL -> PC : dataRecord - pokud má nulovou délku, další data nejsou. Posílána je délka 32 bytů.
|
||||
* -# Spuštění uživatelského programu.
|
||||
* - PC -> MODUL : slaRecord
|
||||
* - bez odpovědi - je to jen pro uživatelské testy a může to špatně skončit. Vlastně je to přidáno
|
||||
* jen proto, že to jde.
|
||||
* */
|
||||
class LinkProtocol {
|
||||
static constexpr unsigned strbuflen = 0x80u;
|
||||
public:
|
||||
explicit LinkProtocol(CdcCmd & g, MemoryBase & b) noexcept;
|
||||
void ParseLine (const char * line);
|
||||
protected:
|
||||
void AcquireDataRecord ();
|
||||
void AcquireElaRecord ();
|
||||
void AcquireSlaRecord ();
|
||||
void AcquireEofRecord ();
|
||||
void AcquireReqRecord ();
|
||||
void AcquireErsRecord ();
|
||||
|
||||
void SendAck (bool ok=true);
|
||||
private:
|
||||
CdcCmd & cdccmd;
|
||||
IntelHex ihx;
|
||||
MemoryBase & driver;
|
||||
|
||||
uint32_t begin_addr;
|
||||
uint32_t start_addr;
|
||||
uint32_t lenght;
|
||||
|
||||
char strbuf [strbuflen];
|
||||
};
|
||||
|
||||
#endif // LINKPROTOCOL_H
|
65
V203F6P6/midi/main.cpp
Normal file
65
V203F6P6/midi/main.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
#include "system.h"
|
||||
#include "midiplayer.h"
|
||||
#include "cdc_class.h"
|
||||
#include "linkprotocol.h"
|
||||
#include "helpers.h"
|
||||
/*
|
||||
* Player na tomto čipu má 12 generátorů, lepší
|
||||
* rozlišení PWM, tedy lepší zvuk.
|
||||
* Zase má menší paměť, takže stálo za zkoušku dát
|
||||
* data melodií do externí SPI flash. Číst to z ní
|
||||
* po bytu by asi bylo dost neefektivní, takže je tam
|
||||
* vložena třída Cache. Zároveň to ukazuje jak efektivní
|
||||
* je použití C++ proti čistému C. Zatímco v C-čku by to
|
||||
* asi byl velký zásah do původního kódu, zde stačí
|
||||
* ve třídě Cache přetížit 3 operátory a do původního
|
||||
* kódu se nemusí takřka zasahovat.
|
||||
* Binární obraz melodií pro externí flash se vytvoří
|
||||
* pomocí Makefile v adresáří img, je nezávislý na
|
||||
* firmware procesoru. Melodie to bere z adresáře mid,
|
||||
* pořadí je víceméně náhodné.
|
||||
*
|
||||
* Vzhledem k tomu, že je tam místa dost, vejde se tam
|
||||
* i programátor nor flash, takže není potřeba přepínat
|
||||
* firmware, stačí připojit do USB a pokud se to do 2s
|
||||
* enumeruje, můžeme programovat.
|
||||
*/
|
||||
extern "C" volatile uint32_t bDeviceState;
|
||||
GpioClass led (GPIOB,8u);
|
||||
// player
|
||||
static MidiPlayer player (led);
|
||||
static PwmClass pcm;
|
||||
// programátor
|
||||
static cdc_class cdc;
|
||||
static MemoryBase mem;
|
||||
static CdcCmd cmd;
|
||||
static LinkProtocol lnk (cmd, mem);
|
||||
|
||||
static void player_loop () {
|
||||
for (;;) {
|
||||
player.pass();
|
||||
}
|
||||
}
|
||||
static void programmer_loop () {
|
||||
led << true;
|
||||
unsigned command = 0u;
|
||||
for (;;) {
|
||||
char * res = cmd.GetLine(command);
|
||||
if (command == 0u) continue;
|
||||
lnk.ParseLine(res);
|
||||
}
|
||||
}
|
||||
|
||||
int main (void) {
|
||||
delay_init ();
|
||||
cdc.init ();
|
||||
Delay_Ms (2000); // čekat se musí poměrně dlouho
|
||||
if (bDeviceState > 1u) { // enumerováno, můžeme programovat
|
||||
cmd += cdc;
|
||||
programmer_loop();
|
||||
} else { // jinak můžeme hrát
|
||||
pcm.attach (player);
|
||||
player_loop ();
|
||||
}
|
||||
return 0;
|
||||
}
|
1
V203F6P6/midi/mid
Symbolic link
1
V203F6P6/midi/mid
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../V203/midi/mid
|
98
V203F6P6/midi/midiplayer.cpp
Normal file
98
V203F6P6/midi/midiplayer.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#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(GpioClass & io) noexcept : OneWay<uint16_t>(), led (io), passcnt (0u), scores(), melody() {
|
||||
pause = 0;
|
||||
scores = 0;
|
||||
melody = * scores++;
|
||||
running = false;
|
||||
}
|
||||
/// 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++;
|
||||
if (!melody) {
|
||||
scores = 0;
|
||||
melody = * scores++;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
45
V203F6P6/midi/midiplayer.h
Normal file
45
V203F6P6/midi/midiplayer.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef DACPLAYER_H
|
||||
#define DACPLAYER_H
|
||||
#include "oneway.h"
|
||||
#include "gpio.h"
|
||||
#include "tone.h"
|
||||
#include "audio.h"
|
||||
#include "pwmclass.h"
|
||||
#include "cache.h"
|
||||
|
||||
/// Třída, která hraje čistě na pozadí.
|
||||
class MidiPlayer : public OneWay<uint16_t> {
|
||||
// Veřejné metody
|
||||
public:
|
||||
/// Konstruktor
|
||||
explicit MidiPlayer (GpioClass & io) 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:
|
||||
GpioClass & led;
|
||||
Tone gens[maxGens]; /// Generátory tónů
|
||||
unsigned passcnt;
|
||||
volatile bool running;
|
||||
Cache<uint32_t, 8u> scores;
|
||||
Cache<uint8_t, 16u> melody;
|
||||
volatile int pause;
|
||||
|
||||
};
|
||||
|
||||
#endif // DACPLAYER_H
|
82
V203F6P6/midi/norflash.cpp
Normal file
82
V203F6P6/midi/norflash.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
//#include "string.h"
|
||||
#include "norflash.h"
|
||||
extern "C" {
|
||||
extern void delay_init ();
|
||||
extern void Delay_Ms (const unsigned dly);
|
||||
};
|
||||
union FlashCommandHeader {
|
||||
struct {
|
||||
FLASH_COMMANDS cmd : 8;
|
||||
uint32_t adr : 24; // adresa je BIG ENDIEN - vyšší byte vystupuje po SPI dříve (MSB FIRST).
|
||||
}__attribute__((packed));
|
||||
uint8_t bytes [4];
|
||||
}__attribute__((packed));
|
||||
static inline uint32_t be_set_24 (const uint32_t p) {
|
||||
return ((p & 0x000000ff) << 16)
|
||||
| ((p & 0x0000ff00) << 0)
|
||||
| ((p & 0x00ff0000) >> 16);
|
||||
}
|
||||
static constexpr unsigned FlashBlockLen = 0x1000u; // 4KiB = 4096 B
|
||||
|
||||
NorFlash::NorFlash() noexcept : spi() {
|
||||
delay_init();
|
||||
}
|
||||
unsigned int NorFlash::ReadBlock(const unsigned int addr, uint8_t * data, const unsigned int len) {
|
||||
FlashCommandHeader header;
|
||||
header.cmd = FLASH_4READ;
|
||||
header.adr = be_set_24(addr);
|
||||
spi.ChipSelect(true);
|
||||
for (unsigned n=0u; n<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
|
||||
for (unsigned n=0u; n<len; n++) data [n] = spi.ReadWriteByte(0xff);
|
||||
spi.ChipSelect(false);
|
||||
return len;
|
||||
}
|
||||
unsigned int NorFlash::WriteBlock(const unsigned int addr, const uint8_t * data, const unsigned int len) {
|
||||
FlashCommandHeader header;
|
||||
header.cmd = FLASH_4PP;
|
||||
header.adr = be_set_24(addr);
|
||||
SingleCommand(FLASH_WREN);
|
||||
spi.ChipSelect(true);
|
||||
for (unsigned n=0u; n<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
|
||||
for (unsigned n=0u; n<len; n++) spi.ReadWriteByte(data [n]);
|
||||
spi.ChipSelect(false);
|
||||
if (!WaitForReady()) return 0;
|
||||
return len;
|
||||
}
|
||||
bool NorFlash::EraseSector(const unsigned int addr) {
|
||||
FlashCommandHeader header;
|
||||
header.cmd = FLASH_4SE;
|
||||
header.adr = be_set_24(addr);
|
||||
SingleCommand(FLASH_WREN);
|
||||
spi.ChipSelect(true);
|
||||
for (unsigned n=0u; n<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
|
||||
spi.ChipSelect(false);
|
||||
if (!WaitForReady(400u)) return false;
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
inline bool NorFlash::SingleCommand(const FLASH_COMMANDS cmd) const {
|
||||
uint8_t tmp = cmd;
|
||||
spi.ChipSelect(true);
|
||||
spi.ReadWriteByte (tmp);
|
||||
spi.ChipSelect(false);
|
||||
return true;
|
||||
}
|
||||
inline uint8_t NorFlash::ReadStatus(const FLASH_COMMANDS cmd) const {
|
||||
uint8_t buf [2] = {cmd, 0};
|
||||
spi.ChipSelect(true);
|
||||
for (unsigned n=0u; n<2u; n++) buf[n] = spi.ReadWriteByte(buf[n]);
|
||||
spi.ChipSelect(false);
|
||||
return buf[1];
|
||||
}
|
||||
bool NorFlash::WaitForReady(const unsigned int ms) const {
|
||||
uint8_t sr;
|
||||
for (unsigned n=0; n<ms; n++) {
|
||||
Delay_Ms (1u);
|
||||
sr = ReadStatus(FLASH_RDSR1);
|
||||
if ((sr & 1u) == 0u) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
40
V203F6P6/midi/norflash.h
Normal file
40
V203F6P6/midi/norflash.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef NORFLASH_H
|
||||
#define NORFLASH_H
|
||||
#include <stdint.h>
|
||||
#include "spiblocked.h"
|
||||
/// Enumerace povelů pro SPI FLASH.
|
||||
enum FLASH_COMMANDS : uint8_t {
|
||||
/* single byte commands */
|
||||
FLASH_WRDI = 0x04, // nepoužito
|
||||
FLASH_WREN = 0x06,
|
||||
FLASH_RDID = 0x9F,
|
||||
FLASHRSTEN = 0x66, // nepoužito
|
||||
FLASH__RST = 0x99, // nepoužito
|
||||
FLASH__RES = 0xAB, // release from power down, nepoužito
|
||||
FLASH__DPD = 0xB9, // power down, nepoužito
|
||||
// multi - byte
|
||||
FLASH_RDSR1 = 0x05,
|
||||
// dále se mohou povely lišit
|
||||
FLASH_RDSR2 = 0x35, // nepoužito
|
||||
FLASH_4READ = 0x03,
|
||||
FLASH_4PP = 0x02,
|
||||
FLASH_4SE = 0x20,
|
||||
};
|
||||
|
||||
class NorFlash {
|
||||
SpiBlocked spi;
|
||||
public:
|
||||
explicit NorFlash () noexcept;
|
||||
unsigned ReadBlock (const unsigned addr, uint8_t * data, const unsigned len);
|
||||
unsigned WriteBlock (const unsigned addr, const uint8_t * data, const unsigned len);
|
||||
bool EraseSector (const unsigned addr);
|
||||
protected:
|
||||
/// Jednoduchý příkaz
|
||||
bool SingleCommand (const FLASH_COMMANDS cmd) const;
|
||||
/// Načtení registru STATUS1 nebo STATUS2 pomocí FLASH_RDSRx
|
||||
uint8_t ReadStatus (const FLASH_COMMANDS cmd) const;
|
||||
/// Čekání na dokončení operace zápisu / mazání
|
||||
bool WaitForReady (const unsigned ms = 5u) const;
|
||||
};
|
||||
|
||||
#endif // NORFLASH_H
|
110
V203F6P6/midi/pwmclass.cpp
Normal file
110
V203F6P6/midi/pwmclass.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
#include "pwmclass.h"
|
||||
#include "gpio.h"
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
extern "C" {
|
||||
[[gnu::interrupt]] extern void DMA1_Channel2_IRQHandler( void );
|
||||
};
|
||||
static PwmClass * pPwmInstance = nullptr;
|
||||
void DMA1_Channel2_IRQHandler( void ) {
|
||||
DMA1_Type::INTFR_DEF state (DMA1.INTFR);
|
||||
if (state.B.GIF2 != RESET) {
|
||||
DMA1.INTFCR.B.CGIF2 = SET;
|
||||
} else return;
|
||||
if (state.B.HTIF2 != RESET) {
|
||||
DMA1.INTFCR.B.CHTIF2 = SET;
|
||||
if (pPwmInstance) pPwmInstance->send(false);
|
||||
}
|
||||
if (state.B.TCIF2 != RESET) {
|
||||
DMA1.INTFCR.B.CTCIF2 = SET;
|
||||
if (pPwmInstance) pPwmInstance->send(true);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* initialize TIM2 for PWM
|
||||
*/
|
||||
inline void PwmClass::TimInit() noexcept {
|
||||
// Enable GPIOA and TIM1
|
||||
RCC.APB2PCENR.modify([] (RCC_Type::APB2PCENR_DEF & r) -> auto {
|
||||
r.B.IOPAEN = SET;
|
||||
r.B.IOPBEN = SET;
|
||||
//r.B.AFIOEN = SET;
|
||||
return r.R;
|
||||
});
|
||||
RCC.APB1PCENR.B.TIM2EN = SET;
|
||||
// PA2 is TIM2_CH3, 10MHz Output alt func, push-pull
|
||||
GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
|
||||
r.B.CNF2 = 2u;
|
||||
r.B.MODE2 = 1u;
|
||||
return r.R;
|
||||
});
|
||||
// PB1 is DEN, active H Output 10 MHz, push-pull
|
||||
GPIOB.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
|
||||
r.B.CNF1 = 0u;
|
||||
r.B.MODE1 = 1u;
|
||||
return r.R;
|
||||
});
|
||||
GPIOB.BSHR.B.BS1 = SET; // set to H
|
||||
// Reset TIM2 to init all regs
|
||||
RCC.APB1PRSTR.B.TIM2RST = SET;
|
||||
RCC.APB1PRSTR.B.TIM2RST = RESET;
|
||||
// CTLR1: default is up, events generated, edge align
|
||||
// SMCFGR: default clk input is CK_INT
|
||||
// Prescaler
|
||||
TIM2.PSC.R = 0u; // 144 MHz
|
||||
// Auto Reload - sets period
|
||||
TIM2.ATRLR.R = MAXPWM - 1; // 24 kHz
|
||||
|
||||
// CH3 Mode is output, PWM1 (CC3S = 00, OC3M = 110)
|
||||
TIM2.CHCTLR2_Output.modify([](TIM2_Type::CHCTLR2_Output_DEF & r) -> auto {
|
||||
r.B.OC3M = 0x6u;
|
||||
return r.R;
|
||||
});
|
||||
// Enable TIM1 outputs
|
||||
TIM2.CCER.modify([](TIM2_Type::CCER_DEF & r) -> auto {
|
||||
// Enable CH3, CH3 output, positive pol
|
||||
r.B.CC3E = SET;
|
||||
//r.B.CC3P = SET; // negative
|
||||
return r.R;
|
||||
});
|
||||
// Reload immediately + Trigger DMA
|
||||
TIM2.SWEVGR.B.UG = SET;
|
||||
TIM2.DMAINTENR.B.UDE = SET;
|
||||
}
|
||||
inline void PwmClass::DmaInit() noexcept {
|
||||
// Enable DMA
|
||||
RCC.AHBPCENR.modify([](RCC_Type::AHBPCENR_DEF & r) -> auto {
|
||||
r.B.SRAMEN = SET;
|
||||
r.B.DMA1EN = SET;
|
||||
return r.R;
|
||||
});
|
||||
// DMA can be configured to attach to T2UP
|
||||
// The system can only DMA out at ~2.2MSPS. 2MHz is stable.
|
||||
DMA1.CNTR2 .R = FULL_LEN;
|
||||
DMA1.MADDR2.R = reinterpret_cast<size_t>(buffer);
|
||||
DMA1.PADDR2.R = reinterpret_cast<size_t>(& TIM2.CH3CVR);
|
||||
DMA1.CFGR2.modify([](DMA1_Type::CFGR2_DEF & r) -> auto {
|
||||
r.B.DIR = SET; // MEM2PERIPHERAL
|
||||
r.B.PL = 2u; // High 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 CH2
|
||||
r.B.EN = SET;
|
||||
return r.R;
|
||||
});
|
||||
}
|
||||
|
||||
PwmClass::PwmClass() noexcept : count(0u), pL(buffer), pH(buffer + HALF_LEN), src(nullptr) {
|
||||
pPwmInstance = this;
|
||||
TimInit ();
|
||||
DmaInit ();
|
||||
// Enable TIM2
|
||||
}
|
||||
void PwmClass::attach(OneWay<uint16_t>& s) {
|
||||
NVIC.EnableIRQ (DMA1_Channel2_IRQn);
|
||||
TIM2.CTLR1.B.CEN = SET;
|
||||
src = & s;
|
||||
}
|
35
V203F6P6/midi/pwmclass.h
Normal file
35
V203F6P6/midi/pwmclass.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef PWMCLASS_H
|
||||
#define PWMCLASS_H
|
||||
#include "system.h"
|
||||
#include "oneway.h"
|
||||
static constexpr unsigned MAXPWM = 6000u;
|
||||
/* Používá TIM2, PWM kanál 3, DMA1 kanál 2, přerušení DMA1_Channel2_IRQHandler */
|
||||
class PwmClass {
|
||||
static constexpr unsigned HALF_LEN = 0x80u;
|
||||
static constexpr unsigned FULL_LEN = 2u * HALF_LEN;
|
||||
volatile unsigned count;
|
||||
uint16_t * const pL;
|
||||
uint16_t * const pH;
|
||||
uint16_t buffer [FULL_LEN];
|
||||
OneWay<uint16_t> * src;
|
||||
public:
|
||||
explicit PwmClass () noexcept;
|
||||
void attach (OneWay<uint16_t> & s);
|
||||
void send (const bool b) {
|
||||
if (!src) return;
|
||||
if (b) src->Send (pH, HALF_LEN);
|
||||
else src->Send (pL, HALF_LEN);
|
||||
if (count) count -= 1u;
|
||||
}
|
||||
void delay (const unsigned frames = 50u) {
|
||||
count = frames;
|
||||
while (count) {
|
||||
asm volatile ("nop");
|
||||
}
|
||||
}
|
||||
protected:
|
||||
void DmaInit () noexcept;
|
||||
void TimInit () noexcept;
|
||||
};
|
||||
|
||||
#endif // PWMCLASS_H
|
66
V203F6P6/midi/spiblocked.cpp
Normal file
66
V203F6P6/midi/spiblocked.cpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#include "system.h"
|
||||
#include "spiblocked.h"
|
||||
enum SPICLK : uint32_t {
|
||||
FPCLK_2 = 0u, // 72 MHz
|
||||
FPCLK_4, // 36 MHz
|
||||
FPCLK_8, // 18 MHz
|
||||
FPCLK_16, // 9 MHz
|
||||
FPCLK_32, // 4.5 MHz
|
||||
FPCLK_64, // 2.25 MHz
|
||||
FPCLK_128, // 1.125 MHz
|
||||
FPCLK_256, // 0.5625 MHz
|
||||
};
|
||||
static constexpr unsigned FM = 3u; // 50 MHz
|
||||
static void InitPins () noexcept {
|
||||
// PA4 - NSS, PA5 - SCK, PA6 - MISO, PA7 - MOSI
|
||||
GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> uint32_t {
|
||||
r.B.MODE4 = FM;
|
||||
r.B.CNF4 = 0u; // gen push - pull
|
||||
r.B.MODE5 = FM;
|
||||
r.B.CNF5 = 2u; // alt push - pull
|
||||
r.B.MODE6 = 0u; // input mode
|
||||
r.B.CNF6 = 1u; // floating
|
||||
r.B.MODE7 = FM;
|
||||
r.B.CNF7 = 2u; // alt push - pull
|
||||
return r.R;
|
||||
});
|
||||
// AFIO - default
|
||||
GPIOA.BSHR.B.BS4 = SET;
|
||||
}
|
||||
|
||||
SpiBlocked::SpiBlocked() noexcept {
|
||||
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> uint32_t {
|
||||
r.B.SPI1EN = SET;
|
||||
r.B.IOPAEN = SET;
|
||||
//r.B.AFIOEN = SET;
|
||||
return r.R;
|
||||
});
|
||||
InitPins();
|
||||
RCC.APB2PRSTR.B.SPI1RST = SET;
|
||||
RCC.APB2PRSTR.B.SPI1RST = RESET;
|
||||
SPI1.CTLR1.modify([](SPI1_Type::CTLR1_DEF & r) -> uint32_t {
|
||||
r.B.CPHA = RESET;
|
||||
r.B.CPOL = RESET;
|
||||
r.B.MSTR = SET; // master
|
||||
r.B.DFF = RESET; // 8 bit
|
||||
r.B.SSM = SET; // software
|
||||
r.B.SSI = SET; // !!! netuším proč, ale jinak se nenastaví MSTR a SPE
|
||||
r.B.LSBFIRST = RESET;
|
||||
r.B.BR = FPCLK_64;
|
||||
return r.R;
|
||||
});
|
||||
SPI1.CRCR.R = 7u;
|
||||
SPI1.CTLR1.B.SPE = SET;
|
||||
}
|
||||
uint8_t SpiBlocked::ReadWriteByte(const uint8_t data) const {
|
||||
while (SPI1.STATR.B.TXE == RESET);
|
||||
SPI1.DATAR.B.DATAR = data;
|
||||
while (SPI1.STATR.B.RXNE == RESET);
|
||||
return SPI1.DATAR.B.DATAR;
|
||||
}
|
||||
void SpiBlocked::ChipSelect(const bool on) const {
|
||||
if (on) GPIOA.BSHR.B.BR4 = SET;
|
||||
else GPIOA.BSHR.B.BS4 = SET;
|
||||
}
|
||||
|
||||
|
12
V203F6P6/midi/spiblocked.h
Normal file
12
V203F6P6/midi/spiblocked.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef SPIBLOCKED_H
|
||||
#define SPIBLOCKED_H
|
||||
#include <stdint.h>
|
||||
|
||||
class SpiBlocked {
|
||||
public:
|
||||
explicit SpiBlocked () noexcept;
|
||||
uint8_t ReadWriteByte (const uint8_t data) const;
|
||||
void ChipSelect (const bool on) const;
|
||||
};
|
||||
|
||||
#endif // SPIBLOCKED_H
|
76
V203F6P6/midi/ton/gen.cpp
Normal file
76
V203F6P6/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);
|
||||
}
|
69
V203F6P6/midi/tone.cpp
Normal file
69
V203F6P6/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
V203F6P6/midi/tone.h
Normal file
26
V203F6P6/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
|
80
V203F6P6/midi/usb_desc.cpp
Normal file
80
V203F6P6/midi/usb_desc.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : usb_desc.c
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2022/08/20
|
||||
* Description : usb device descriptor,configuration descriptor,
|
||||
* string descriptors and other descriptors.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "usb_desc.h"
|
||||
|
||||
/* Device Descriptor */
|
||||
const uint8_t USBD_DeviceDescriptor[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x10, 0x01, // bcdUSB 1.10
|
||||
0x02, // bDeviceClass
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
DEF_USBD_UEP0_SIZE, // bMaxPacketSize0 64
|
||||
(uint8_t)DEF_USB_VID, (uint8_t)(DEF_USB_VID >> 8), // idVendor 0x1A86
|
||||
(uint8_t)DEF_USB_PID, (uint8_t)(DEF_USB_PID >> 8), // idProduct 0x5537
|
||||
DEF_IC_PRG_VER, 0x00, // bcdDevice 0.01
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x03, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
/* Configuration Descriptor */
|
||||
const uint8_t USBD_ConfigDescriptor[] =
|
||||
{
|
||||
/* Configure descriptor */
|
||||
0x09, 0x02, 0x43, 0x00, 0x02, 0x01, 0x00, 0x80, 0x32,
|
||||
|
||||
/* Interface 0 (CDC) descriptor */
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00,
|
||||
|
||||
/* Functional Descriptors */
|
||||
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||
|
||||
/* Length/management descriptor (data class interface 1) */
|
||||
0x05, 0x24, 0x01, 0x00, 0x01,
|
||||
0x04, 0x24, 0x02, 0x02,
|
||||
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||
|
||||
/* Interrupt upload endpoint descriptor */
|
||||
0x07, 0x05, 0x81, 0x03, (uint8_t)DEF_USBD_ENDP1_SIZE, (uint8_t)( DEF_USBD_ENDP1_SIZE >> 8 ), 0x01,
|
||||
|
||||
/* Interface 1 (data interface) descriptor */
|
||||
0x09, 0x04, 0x01, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x00,
|
||||
|
||||
/* Endpoint descriptor */
|
||||
0x07, 0x05, 0x02, 0x02, (uint8_t)DEF_USBD_ENDP2_SIZE, (uint8_t)( DEF_USBD_ENDP2_SIZE >> 8 ), 0x00,
|
||||
|
||||
/* Endpoint descriptor */
|
||||
0x07, 0x05, 0x83, 0x02, (uint8_t)DEF_USBD_ENDP3_SIZE, (uint8_t)( DEF_USBD_ENDP3_SIZE >> 8 ), 0x00,
|
||||
};
|
||||
#define DEF_STRDESC(p,n) w_text<(sizeof(p)>>1)>n={sizeof(n)-2u,3u,{p}}
|
||||
template<const unsigned N> struct w_text {
|
||||
uint8_t len, typ;
|
||||
const char16_t str [N];
|
||||
};
|
||||
static const DEF_STRDESC((u"Kizarm Labs."), str_1);
|
||||
static const DEF_STRDESC((u"Flash Programmer"),str_2);
|
||||
static const DEF_STRDESC((u"0001"), str_3);
|
||||
/* Language Descriptor */
|
||||
static const uint8_t LangDescr[] = {
|
||||
0x04, 0x03, 0x09, 0x04
|
||||
};
|
||||
const uint8_t * USBD_StringLangID = reinterpret_cast<const uint8_t *>(LangDescr);
|
||||
const uint8_t * USBD_StringVendor = reinterpret_cast<const uint8_t *>(&str_1);
|
||||
const uint8_t * USBD_StringProduct = reinterpret_cast<const uint8_t *>(&str_2);
|
||||
const uint8_t * USBD_StringSerial = reinterpret_cast<const uint8_t *>(&str_3);
|
||||
|
Loading…
Add table
Reference in a new issue