add hello

This commit is contained in:
Kizarm 2024-08-17 14:27:07 +02:00
parent 00ed06384b
commit 7b5eae57c9
18 changed files with 621 additions and 0 deletions

1
V203/ch32v203/hello.mk Normal file
View file

@ -0,0 +1 @@
OBJS += pwmclass.o

55
V203/hello/Makefile Normal file
View file

@ -0,0 +1,55 @@
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 -Wno-parentheses -ggdb -fno-exceptions -ffunction-sections -fdata-sections
CFLAGS+= -I. -I./$(TARGET) -I./common
DEL = rm -f
# zdrojaky
OBJS = main.o generator.o morse.o
#OBJS +=
include $(TARGET)/$(TOOL).mk
include $(TARGET)/hello.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
# preloz co je potreba
$(BLD)%.o: %.c
-@echo [CC $(TOOL),$(TARGET)] $@
@$(CC) -std=gnu99 -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
# vycisti
clean:
$(DEL) $(BLD)* *.lst *.bin *.elf *.map *~
.PHONY: all clean flash run

1
V203/hello/ch32v203 Symbolic link
View file

@ -0,0 +1 @@
../ch32v203/

1
V203/hello/common Symbolic link
View file

@ -0,0 +1 @@
../common/

31
V203/hello/generator.cpp Normal file
View file

@ -0,0 +1,31 @@
#include "generator.h"
#include "utils.h"
static constexpr unsigned W_TB = 8u;
static constexpr double AMPL = 3000.0;
static constexpr int ULEN = 1 << W_TB;
static constexpr uint16_t u16_sin (const int x) {
const double a = (double (x) * D_PI) / double (ULEN);
const double s = AMPL * (1.0 + 0.96 * sincos (a, true));
return i_round (s);
}
static const TABLE<uint16_t, ULEN> sin_tab (u16_sin);
extern void print_sinus_table (const TABLE<uint16_t, ULEN> & tab);
Generator::Generator (const unsigned f) noexcept : OneWay(),
freq (f), base(0u), incr (0u), ms_count (0u) {
#ifdef __linux__
print_sinus_table(sin_tab);
#endif
}
uint16_t Generator::step() {
const uint16_t v = sin_tab [base >> 24];
base += incr;
return v;
}
unsigned int Generator::Send(uint16_t * const ptr, const unsigned int len) {
for (unsigned n=0u; n<len; n++) ptr [n] = step();
if (ms_count) ms_count -= 1u; // průchod zde je za 1 ms přesně
return len;
}

23
V203/hello/generator.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef GENERATOR_H
#define GENERATOR_H
#include "oneway.h"
/* Něco jako DDS, přesná frekvence není řešena (závisí na TIM1). */
class Generator : public OneWay {
const unsigned freq;
unsigned base, incr;
volatile unsigned ms_count;
public:
explicit Generator (const unsigned f) noexcept;
unsigned Send (uint16_t * const ptr, const unsigned len) override;
void delay (const unsigned ms) {
ms_count = ms;
while (ms_count);
}
void on () volatile { incr = freq; }
void off () volatile { base = 0u; incr = 0u; }
protected:
uint16_t step ();
};
#endif // GENERATOR_H

View file

@ -0,0 +1,79 @@
#include "pwmclass.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");
PwmClass * pA = (PwmClass *) 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;
}
PwmClass::PwmClass() noexcept : pL(buffer), pH(buffer + HALF_LEN), src(nullptr) {
signal (SIGINT, sig_handler);
open_alsa_device(2, 24000);
pthread_create (&rc, NULL, WriteHandler, this);
}

11
V203/hello/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/hello/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/hello/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
};
static constexpr uint32_t GPIOA = 0u;
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,2 @@
OBJS += alsasound.o print.o

View file

@ -0,0 +1,22 @@
#include <cstdio>
#include "utils.h"
void print_morse_table (const TABLE<unsigned char, 64> & tab) {
int n = 0;
printf("static const unsigned char compressed_table [] = {");
for (auto & e: tab) {
if ((n % 16) == 0) printf("\n");
printf("0x%02x,", e);
n++;
}
printf("\n};\n");
}
void print_sinus_table (const TABLE<unsigned short, 256> & tab) {
int n = 0;
printf("static const uint16_t sin_tab [] = {");
for (auto & e: tab) {
if ((n % 16) == 0) printf("\n");
printf("%5du,", e);
n++;
}
printf("\n};\n");
}

20
V203/hello/main.cpp Normal file
View file

