diff --git a/V203F6P6/gsm/GsmDecoder.cpp b/V203F6P6/gsm/GsmDecoder.cpp new file mode 100644 index 0000000..aca6a94 --- /dev/null +++ b/V203F6P6/gsm/GsmDecoder.cpp @@ -0,0 +1,38 @@ +#include "pwmclass.h" +#include "GsmDecoder.h" + +static const unsigned int DataLenght = 2529384u; +extern "C" void * memcpy(void * dest, const void * src, size_t n); + +GsmDecoder::GsmDecoder(GpioClass & io) noexcept : OneWay(), + led(io), flash(), gsm(), count(0u), pass(false) { +} +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; +} +unsigned GsmDecoder::Send(uint16_t * dptr, const unsigned len) { + led << pass; + pass = ! pass; + const unsigned step = sizeof(gsm_frame); + static gsm_frame e; + flash.ReadBlock(count, e, step); + count += step; + gsm.decode (e, tmp_buf); + if (count >= DataLenght) count = 0u; + for (unsigned n=0u, k=0u; k { + GpioClass & led; + NorFlash flash; + GsmStatic gsm; + unsigned count; + bool pass; + gsm_signal tmp_buf [GSMLEN]; + public: + explicit GsmDecoder(GpioClass & io) noexcept; + unsigned Send (uint16_t * ptr, const unsigned len) override; +}; + +#endif // GSMDECODER_H diff --git a/V203F6P6/gsm/Makefile b/V203F6P6/gsm/Makefile new file mode 100644 index 0000000..d711291 --- /dev/null +++ b/V203F6P6/gsm/Makefile @@ -0,0 +1,60 @@ +TARGET?= ch32v203 + +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 -I./lib/gsm/inc +DEL = rm -f + +# zdrojaky +OBJS = main.o pwmclass.o +OBJS += GsmDecoder.o wrap.o +OBJS += spiblocked.o norflash.o + +include $(TARGET)/$(TOOL).mk +BOBJS = $(addprefix $(BLD),$(OBJS)) + +all: $(BLD) $(PRJ).elf +# ... atd. +-include $(BLD)*.d +LDLIBS += -L./lib -lgsm +# linker +$(PRJ).elf: $(BOBJS) ./lib/libgsm.a + -@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)%.o: %.s + -@echo [AS $(TOOL),$(TARGET)] $@ + @$(AS) $(CCPU) $< -o $@ +$(BLD): + mkdir $(BLD) +./lib/libgsm.a: + cd ./lib/gsm/src && $(MAKE) TARGET=$(TARGET) all +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/V203F6P6/gsm/ch32v203 b/V203F6P6/gsm/ch32v203 new file mode 120000 index 0000000..7650c85 --- /dev/null +++ b/V203F6P6/gsm/ch32v203 @@ -0,0 +1 @@ +../ch32v203/ \ No newline at end of file diff --git a/V203F6P6/gsm/common b/V203F6P6/gsm/common new file mode 120000 index 0000000..8332399 --- /dev/null +++ b/V203F6P6/gsm/common @@ -0,0 +1 @@ +../common/ \ No newline at end of file diff --git a/V203F6P6/gsm/lib b/V203F6P6/gsm/lib new file mode 120000 index 0000000..0ca1730 --- /dev/null +++ b/V203F6P6/gsm/lib @@ -0,0 +1 @@ +../../V203/gsm/lib/ \ No newline at end of file diff --git a/V203F6P6/gsm/main.cpp b/V203F6P6/gsm/main.cpp new file mode 100644 index 0000000..fd0560f --- /dev/null +++ b/V203F6P6/gsm/main.cpp @@ -0,0 +1,22 @@ +#include "pwmclass.h" +#include "GsmDecoder.h" +#include "norflash.h" +#include "oneway.h" +#include "gpio.h" +/////////////////////////////////////////////////////////// +/* GSM kecátko funguje i na tomto menším procesoru. + * Jen pak nusí být data v externí flash (připojené) přes + * SPI. Číst je je možné i v přerušení DMA, trvá to krátce. + * */ +/////////////////////////////////////////////////////////// +static PwmClass pwm; +static GpioClass pwr (GPIOB, 8); +static GsmDecoder decoder (pwr); + +int main () { + pwr << true; + pwm.attach (decoder); + for (;;) { + } + return 0; +} diff --git a/V203F6P6/gsm/norflash.cpp b/V203F6P6/gsm/norflash.cpp new file mode 100644 index 0000000..5f002b6 --- /dev/null +++ b/V203F6P6/gsm/norflash.cpp @@ -0,0 +1,45 @@ +#include "string.h" +#include "norflash.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, +}; +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); +} +/* Nic jiného zatím není potřeba, ale šlo by dodělat i zápis včetně mazání bloku. +*/ +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 +#include "spiblocked.h" + +class NorFlash { + SpiBlocked spi; + public: + explicit NorFlash () noexcept : spi() {} + unsigned ReadBlock (const unsigned addr, uint8_t * data, const unsigned len); +}; + +#endif // NORFLASH_H diff --git a/V203F6P6/gsm/pwmclass.cpp b/V203F6P6/gsm/pwmclass.cpp new file mode 100644 index 0000000..a932938 --- /dev/null +++ b/V203F6P6/gsm/pwmclass.cpp @@ -0,0 +1,107 @@ +#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(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) { + pPwmInstance = this; + TimInit (); + DmaInit (); + // Enable TIM2 + TIM2.CTLR1.B.CEN = SET; +} diff --git a/V203F6P6/gsm/pwmclass.h b/V203F6P6/gsm/pwmclass.h new file mode 100644 index 0000000..3572de2 --- /dev/null +++ b/V203F6P6/gsm/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 = 3u * 160u; + 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/gsm/spiblocked.cpp b/V203F6P6/gsm/spiblocked.cpp new file mode 100644 index 0000000..77576ab --- /dev/null +++ b/V203F6P6/gsm/spiblocked.cpp @@ -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) { + 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) { + if (on) GPIOA.BSHR.B.BR4 = SET; + else GPIOA.BSHR.B.BS4 = SET; +} + + diff --git a/V203F6P6/gsm/spiblocked.h b/V203F6P6/gsm/spiblocked.h new file mode 100644 index 0000000..4ca59c9 --- /dev/null +++ b/V203F6P6/gsm/spiblocked.h @@ -0,0 +1,12 @@ +#ifndef SPIBLOCKED_H +#define SPIBLOCKED_H +#include + +class SpiBlocked { +public: + explicit SpiBlocked () noexcept; + uint8_t ReadWriteByte (const uint8_t data); + void ChipSelect (const bool on); +}; + +#endif // SPIBLOCKED_H diff --git a/V203F6P6/gsm/wrap.c b/V203F6P6/gsm/wrap.c new file mode 100644 index 0000000..bb42b02 --- /dev/null +++ b/V203F6P6/gsm/wrap.c @@ -0,0 +1,22 @@ +/* Funkce z newlib, použité ve zdrojácích. + * Velmi zjednodušeno. + */ +typedef struct { + int quot, rem; +} div_t; +typedef __SIZE_TYPE__ size_t; +div_t div (int numerator, int denominator) { + const div_t result = { numerator / denominator, numerator % denominator }; + return result; +} +void * memset(void * s, int c, size_t n) { + char * p = (char*) s; + for (unsigned i=0u; i