add thermometer as counter

This commit is contained in:
Kizarm 2025-01-31 16:38:37 +01:00
parent f11366d182
commit 51d6bea95e
24 changed files with 2415 additions and 0 deletions

View file

@ -0,0 +1,44 @@
#include "pwmclass.h"
#include "GsmDecoder.h"
GsmDecoder::GsmDecoder(FIFO<uint32_t, FIFOLEN> & f) noexcept :
OneWay<uint16_t>(), fifo(f), enable(GPIOB, 1u), flash(), gsm(), idle(0u) {
}
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) {
uint32_t source;
if (fifo.Read(source)) {
enable << true; // zapni budič
idle = 0u;
gsm_frame tmp;
flash.ReadBlock(source, tmp, sizeof(gsm_frame));
gsm.decode(tmp, tmpbuf);
for (unsigned n=0u, k=0u; k<len; n++) {
const int16_t s = tmpbuf[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 GSMLEN;
}
idle += 1u;
if (idle > 3u) { // pro vypnutí budiče je lépe chvilinku počkat
idle = 3u;
enable << false;
}
for (unsigned n=0u; n<len; n++) {
dptr [n] = MAXPWM >> 1;
}
return 0;
}

View file

@ -0,0 +1,21 @@
#ifndef GSMDECODER_H
#define GSMDECODER_H
#include "gpio.h"
#include "player.h"
#include "oneway.h"
#include "gsm.h"
#include "norflash.h"
class GsmDecoder : public OneWay<uint16_t> {
FIFO<uint32_t, FIFOLEN> & fifo;
GpioClass enable;
NorFlash flash;
GsmStatic gsm;
gsm_signal tmpbuf [GSMLEN];
unsigned idle;
public:
explicit GsmDecoder(FIFO<uint32_t, FIFOLEN> & f) noexcept;
unsigned Send (uint16_t * ptr, const unsigned len) override;
};
#endif // GSMDECODER_H

View file

@ -0,0 +1,61 @@
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 -DEXTROM=1
DEL = rm -f
# zdrojaky
OBJS = main.o pwmclass.o
OBJS += GsmDecoder.o wrap.o
OBJS += spiblocked.o norflash.o
OBJS += extdata.o player.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

View file

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

1
V203F6P6/thermometer/common Symbolic link
View file

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

View file

@ -0,0 +1,46 @@
/* GENERATED FILE DO NOT EDIT */
#ifndef EXTROM
#error "Compile with -DEXTROM=1"
#endif
#include "gsmdata.h"
const SayedTexts sayed_texts = {
.nula = { 0x0000a2a0u, 22 },
.jedna = { 0x00009f60u, 25 },
.dva = { 0x00009c80u, 22 },
.dve = { 0x00009920u, 26 },
.tri = { 0x00009680u, 20 },
.ctyri = { 0x00009300u, 27 },
.pet = { 0x00009080u, 19 },
.sest = { 0x00008d20u, 26 },
.sedm = { 0x00008a20u, 23 },
.osm = { 0x000087e0u, 17 },
.devet = { 0x00008460u, 27 },
.deset = { 0x000080e0u, 27 },
.jedenact = { 0x00007c00u, 37 },
.dvanact = { 0x00007740u, 36 },
.trinact = { 0x00007280u, 36 },
.ctrnact = { 0x00006da0u, 37 },
.patnact = { 0x00006920u, 34 },
.sestnact = { 0x00006300u, 47 },
.sedmnact = { 0x00005d40u, 44 },
.osmnact = { 0x00005840u, 38 },
.devatenact = { 0x000052a0u, 43 },
.dvacet = { 0x00004ea0u, 31 },
.tricet = { 0x00004ae0u, 29 },
.ctyricet = { 0x00004640u, 35 },
.padesat = { 0x000041c0u, 34 },
.sedesat = { 0x00003c80u, 40 },
.sedmdesat = { 0x000036a0u, 45 },
.osmdesat = { 0x00003180u, 39 },
.devadesat = { 0x00002c20u, 41 },
.sto = { 0x00002940u, 22 },
.dveste = { 0x00002440u, 38 },
.sta = { 0x00002180u, 21 },
.set = { 0x00001ec0u, 21 },
.tisic = { 0x00001ac0u, 31 },
.tisice = { 0x000015a0u, 39 },
.minus = { 0x000011e0u, 29 },
.point = { 0x00000e20u, 29 },
.hafo = { 0x00000ac0u, 26 },
.units = { 0x00000280u, 64 },
};

View file

@ -0,0 +1,27 @@
PR = test
VPATH = .
CXX = g++
CC = gcc
CFLAGS = -Wall -Os -I.. -I../lib/gsm/inc
#CFLAGS+= -DDEBUG
MFLAGS = -o $(PR)
LFLAGS =
#arm-none-eabi-
all: data.elf $(PR)
OBJECTS = main.o gsmdata.o texts.o
$(PR): $(OBJECTS)
$(CXX) $(MFLAGS) $(OBJECTS) $(LFLAGS)
clean:
rm -f *.o *~ data.elf data.map data.hex $(PR)
%.o: %.cpp
$(CXX) -std=c++14 -c $(CFLAGS) -o $@ $<
%.o: %.c
$(CC) -c $(CFLAGS) -o $@ $<
data.elf: gsmdata.c
gcc -Wall -Os -I.. -I../lib/gsm/inc -Wl,-Map=data.map,--gc-sections -nostdlib -nostartfiles -o data.elf gsmdata.c -L. -T script.ld
objcopy -O binary data.elf data.bin
#objcopy -O ihex data.elf data.hex
.PHONY: all clean

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,127 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include "gsmdata.h"
#include "texts.h"
/* Jedna z možností jak dostat data do externí flash. Sice to poněkud
* nabobtná (na začátku je zbytečně celá struktura SayedTexts + padding),
* ale funguje to.
* 1. zdroják gsmdata.c se přeloží a pomocí linker skriptu se vytvoří elf
* 2. z elf se udělá binárka, příp. hex soubor standardním binutils
* 3. binárka se tímto přečte a do extdata.c se extrahují potřebné informace.
* extdata.c pak obsahuje adresy slov a jejich délky potřebné pro čtení.
* Nezávisí to na tom, zda se binárka vytváří na 64. nebo 32. bit stroji. */
static void test () {
return;
// delky sedi
printf("l=0x%zX, f=%zd\n", sizeof(sayed_texts), sizeof(gsm_frame));
const gsm_frame * p = sayed_texts.nula.frames;
const int n = sayed_texts.nula.no_frames;
const unsigned char * o = (const unsigned char *) p;
for (int i=0; i<n; i++) {
const unsigned char * c = p [i];
int d = int (c - o);
printf("p=%p, d=%d\n", c, d);
o = c;
}
}
class Reader {
unsigned char * file_data;
int file_size;
public:
explicit Reader() : file_data(nullptr), file_size(0) {}
~Reader() {
if (file_data) delete [] file_data;
}
int process (const char * filename);
protected:
void check ();
void compare (const size_t ne, const gsm_frame * c, const int f);
void generate();
};
int Reader::process(const char * filename) {
struct stat statbuf;
int r = ::stat(filename, &statbuf);
if (r < 0) return -1;
if (file_data) delete [] file_data;
file_data = new unsigned char [statbuf.st_size];
FILE * in_file = fopen(filename, "r");
r = fread (file_data, 1, statbuf.st_size, in_file);
file_size = r;
printf("readen %d bytes (%g 0x1000 blocks)\n", file_size, double(file_size)/4096.0);
if (!in_file) return -1;
fclose(in_file);
check(); // kontrola - mělo by sedět to co je v data.bin a přeložené gsmdata.c
generate(); // generuje C-soubor s adresami a počty rámců
return 0;
}
void Reader::check() {
if (!file_size) return;
// file_data[1000] = 0; // když něco změním, opravdu nastane chyba
SayedTexts st;
memcpy (&st, file_data, sizeof(SayedTexts));
const text_p * ebase = (text_p *) & st;
const text_p * cbase = (text_p *) & sayed_texts;
const size_t count = sizeof(SayedTexts) / sizeof (text_p);
printf("count=%zd\n", count);
for (unsigned n=0; n<count; n++) {
const text_p & ep = ebase [n];
const text_p & cp = cbase [n];
if (ep.no_frames != cp.no_frames) // struct SayedTexts OK.
printf("eframe: %p len=%d, cframe: %p, len=%d\n", ep.frames, ep.no_frames, cp.frames, cp.no_frames);
compare (size_t (ep.frames), cp.frames, cp.no_frames);
}
}
static bool compare_frame (const unsigned char * a, const unsigned char * b, const int n) {
for (int i=0; i<n; i++) {
if (a [i] != b [i]) return false;
}
return true;
}
void Reader::compare(const size_t ne, const gsm_frame * c, const int f) {
// ofset + adresa pameti
const gsm_frame * e = (gsm_frame *) (ne + (size_t) file_data);
for (int k=0; k<f; k++) {
const unsigned char * a = c [k];
const unsigned char * b = e [k];
if (!k) { // jen aby bylo neco videt
for (int n=0; n<10; n++) printf("%02X:%02X|", a[n], b[n]);
printf("...\n");
}
if (!compare_frame(a, b, sizeof(gsm_frame))) printf ("error:%d\n", k);
}
}
// Jen toto je podstatné - vytvoření extdata.c
static const char * prefix = R"---(/* GENERATED FILE DO NOT EDIT */
#ifndef EXTROM
#error "Compile with -DEXTROM=1"
#endif
#include "gsmdata.h"
const SayedTexts sayed_texts = {
)---";
void Reader::generate() {
FILE * out = fopen("../extdata.c","w");
fprintf(out, "%s", prefix);
SayedTexts st;
memcpy (&st, file_data, sizeof(SayedTexts));
constexpr size_t count = sizeof( SayedTexts) / sizeof (text_p);
constexpr size_t txtct = sizeof(eSayedTexts) / sizeof (text_pair);
static_assert (count == txtct, "Bad size, regenerate sources");
const text_p * ebase = (text_p *) & st; // pole ze struktury (stejných prvků)
const text_pair * tbase = (text_pair*) & helper_texts;
for (unsigned n=0; n<count; n++) {
const text_p & ep = ebase [n];
const text_pair & tp = tbase [n];
fprintf(out, " .%s = { 0x%08lxu, %d },\n", tp.name, size_t (ep.frames), ep.no_frames);
}
fprintf(out, "};\n");
fclose(out);
}
/// main ()
int main (int argc, char *argv[]) {
test(); // gsm ramce jsou pakovane, nemel by byt problem.
Reader read;
return read.process("data.bin");
}

View file

@ -0,0 +1,40 @@
/*OUTPUT_FORMAT (binary) možnosti zjistíme příkazem "objdump -i" (lze i ihex) */
ENTRY (sayed_texts) /* odtud se začne (start po resetu) */
/* popis pamětí */
MEMORY {
ram (RWX) : ORIGIN = 0x00, LENGTH = 0x40000
}
/* popis sekcí */
SECTIONS {
.fixed : { /* výstupní sekce v rom - nazveme např. .fixed */
. = ALIGN (8); /* zarovnání příští sekce na 4 byty (celkem zbytečné) */
*(.data.rel.ro*)
*(.rodata*) /* .rodata* - stejně jako .text*, ale pro const proměnné (data) (-fdata-sections) */
. = ALIGN (8);
*(.text*) /* gcc pojmenovává sekce s kódem jako .text, pokud je použito -ffunction-sections,
pak bude každá funkce v jiné sekci s názvy .text.jmeno_funkce, proto ta * na konci */
. = ALIGN (8);
*(.data*) /* .data* - stejně jako .text*, ale pro proměnné (data inicializovaná) */
. = ALIGN (8);
*(.bss*) /* .bss* - stejně jako .text*, ale pro proměnné (data inicializovaná na 0) */
*(COMMON*)
*(.note.*)
. = ALIGN (4096);
relocate_end = .; /* konec dat v sekci .relocate - podle toho se např. nějak určí hodnota SP */
end = .;
} > ram /* tady je řečeno, že to má být v ram */
/DISCARD/ : {
*(.rela*)
*(.dynamic)
*(.eh*)
*(.debug*)
*(.comment*)
*(.interp)
*(.dynsym)
*(.dynstr)
*(.hash)
*(.gnu.hash)
*(.header)
} : phdr
}

View file

@ -0,0 +1,47 @@
#include "texts.h"
const eSayedTexts helper_texts = {
.nula = { "nula", "nula" },
.jedna = { "jedna", "jedna" },
.dva = { "dva", "dva" },
.dve = { "dve", "dvě" },
.tri = { "tri", "tři" },
.ctyri = { "ctyri", "čtyři" },
.pet = { "pet", "pět" },
.sest = { "sest", "šest" },
.sedm = { "sedm", "sedm" },
.osm = { "osm", "osm" },
.devet = { "devet", "devět" },
.deset = { "deset", "deset" },
.jedenact = { "jedenact", "jedenáct" },
.dvanact = { "dvanact", "dvanáct" },
.trinact = { "trinact", "třináct" },
.ctrnact = { "ctrnact", "čtrnáct" },
.patnact = { "patnact", "patnáct" },
.sestnact = { "sestnact", "šestnáct" },
.sedmnact = { "sedmnact", "sedmnáct" },
.osmnact = { "osmnact", "osmnáct" },
.devatenact = { "devatenact", "devatenáct" },
.dvacet = { "dvacet", "dvacet" },
.tricet = { "tricet", "třicet" },
.ctyricet = { "ctyricet", "čtyřicet" },
.padesat = { "padesat", "padesát" },
.sedesat = { "sedesat", "šedesát" },
.sedmdesat = { "sedmdesat", "sedmdesát" },
.osmdesat = { "osmdesat", "osmdesát" },
.devadesat = { "devadesat", "devadesát" },
.sto = { "sto", "sto" },
.dveste = { "dveste", "dvěstě" },
.sta = { "sta", "sta" },
.set = { "set", "set" },
.tisic = { "tisic", "tisíc" },
.tisice = { "tisice", "tisíce" },
.minus = { "minus", "mínus" },
.point = { "point", "celých" },
.hafo = { "hafo", "hafo" },
.units = { "units", "stupňů Celzia" },
//.svejk = { "svejk", svejk_t },
};

View file

@ -0,0 +1,26 @@
#ifndef TEXTS_H
#define TEXTS_H
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef struct text_pair_s {
const char * name;
const char * text;
} text_pair;
typedef struct eSayedTexts_s {
// mandatorní část použitá v playeru :
text_pair nula, jedna, dva, dve, tri, ctyri, pet, sest, sedm, osm, devet;
text_pair deset, jedenact, dvanact, trinact, ctrnact, patnact;
text_pair sestnact, sedmnact, osmnact, devatenact;
text_pair dvacet, tricet, ctyricet, padesat, sedesat, sedmdesat, osmdesat, devadesat;
text_pair sto, dveste, sta, set, tisic, tisice;
text_pair minus, point, hafo, units;
// uživatelské texty :
// text_pair svejk;
} eSayedTexts;
extern const eSayedTexts helper_texts;
#ifdef __cplusplus
};
#endif // __cplusplus
#endif // TEXTS_H

View file

@ -0,0 +1,62 @@
/* GENERATED FILE DO NOT EDIT !!! */
#ifndef _GSM_DATA_H
#define _GSM_DATA_H
#include "gsm.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef struct gsm_data_s {
#ifdef EXTROM
unsigned long frames;
#else
const gsm_frame * frames;
#endif
int no_frames;
} text_p;
typedef struct SayedTexts_s {
text_p nula;
text_p jedna;
text_p dva;
text_p dve;
text_p tri;
text_p ctyri;
text_p pet;
text_p sest;
text_p sedm;
text_p osm;
text_p devet;
text_p deset;
text_p jedenact;
text_p dvanact;
text_p trinact;
text_p ctrnact;
text_p patnact;
text_p sestnact;
text_p sedmnact;
text_p osmnact;
text_p devatenact;
text_p dvacet;
text_p tricet;
text_p ctyricet;
text_p padesat;
text_p sedesat;
text_p sedmdesat;
text_p osmdesat;
text_p devadesat;
text_p sto;
text_p dveste;
text_p sta;
text_p set;
text_p tisic;
text_p tisice;
text_p minus;
text_p point;
text_p hafo;
text_p units;
} SayedTexts;
extern const SayedTexts sayed_texts;
#ifdef __cplusplus
};
#endif // __cplusplus
#endif // _GSM_DATA_H

