diff --git a/V203F6P6/blink/Makefile b/V203F6P6/blink/Makefile index e34b2b8..e268569 100644 --- a/V203F6P6/blink/Makefile +++ b/V203F6P6/blink/Makefile @@ -1,11 +1,11 @@ TARGET?= ch32v203 -TOOL ?= gcc -#TOOL ?= clang +#TOOL ?= gcc +TOOL ?= clang PRJ = example -VPATH = . ./$(TARGET) +VPATH = . ./$(TARGET) ./common BLD = ./build/ DFLAGS = -d LFLAGS = -g @@ -13,11 +13,11 @@ LDLIBS = BFLAGS = --strip-unneeded CFLAGS = -MMD -Wall -Wno-parentheses -ggdb -fno-exceptions -ffunction-sections -fdata-sections -CFLAGS+= -I. -I./$(TARGET) +CFLAGS+= -I. -I./$(TARGET) -I./common DEL = rm -f # zdrojaky -OBJS = main.o +OBJS = main.o pwmclass.o morse.o generator.o #OBJS += include $(TARGET)/$(TOOL).mk diff --git a/V203F6P6/blink/common b/V203F6P6/blink/common new file mode 120000 index 0000000..8332399 --- /dev/null +++ b/V203F6P6/blink/common @@ -0,0 +1 @@ +../common/ \ No newline at end of file diff --git a/V203F6P6/blink/generator.cpp b/V203F6P6/blink/generator.cpp new file mode 100644 index 0000000..6793ec4 --- /dev/null +++ b/V203F6P6/blink/generator.cpp @@ -0,0 +1,28 @@ +#include "generator.h" +#include "pwmclass.h" +#include "utils.h" + +static constexpr unsigned W_TB = 8u; +static constexpr double AMPL = MAXPWM >> 1; +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); + +Generator::Generator (const unsigned f) noexcept : OneWay(), + freq (f), base(0u), incr (0u), ms_count (0u) { +} +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 { + 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 diff --git a/V203F6P6/blink/main.cpp b/V203F6P6/blink/main.cpp index 248838e..5e3d28a 100644 --- a/V203F6P6/blink/main.cpp +++ b/V203F6P6/blink/main.cpp @@ -1,28 +1,24 @@ -#include "system.h" -#include "gpio.h" -//////////////////////////////////////////////////////////////////////// -static constexpr unsigned ticks = SYSTEM_CORE_CLOCK / 1000u; -static volatile unsigned counter = 0u; -extern "C" [[gnu::interrupt]] void SysTick_Handler (); -//////////////////////////////////////////////////////////////////////// -void SysTick_Handler () { - SysTick.SR = 0u; - if (counter) counter -= 1u; -} -static void delay (const unsigned dly = 200u) { - counter = dly; - while (counter); -} - +/* SIMPLE EXAMPLE: LED blinking */ +/* Když už mám PWM hotové, tak to může pípat na pinu PA2. Je Tx pin pro + * budič RS485, na tuto sběrnici je možné připojit "špunty do uší" + * 32Ohm v sérii a zvuk je dostatečně hlasitý, čip to utáhne. + * Frekvence je 1kHz - čistý sinus. + * + * Pro tento procesor 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. + * + * Kód je fakticky recyklovaný z plného CH32V203, drobné úpravy tam jsou + * protože je to pinově trochu jinak, princip je stejný. + * */ +#include "morse.h" +////////////////////////////////////// +static GpioClass led (GPIOB, 8); +static Morse morse (led, 100u); int main () { - GpioClass led (GPIOB, 8); - SysTick.Config (ticks); - unsigned pass_cnt = 0u; for (;;) { - delay(); - const bool b = pass_cnt & 1u; - led << b; - pass_cnt += 1u; + morse << "hello world"; } return 0; } diff --git a/V203F6P6/blink/morse.cpp b/V203F6P6/blink/morse.cpp new file mode 100644 index 0000000..c399929 --- /dev/null +++ b/V203F6P6/blink/morse.cpp @@ -0,0 +1,87 @@ +#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 const TABLE compressed_table + ([](const unsigned n) -> auto { + 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 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); + } + if (state.B.TCIF2 != RESET) { + DMA1.INTFCR.B.CTCIF2 = SET; + if (pInstance) pInstance->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(buffer); + DMA1.PADDR2.R = reinterpret_cast(& TIM2.CH3CVR); + NVIC.EnableIRQ (DMA1_Channel2_IRQn); + 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) { + pInstance = this; + TimInit (); + DmaInit (); + // Enable TIM2 + TIM2.CTLR1.B.CEN = SET; +} diff --git a/V203F6P6/blink/pwmclass.h b/V203F6P6/blink/pwmclass.h new file mode 100644 index 0000000..dfb6d08 --- /dev/null +++ b/V203F6P6/blink/pwmclass.h @@ -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 = 24u; + 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 * 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); + 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 diff --git a/V203F6P6/blink/utils.h b/V203F6P6/blink/utils.h new file mode 100644 index 0000000..fa7284c --- /dev/null +++ b/V203F6P6/blink/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