@ -0,0 +1,20 @@
/* SIMPLE EXAMPLE: LED blinking */
/* Když už mám PWM hotové, tak to může na pinech PA7 a PA8 pípat.
* Frekvence je 1kHz - čistý sinus.
*
* Pro tento čip je možné použít pro překlad clang. Pak je možné
* tabulky pro sinus i pro komprimovaný kód morse použít konstantní
* výrazy. Nezvětšuje to délku kódu a je z toho vidět, jak se tyto
* věci počítají, aniž by bylo nutné použít nějaký externí nástroj.
* TARGET linux vypíše obě tabulky na konzoli - funguje i zvuk jako demo.
* */
#include "morse.h"
//////////////////////////////////////
static GpioClass led (GPIOA, 0);
static Morse morse (led, 100u);
int main () {
for (;;) {
morse << "hello world";
}
return 0;
}

91
V203/hello/morse.cpp Normal file
View file

@ -0,0 +1,91 @@
#include "morse.h"
#include "utils.h"
// Spočteme číslo (pro 1kHz) jako (1000 << 32) / 24000 (24 kHz je samplerate).
static constexpr unsigned F0 = (1000ull << 32) / 24000u;
static constexpr const char * const morse_code [] = { /* nedefinované znaky nahrazeny mezerou */
" ", /* */ " ", /*!*/ ".-..-.", /*\"*/ " ", /*#*/ " ", /*$*/
" ", /*%*/ " ", /*&*/ ".----.", /*\'*/ "-.--.", /*(*/ "-.--.-", /*)*/
" ", /***/ ".-.-.", /*+*/ "--..--", /*,*/ "-....-", /*-*/ ".-.-.-", /*.*/ "-..-." , /*/*/
"-----", /*0*/ ".----", /*1*/ "..---", /*2*/ "...--", /*3*/ "....-", /*4*/
".....", /*5*/ "-....", /*6*/ "--...", /*7*/ "---..", /*8*/ "----.", /*9*/
"---...", /*:*/ "-.-.-.", /*;*/ " ", /*<*/ "-...-" , /*=*/ " ", /*>*/ "..--..", /*?*/
".--.-.", /*@*/
".-", /*A*/ "-...", /*B*/ "-.-.", /*C*/ "-..", /*D*/ ".", /*E*/ "..-.", /*F*/
"--.", /*G*/ "....", /*H*/ "..", /*I*/ ".---", /*J*/ "-.-", /*K*/ ".-..", /*L*/
"--", /*M*/ "-.", /*N*/ "---", /*O*/ ".--.", /*P*/ "--.-", /*Q*/ ".-.", /*R*/
"...", /*S*/ "-", /*T*/ "..-", /*U*/ "...-", /*V*/ ".--", /*W*/ "-..-", /*X*/
"-.--", /*Y*/ "--..", /*Z*/ " ", " ", " ", " ", "..--.-", /*_*/
};
static constexpr unsigned slen (const char * const str) {
unsigned n = 0;
while (str[n]) n++;
return n;
}
static constexpr unsigned char compress (const unsigned n) {
const char * const ptr = morse_code [n];
const unsigned len = slen (ptr);
unsigned char mb = 0u;
if (ptr [0] == ' ') return mb;
mb = (len & 7u) << 5;
for (unsigned n=0; n<len; n++) {
if (ptr [n] == '-') mb |= (1u << n);
}
return mb;
}
static const TABLE<unsigned char, array_size (morse_code)> compressed_table (compress);
extern void print_morse_table (const TABLE<unsigned char, array_size(morse_code)> & tab);
Morse::Morse(const GpioClass & pin, const unsigned int ms) noexcept : unit (ms), led (pin),
gen (F0), pwm () {
pwm.attach(gen);
#ifdef __linux__
print_morse_table (compressed_table);
#endif
}
const Morse & Morse::operator<< (const char * text) {
for (unsigned n=0; ; n++) {
const char c = text [n];
if (c == '\0') break;
morse_byte mb;
if (c < '\x20') {
} else if (c < '`') {
const int i = c - '\x20';
mb.byte = compressed_table [i];
} else if (c == '`') {
} else if (c <= 'z') {
const int i = c - '\x40';
mb.byte = compressed_table [i];
} else {
}
out (mb);
}
gen.delay (10 * unit);
return * this;
}
/* . => 1 x unit
* - => 3 x unit
* mezera mezi značkami => 1 x unit
* mezera mezi znaky => 3 x unit
* mezera mezi slovy => 7 x unit
* */
void Morse::out (const morse_byte mb) {
/* Finta je v tom, že i když se pole mlen a bits překrývají,
* nevadí to - maximální délka je 6, takže v nejnižším bitu
* mlen může být obsažen 1 bit 6.bitového znaku.
* Takhle napsáno to běhá jen na malém indiánu, přepisovat
* to do bitových posunů nebudu, i když by to bylo čistší.
* */
const unsigned len = mb.mlen > 6u ? 6u : mb.mlen;
if (!len) { gen.delay (4 * unit); return; }
for (unsigned n=0; n<len; n++) {
off ();
gen.delay (unit);
const unsigned v = (1u << n) & mb.bits;
const unsigned d = v ? 3u : 1u;
on ();
gen.delay (d * unit);
off ();
}
gen.delay (3 * unit);
}

