Compare commits
2 commits
40cc0ee799
...
7a8411ad33
Author | SHA1 | Date | |
---|---|---|---|
|
7a8411ad33 | ||
|
a9351ebff7 |
10 changed files with 526 additions and 3 deletions
26
README.md
26
README.md
|
@ -10,3 +10,29 @@ minichlink, který umí flashnout firmware a GDB server.
|
||||||
## hello
|
## hello
|
||||||
Základní program, používá GPIO a SysTick v režimu přerušení pro blikání LEDkou.
|
Základní program, používá GPIO a SysTick v režimu přerušení pro blikání LEDkou.
|
||||||
|
|
||||||
|
## pwm, adc, serial
|
||||||
|
DEMO pro základní seznámení s periferiemi.
|
||||||
|
|
||||||
|
## hdo
|
||||||
|
Tohle je trochu komplexnější příklad.
|
||||||
|
|
||||||
|
Na vstup PC4 přivádíme ze síťového transformátoru napětí
|
||||||
|
cca 0.7-1 V efektivního napětí. Vstup musí být tedy připojen
|
||||||
|
na odporový dělič 1:1, zapojený mezi VCC a GND a trafo k tomuto
|
||||||
|
bodu připojíme přes vhodný kondenzátor, tak, aby střídavé napětí
|
||||||
|
nebylo limitováno.
|
||||||
|
|
||||||
|
Firmware z toho Goertzelovým algoritmem vytáhne signál HDO (zde 216.6 Hz)
|
||||||
|
a vyhodnotí jednotlivé pulsy. Velikost pulsu je vypisována na sériový
|
||||||
|
port (115200 Bd) a pokud překročí hodnotu trigger, rozsvítí LED na PD2 (aktivní v L).
|
||||||
|
Dále je pak z pulsů sestaven telegram, opět vypsán sériový port,
|
||||||
|
a pokud se shoduje s povelem uvedeným v konstruktoru Hdo, sepne / rozepne
|
||||||
|
relé na portu PD4 (aktivní v H) podle vysílaného signálu.
|
||||||
|
Celé se to vejde do 3.5 KiB flash a 1 KiB RAM, i v takto malém procesoru
|
||||||
|
tedy zbývá poměrně dost místa na jiné kraviny.
|
||||||
|
|
||||||
|
Tvar výpisů je tento:
|
||||||
|
A1---B---4---- DP: VVZZ ZZZV ZZZZ VVZV
|
||||||
|
A--3-B--34---- DP: --VV Z-V- -V-V ----
|
||||||
|
A1---B-------8 DP: VVVZ VZVV ZZZV -VZV
|
||||||
|
A1---B---45678 DP: ---Z -Z-V Z-Z- -VZV
|
||||||
|
|
|
@ -5,9 +5,31 @@ enum CLKSRC : uint32_t {
|
||||||
CLK_HSE,
|
CLK_HSE,
|
||||||
CLK_PLL,
|
CLK_PLL,
|
||||||
};
|
};
|
||||||
|
// HSE i HSI mají frekvenci 24 MHz
|
||||||
void SystemInit(void) {
|
void SystemInit(void) {
|
||||||
RCC.CFGR0.R = 0u; // prescaler OFF
|
RCC.CFGR0.R = 0u; // prescaler OFF
|
||||||
|
#if USE_HSE
|
||||||
|
RCC.CTLR.modify([](RCC_Type::CTLR_DEF & r) -> auto {
|
||||||
|
r.B.HSITRIM = 0x10u;
|
||||||
|
r.B.HSION = SET;
|
||||||
|
r.B.HSEON = SET;
|
||||||
|
r.B.HSEBYP = RESET; // krystal
|
||||||
|
r.B.CSSON = SET;
|
||||||
|
r.B.PLLON = RESET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
while (RCC.CTLR.B.HSERDY == RESET);
|
||||||
|
RCC.CFGR0.modify([](RCC_Type::CFGR0_DEF & r) -> auto {
|
||||||
|
r.B.SW = CLK_HSE;
|
||||||
|
r.B.PLLSRC = SET; // write only when PLL is off
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
RCC.CTLR.modify([](RCC_Type::CTLR_DEF & r) -> auto {
|
||||||
|
//r.B.HSION = RESET; // je možné vypnout, ale není to dobrý nápad, pak je nutný unbrick
|
||||||
|
r.B.PLLON = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
#else // HSI
|
||||||
RCC.CTLR.modify([](RCC_Type::CTLR_DEF & r) -> auto {
|
RCC.CTLR.modify([](RCC_Type::CTLR_DEF & r) -> auto {
|
||||||
r.B.HSITRIM = 0x10u;
|
r.B.HSITRIM = 0x10u;
|
||||||
r.B.HSION = SET;
|
r.B.HSION = SET;
|
||||||
|
@ -16,9 +38,11 @@ void SystemInit(void) {
|
||||||
r.B.PLLON = SET;
|
r.B.PLLON = SET;
|
||||||
return r.R;
|
return r.R;
|
||||||
});
|
});
|
||||||
|
#endif // USE_HSE
|
||||||
FLASH.ACTLR.B.LATENCY = SET;
|
FLASH.ACTLR.B.LATENCY = SET;
|
||||||
RCC.INTR.R = 0x009F0000u; // clear interrupts
|
RCC.INTR.R = 0x009F0000u; // clear interrupts
|
||||||
while (RCC.CTLR.B.PLLRDY == RESET);
|
while (RCC.CTLR.B.PLLRDY == RESET);
|
||||||
RCC.CFGR0.B.SW = CLK_PLL;
|
// USE PLL - zdvojení frekvence, tj. 48 MHz
|
||||||
|
RCC.CFGR0.B.SW = CLK_PLL ;
|
||||||
while (RCC.CFGR0.B.SWS != CLK_PLL);
|
while (RCC.CFGR0.B.SWS != CLK_PLL);
|
||||||
}
|
}
|
||||||
|
|
52
hdo/Makefile
Normal file
52
hdo/Makefile
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# ch32v003
|
||||||
|
TARGET?= ch32v003
|
||||||
|
TOOL ?= gcc
|
||||||
|
|
||||||
|
PRJ = example
|
||||||
|
|
||||||
|
VPATH = . ./$(TARGET) ./common
|
||||||
|
BLD = ./build/
|
||||||
|
DFLAGS = -d
|
||||||
|
LFLAGS = -g
|
||||||
|
LDLIBS =
|
||||||
|
BFLAGS = --strip-unneeded
|
||||||
|
|
||||||
|
CFLAGS = -MMD -Wall -ggdb -fno-exceptions -ffunction-sections -fdata-sections
|
||||||
|
CFLAGS+= -I. -I./common -I./$(TARGET) -I/usr/include/newlib -DUSE_HSE=1
|
||||||
|
DEL = rm -f
|
||||||
|
|
||||||
|
# zdrojaky
|
||||||
|
OBJS = main.o adcclass.o hdo.o
|
||||||
|
OBJS += usartclass.o print.o
|
||||||
|
|
||||||
|
include $(TARGET)/$(TOOL).mk
|
||||||
|
BOBJS = $(addprefix $(BLD),$(OBJS))
|
||||||
|
|
||||||
|
all: $(BLD) $(PRJ).elf
|
||||||
|
# ... atd.
|
||||||
|
-include $(BLD)*.d
|
||||||
|
# linker
|
||||||
|
$(PRJ).elf: $(BOBJS)
|
||||||
|
-@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) -c $(CFLAGS) $< -o $@
|
||||||
|
$(BLD)%.o: %.cpp
|
||||||
|
-@echo [CX $(TOOL),$(TARGET)] $@
|
||||||
|
@$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@
|
||||||
|
$(BLD):
|
||||||
|
mkdir $(BLD)
|
||||||
|
flash: $(PRJ).elf
|
||||||
|
minichlink -w $(PRJ).bin flash -b
|
||||||
|
# vycisti
|
||||||
|
clean:
|
||||||
|
$(DEL) $(BLD)* *.lst *.bin *.elf *.map sin.c *~
|
||||||
|
.PHONY: all clean
|
108
hdo/adcclass.cpp
Normal file
108
hdo/adcclass.cpp
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
#include "system.h"
|
||||||
|
#include "oneway.h"
|
||||||
|
#include "adcclass.h"
|
||||||
|
|
||||||
|
static AdcClass * pInstance = nullptr;
|
||||||
|
|
||||||
|
extern "C" void DMA1_Channel1_IRQHandler( void ) __attribute__((interrupt));
|
||||||
|
void DMA1_Channel1_IRQHandler( void ) {
|
||||||
|
DMA1_Type::INTFR_DEF state (DMA1.INTFR);
|
||||||
|
DMA1.INTFCR.R = state.R; // clear all
|
||||||
|
if (!pInstance) return;
|
||||||
|
if (state.B.HTIF1 != RESET) pInstance->send (false);
|
||||||
|
else if (state.B.TCIF1 != RESET) pInstance->send (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void EnableClock (void) noexcept {
|
||||||
|
// Enable DMA
|
||||||
|
RCC.AHBPCENR.modify([](RCC_Type::AHBPCENR_DEF & r) -> auto {
|
||||||
|
r.B.SRAMEN = SET;
|
||||||
|
r.B.DMA1EN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
// Enable ADC + GPIOC
|
||||||
|
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> auto {
|
||||||
|
r.B.ADC1EN = SET;
|
||||||
|
r.B.IOPCEN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
RCC.APB1PCENR.B.TIM2EN = SET; // Enable TIM2
|
||||||
|
RCC.CFGR0.B.ADCPRE = 0u; // 000xx: AHBCLK divided by 2 as ADC clock (24 MHz max).
|
||||||
|
// PIN PC4 / A2
|
||||||
|
GPIOC.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
|
||||||
|
r.B.MODE4 = 0u;
|
||||||
|
r.B.CNF4 = 0u;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static inline void Timer2Init (uint32_t us) noexcept {
|
||||||
|
TIM2.PSC.R = 47u; // 1 MHz Fs
|
||||||
|
TIM2.ATRLR.R = us - 1u;
|
||||||
|
// TRGO update for ADC
|
||||||
|
TIM2.CTLR2.B.MMS = 2u;
|
||||||
|
}
|
||||||
|
static inline void AdcCalibrate (void) noexcept {
|
||||||
|
// RESET
|
||||||
|
RCC.APB2PRSTR.B.ADC1RST = SET;
|
||||||
|
RCC.APB2PRSTR.B.ADC1RST = RESET;
|
||||||
|
// set channels
|
||||||
|
ADC1.RSQR3.B.SQ1 = 2u; // CH2
|
||||||
|
ADC1.RSQR1.B.L = 0u; // 1 regular conversion
|
||||||
|
ADC1.SAMPTR2_CHARGE2.B.SMP2_TKCG2 = 7u;
|
||||||
|
ADC1.CTLR1.B.SCAN = SET;
|
||||||
|
|
||||||
|
ADC1.CTLR2.B.ADON = SET;
|
||||||
|
ADC1.CTLR2.B.RSTCAL = SET; // Launch the calibration by setting RSTCAL
|
||||||
|
while (ADC1.CTLR2.B.RSTCAL != RESET); // Wait until RSTCAL=0
|
||||||
|
ADC1.CTLR2.B.CAL = SET; // Launch the calibration by setting CAL
|
||||||
|
while (ADC1.CTLR2.B.CAL != RESET); // Wait until CAL=0
|
||||||
|
}
|
||||||
|
typedef __SIZE_TYPE__ size_t;
|
||||||
|
static inline void Dma1Ch1Init (void * ptr) noexcept {
|
||||||
|
// Configure the peripheral data register address
|
||||||
|
DMA1.PADDR1.R = reinterpret_cast<size_t> (& ADC1.RDATAR);
|
||||||
|
// Configure the memory address
|
||||||
|
DMA1.MADDR1.R = reinterpret_cast<size_t> (ptr);
|
||||||
|
// Configure the number of DMA tranfer to be performs on DMA channel 1
|
||||||
|
DMA1.CNTR1 .R = FULL_LEN;
|
||||||
|
// Configure increment, size, interrupts and circular mode
|
||||||
|
DMA1.CFGR1.modify([] (DMA1_Type::CFGR1_DEF & r) -> auto {
|
||||||
|
r.B.PL = 3u; // highest priority
|
||||||
|
r.B.MEM2MEM = RESET; // periferal -> memory
|
||||||
|
r.B.MINC = SET; // memory increment
|
||||||
|
r.B.MSIZE = 1u; // 16-bit
|
||||||
|
r.B.PSIZE = 1u; // 16-bit
|
||||||
|
r.B.HTIE = SET; // INT Enable HALF
|
||||||
|
r.B.TCIE = SET; // INT Enable FULL
|
||||||
|
r.B.CIRC = SET; // Circular MODE
|
||||||
|
// Enable DMA Channel 1
|
||||||
|
r.B.EN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static inline void AdcPostInit (void) noexcept {
|
||||||
|
ADC1.CTLR2.modify([](ADC1_Type::CTLR2_DEF & r) -> auto {
|
||||||
|
r.B.DMA = SET;
|
||||||
|
r.B.EXTTRIG = SET;
|
||||||
|
r.B.EXTSEL = 3u; // TRGO event of timer 2
|
||||||
|
r.B.SWSTART = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AdcClass::AdcClass() noexcept : pL (buffer), pH (buffer + HALF_LEN), dst (nullptr) {
|
||||||
|
pInstance = this;
|
||||||
|
EnableClock ();
|
||||||
|
Timer2Init (1000u);
|
||||||
|
NVIC.EnableIRQ (DMA1_Channel1_IRQn);
|
||||||
|
AdcCalibrate();
|
||||||
|
Dma1Ch1Init (buffer);
|
||||||
|
AdcPostInit ();
|
||||||
|
// start timer
|
||||||
|
TIM2.CTLR1.B.CEN = SET;
|
||||||
|
}
|
||||||
|
inline void AdcClass::send(const bool b) {
|
||||||
|
if (!dst) return;
|
||||||
|
if (b) dst->Send (pH, HALF_LEN);
|
||||||
|
else dst->Send (pL, HALF_LEN);
|
||||||
|
}
|
21
hdo/adcclass.h
Normal file
21
hdo/adcclass.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef ADCCLASS_H
|
||||||
|
#define ADCCLASS_H
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class OneWay;
|
||||||
|
static constexpr unsigned HALF_LEN = 120u;
|
||||||
|
static constexpr unsigned FULL_LEN = HALF_LEN * 2u;
|
||||||
|
|
||||||
|
class AdcClass {
|
||||||
|
uint16_t * pL;
|
||||||
|
uint16_t * pH;
|
||||||
|
uint16_t buffer [FULL_LEN];
|
||||||
|
OneWay * dst;
|
||||||
|
public:
|
||||||
|
explicit AdcClass () noexcept;
|
||||||
|
void attach (OneWay & d) { dst = & d; }
|
||||||
|
void send (const bool b);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ADCCLASS_H
|
1
hdo/ch32v003
Symbolic link
1
hdo/ch32v003
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../ch32v003/
|
1
hdo/common
Symbolic link
1
hdo/common
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../common/
|
189
hdo/hdo.cpp
Normal file
189
hdo/hdo.cpp
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
#include "hdo.h"
|
||||||
|
static constexpr int factor = int (double(1U << 20) * (1.0 / 1330.0) + 0.5);
|
||||||
|
static constexpr int period = 120; // perioda vyhodnocení Goertzelovým algoritmem
|
||||||
|
// pro výpočet dělení použijeme tento union
|
||||||
|
union divu {
|
||||||
|
int32_t val;
|
||||||
|
struct {
|
||||||
|
uint32_t unu : 16;
|
||||||
|
uint32_t res : 4;
|
||||||
|
uint32_t div : 12;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
static constexpr const int index_table [] = {19,20,21,22, 24,25,26,27, 29,30,31,32, 34,35,36,37};
|
||||||
|
|
||||||
|
unsigned Hdo::Send (uint16_t *const ptr, const unsigned len) {
|
||||||
|
int q2 = 0, q1 = 0;
|
||||||
|
for (unsigned n=0; n<len; n++) {
|
||||||
|
// Vlastní Goertzelův algoritmus.
|
||||||
|
int q0 = coeff * q1;
|
||||||
|
// pokud byl coeff zvětšen, je třeba to tu zase zmenšit
|
||||||
|
q0 >>= ISHIFT; // zmenšení pro int
|
||||||
|
q0 += ((int) ptr [n]) - q2; // vlastní výpočet
|
||||||
|
q2 = q1; // posuv o vzorek
|
||||||
|
q1 = q0; // (rekurze)
|
||||||
|
}
|
||||||
|
int rv = q1 * q2;
|
||||||
|
rv *= -coeff;
|
||||||
|
rv >>= ISHIFT; // tady nutno zmenšit tak, jak bylo zvětšeno v calc_coeff()
|
||||||
|
rv += q1 * q1 + q2 * q2; // výkon by byl sqrt (rv), není nutné počítat, napětí stačí
|
||||||
|
data.Write (rv); // dáme do FIFO, vybíráme v main()
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
void Hdo::pass () {
|
||||||
|
int value;
|
||||||
|
if (!data.Read (value)) return;
|
||||||
|
// DEBUG value
|
||||||
|
cout << value << " \r";
|
||||||
|
value -= trigger;
|
||||||
|
|
||||||
|
if (value > 0) led << false; // LED je zapojená proti VCC
|
||||||
|
else led << true;
|
||||||
|
// Konečné vyhodnocení.
|
||||||
|
if (Decode (value, buf1)) { // Telegram OK.
|
||||||
|
HumanRead (buf1, buf2); // Převeď ho do čitelné podoby
|
||||||
|
|
||||||
|
cout << buf2 << EOL; // Vypíšeme telegram
|
||||||
|
|
||||||
|
int i = Action (buf2, cmd); // Nakonec proveď akci
|
||||||
|
if (i == +1) relay << true;
|
||||||
|
if (i == -1) relay << false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
int Hdo::Decode(int num, char * str) {
|
||||||
|
int rv = 0;
|
||||||
|
uint32_t cv = 0, bi = 0;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case WAIT_FOR_BEGIN:
|
||||||
|
counter = 0;
|
||||||
|
// start telegramu
|
||||||
|
if (num > 0) status = SYNC_PULSE;
|
||||||
|
break;
|
||||||
|
case SYNC_PULSE:
|
||||||
|
counter++;
|
||||||
|
if (num < 0) { // pokles
|
||||||
|
if (counter > SYNC_HI) { // pokud je včas, určuje další časování
|
||||||
|
counter = 0;
|
||||||
|
status = SYNC_SPACE;
|
||||||
|
}
|
||||||
|
else // chyba
|
||||||
|
status = WAIT_FOR_BEGIN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SYNC_SPACE:
|
||||||
|
counter++;
|
||||||
|
if (num > 0) // vzestup během mezery = chyba
|
||||||
|
status = WAIT_FOR_BEGIN;
|
||||||
|
if (counter > SYNC_LO) { // celá synchronizační mezera
|
||||||
|
counter = -1; // jeden prázdný cyklus
|
||||||
|
suma = 0;
|
||||||
|
bits = 0;
|
||||||
|
status = CORELATE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// Budeme to dělat jako korelaci. Perioda pak nemusí být pevná,
|
||||||
|
// nakonec je to jednodušší a pochopitejnější.
|
||||||
|
case CORELATE:
|
||||||
|
counter++;
|
||||||
|
if (counter <= 0) break; // ten prázdný cyklus (synchronizace, určeno měřením)
|
||||||
|
divu dv;
|
||||||
|
// Tohle je fakticky dělení periodou 1330 ms
|
||||||
|
dv.val = counter * period * factor;
|
||||||
|
bi = dv.div; // index bitu (kolikátá perioda, podíl)
|
||||||
|
cv = dv.res; // 0..15 v periodě (zbytek po dělení)
|
||||||
|
|
||||||
|
if (bi != bits) { // napřed vyhodnoceni předchozího
|
||||||
|
bits = bi;
|
||||||
|
if (suma > 0) suma = 1;
|
||||||
|
else suma = 0;
|
||||||
|
// output '0' or '1'
|
||||||
|
str [bits - 1] = (char) suma + 0x30;
|
||||||
|
suma = 0;
|
||||||
|
if (bits >= 44) { // TELEGRAM END
|
||||||
|
status = WAIT_FOR_BEGIN;
|
||||||
|
rv = 1;
|
||||||
|
str [bits] = 0;
|
||||||
|
}
|
||||||
|
} // pak korelace
|
||||||
|
if (cv < 14) suma += num; // 0..13 kladná korelace (puls)
|
||||||
|
else suma -= num; // jinak záporná korelace (mezera)
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
void Hdo::HumanRead(const char * src, char * dst) {
|
||||||
|
int tindex, windex = 0;
|
||||||
|
char c, avg;
|
||||||
|
|
||||||
|
for (tindex = 0; tindex < 44; tindex++) {
|
||||||
|
avg = src [tindex] - 0x30;
|
||||||
|
// Doplním písmenka
|
||||||
|
if (tindex == 0) dst [windex++] = 'A';
|
||||||
|
if (tindex == 4) dst [windex++] = 'B';
|
||||||
|
if (tindex == 12) {
|
||||||
|
dst [windex++] = ' ';
|
||||||
|
dst [windex++] = 'D';
|
||||||
|
dst [windex++] = 'P';
|
||||||
|
dst [windex++] = ':';
|
||||||
|
}
|
||||||
|
// Skupina A
|
||||||
|
if (tindex < 4) {
|
||||||
|
if (avg) c = '1' + tindex;
|
||||||
|
else c = '-';
|
||||||
|
dst [windex++] = c;
|
||||||
|
}
|
||||||
|
// Skupina B
|
||||||
|
else if (tindex < 12) {
|
||||||
|
if (avg) c = '1' + tindex - 4;
|
||||||
|
else c = '-';
|
||||||
|
dst [windex++] = c;
|
||||||
|
}
|
||||||
|
// Dvojpovel
|
||||||
|
else {
|
||||||
|
// skupiny po 4 oddělit mezerami pro lepší čitelnost
|
||||||
|
if (!((tindex+4) % 8)) dst [windex++] = ' ';
|
||||||
|
if (tindex % 2) { // bit "vypnuto" na tindex - to až následně
|
||||||
|
if (dst [windex] == '-') { // zapnuto nebylo
|
||||||
|
if (avg) dst [windex] = 'V'; // tedy je vypnuto
|
||||||
|
}
|
||||||
|
else { // bylo zapnuto
|
||||||
|
if (avg) dst [windex] = 'E'; // tedy je chyba - nemůže být obojí
|
||||||
|
}
|
||||||
|
windex++;
|
||||||
|
}
|
||||||
|
else { // bit "zapnuto" na tindex - to je první !!!
|
||||||
|
if (avg) dst [windex] = 'Z'; // je zapnuto
|
||||||
|
else dst [windex] = '-'; // ještě se uvidí
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst [windex] = 0;
|
||||||
|
}
|
||||||
|
int Hdo::Action(char * tlg, const char * command) {
|
||||||
|
int i, j;
|
||||||
|
char c;
|
||||||
|
i = command [1] - '1'; // An
|
||||||
|
if ((i < 0) || (i > 3)) return 0; // chyba
|
||||||
|
if (tlg [i+1] == '-') return 0; // není pro mne
|
||||||
|
i = command [3] - '1'; // Bn
|
||||||
|
if ((i < 0) || (i > 7)) return 0; // chyba
|
||||||
|
if (tlg [i+6] == '-') return 0; // není pro mne
|
||||||
|
i = command [6] - '0'; // DPn
|
||||||
|
j = command [7]; // DPn+1
|
||||||
|
if (j) {
|
||||||
|
j -= '0';
|
||||||
|
i *= 10;
|
||||||
|
i += j;
|
||||||
|
} // v i je číslo za DP
|
||||||
|
if ((i < 1) || (i > 16)) return 0; // chyba
|
||||||
|
i--; // index bude o 1 menší
|
||||||
|
j = index_table [i]; // v telegramu na pozici j
|
||||||
|
c = tlg [j]; // je písmeno
|
||||||
|
if (c == 'Z') return +1; // Z - potom zapni
|
||||||
|
if (c == 'V') return -1; // V - vypni
|
||||||
|
return 0;
|
||||||
|
}
|
63
hdo/hdo.h
Normal file
63
hdo/hdo.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef HDO_H
|
||||||
|
#define HDO_H
|
||||||
|
#include "gpio.h"
|
||||||
|
#include "usartclass.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "oneway.h"
|
||||||
|
|
||||||
|
static constexpr int ISHIFT = 12;
|
||||||
|
/* Tady je ten výpočet proveden externě.
|
||||||
|
static constexpr int calc_coeff (const double nfreq) {
|
||||||
|
return lround (double(2UL << ISHIFT) * cos (2.0 * 3.14159265358979323846 * nfreq));
|
||||||
|
}
|
||||||
|
coeff = calc_coeff (216.6 / 1000.0) = 1706
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
\enum stat
|
||||||
|
Stavy konečného automatu vyhodnocení telegramu
|
||||||
|
*/
|
||||||
|
enum stat {
|
||||||
|
WAIT_FOR_BEGIN = 0, //!< čekání na začátek telegramu
|
||||||
|
SYNC_PULSE, //!< startovací puls probíhá
|
||||||
|
SYNC_SPACE, //!< synchronizační mezera probíhá
|
||||||
|
CORELATE, //!< datový puls probíhá
|
||||||
|
};
|
||||||
|
static constexpr int SYNC_HI = 17; //!< 2.33 sec délka startovacího pulsu
|
||||||
|
static constexpr int SYNC_LO = 22; //!< 2.99 sec délka synchronizační mezery
|
||||||
|
static constexpr int TBUFLEN = 64;
|
||||||
|
|
||||||
|
class Hdo : public OneWay {
|
||||||
|
GpioClass led, relay;
|
||||||
|
UsartClass serial;
|
||||||
|
Print cout;
|
||||||
|
FIFO<int, 8> data;
|
||||||
|
const int coeff;
|
||||||
|
const int trigger; //!< rozhodovací úroveň (napevno)
|
||||||
|
const char * cmd; //!< rozhodovací, řídící string
|
||||||
|
|
||||||
|
char buf1[TBUFLEN];
|
||||||
|
char buf2[TBUFLEN];
|
||||||
|
int suma;
|
||||||
|
uint32_t bits;
|
||||||
|
int counter; //!< čítač period
|
||||||
|
stat status; //!< stav konečného automatu detekce
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Hdo (const char * command) noexcept : OneWay (),
|
||||||
|
led (GPIOD, 2), relay (GPIOD, 4), serial (115200u), cout (DEC), data(), coeff (1706), trigger (0x4000),
|
||||||
|
cmd (command), suma (0), bits (0), counter (0), status (WAIT_FOR_BEGIN) {
|
||||||
|
/* trigger musí být nastaven tak do 1/3 až do 1/2 maximální vyhodnocené hodnoty (viz výpis)
|
||||||
|
* Je nutné použít HSE, tj. krystal 24 HHz. Bez toho to fakt nechodí a to i na procesorech i.e. STM.
|
||||||
|
* */
|
||||||
|
cout += serial;
|
||||||
|
led << true;
|
||||||
|
}
|
||||||
|
unsigned Send (uint16_t * const ptr, const unsigned len) override;
|
||||||
|
void pass ();
|
||||||
|
protected:
|
||||||
|
int Decode (int num, char * str);
|
||||||
|
void HumanRead (const char * src, char * dst);
|
||||||
|
int Action (char * tlg, const char * command);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HDO_H
|
38
hdo/main.cpp
Normal file
38
hdo/main.cpp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#include "adcclass.h"
|
||||||
|
#include "hdo.h"
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
/* Tohle je trochu komplexnější příklad.
|
||||||
|
*
|
||||||
|
* Na vstup PC4 přivádíme ze síťového transformátoru napětí
|
||||||
|
* cca 0.7-1 V efektivního napětí. Vstup musí být tedy připojen
|
||||||
|
* na odporový dělič 1:1, zapojený mezi VCC a GND a trafo k tomuto
|
||||||
|
* bodu připojíme přes vhodný kondenzátor, tak, aby střídavé napětí
|
||||||
|
* nebylo limitováno.
|
||||||
|
* Firmware z toho Goertzelovým algoritmem vytáhne signál HDO (zde 216.6 Hz)
|
||||||
|
* a vyhodnotí jednotlivé pulsy. Velikost pulsu je vypisována na sériový
|
||||||
|
* port (115200 Bd) a pokud překročí hodnotu trigger, rozsvítí LED na PD2 (aktivní v L).
|
||||||
|
* Dále je pak z pulsů sestaven telegram, opět vypsán sériový port,
|
||||||
|
* a pokud se shoduje s povelem uvedeným v konstruktoru Hdo, sepne / rozepne
|
||||||
|
* relé na portu PD4 (aktivní v H) podle vysílaného signálu.
|
||||||
|
* Celé se to vejde do 3.5 KiB flash a 1 KiB RAM, i v takto malém procesoru
|
||||||
|
* tedy zbývá poměrně dost místa na jiné kraviny.
|
||||||
|
*
|
||||||
|
* Tvar výpisů je tento:
|
||||||
|
* A1---B---4---- DP: VVZZ ZZZV ZZZZ VVZV
|
||||||
|
* A--3-B--34---- DP: --VV Z-V- -V-V ----
|
||||||
|
* A1---B-------8 DP: VVVZ VZVV ZZZV -VZV
|
||||||
|
* A1---B---45678 DP: ---Z -Z-V Z-Z- -VZV
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* !!! Krystal 24 MHz nutný !!!
|
||||||
|
* */
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
static AdcClass adc;
|
||||||
|
static Hdo hdo ("A3B1DP7");
|
||||||
|
int main () {
|
||||||
|
adc.attach(hdo);
|
||||||
|
for (;;) {
|
||||||
|
hdo.pass();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue