diff --git a/V203/ch32v203/usart.cpp b/V203/ch32v203/usart.cpp new file mode 100644 index 0000000..6f8bf97 --- /dev/null +++ b/V203/ch32v203/usart.cpp @@ -0,0 +1,85 @@ +#include "system.h" +#include "usart.h" +static Usart * pInstance = nullptr; +static constexpr unsigned HCLK = SYSTEM_CORE_CLOCK; + +extern "C" void USART1_IRQHandler (void) __attribute__((interrupt)); +void USART1_IRQHandler (void) { + if (pInstance) pInstance->irq(); +}; + +Usart::Usart(const uint32_t _baud) noexcept : BaseLayer (), tx_ring () { + pInstance = this; + // 1. Clock Enable + RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> auto { + r.B.USART1EN = SET; + r.B.IOPAEN = SET; + //r.B.AFIOEN = SET; + return r.R; + }); + // 2. GPIO Alternate Config - default TX/PA9, RX/PA10 + GPIOA.CFGHR.modify([](GPIOA_Type::CFGHR_DEF & r) -> auto { + r.B.MODE9 = 1u; + r.B.CNF9 = 2u; // or 3u for open drain + r.B.MODE10 = 0u; + r.B.CNF10 = 1u; // floating input + return r.R; + }); + RCC.APB2PRSTR.B.USART1RST = SET; + RCC.APB2PRSTR.B.USART1RST = RESET; + // 5. USART registry 8.bit bez parity + USART1.CTLR1.modify([] (USART1_Type::CTLR1_DEF & r) -> auto { + r.B.RE = SET; + r.B.TE = SET; + r.B.RXNEIE = SET; + return r.R; + }); + USART1.CTLR2.modify ([](USART1_Type::CTLR2_DEF & r) -> auto { + r.B.STOP = 0u; + return r.R; + }); + const uint32_t tmp = HCLK / _baud; + USART1.BRR.R = tmp; + // NVIC + NVIC.EnableIRQ (USART1_IRQn); + USART1.CTLR1.B.UE = SET; // nakonec povolit globálně +} +void Usart::irq () { + volatile USART1_Type::STATR_DEF status (USART1.STATR); // načti status přerušení + char rdata, tdata; + + if (status.B.TXE) { // od vysílače + if (tx_ring.Read (tdata)) { // pokud máme data + USART1.DATAR.B.DR = (uint8_t) tdata; // zapíšeme do výstupu + } else { // pokud ne + // Předpoklad je half-duplex i.e. RS485, jinak jen zakázat TXEIE + rdata = (USART1.DATAR.B.DR); // dummy read + USART1.CTLR1.modify([](USART1_Type::CTLR1_DEF & r) -> auto { + r.B.RE = SET; // povol prijem + r.B.TXEIE = RESET; // je nutné zakázat přerušení od vysílače + return r.R; + }); + } + } + if (status.B.RXNE) { // od přijímače + rdata = (USART1.DATAR.B.DR); // načteme data + Up (&rdata, 1u); // a pošleme dál + } +} +uint32_t Usart::Down(const char * data, const uint32_t len) { + unsigned n = 0u; + for (n=0u; n auto { + r.B.RE = RESET; + r.B.TXEIE = SET; // po povolení přerušení okamžitě přeruší + return r.R; + }); + return n; +} +void Usart::SetRS485 (const bool polarity) const { +} +void Usart::SetHalfDuplex (const bool on) const { +} + diff --git a/V203/common/print.cpp b/V203/common/print.cpp new file mode 100644 index 0000000..aa785b1 --- /dev/null +++ b/V203/common/print.cpp @@ -0,0 +1,85 @@ +#include "print.h" + +#define sleep() + +static const char * hexStr = "0123456789ABCDEF"; +static const uint16_t numLen[] = {1, 32, 1, 11, 8, 0}; + +Print::Print (PrintBases b) : BaseLayer () { + base = b; +} +// Výstup blokujeme podle toho, co se vrací ze spodní vrstvy +uint32_t Print::BlockDown (const char * buf, uint32_t len) { + uint32_t n, ofs = 0, req = len; + for (;;) { + // spodní vrstva může vrátit i nulu, pokud je FIFO plné + n = BaseLayer::Down (buf + ofs, req); + ofs += n; // Posuneme ukazatel + req -= n; // Zmenšíme další požadavek + if (!req) break; + sleep(); // A klidně můžeme spát + } + return ofs; +} + +Print& Print::operator<< (const char * str) { + uint32_t i = 0; + while (str[i++]); // strlen + BlockDown (str, --i); + return *this; +} + +Print& Print::operator<< (const int num) { + uint32_t i = BUFLEN; + + if (base == DEC) { + unsigned int u; + if (num < 0) u = -num; + else u = num; + do { + // Knihovní div() je nevhodné - dělí 2x. + // Přímočaré a funkční řešení + uint32_t rem; + rem = u % (unsigned) DEC; // 1.dělení + u = u / (unsigned) DEC; // 2.dělení + buf [--i] = hexStr [rem]; + } while (u); + if (num < 0) buf [--i] = '-'; + } else { + uint32_t m = (1U << (uint32_t) base) - 1U; + uint32_t l = (uint32_t) numLen [(int) base]; + uint32_t u = (uint32_t) num; + for (unsigned n=0; n>= (unsigned) base; + } + if (base == BIN) buf [--i] = 'b'; + if (base == HEX) buf [--i] = 'x'; + buf [--i] = '0'; + } + BlockDown (buf+i, BUFLEN-i); + return *this; +} + +Print& Print::operator<< (const PrintBases num) { + base = num; + return *this; +} +void Print::out (const void * p, uint32_t l) { + const unsigned char* q = (const unsigned char*) p; + unsigned char uc; + uint32_t k, n = 0; + for (uint32_t i=0; i> 4; + buf[n++] = hexStr [k]; + k = uc & 0x0f; + buf[n++] = hexStr [k]; + buf[n++] = '>'; + } + buf[n++] = '\r'; + buf[n++] = '\n'; + BlockDown (buf, n); +} + diff --git a/V203/common/print.h b/V203/common/print.h new file mode 100644 index 0000000..f4489eb --- /dev/null +++ b/V203/common/print.h @@ -0,0 +1,73 @@ +#ifndef PRINT_H +#define PRINT_H + +#include "baselayer.h" + +#define EOL "\r\n" +#define BUFLEN 64 + +/** + * @file + * @brief Něco jako ostream. + * + */ + +/// Základy pro zobrazení čísla. +enum PrintBases { + BIN=1, OCT=3, DEC=10, HEX=4 +}; + +/** + * @class Print + * @brief Třída pro výpisy do Down(). + * + * + * V main pak přibude jen definice instance této třídy + * @code + static Print print; + * @endcode + * a ukázka, jak se s tím pracuje: + * @snippet main.cpp Main print example + * Nic na tom není - operátor << má přetížení pro string, číslo a volbu formátu čísla (enum PrintBases). + * Výstup je pak do bufferu a aby nám to "neutíkalo", tedy aby se vypsalo vše, + * zavedeme blokování, vycházející z toho, že spodní třída vrátí jen počet bytů, + * které skutečně odeslala. Při čekání spí, takže nepoužívat v přerušení. + * @snippet src/print.cpp Block example + * Toto blokování pak není použito ve vrchních třídách stacku, + * blokovaná metoda je BlockDown(). Pokud bychom použili přímo Down(), blokování by pak + * používaly všechny vrstvy nad tím. A protože mohou Down() používat v přerušení, byl by problém. + * + * Metody pro výpisy jsou sice dost zjednodušené, ale zase to nezabere + * moc místa - pro ladění se to použít dá. Délka vypisovaného stringu není omezena + * délkou použitého buferu. + * + */ + +class Print : public BaseLayer { + public: + /// Konstruktor @param b Default decimální výpisy. + Print (PrintBases b = DEC); + /// Blokování výstupu + /// @param buf Ukazatel na data + /// @param len Délka přenášených dat + /// @return Počet přenesených bytů (rovno len) + uint32_t BlockDown (const char * buf, uint32_t len); + /// Výstup řetězce bytů + /// @param str Ukazatel na řetězec + /// @return Odkaz na tuto třídu kvůli řetězení. + Print & operator << (const char * str); + /// Výstup celého čísla podle base + /// @param num Číslo + /// @return Odkaz na tuto třídu kvůli řetězení. + Print & operator << (const int num); + /// Změna základu pro výstup čísla + /// @param num enum PrintBases + /// @return Odkaz na tuto třídu kvůli řetězení. + Print & operator << (const PrintBases num); + void out (const void* p, uint32_t l); + private: + PrintBases base; //!< Základ pro výstup čísla. + char buf[BUFLEN]; //!< Buffer pro výstup čísla. +}; + +#endif // PRINT_H diff --git a/V203/common/usart.h b/V203/common/usart.h new file mode 100644 index 0000000..7ae814c --- /dev/null +++ b/V203/common/usart.h @@ -0,0 +1,21 @@ +#ifndef USART_H +#define USART_H +#include "fifo.h" +#include "baselayer.h" +/** @class Usart + * @brief Sériový port. + * + * Zde RS485, jen výstup. + */ +class Usart : public BaseLayer { + FIFO tx_ring; + public: + explicit Usart (const uint32_t baud = 9600) noexcept; + uint32_t Down (const char * data, const uint32_t len) override; + void SetRS485 (const bool polarity) const; + + void irq (void); + void SetHalfDuplex (const bool on) const; +}; + +#endif // USART_H diff --git a/V203/usart/Makefile b/V203/usart/Makefile new file mode 100644 index 0000000..baefab2 --- /dev/null +++ b/V203/usart/Makefile @@ -0,0 +1,53 @@ +TARGET?= ch32v203 + +#TOOL ?= gcc +TOOL ?= clang + +PRJ = example + +VPATH = . ./common ./$(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 +DEL = rm -f + +# zdrojaky +OBJS = main.o usart.o print.o adcdma.o +#OBJS += + +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) -std=gnu99 -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 *~ +.PHONY: all clean flash run diff --git a/V203/usart/adcdma.cpp b/V203/usart/adcdma.cpp new file mode 100644 index 0000000..82905a2 --- /dev/null +++ b/V203/usart/adcdma.cpp @@ -0,0 +1,114 @@ +#include "system.h" +#include "oneway.h" +#include "adcdma.h" + +static AdcDma * 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 + GPIOA + RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> auto { + r.B.ADC1EN = SET; + r.B.IOPAEN = SET; + return r.R; + }); + RCC.APB1PCENR.B.TIM3EN = SET; // Enable TIM3 + RCC.CFGR0.B.ADCPRE = 3u; // 11: PCLK2 divided by 8 // max 14 MHz (18) ? + // PIN PA2 / A2 + GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto { + r.B.MODE2 = 0u; + r.B.CNF2 = 0u; + return r.R; + }); +} +static inline void TimerInit (uint32_t us) noexcept { + TIM3.PSC.R = 143u; // 1 MHz Fs + TIM3.ATRLR.R = us - 1u; + // TRGO update for ADC + TIM3.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__CHANNEL.B.SQ1__CHSEL = 2u; // CH2 + ADC1.SAMPTR2_CHARGE2.B.SMP2_TKCG2 = 7u; + /* + ADC1.RSQR3__CHANNEL.B.SQ1__CHSEL = 16u; // teplota + ADC1.SAMPTR1_CHARGE1.B.SMP16_TKCG16 = 7u; + */ + ADC1.RSQR1.B.L = 0u; // 1 regular conversion + ADC1.CTLR1.B.SCAN = SET; + + ADC1.CTLR2.B.ADON = SET; +//ADC1.CTLR2.B.TSVREFE = 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 (& ADC1.RDATAR_DR_ACT_DCG); + // Configure the memory address + DMA1.MADDR1.R = reinterpret_cast (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 = 4u; // TRGO event of timer 3 + r.B.SWSTART = SET; + return r.R; + }); +} +//////////////////////////////////////////////////////////////////////////////////// +AdcDma::AdcDma() noexcept : pL (buffer), pH (buffer + HALF_LEN), dst (nullptr) { + pInstance = this; + EnableClock (); + TimerInit (1000u); + NVIC.EnableIRQ (DMA1_Channel1_IRQn); + AdcCalibrate(); + Dma1Ch1Init (buffer); + AdcPostInit (); + // start timer + TIM3.CTLR1.B.CEN = SET; +} +inline void AdcDma::send(const bool b) { + if (!dst) return; + if (b) dst->Send (pH, HALF_LEN); + else dst->Send (pL, HALF_LEN); +} diff --git a/V203/usart/adcdma.h b/V203/usart/adcdma.h new file mode 100644 index 0000000..8e91222 --- /dev/null +++ b/V203/usart/adcdma.h @@ -0,0 +1,21 @@ +#ifndef ADCDMA_H +#define ADCDMA_H +#include + +class OneWay; +static constexpr unsigned HALF_LEN = 120u; +static constexpr unsigned FULL_LEN = HALF_LEN * 2u; + +class AdcDma { + uint16_t * pL; + uint16_t * pH; + uint16_t buffer [FULL_LEN]; + OneWay * dst; + public: + explicit AdcDma () noexcept; + void attach (OneWay & d) { dst = & d; } + void send (const bool b); + +}; + +#endif // ADCDMA_H diff --git a/V203/usart/ch32v203 b/V203/usart/ch32v203 new file mode 120000 index 0000000..7650c85 --- /dev/null +++ b/V203/usart/ch32v203 @@ -0,0 +1 @@ +../ch32v203/ \ No newline at end of file diff --git a/V203/usart/common b/V203/usart/common new file mode 120000 index 0000000..8332399 --- /dev/null +++ b/V203/usart/common @@ -0,0 +1 @@ +../common/ \ No newline at end of file diff --git a/V203/usart/main.cpp b/V203/usart/main.cpp new file mode 100644 index 0000000..8402505 --- /dev/null +++ b/V203/usart/main.cpp @@ -0,0 +1,48 @@ +#include "usart.h" +#include "print.h" +#include "adcdma.h" +#include "oneway.h" +#include "fifo.h" +//////////////////////////////////////////////////////// +/* Voltmetr měří na pinu PA2 napětí a vypisuje v mV. + * Frekvence vzorkování je 1kHz, průměruje se to a + * zobrazuje jednou za 120 ms (9600 Bd). */ +//////////////////////////////////////////////////////// +class Meassure : public OneWay { + FIFO fifo; + unsigned avg; + public: + explicit Meassure () noexcept : OneWay(), fifo(), avg(0u) {} + unsigned int Send (uint16_t * const ptr, const unsigned int len) override; + void out (); +}; +//////////////////////////////////////////////////////// +static Usart serial (9600u); +static Print cout (DEC); +static AdcDma adc; +static Meassure meas; +//////////////////////////////////////////////////////// +int main () { + cout += serial; + adc.attach (meas); + for (;;) { + meas.out(); + } + return 0; +} +//////////////////////////////////////////////////////// +static constexpr unsigned BK = 3316u << 4; // Mělo by to být přesně 3300. +unsigned int Meassure::Send(uint16_t * const ptr, const unsigned int len) { + for (unsigned n=0; n> 16; + avg = (avg * 15 + mv) >> 4; // klouzavý průměr s postupným zapomínáním + } + fifo.Write (avg); + return 0; +} +void Meassure::out() { + unsigned t; + if (fifo.Read (t)) { + cout << t << " mV\r\n"; + } +}