36
V203/hello/morse.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef MORSE_H
#define MORSE_H
#include "gpio.h"
#include "generator.h"
#include "pwmclass.h"
union morse_byte {
struct {
unsigned char bits : 5;
unsigned char mlen : 3;
};
unsigned char byte;
explicit constexpr morse_byte () noexcept : byte (0u) {}
};
class Morse {
const unsigned unit;
const GpioClass & led;
Generator gen;
PwmClass pwm;
public:
explicit Morse (const GpioClass & pin, const unsigned ms = 100u) noexcept;
const Morse & operator<< (const char * text);
protected:
void out (const morse_byte mb);
void on () {
led << false; // LED je připojena proti VCC, zde se tedy rozsvítí
gen.on ();
}
void off () {
led << true;
gen.off();
}
};
#endif // MORSE_H

114
V203/hello/pwmclass.cpp Normal file
View file

@ -0,0 +1,114 @@
#include "pwmclass.h"
#include "gpio.h"
#include "system.h"
static PwmClass * 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);
//pInstance->signalize(); // zbytečný efekt
}
/*
* 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 = 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 ch5
r.B.EN = SET;
return r.R;
});
}
PwmClass::PwmClass() noexcept : pL(buffer), pH(buffer + HALF_LEN), src(nullptr) {
pInstance = this;
tim1pwm_init ();
dma1ch5_init (buffer);
}

25
V203/hello/pwmclass.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef PWMCLASS_H
#define PWMCLASS_H
#include "gpio.h"
#include "oneway.h"
static constexpr unsigned HALF_LEN = 24u;
static constexpr unsigned FULL_LEN = 2u * HALF_LEN;
static constexpr unsigned MAXPWM = 6000u;
/* Používá TIM1, PWM kanál 1, DMA1 kanál 5, přerušení DMA1_Channel5_IRQHandler */
class PwmClass {
uint16_t * const pL;
uint16_t * const pH;
uint16_t buffer [FULL_LEN];
OneWay * src;
public:
explicit PwmClass () noexcept;
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);
}
uint16_t * getBuff () const { return pL; }
};
#endif // PWMCLASS_H

60
V203/hello/utils.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef UTILS_H
#define UTILS_H
typedef __SIZE_TYPE__ size_t;
template<class T, size_t N>constexpr size_t array_size (T (&) [N]) { return N; }
template<class T, const int N> class TABLE {
T data [N];
public:
/** @brief Konstruktor.
* @param f Ukazatel na constexpr funkci, která pak vytvoří tabulku.
* */
template<typename F> explicit constexpr TABLE (F f) noexcept {
for (int n=0; n<N; n++) data [n] = f (n);
}
/** operator[] vrátí konstantní odkaz na prvek pole, protože se předpokládá,
* že instance této třídy bude jako taková též konstantní
*/
const T & operator[] (const int index) const {
return data [index];
}
/** @class iterator
* @brief range-based for () */
class iterator {
const T * ptr;
public:
iterator(const T * _ptr) : ptr (_ptr) {}
iterator operator++ () { ++ptr; return * this; }
bool operator!= (const iterator & other) const { return ptr != other.ptr; }
const T & operator* () const { return * ptr; }
};
iterator begin () const { return iterator (data ); }
iterator end () const { return iterator (data + N); }
protected:
};
static constexpr double X_PI = 3.14159265358979323846;
static constexpr double D_PI = 2.0 * X_PI;
/* cmath nejde použít, tak si to mírně zjednodušíme, ale musí to fungovat */
static constexpr double dabs (const double a) { return a < 0.0 ? -a : +a; }
static constexpr int i_round (const double a) { return a < 0.0 ? int (a - 0.5) : int (a + 0.5); }
/* tahle divná funkce počítá sinus, pokud even=true i kosinus, pokud even=false */
static constexpr double sincos (const double x, const bool even) {
double result (0.0), element(1.0), divider(0.0);
if (even) { element *= x; divider += 1.0; }
constexpr double eps = 1.0e-9; // maximální chyba výpočtu
const double aa = - (x * x);
for (;;) {
result += element;
if (dabs (element) < eps) break;
divider += 1.0;
double fact = divider;
divider += 1.0;
fact *= divider;
element *= aa / fact;
}
return result;
}
#endif // UTILS_H