diff --git a/V203/ch32v203/hello.mk b/V203/ch32v203/hello.mk new file mode 100644 index 0000000..5a55f5a --- /dev/null +++ b/V203/ch32v203/hello.mk @@ -0,0 +1 @@ +OBJS += pwmclass.o diff --git a/V203/hello/Makefile b/V203/hello/Makefile new file mode 100644 index 0000000..36f68f2 --- /dev/null +++ b/V203/hello/Makefile @@ -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 diff --git a/V203/hello/ch32v203 b/V203/hello/ch32v203 new file mode 120000 index 0000000..7650c85 --- /dev/null +++ b/V203/hello/ch32v203 @@ -0,0 +1 @@ +../ch32v203/ \ No newline at end of file diff --git a/V203/hello/common b/V203/hello/common new file mode 120000 index 0000000..8332399 --- /dev/null +++ b/V203/hello/common @@ -0,0 +1 @@ +../common/ \ No newline at end of file diff --git a/V203/hello/generator.cpp b/V203/hello/generator.cpp new file mode 100644 index 0000000..4c595cf --- /dev/null +++ b/V203/hello/generator.cpp @@ -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 sin_tab (u16_sin); +extern void print_sinus_table (const TABLE & 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 +#include +#include + +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 +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_ diff --git a/V203/hello/linux/hello.mk b/V203/hello/linux/hello.mk new file mode 100644 index 0000000..9d29fa6 --- /dev/null +++ b/V203/hello/linux/hello.mk @@ -0,0 +1,2 @@ +OBJS += alsasound.o print.o + diff --git a/V203/hello/linux/print.cpp b/V203/hello/linux/print.cpp new file mode 100644 index 0000000..66bba97 --- /dev/null +++ b/V203/hello/linux/print.cpp @@ -0,0 +1,22 @@ +#include +#include "utils.h" +void print_morse_table (const TABLE & 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 & 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"); +} diff --git a/V203/hello/main.cpp b/V203/hello/main.cpp new file mode 100644 index 0000000..14eb7e9 --- /dev/null +++ b/V203/hello/main.cpp @@ -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; +} diff --git a/V203/hello/morse.cpp b/V203/hello/morse.cpp new file mode 100644 index 0000000..e20c725 --- /dev/null +++ b/V203/hello/morse.cpp @@ -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 compressed_table (compress); +extern void print_morse_table (const TABLE & 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; nsend(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(ptr); + DMA1.PADDR5.R = reinterpret_cast(& 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); +} diff --git a/V203/hello/pwmclass.h b/V203/hello/pwmclass.h new file mode 100644 index 0000000..21bba7a --- /dev/null +++ b/V203/hello/pwmclass.h @@ -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 diff --git a/V203/hello/utils.h b/V203/hello/utils.h new file mode 100644 index 0000000..fa7284c --- /dev/null +++ b/V203/hello/utils.h @@ -0,0 +1,60 @@ +#ifndef UTILS_H +#define UTILS_H + +typedef __SIZE_TYPE__ size_t; +templateconstexpr size_t array_size (T (&) [N]) { return N; } + +template class TABLE { + T data [N]; + public: + /** @brief Konstruktor. + * @param f Ukazatel na constexpr funkci, která pak vytvoří tabulku. + * */ + template explicit constexpr TABLE (F f) noexcept { + for (int n=0; n