1
V203F6P6/thermometer/lib Symbolic link
View file

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

View file

@ -0,0 +1,30 @@
#include "pwmclass.h"
#include "fifo.h"
#include "player.h"
#include "GsmDecoder.h"
// #include "adcdma.h"
#include "oneway.h"
#include "gpio.h"
////////////////////////////////////////////////////////
/* Demo, které jen počítá od 0 do 1000000. Výstup je PWM
* na pinu PA2 24kHz, enable PB1. Odvozeno z teploměru na
* https://github.com/Kizarm/TTSCP_Client/tree/main/kecal/stm
*
*/
////////////////////////////////////////////////////////
static GpioClass led (GPIOB, 8);
static PwmClass pwm;
static FIFO<uint32_t, FIFOLEN> fifo;
static TextPlayer player (fifo, led);
static GsmDecoder decoder(fifo);
int main () {
led << true;
unsigned counter = 0u;
pwm.attach (decoder);
for (;;) {
player.say(counter++);
pwm.delay ();
}
return 0;
}

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

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

View file

@ -0,0 +1,153 @@
#include <stdlib.h>
#include "player.h"
/******************************************************************/
static constexpr int dmult (const int n) {
int r = 1;
for (int i=0; i<n; i++) r *= 10;
return r;
}
static constexpr int strip_zeros (const int n) {
if ((n % 10) != 0) return n;
return strip_zeros(n / 10);
}
void TextPlayer::say(const int number) {
if (!number) out(m_t.nula);
else sre (number);
}
void TextPlayer::say(const int number, const int dnum) {
if (dnum <= 0 or dnum > 3) { out(m_t.hafo); return; } // meze 1..3
if (number < 0) {
out (m_t.minus); say (-number, dnum); return;
}
const div_t dt = ::div (number, dmult(dnum));
if (dt.quot) sre(dt.quot);
else out(m_t.nula);
out(m_t.point);
mil(dt.rem, dnum);
}
void TextPlayer::mil(const int number, const int dnum) {
if (number == 0) { out(m_t.nula); return; }
const int max = dmult(dnum - 1);
if (number < max) {
out(m_t.nula);
mil(number * 10, dnum);
return;
}
sre (strip_zeros(number));
}
void TextPlayer::sre(const int number) {
if (number < 0) {
out (m_t.minus);
sre (-number);
return;
}
if (number >= 1000000) {
out(m_t.hafo);
return;
}
if (number == 1000) {
out(m_t.tisic);
return;
}
if (number > 1000) {
const div_t dt = ::div (number, 1000);
sre (dt.quot);
const int ln = dt.quot % 10;
(ln < 2 or ln > 4) ? out(m_t.tisic) : out(m_t.tisice);
sre (dt.rem);
return;
}
if (number == 100) {
out(m_t.sto);
return;
}
if (number > 100) {
const div_t dt = ::div (number, 100);
const int ln = dt.quot % 10;
//printf ("stovky:%d ", ln);
hec (ln);
sre (dt.rem);
return;
}
if (number == 20) {
out(m_t.dvacet);
return;
}
if (number > 20) {
const div_t dt = ::div (number, 10);
const int ln = dt.quot % 10;
//printf ("desitky:%d ", ln);
dek (ln);
sre (dt.rem);
return;
}
if (number == 0) return;
// jednotky 1 .. 19
// printf("jednotky:%d ", number);
one (number);
}
void TextPlayer::hec(const int number) {
switch (number) {
case 1: out (m_t.sto); break;
case 2: out (m_t.dveste); break;
case 3:
case 4: one(number); out (m_t.sta); break;
case 5:
case 6:
case 7:
case 8:
case 9: one(number); out (m_t.set); break;
default: break;
};
}
void TextPlayer::dek(const int number) {
switch (number) {
case 2: out (m_t.dvacet); break;
case 3: out (m_t.tricet); break;
case 4: out (m_t.ctyricet); break;
case 5: out (m_t.padesat); break;
case 6: out (m_t.sedesat); break;
case 7: out (m_t.sedmdesat); break;
case 8: out (m_t.osmdesat); break;
case 9: out (m_t.devadesat); break;
default: break;
};
}
void TextPlayer::one(const int number) {
// 1..19
switch (number) {
case 1: out (m_t.jedna); break;
case 2: out (m_t.dva); break;
case 3: out (m_t.tri); break;
case 4: out (m_t.ctyri); break;
case 5: out (m_t.pet); break;
case 6: out (m_t.sest); break;
case 7: out (m_t.sedm); break;
case 8: out (m_t.osm); break;
case 9: out (m_t.devet); break;
case 10: out (m_t.deset); break;
case 11: out (m_t.jedenact); break;
case 12: out (m_t.dvanact); break;
case 13: out (m_t.trinact); break;
case 14: out (m_t.ctrnact); break;
case 15: out (m_t.patnact); break;
case 16: out (m_t.sestnact); break;
case 17: out (m_t.sedmnact); break;
case 18: out (m_t.osmnact); break;
case 19: out (m_t.devatenact); break;
default: break;
};
}
void TextPlayer::out(const text_p & o) {
led << true; // led svítí, pokud to mluví
const int len = o.no_frames;
for (int n=0; n<len; n++) {
const uint32_t addr = o.frames + n * sizeof (gsm_frame);
while (true) {
if (fifo.Write(addr)) break;
}
}
led << false;
}

View file

@ -0,0 +1,30 @@
#ifndef PLAYER_H
#define PLAYER_H
#include "gpio.h"
#include "gsmdata.h"
#include "fifo.h"
static constexpr int GSMLEN = 160;
static constexpr int FIFOLEN = 8;
/* Algoritmus vyslovení čísla v češtině. Sémantika není dokonalá, ale dá tomu rozumět.
* */
class TextPlayer {
GpioClass & led;
const SayedTexts & m_t;
FIFO<uint32_t, FIFOLEN> & fifo;
public:
explicit TextPlayer(FIFO<uint32_t, FIFOLEN> & f, GpioClass & io) noexcept :
led (io), m_t(sayed_texts), fifo(f) {}
void say (const int number); // celé číslo
void say (const int number, const int dnum); // celé číslo jako desetinné s dnum míst (1 .. 3)
void say (const text_p & o) { out (o); } // přetížení pro uživatelská slova
protected:
void sre (const int number);
void hec (const int number);
void dek (const int number);
void one (const int number);
void mil (const int number, const int dnum);
void out (const text_p & o);
};
#endif // PLAYER_H

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

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

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

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