This commit is contained in:
Kizarm 2025-01-30 19:27:19 +01:00
parent fa73dbffbc
commit f11366d182
14 changed files with 445 additions and 0 deletions

View file

@ -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<uint16_t>(),
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<len; n++) {
const int16_t s = tmp_buf[n];
dptr [k++] = pwm_sd (s); // vyzkoušená metoda jak vylepšit PWM
dptr [k++] = pwm_sd (s); // pokud máme frekvenci PWM větší
dptr [k++] = pwm_sd (s); // než vzorkovací frekvence signálu ft = n * fs, n = 3
}
return 0;
}

22
V203F6P6/gsm/GsmDecoder.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef GSMDECODER_H
#define GSMDECODER_H
#include "gpio.h"
#include "oneway.h"
#include "gsm.h"
#include "norflash.h"
static constexpr int GSMLEN = 160;
class GsmDecoder : public OneWay<uint16_t> {
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

60
V203F6P6/gsm/Makefile Normal file
View file

@ -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

1
V203F6P6/gsm/ch32v203 Symbolic link
View file

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

1
V203F6P6/gsm/common Symbolic link
View file

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

1
V203F6P6/gsm/lib Symbolic link
View file

@ -0,0 +1 @@
../../V203/gsm/lib/

22
V203F6P6/gsm/main.cpp Normal file
View file

@ -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;
}

45
V203F6P6/gsm/norflash.cpp Normal file
View file

@ -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<sizeof(header); n++) spi.ReadWriteByte(header.bytes[n]);
for (unsigned n=0u; n<len; n++) data [n] = spi.ReadWriteByte(0xff);
spi.ChipSelect(false);
return len;
}

13
V203F6P6/gsm/norflash.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef NORFLASH_H
#define NORFLASH_H
#include <stdint.h>
#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

107
V203F6P6/gsm/pwmclass.cpp Normal file
View file

@ -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<size_t>(buffer);
DMA1.PADDR2.R = reinterpret_cast<size_t>(& 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;
}

35
V203F6P6/gsm/pwmclass.h Normal file
View file

@ -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<uint16_t> * src;
public:
explicit PwmClass () noexcept;
void attach (OneWay<uint16_t> & 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

View file

@ -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;
}

12
V203F6P6/gsm/spiblocked.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef SPIBLOCKED_H
#define SPIBLOCKED_H
#include <stdint.h>
class SpiBlocked {
public:
explicit SpiBlocked () noexcept;
uint8_t ReadWriteByte (const uint8_t data);
void ChipSelect (const bool on);
};
#endif // SPIBLOCKED_H

22
V203F6P6/gsm/wrap.c Normal file
View file

@ -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<n; i++) p [i] = c;
return s;
}
void * memcpy(void * dest, const void * src, size_t n) {
char * d = (char*) dest;
const char * s = (const char*) src;
for (unsigned i=0u; i<n; i++) d [i] = s [i];
return dest;
}