From de48701e8c96bc78feb48bc90f4eae540b04176b Mon Sep 17 00:00:00 2001 From: Kizarm Date: Sun, 17 Mar 2024 16:33:48 +0100 Subject: [PATCH] add sound to hello --- hello/Makefile | 11 ++--- hello/common | 1 + hello/generator.cpp | 14 ++++++ hello/generator.h | 24 ++++++++++ hello/main.cpp | 8 +++- hello/morse.cpp | 49 ++++++++------------- hello/morse.h | 18 ++++++-- hello/pwmclass.cpp | 104 ++++++++++++++++++++++++++++++++++++++++++++ hello/pwmclass.h | 23 ++++++++++ hello/sin.py | 27 ++++++++++++ 10 files changed, 238 insertions(+), 41 deletions(-) create mode 120000 hello/common create mode 100644 hello/generator.cpp create mode 100644 hello/generator.h create mode 100644 hello/pwmclass.cpp create mode 100644 hello/pwmclass.h create mode 100755 hello/sin.py diff --git a/hello/Makefile b/hello/Makefile index 354b4dc..44fd83d 100644 --- a/hello/Makefile +++ b/hello/Makefile @@ -1,8 +1,6 @@ # ch32v003 TARGET?= ch32v003 TOOL ?= gcc -#TOOL ?= clang - PRJ = example @@ -14,11 +12,11 @@ LDLIBS = BFLAGS = --strip-unneeded CFLAGS = -MMD -Wall -ggdb -fno-exceptions -ffunction-sections -fdata-sections -CFLAGS+= -I. -I./$(TARGET) -I/usr/include/newlib +CFLAGS+= -I. -I./$(TARGET) -I./common -I/usr/include/newlib DEL = rm -f # zdrojaky -OBJS = main.o morse.o +OBJS = main.o morse.o generator.o pwmclass.o sin.o include $(TARGET)/$(TOOL).mk BOBJS = $(addprefix $(BLD),$(OBJS)) @@ -36,6 +34,7 @@ $(PRJ).elf: $(BOBJS) $(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)] $@ @@ -45,9 +44,11 @@ $(BLD)%.o: %.cpp @$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@ $(BLD): mkdir $(BLD) +sin.c: sin.py + ./sin.py flash: $(PRJ).elf minichlink -w $(PRJ).bin flash -b # vycisti clean: - $(DEL) $(BLD)* *.lst *.bin *.elf *.map *~ + $(DEL) $(BLD)* *.lst *.bin *.hex *.elf *.map sin.c *~ .PHONY: all clean diff --git a/hello/common b/hello/common new file mode 120000 index 0000000..8332399 --- /dev/null +++ b/hello/common @@ -0,0 +1 @@ +../common/ \ No newline at end of file diff --git a/hello/generator.cpp b/hello/generator.cpp new file mode 100644 index 0000000..598a79b --- /dev/null +++ b/hello/generator.cpp @@ -0,0 +1,14 @@ +#include "generator.h" + +extern "C" const uint16_t sin_tab[0x100]; + +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 1 x unit @@ -51,7 +38,7 @@ const Morse & Morse::operator<< (const char * text) const { * mezera mezi znaky => 3 x unit * mezera mezi slovy => 7 x unit * */ -void Morse::out (const morse_byte mb) const { +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. @@ -59,15 +46,15 @@ void Morse::out (const morse_byte mb) const { * to do bitových posunů nebudu, i když by to bylo čistší. * */ const unsigned len = mb.mlen > 6u ? 6u : mb.mlen; - if (!len) { delay (4 * unit); return; } + if (!len) { gen.delay (4 * unit); return; } for (unsigned n=0; nsend(false); + else if (state.B.TCIF5 != RESET) pInstance->send(true); +} + +/* + * initialize TIM1 for PWM + */ +static inline void tim1pwm_init () noexcept { + // Enable GPIOD and TIM1 + RCC.APB2PCENR.modify([] (RCC_Type::APB2PCENR_DEF & r) -> auto { + r.B.IOPDEN = SET; + r.B.TIM1EN = SET; + return r.R; + }); + // PD0 is T1CH1N, PD2 is T1CH1, 10MHz Output alt func, push-pull + GPIOD.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto { + r.B.CNF0 = 2u; + r.B.MODE0 = 1u; + r.B.CNF2 = 2u; + r.B.MODE2 = 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/hello/pwmclass.h b/hello/pwmclass.h new file mode 100644 index 0000000..ced910c --- /dev/null +++ b/hello/pwmclass.h @@ -0,0 +1,23 @@ +#ifndef PWMCLASS_H +#define PWMCLASS_H +#include "oneway.h" +static constexpr unsigned HALF_LEN = 24u; +static constexpr unsigned FULL_LEN = 2u * HALF_LEN; +static constexpr unsigned MAXPWM = 2000u; +/* 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); + } +}; + +#endif // PWMCLASS_H diff --git a/hello/sin.py b/hello/sin.py new file mode 100755 index 0000000..9d7948b --- /dev/null +++ b/hello/sin.py @@ -0,0 +1,27 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import math + +header = '''/* Generated file */ +#include +const uint16_t sin_tab[] = {{{0:s} +}}; +''' + +def generate(): + s = '' + for n in range(0,256): + if (n % 16) == 0: + s += '\n ' + a = float(n) * math.pi / 128.0 + v = int (round (1000.0 * (1.0 + math.sin (a)))); + s += '{0:+6d},'.format(v) + return s + +if __name__ == '__main__': + s = generate() + f = open ('sin.c','w') + f.write(header.format(s)) + f.close() +