From 5b32ee9eae3e79b1e1411596bb9ffd0933fb1f39 Mon Sep 17 00:00:00 2001 From: Kizarm Date: Mon, 21 Oct 2024 10:26:41 +0200 Subject: [PATCH] add scope --- .gitignore | 8 + V203/usb/ch32v203/adcscope.cpp | 166 +++++++++++++ V203/usb/common/adcscope.h | 46 ++++ V203/usb/scope/firmware/Makefile | 52 ++++ V203/usb/scope/firmware/ch32v203 | 1 + V203/usb/scope/firmware/common | 1 + V203/usb/scope/firmware/hack.c | 21 ++ V203/usb/scope/firmware/main.cpp | 29 +++ V203/usb/scope/firmware/main.h | 8 + V203/usb/scope/firmware/samplering.cpp | 182 ++++++++++++++ V203/usb/scope/firmware/samplering.h | 50 ++++ V203/usb/scope/firmware/structures.h | 88 +++++++ V203/usb/scope/firmware/usb_desc.cpp | 83 +++++++ V203/usb/scope/firmware/usb_desc.h | 66 ++++++ V203/usb/scope/software/datasource.cpp | 177 ++++++++++++++ V203/usb/scope/software/datasource.h | 52 ++++ V203/usb/scope/software/displaywidget.cpp | 277 ++++++++++++++++++++++ V203/usb/scope/software/displaywidget.h | 58 +++++ V203/usb/scope/software/helpers.cpp | 47 ++++ V203/usb/scope/software/helpers.h | 8 + V203/usb/scope/software/ico.png | Bin 0 -> 1545 bytes V203/usb/scope/software/main.cpp | 9 + V203/usb/scope/software/main.h | 11 + V203/usb/scope/software/mainwindow.cpp | 151 ++++++++++++ V203/usb/scope/software/mainwindow.h | 33 +++ V203/usb/scope/software/mainwindow.ui | 238 +++++++++++++++++++ V203/usb/scope/software/src.pro | 41 ++++ V203/usb/scope/software/src.qrc | 8 + V203/usb/scope/software/structures.h | 1 + V203/usb/scope/software/time.png | Bin 0 -> 606 bytes V203/usb/scope/software/volt.png | Bin 0 -> 595 bytes 31 files changed, 1912 insertions(+) create mode 100644 V203/usb/ch32v203/adcscope.cpp create mode 100644 V203/usb/common/adcscope.h create mode 100644 V203/usb/scope/firmware/Makefile create mode 120000 V203/usb/scope/firmware/ch32v203 create mode 120000 V203/usb/scope/firmware/common create mode 100644 V203/usb/scope/firmware/hack.c create mode 100644 V203/usb/scope/firmware/main.cpp create mode 100644 V203/usb/scope/firmware/main.h create mode 100644 V203/usb/scope/firmware/samplering.cpp create mode 100644 V203/usb/scope/firmware/samplering.h create mode 100644 V203/usb/scope/firmware/structures.h create mode 100644 V203/usb/scope/firmware/usb_desc.cpp create mode 100644 V203/usb/scope/firmware/usb_desc.h create mode 100644 V203/usb/scope/software/datasource.cpp create mode 100644 V203/usb/scope/software/datasource.h create mode 100644 V203/usb/scope/software/displaywidget.cpp create mode 100644 V203/usb/scope/software/displaywidget.h create mode 100644 V203/usb/scope/software/helpers.cpp create mode 100644 V203/usb/scope/software/helpers.h create mode 100644 V203/usb/scope/software/ico.png create mode 100644 V203/usb/scope/software/main.cpp create mode 100644 V203/usb/scope/software/main.h create mode 100644 V203/usb/scope/software/mainwindow.cpp create mode 100644 V203/usb/scope/software/mainwindow.h create mode 100644 V203/usb/scope/software/mainwindow.ui create mode 100644 V203/usb/scope/software/src.pro create mode 100644 V203/usb/scope/software/src.qrc create mode 120000 V203/usb/scope/software/structures.h create mode 100644 V203/usb/scope/software/time.png create mode 100644 V203/usb/scope/software/volt.png diff --git a/.gitignore b/.gitignore index 8df9f6c..29d07b9 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,11 @@ V203/pwm/sin.c V203/gsm/lib/libgsm.a V203/usb/cdc/mystrings.inc +V203/usb/scope/bin/* +V203/usb/scope/software/.qmake.stash +V203/usb/scope/software/Makefile +V203/usb/scope/software/moc/* +V203/usb/scope/software/obj/* +V203/usb/scope/software/qrc_src.cpp +V203/usb/scope/software/ui_mainwindow.h + diff --git a/V203/usb/ch32v203/adcscope.cpp b/V203/usb/ch32v203/adcscope.cpp new file mode 100644 index 0000000..1d39650 --- /dev/null +++ b/V203/usb/ch32v203/adcscope.cpp @@ -0,0 +1,166 @@ +#include "system.h" +#include "adcscope.h" +#include "structures.h" + +static AdcClass * AdcClassInstance = nullptr; +enum TRANSFER_SIZES { + XFER_BYTE = 0, + XFER_HALF, + XFER_WORD, +}; +static const unsigned DmaLenTable [] = { + DATA_FULL_LEN * ADC_MAXCHANNELS, ADC_MAXCHANNELS * 2, +}; +static constexpr unsigned LSH = 2u, DIVL = 36u, DIVH = 36'000u; +static const TimeBaseDescriptor BaseTable [] { + {(2u << LSH) - 1u, DIVL - 1u, TIME_BASE_TRIGERED}, // 2us + {(5u << LSH) - 1u, DIVL - 1u, TIME_BASE_TRIGERED}, // 5us + {(10u << LSH) - 1u, DIVL - 1u, TIME_BASE_TRIGERED}, // 10us + {(20u << LSH) - 1u, DIVL - 1u, TIME_BASE_TRIGERED}, // 20us + {(50u << LSH) - 1u, DIVL - 1u, TIME_BASE_TRIGERED}, // 50us + {(100u << LSH) - 1u, DIVL - 1u, TIME_BASE_TRIGERED}, // 100us + {(200u << LSH) - 1u, DIVL - 1u, TIME_BASE_TRIGERED}, // 200us + {(500u << LSH) - 1u, DIVL - 1u, TIME_BASE_TRIGERED}, // 500us + + {(1000u << LSH) - 1u, DIVL - 1u, TIME_BASE_CONTINUOUS}, // 1ms + {(2u << LSH) - 1u, DIVH - 1u, TIME_BASE_CONTINUOUS}, // 2ms + {(5u << LSH) - 1u, DIVH - 1u, TIME_BASE_CONTINUOUS}, // 5ms + {(10u << LSH) - 1u, DIVH - 1u, TIME_BASE_CONTINUOUS}, // 10ms + {(20u << LSH) - 1u, DIVH - 1u, TIME_BASE_CONTINUOUS}, // 20ms + {(50u << LSH) - 1u, DIVH - 1u, TIME_BASE_CONTINUOUS}, // 50ms + {(100u << LSH) - 1u, DIVH - 1u, TIME_BASE_CONTINUOUS}, // 100ms + {(200u << LSH) - 1u, DIVH - 1u, TIME_BASE_CONTINUOUS}, // 200ms + + {(500u << LSH) - 1u, DIVH - 1u, TIME_BASE_CONTINUOUS}, // 500ms + {(1000u << LSH) - 1u, DIVH - 1u, TIME_BASE_CONTINUOUS}, // 1s +}; +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.IOPAEN = SET; + return r.R; + }); + RCC.APB1PCENR.B.TIM3EN = SET; // Enable TIM3 + RCC.CFGR0.B.ADCPRE = 3u; // PCLK2 divided by 8 as ADC clock (18 MHz, ! pretaktovano 14 MHz max). + // 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 Timer3Init (const TimeBaseDescriptor & tb) noexcept { + TIM3.PSC.R = tb.presc; // 4 MHz Fs + TIM3.ATRLR.R = tb.divider; + // 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.RSQR3__CHANNEL.B.SQ2 = 3u; // CH3 + ADC1.RSQR1.B.L = ADC_MAXCHANNELS - 1U; // 2 regular conversion + static constexpr unsigned ts = 0u; + ADC1.SAMPTR2_CHARGE2.B.SMP2_TKCG2 = ts; + ADC1.SAMPTR2_CHARGE2.B.SMP3_TKCG3 = ts; + 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 (const void * ptr, const unsigned n = 0u) 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 = DmaLenTable [n]; // DATA_FULL_LEN * ADC_MAXCHANNELS; + // 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 = XFER_HALF;// 16-bit + r.B.PSIZE = XFER_HALF;// 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; + }); +} +/* *********************************************************************************************/ +void AdcClass::Init () { + AdcClassInstance = this; + EnableClock (); + Timer3Init (BaseTable [6]); + NVIC.EnableIRQ (DMA1_Channel1_IRQn); + AdcCalibrate(); + Dma1Ch1Init (buffer); + AdcPostInit (); + // start timer + TIM3.CTLR1.B.CEN = SET; +} +// nekonzistentni, ale funkcni +void SampleRing::ReloadTimer(const unsigned int n) { + const TimeBaseDescriptor & tb = BaseTable [n]; + TIM3.CTLR1.B.CEN = RESET; + TIM3.CNT.R = 0u; + TIM3.PSC.R = tb.presc; + TIM3.ATRLR.R = tb.divider; + TIM3.CTLR1.B.CEN = SET; + if (tb.mode != m_mode) { + if (!AdcClassInstance) return; + const DATA_BLOCK * buffer = AdcClassInstance->setPtrH (tb.mode); + DMA1.CFGR1.B.EN = RESET; + NVIC.DisableIRQ (DMA1_Channel1_IRQn); + Dma1Ch1Init (buffer, tb.mode); + // m_head = m_tail = m_lenght = 0u; + m_mode = tb.mode; + NVIC.EnableIRQ (DMA1_Channel1_IRQn); + } +} + +/* *********************************************************************************************/ + +void AdcClass::drq() { + led << false; + DMA1_Type::INTFR_DEF state (DMA1.INTFR); + DMA1.INTFCR.R = state.R; // clear all + if (state.B.HTIF1 != RESET) { + ring.write (ptrl); + } else if (state.B.TCIF1 != RESET) { + ring.write (ptrh); + } + led << true; +} +extern "C" { + [[gnu::interrupt]] extern void DMA1_Channel1_IRQHandler(); +} +void DMA1_Channel1_IRQHandler( void ) { + if (AdcClassInstance) AdcClassInstance->drq(); +} diff --git a/V203/usb/common/adcscope.h b/V203/usb/common/adcscope.h new file mode 100644 index 0000000..898b889 --- /dev/null +++ b/V203/usb/common/adcscope.h @@ -0,0 +1,46 @@ +#ifndef ADCCLASS_SCOPE_H +#define ADCCLASS_SCOPE_H +#include +#include "samplering.h" +#include "gpio.h" + +/** @file + * @brief A/D převodník. + * */ + +/** + * @class AdcClass + * @brief A/D převodník. + * + * Výhradně pro projekt osciloskopu. + * Původně to bylo pro STM32L452, ale v podstatě je to stejné pro CH32V203. + * Tedy deklarace třídy, nikoli její definice. + * */ + +class AdcClass { + public: + AdcClass(SampleRing & r) : ring(r), led(GPIOA, 1), ptrl (buffer), ptrh (buffer + DATA_HALF_LEN) { + }; + /** + * @brief Inicializace ADC. + * + * Včetně vstupů. + * + */ + void Init (); + DATA_BLOCK * setPtrH (const int n) { + if (n) { ptrh = buffer + 1; + } else { ptrh = buffer + DATA_HALF_LEN; } + return buffer; + } + public: // UNUSABLE + void drq (); //!< obsluha přerušení, interní metoda, ale musí mít public atribut + private: + SampleRing & ring; + GpioClass led; + DATA_BLOCK * ptrl; + DATA_BLOCK * ptrh; + [[gnu::aligned(4)]]DATA_BLOCK buffer [DATA_FULL_LEN]; //!< data +}; + +#endif // ADCCLASS_SCOPE_H diff --git a/V203/usb/scope/firmware/Makefile b/V203/usb/scope/firmware/Makefile new file mode 100644 index 0000000..81cc043 --- /dev/null +++ b/V203/usb/scope/firmware/Makefile @@ -0,0 +1,52 @@ +TARGET?= ch32v203 +TOOL ?= gcc +#TOOL ?= clang + +PRJ = example + +VPATH = . ./$(TARGET) ./common +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 hack.o +OBJS += usb_desc.o cdc_class.o adcscope.o samplering.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) -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/usb/scope/firmware/ch32v203 b/V203/usb/scope/firmware/ch32v203 new file mode 120000 index 0000000..41e3e87 --- /dev/null +++ b/V203/usb/scope/firmware/ch32v203 @@ -0,0 +1 @@ +../../ch32v203/ \ No newline at end of file diff --git a/V203/usb/scope/firmware/common b/V203/usb/scope/firmware/common new file mode 120000 index 0000000..248927d --- /dev/null +++ b/V203/usb/scope/firmware/common @@ -0,0 +1 @@ +../../common/ \ No newline at end of file diff --git a/V203/usb/scope/firmware/hack.c b/V203/usb/scope/firmware/hack.c new file mode 100644 index 0000000..391b7bf --- /dev/null +++ b/V203/usb/scope/firmware/hack.c @@ -0,0 +1,21 @@ +#include +#include +typedef __SIZE_TYPE__ size_t; +size_t strlen (const char *s) { + size_t l = 0; + while (*s++) l++; + return l; +} +void *memcpy (void *dest, const void *src, size_t n) { + const char *s = (const char *) src; + char *d = (char *) dest; + int i; + for (i=0; i= '0') and (c <= '9')) { + result += (unsigned) (c - '0'); + } else if ((c >= 'A') and (c <= 'F')) { + result += (unsigned) (c - 'A' + 10); + } else if ((c >= 'a') and (c <= 'f')) { + result += (unsigned) (c - 'a' + 10); + } else { + // chyba : nech byt + } + } + CommandPass (result); +} +void SampleRing::CommandPass(const unsigned int cmd) { + RcvdHeader header; + header.common = cmd & 0xFFFF; + const DESTINATION dest = static_cast (header.bits.destinat); + switch (dest) { + case DEST_CHA: + case DEST_CHB: + break; + case DEST_BASE: + ReloadTimer (header.bits.cmd_value); + break; + case DEST_TRIG: { + const TRIGGER_CMD command = static_cast (header.bits.cmd_type); + switch (command) { + case TRIGGER_CMD_OFSET: m_settings.offset = header.bits.cmd_value; break; + case TRIGGER_CMD_VALUE: m_settings.value = header.bits.cmd_value; break; + case TRIGGER_CMD_MODE: { + TriggerModeUnion new_mode_union; + new_mode_union.common = header.bits.cmd_value; + const TRIGER_MODE new_mode = static_cast (new_mode_union.bits.mode); + if (m_settings.mode != new_mode) m_settings.mode = new_mode; + const ADC_CHANNELS new_channel = static_cast(new_mode_union.bits.channel); + if (m_settings.channel != new_channel) m_settings.channel = new_channel; + const bool new_rising = new_mode_union.bits.rissing ? true : false; + if (m_settings.rising != new_rising) m_settings.rising = new_rising; + } break; + } + } break; + } +} + +void SampleRing::write(DATA_BLOCK const * data) { + if (m_finished) return; + unsigned t_head = m_head; // dočasné proměnné kvůli zrychlení + unsigned t_lenght = m_lenght; // následujícího cyklu + if (m_mode == TIME_BASE_TRIGERED) { + for (unsigned n=0u; n m_settings.value); + if (compare and m_old_triger and !m_trigered) { // TRIGERED + if (t_lenght >= m_settings.offset) { + t_lenght = m_settings.offset; + m_trigered = true; + } + } + m_old_triger = !compare; + if (t_lenght >= RING_LEN) { + // zastavit odesílání dat, pokud není AUTO a není splněna podmínka trigeru + if ((m_settings.mode != TRIGER_MODE_AUTO) and !m_trigered) continue; + t_lenght = RING_LEN; + m_tail = t_head; + m_finished = true; + break; + } + } + } else { + const DATA_BLOCK & sample = data [0]; + ring_buffer [t_head].common_data = sample.common_data; + t_head += 1u; + t_head &= RING_MSK; + t_lenght += 1u; + m_finished = true; + } + m_head = t_head; // vrať zpátky hodnoty + m_lenght = t_lenght; +} +bool SampleRing::read(DATA_BLOCK & sample) { + if (!m_finished) return false; + if (!m_lenght) { + m_head = m_tail = m_lenght = 0u; + m_trigered = false; + m_finished = false; + return false; + } + sample = ring_buffer [m_tail]; + m_tail += 1u; + m_tail &= RING_MSK; + m_lenght -= 1u; + return true; +} +uint32_t SampleRing::BlockSend (const char * buf, const 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; + } + return ofs; +} + +void SampleRing::pass() { + if (!m_finished) { + return; + } + SendPrefix(); + DATA_BLOCK data; + while (read(data)) { + SendData (data); + } + BlockSend("\r\n", 2); +} +static int to_str (char * buffer, const uint16_t data, const int len = 3) { + unsigned val = data; + for (int i=len-1; i>=0; --i) { + buffer [i] = hexstr [val & 0xF]; + val >>= 4; + } + return len; +} +uint32_t SampleRing::SendPrefix() { + const int buflen = 8; + char buffer [buflen]; + int n = 0; + buffer [n++] = '$'; + SendHeader sh; + sh.bits.trig_flg = m_trigered ? 1 : 0; + sh.bits.pack_len = m_lenght; + n += to_str (buffer + n, sh.common, 4); + buffer [n++] = '#'; + return BlockSend (buffer, n); +} +uint32_t SampleRing::SendData (const DATA_BLOCK & data) { + const int buflen = 8; + char buffer [buflen]; + int n = 0; + n += to_str (buffer + n, data.channels[0]); + n += to_str (buffer + n, data.channels[1]); + return BlockSend (buffer, n); +} + diff --git a/V203/usb/scope/firmware/samplering.h b/V203/usb/scope/firmware/samplering.h new file mode 100644 index 0000000..ccab247 --- /dev/null +++ b/V203/usb/scope/firmware/samplering.h @@ -0,0 +1,50 @@ +#ifndef SAMPLERING_H +#define SAMPLERING_H +#include +#include "structures.h" +#include "baselayer.h" + +static constexpr unsigned RING_BIT = 10; +static constexpr unsigned RING_LEN = 1u << RING_BIT; +static constexpr unsigned RING_MSK = RING_LEN - 1u; + +static constexpr unsigned DATA_HALF_LEN = 1024u; +static constexpr unsigned DATA_FULL_LEN = DATA_HALF_LEN * 2u; + +static constexpr unsigned RCVD_BUFLEN = 16; +enum RCVD_STATUS { + RCVD_IDLE = 0, + RCVD_DATA, +}; + +class SampleRing : public BaseLayer { + [[gnu::aligned(4)]]DATA_BLOCK ring_buffer [RING_LEN]; + TrigerSettings m_settings; + volatile unsigned m_head, m_tail; + volatile unsigned m_lenght; + bool m_old_triger; + bool m_trigered; + volatile bool m_finished; + TIME_BASE_MODE m_mode; + + [[gnu::aligned(4)]]char rcvd_buffer [RCVD_BUFLEN]; + unsigned rcvd_counter; + RCVD_STATUS rcvd_status; + public: + explicit SampleRing () noexcept : BaseLayer(), m_settings(), m_head(0), m_tail(0), m_lenght(0), + m_old_triger(false), m_trigered(false), m_finished(false), m_mode (TIME_BASE_TRIGERED), + rcvd_counter(0u), rcvd_status (RCVD_IDLE) {}; + uint32_t Up (const char * data, const uint32_t len) override; + void write (DATA_BLOCK const * data); + void pass (); + protected: + uint32_t BlockSend (const char * buf, const uint32_t len); + uint32_t SendPrefix (); + uint32_t SendData (const DATA_BLOCK & data); + bool read (DATA_BLOCK & sample); + void CmdReceived (); + void CommandPass (const unsigned cmd); + void ReloadTimer (const unsigned n); +}; + +#endif // SAMPLERING_H diff --git a/V203/usb/scope/firmware/structures.h b/V203/usb/scope/firmware/structures.h new file mode 100644 index 0000000..4f2ad8b --- /dev/null +++ b/V203/usb/scope/firmware/structures.h @@ -0,0 +1,88 @@ +#ifndef STRUCTURES_DEFINE_H +#define STRUCTURES_DEFINE_H +#include + +enum ADC_CHANNELS : uint16_t { + V1_VSENSE = 0, + V2_VSENSE, + + ADC_MAXCHANNELS, // interní konstanta - počet kanálů +}; +typedef uint16_t SAMPLE; +union DATA_BLOCK { + SAMPLE channels [ADC_MAXCHANNELS]; + uint32_t common_data; +}; +enum TRIGER_MODE : uint16_t { + TRIGER_MODE_AUTO = 0, + TRIGER_MODE_NORMAL, + TRIGER_MODE_SINGLE +}; +struct TrigerSettings { + uint16_t value; + uint16_t offset; + TRIGER_MODE mode; + ADC_CHANNELS channel; + bool rising; + explicit TrigerSettings () noexcept : value(0x80u), offset(100u), mode(TRIGER_MODE_AUTO), channel(V1_VSENSE), rising(false) {} +}; +static_assert (sizeof(TrigerSettings) == 10, "TrigerSettings error"); +union SendHeader { + struct s_bits { + uint16_t pack_len : 15; + uint16_t trig_flg : 1; + } bits; + uint16_t common; +}; +static_assert (sizeof(SendHeader) == 2, "SendHeader error"); +enum DESTINATION { + DEST_CHA = 0, + DEST_CHB, + DEST_TRIG, + DEST_BASE, +}; + +enum TRIGGER_CMD { + TRIGGER_CMD_MODE = 0, + TRIGGER_CMD_VALUE, + TRIGGER_CMD_OFSET, +}; +union TriggerModeUnion { + struct s_bits { + uint16_t mode : 2; + uint16_t channel : 1; + uint16_t rissing : 1; + uint16_t unused : 12; + } bits; + uint16_t common; +}; +static_assert (sizeof(TriggerModeUnion) == 2, "TriggerModeUnion error"); + +union RcvdHeader { + struct s_bits { + uint16_t cmd_value : 12; + uint16_t cmd_type : 2; + uint16_t destinat : 2; + } bits; + uint16_t common; +}; +static_assert (sizeof(RcvdHeader) == 2, "RcvdHeader error"); + +enum MOVE_ITEMS { + MOVE_VALUE = 0, + MOVE_OFSET, + MOVE_MARKERA, + MOVE_MARKERB, + TIME_ZOOM, +}; +enum TIME_BASE_MODE { + TIME_BASE_TRIGERED = 0, + TIME_BASE_CONTINUOUS, +}; +struct TimeBaseDescriptor { + uint16_t divider; + uint16_t presc; + TIME_BASE_MODE mode; +}; + +#endif // STRUCTURES_DEFINE_H diff --git a/V203/usb/scope/firmware/usb_desc.cpp b/V203/usb/scope/firmware/usb_desc.cpp new file mode 100644 index 0000000..f932f33 --- /dev/null +++ b/V203/usb/scope/firmware/usb_desc.cpp @@ -0,0 +1,83 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : usb_desc.c + * Author : WCH + * Version : V1.0.0 + * Date : 2022/08/20 + * Description : usb device descriptor,configuration descriptor, + * string descriptors and other descriptors. +********************************************************************************* +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* Attention: This software (modified or not) and binary are used for +* microcontroller manufactured by Nanjing Qinheng Microelectronics. +*******************************************************************************/ + +#include "usb_desc.h" + +/* Device Descriptor */ +const uint8_t MyDevDescr[] = +{ + 0x12, // bLength + 0x01, // bDescriptorType (Device) + 0x10, 0x01, // bcdUSB 1.10 + 0x02, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + DEF_USBD_UEP0_SIZE, // bMaxPacketSize0 64 + (uint8_t)DEF_USB_VID, (uint8_t)(DEF_USB_VID >> 8), // idVendor 0x1A86 + (uint8_t)DEF_USB_PID, (uint8_t)(DEF_USB_PID >> 8), // idProduct 0x5537 + DEF_IC_PRG_VER, 0x00, // bcdDevice 0.01 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x03, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 +}; + +/* Configuration Descriptor */ +const uint8_t MyCfgDescr[] = +{ + /* Configure descriptor */ + 0x09, 0x02, 0x43, 0x00, 0x02, 0x01, 0x00, 0x80, 0x32, + + /* Interface 0 (CDC) descriptor */ + 0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00, + + /* Functional Descriptors */ + 0x05, 0x24, 0x00, 0x10, 0x01, + + /* Length/management descriptor (data class interface 1) */ + 0x05, 0x24, 0x01, 0x00, 0x01, + 0x04, 0x24, 0x02, 0x02, + 0x05, 0x24, 0x06, 0x00, 0x01, + + /* Interrupt upload endpoint descriptor */ + 0x07, 0x05, 0x81, 0x03, (uint8_t)DEF_USBD_ENDP1_SIZE, (uint8_t)( DEF_USBD_ENDP1_SIZE >> 8 ), 0x01, + + /* Interface 1 (data interface) descriptor */ + 0x09, 0x04, 0x01, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x00, + + /* Endpoint descriptor */ + 0x07, 0x05, 0x02, 0x02, (uint8_t)DEF_USBD_ENDP2_SIZE, (uint8_t)( DEF_USBD_ENDP2_SIZE >> 8 ), 0x00, + + /* Endpoint descriptor */ + 0x07, 0x05, 0x83, 0x02, (uint8_t)DEF_USBD_ENDP3_SIZE, (uint8_t)( DEF_USBD_ENDP3_SIZE >> 8 ), 0x00, +}; +#define DEF_STRDESC(p,n) w_text<(sizeof(p)>>1)>n={sizeof(n)-2u,3u,{p}} +template struct w_text { + uint8_t len, typ; + const char16_t str [N]; +}; +static const DEF_STRDESC((u"Kizarm Labs."), str_1); +static const DEF_STRDESC((u"USB Osciloscope"),str_2); +static const DEF_STRDESC((u"00001"), str_3); +/* Language Descriptor */ +static const uint8_t LangDescr[] = { + 0x04, 0x03, 0x09, 0x04 +}; +const uint8_t * StringDescArray [DEF_MAX_STRINGS] = { + LangDescr, + reinterpret_cast (&str_1), + reinterpret_cast (&str_2), + reinterpret_cast (&str_3), +}; + + diff --git a/V203/usb/scope/firmware/usb_desc.h b/V203/usb/scope/firmware/usb_desc.h new file mode 100644 index 0000000..9b578b9 --- /dev/null +++ b/V203/usb/scope/firmware/usb_desc.h @@ -0,0 +1,66 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : usb_desc.h + * Author : WCH + * Version : V1.0.0 + * Date : 2022/08/20 + * Description : header file of usb_desc.c +********************************************************************************* +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* Attention: This software (modified or not) and binary are used for +* microcontroller manufactured by Nanjing Qinheng Microelectronics. +*******************************************************************************/ + +#ifndef USER_USB_DESC_H_ +#define USER_USB_DESC_H_ + +#include + +/******************************************************************************/ +/* global define */ +/* file version */ +#define DEF_FILE_VERSION 0x01 +/* usb device info define */ +#define DEF_USB_VID 0x1A86 +#define DEF_USB_PID 0xFE0C +/* USB device descriptor, device serial number(bcdDevice) */ +#define DEF_IC_PRG_VER DEF_FILE_VERSION + +/******************************************************************************/ +/* usb device endpoint size define */ +#define DEF_USBD_UEP0_SIZE 64 /* usb hs/fs device end-point 0 size */ +/* FS */ +#define DEF_USBD_FS_PACK_SIZE 64 /* usb fs device max bluk/int pack size */ +#define DEF_USBD_FS_ISO_PACK_SIZE 1023 /* usb fs device max iso pack size */ +/* LS */ +#define DEF_USBD_LS_UEP0_SIZE 8 /* usb ls device end-point 0 size */ +#define DEF_USBD_LS_PACK_SIZE 64 /* usb ls device max int pack size */ + +/* Pack size */ +#define DEF_USBD_ENDP1_SIZE DEF_USBD_FS_PACK_SIZE +#define DEF_USBD_ENDP2_SIZE DEF_USBD_FS_PACK_SIZE +#define DEF_USBD_ENDP3_SIZE DEF_USBD_FS_PACK_SIZE +#define DEF_USBD_ENDP4_SIZE DEF_USBD_FS_PACK_SIZE +#define DEF_USBD_ENDP5_SIZE DEF_USBD_FS_PACK_SIZE +#define DEF_USBD_ENDP6_SIZE DEF_USBD_FS_PACK_SIZE +#define DEF_USBD_ENDP7_SIZE DEF_USBD_FS_PACK_SIZE + +/******************************************************************************/ +/* usb device Descriptor length, length of usb descriptors, if one descriptor not + * exists , set the length to 0 */ +#define DEF_USBD_DEVICE_DESC_LEN ((uint8_t)MyDevDescr[0]) +#define DEF_USBD_CONFIG_DESC_LEN ((uint16_t)MyCfgDescr[2] + (uint16_t)(MyCfgDescr[3] << 8)) +#define DEF_USBD_REPORT_DESC_LEN 0 + +#define DEF_MAX_STRINGS (4) +/******************************************************************************/ +/* external variables */ +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + extern const uint8_t MyDevDescr[ ]; + extern const uint8_t MyCfgDescr[ ]; + extern const uint8_t * StringDescArray [DEF_MAX_STRINGS]; +#ifdef __cplusplus +}; +#endif // __cplusplus +#endif /* USER_USB_DESC_H_ */ diff --git a/V203/usb/scope/software/datasource.cpp b/V203/usb/scope/software/datasource.cpp new file mode 100644 index 0000000..c06fa00 --- /dev/null +++ b/V203/usb/scope/software/datasource.cpp @@ -0,0 +1,177 @@ +#include "datasource.h" +#include "helpers.h" + +static constexpr unsigned DATASIZE = 0x2000; + +DataSource::DataSource(QObject * p) : QObject (p), + usart (QString("/dev/serial/by-id/usb-Kizarm_Labs._USB_Osciloscope_00001-if00")), + trigerSettings () { + in_buffer = new char [DATASIZE]; + packet_buf = new char [DATASIZE]; + packet_cnt = 0; + state = StateIdle; + catching = true; + + connect (&usart, SIGNAL (readyRead()), this, SLOT (read_data())); + usart.open (QIODevice::ReadWrite); +} +DataSource::~DataSource() { + usart.close(); + delete [] in_buffer; + delete [] packet_buf; +} +void DataSource::Start() { + catching = true; +} + +void DataSource::read_data() { + const long numRead = usart.read(in_buffer, DATASIZE); + if (numRead == 0) return; + // qDebug ("readen = %ld", numRead); + parse_input (in_buffer, numRead); +} +void DataSource::parse_input(const char * data, const long len) { + for (long i=0; i= (int) DATASIZE) { + packet_cnt = 0; + qDebug ("Buffer overflow"); + } + break; + } + } +} +void DataSource::parse_header() { + if (packet_cnt != 4) return; + packet_buf [4] = '\0'; + QString h (packet_buf); + bool ok = false; + const unsigned hdr = h.toUInt(&ok, 16); + if (!ok) return; + header.common = hdr; + //qDebug ("header:%04X", hdr); + if (header.bits.trig_flg) { + emit PaketTriggered (); + } +} +void DataSource::parse_packet() { + QVector ChA, ChB; + if (state != StateData) return; + bool ok = false; + int k=0; + for (int n=0; n (n); + send_trig_mode(); +} +void DataSource::SendTrigerEdge(int n) { + trigerSettings.rising = n ? true : false; + send_trig_mode(); +} +void DataSource::SendTrigerChan(int n) { + trigerSettings.channel = static_cast (n); + send_trig_mode(); +} + +void DataSource::send_trig_mode () { + RcvdHeader hdr; + hdr.common = 0u; + hdr.bits.destinat = DEST_TRIG; + hdr.bits.cmd_type = TRIGGER_CMD_MODE; + TriggerModeUnion tmu; + tmu.common = 0u; + tmu.bits.mode = trigerSettings.mode; + tmu.bits.channel = trigerSettings.channel; + tmu.bits.rissing = trigerSettings.rising ? 1u : 0u; + hdr.bits.cmd_value = tmu.common; + + const unsigned len = 64; + char buffer [len]; + int r = snprintf(buffer, len, "$%04X\r\n", (int) hdr.common); + buffer [r] = '\0'; + usart.write (buffer, r); + qDebug ("%d:%s", r, strip_eol(buffer)); +} +void DataSource::SettingChanged(int n) { + const MOVE_ITEMS items = static_cast(n); + RcvdHeader hdr; + hdr.common = 0u; + hdr.bits.destinat = DEST_TRIG; + switch (items) { + case MOVE_VALUE: hdr.bits.cmd_type = TRIGGER_CMD_VALUE; hdr.bits.cmd_value = trigerSettings.value; break; + case MOVE_OFSET: hdr.bits.cmd_type = TRIGGER_CMD_OFSET; hdr.bits.cmd_value = trigerSettings.offset; break; + default : break; + } + + const unsigned len = 64; + char buffer [len]; + int r = snprintf(buffer, len, "$%04X\r\n", (int) hdr.common); + buffer [r] = '\0'; + usart.write (buffer, r); + // qDebug ("%d::%d:%s", n, r, strip_eol(buffer)); +} +void DataSource::SendBaseRange (int n) { + RcvdHeader hdr; + hdr.common = 0u; + hdr.bits.destinat = DEST_BASE; + hdr.bits.cmd_value = n; + const unsigned len = 64; + char buffer [len]; + int r = snprintf(buffer, len, "$%04X\r\n", (int) hdr.common); + buffer [r] = '\0'; + usart.write (buffer, r); + qDebug ("%d:%s", r, strip_eol(buffer)); +} + + + diff --git a/V203/usb/scope/software/datasource.h b/V203/usb/scope/software/datasource.h new file mode 100644 index 0000000..eeb94c5 --- /dev/null +++ b/V203/usb/scope/software/datasource.h @@ -0,0 +1,52 @@ +#ifndef DATASOURCE_H +#define DATASOURCE_H + +#include +#include +#include +#include +#include "structures.h" + +enum ParserState { + StateIdle = 0, + StateHeader, + StateData, +}; +/** + */ +class DataSource : public QObject { + Q_OBJECT + QSerialPort usart; + TrigerSettings trigerSettings; + char * in_buffer; + ParserState state; + char * packet_buf; + int packet_cnt; + SendHeader header; + bool catching; + public: + explicit DataSource (QObject * p = nullptr); + virtual ~DataSource (); + TrigerSettings * getTrigger () { + return & trigerSettings; + } + void SendTrigerMode (int n); + void SendTrigerEdge (int n); + void SendTrigerChan (int n); + void SendBaseRange (int n); + void Start (); + public slots: + void read_data (); + void SettingChanged (int n); + signals: + void Channels_received(QVector, QVector); + void PaketTriggered (); + protected: + void parse_input (const char * data, const long len); + void parse_packet (); + void parse_header (); + protected: + void send_trig_mode (); +}; + +#endif // DATASOURCE_H diff --git a/V203/usb/scope/software/displaywidget.cpp b/V203/usb/scope/software/displaywidget.cpp new file mode 100644 index 0000000..cc5b62a --- /dev/null +++ b/V203/usb/scope/software/displaywidget.cpp @@ -0,0 +1,277 @@ +#include +#include +#include +#include +#include +#include +#include "displaywidget.h" +#include "helpers.h" + +static const double TimeBaseSteps [] = { + 2.0e-6, 5.0e-6, 1.0e-5, 2.0e-5, 5.0e-5, 1.0e-4, 2.0e-4, 5.0e-4, + 1.0e-3, 2.0e-3, 5.0e-3, 1.0e-2, 2.0e-2, 5.0e-2, 1.0e-1, 2.0e-1, + + 5.0e-1, 1.0, +}; +static const double ChannelsSteps [] = { + 1.0, 2.0, 5.0, +}; +static constexpr double REF_Y = 3.3; // maximum napětí převodníku +static constexpr double MAX_Y = double (1 << 12); // 12. bit rozlišení - 4096 steps +static constexpr double STEP_Y = REF_Y / MAX_Y; // jeden krok ve Voltech + +DisplayWidget::DisplayWidget(QWidget * p) : QWidget(p), ACopy(), BCopy(), m_forward(), m_inverse(), background(), m_ts(nullptr), + ChA (1024), ChB(1024), m_items (MOVE_VALUE), m_timeBase(6), m_ChBase(0), m_timeCount(0u) { + x_lenght = 1024; + m_time.a = 200.0; + m_time.b = 300.0; + m_volt.a = 1.0 / STEP_Y; + m_volt.b = 2.0 / STEP_Y; + marker_type = MARKER_TIME; + int n = 0; + for (QPointF & e : ChA) { const QPointF p (n++, 0); e = p; } + n = 0; + for (QPointF & e : ChB) { const QPointF p (n++, 0); e = p; } +} +DisplayWidget::~DisplayWidget() { +} +void DisplayWidget::MarkerChanged (bool b) { + // qDebug ("Marker : %s", b ? "Time" : "Volt"); // OK + if (b) marker_type = MARKER_TIME; + else marker_type = MARKER_VOLT; + drawBackground(); + update(); +} + +void DisplayWidget::TriggerValues(int n) { + m_items = static_cast(n); +} +static constexpr double FMULT = 1.189207115002721; +static constexpr double IMULT = 1.0 / FMULT; +void DisplayWidget::wheelEvent(QWheelEvent * event) { + if (m_ts == nullptr) return; + const QPoint d = event->angleDelta(); + const int increment = d.y() > 0 ? +1 : -1; + switch (m_items) { + case MOVE_VALUE: { + m_ts->value += increment * 4; + emit SettingsChanged (m_items); + } break; + case MOVE_OFSET: { + m_ts->offset += increment; + emit SettingsChanged (m_items); + } break; + case MOVE_MARKERA: { + if (marker_type == MARKER_TIME) m_time.a += increment; + else m_volt.a += increment; + } break; + case MOVE_MARKERB: { + if (marker_type == MARKER_TIME) m_time.b += increment; + else m_volt.b += increment; + } break; + case TIME_ZOOM: { + const double mx = FMULT * double (size().width()) / x_lenght; + if ((m_forward.m11() > mx) or (increment > 0)) { // limit to window + const QMatrix m = increment > 0 ? QMatrix (FMULT,0,0,1,0,0) : QMatrix (IMULT,0,0,1,0,0); + m_forward *= m; + m_inverse = m_forward.inverted(); + } + } break; + default : { + qDebug ("wheelEvent : %d", int (m_items)); + } break; + } + drawBackground(); + update(); + QWidget::wheelEvent(event); +} +void DisplayWidget::mousePressEvent(QMouseEvent * event) { + if (m_ts == nullptr) return; + const QPointF dp = m_inverse.map (event->pos ()); + // qDebug ("dp:[%g, %g]", dp.x(), dp.y()); + switch (m_items) { + case MOVE_VALUE: { + m_ts->value = dp.y(); + emit SettingsChanged (m_items); + } break; + case MOVE_OFSET: { + m_ts->offset = dp.x(); + emit SettingsChanged (m_items); + } break; + case MOVE_MARKERA: { + if (marker_type == MARKER_TIME) m_time.a = dp.x(); + else m_volt.a = dp.y(); + } break; + case MOVE_MARKERB: { + if (marker_type == MARKER_TIME) m_time.b = dp.x(); + else m_volt.b = dp.y(); + } break; + case TIME_ZOOM: QWidget::mousePressEvent(event); return; + default : { + qDebug ("mousePressEvent : %d", int (m_items)); + } break; + } + drawBackground(); + update(); + QWidget::mousePressEvent(event); +} + +void DisplayWidget::resizeEvent(QResizeEvent * event) { + const QSize sz = event->size(); + reloadMatrix (sz); + QImage tmpi (sz, QImage::Format_ARGB32); + background = tmpi; + drawBackground(); + QWidget::resizeEvent(event); +} +void DisplayWidget::paintEvent(QPaintEvent * event) { + QPainter p (this); + p.drawImage(event->rect(), background); + QPen pa (QColor(0,255, 0,255), 2); + QPen pb (QColor(255,64,0,255), 2); + p.setPen(pb); + p.drawPolyline (m_forward.map(ChB)); + p.setPen(pa); + p.drawPolyline (m_forward.map(ChA)); +} +void DisplayWidget::setTrigger(TrigerSettings * ts) { + m_ts = ts; +} +void DisplayWidget::DispChannels(QVector cha, QVector chb) { + ACopy = cha; + BCopy = chb; + reloadData(); + update(); +} +static constexpr unsigned T_SIZE = 1u << 10; +static constexpr unsigned T_MASK = T_SIZE - 1u; +void DisplayWidget::reloadData () { + const int Alen = ACopy.size(); + const int Blen = BCopy.size(); + QVector va (Alen), vb (Blen); + if (Alen == 1 and Blen == 1) { // kontinuální mód + if (ChA.size() != T_SIZE) { ChA = QPolygonF (T_SIZE); } + if (ChB.size() != T_SIZE) { ChB = QPolygonF (T_SIZE); } + const QPointF pta (m_timeCount, ACopy [0]), ptb (m_timeCount, BCopy [0]); + ChA[m_timeCount] = pta; + ChB[m_timeCount] = ptb; + // printf("mode continuum\n"); + m_timeCount += 1u; + m_timeCount &= T_MASK; + return; + } + for (int n=0u; n Blen ? Alen : Blen; + if (l != x_lenght) { + x_lenght = l; + reloadMatrix (size()); + } +} +void DisplayWidget::reloadMatrix(const QSize & sz) { + const double xm = sz.width(); + const double ym = sz.height(); + const double xz = xm / (x_lenght); // převod zpět ma pixely + const double yz = ym / (MAX_Y); + const QMatrix tm (xz,0,0,-yz, 0, ym); + m_forward = tm; + m_inverse = m_forward.inverted(); +} + +void DisplayWidget::TimeBaseRange(int n) { + m_timeBase = n; + reloadMatrix (size()); + drawBackground(); + update(); +} + +void DisplayWidget::drawBackground() { + QPainter p (&background); + QRect r = background.rect(); + p.fillRect (r, QBrush(Qt::black, Qt::SolidPattern)); + if (!m_ts) return; + const double yb = 0.0, ye = MAX_Y; + const double xb = 0.0, xe = x_lenght; + // rastr + QPen pg (QColor(64,64,64), 1); + p.setPen(pg); + const double dx = 100.0; + for (double x=xb; xoffset, m_ts->value); + QPen pc (QColor(0,0,255), 1); + p.setPen(pc); + const QLineF hline (xb, trg.y(), xe, trg.y()); + const QLineF vline (trg.x(), yb, trg.x(), ye); + p.drawLine (m_forward.map(hline)); + p.drawLine (m_forward.map(vline)); + // markers + QPen pm (QColor(255,255,0,196), 2); + p.setPen(pm); + if (marker_type == MARKER_TIME) { + const QLineF ma (m_time.a, yb, m_time.a, ye); + const QLineF mb (m_time.b, yb, m_time.b, ye); + p.drawLine (m_forward.map(ma)); + p.drawLine (m_forward.map(mb)); + } else { + const QLineF ma (xb, m_volt.a, xe, m_volt.a); + const QLineF mb (xb, m_volt.b, xe, m_volt.b); + p.drawLine (m_forward.map(ma)); + p.drawLine (m_forward.map(mb)); + } + // text + p.setPen (QPen (QColor(255,255,0))); + QFont font = p.font(); + font.setPixelSize(16); + p.setFont (font); + QString desc; + const double xz = TimeBaseSteps [m_timeBase]; + const double yz = STEP_Y; + const int my = r.size().height() - 10; + desc.sprintf("T=%ss/d; A,B=%gV/d", ing_fmt (xz * dx).c_str() , dv); + p.drawText(10,20, desc); + + if (marker_type == MARKER_TIME) { + const double delta = xz * (m_time.b - m_time.a); + const double freq = delta == 0.0 ? 0.0 : 1.0 / delta; + desc.sprintf("Marker A: %ss, Marker B: %ss, Δ=%ss, f=%sHz", ing_fmt(xz * m_time.a).c_str(), ing_fmt(xz * m_time.b).c_str(), + ing_fmt(delta).c_str(), ing_fmt(freq).c_str()); + } else { + const double delta = yz * (m_volt.b - m_volt.a); + desc.sprintf("Marker A: %sV, Marker B: %sV, Δ=%sV", ing_fmt(yz * m_volt.a).c_str(), ing_fmt(yz * m_volt.b).c_str(), + ing_fmt(delta).c_str()); + } + p.drawText(10,my, desc); +} +void DisplayWidget::saveSettings(QSettings & setting) { + setting.setValue("TimeBaseIndex", m_timeBase); + setting.setValue("TimeA", m_time.a); + setting.setValue("TimeB", m_time.b); + setting.setValue("VoltA", m_volt.a); + setting.setValue("VoltB", m_volt.b); +} +void DisplayWidget::restSettings(QSettings & setting) { + m_time.a = setting.value("TimeA").toDouble(); + m_time.b = setting.value("TimeB").toDouble(); + m_volt.a = setting.value("VoltA").toDouble(); + m_volt.b = setting.value("VoltB").toDouble(); +} diff --git a/V203/usb/scope/software/displaywidget.h b/V203/usb/scope/software/displaywidget.h new file mode 100644 index 0000000..7d93985 --- /dev/null +++ b/V203/usb/scope/software/displaywidget.h @@ -0,0 +1,58 @@ +#ifndef DISPLAYWIDGET_H +#define DISPLAYWIDGET_H +#include +#include +#include +#include +#include +#include "structures.h" +/** + */ +class QSettings; +class DisplayWidget : public QWidget { + Q_OBJECT + QVector ACopy; + QVector BCopy; + QMatrix m_forward; + QMatrix m_inverse; + QImage background; + TrigerSettings * m_ts; + QPolygonF ChA, ChB; + MOVE_ITEMS m_items; + int m_timeBase; + int m_ChBase; + double x_lenght; + struct MarkSetting { + double a,b; + } m_time, m_volt; + enum MARKER_ENUM { + MARKER_TIME = 0, + MARKER_VOLT, + } marker_type; + unsigned m_timeCount; + public: + explicit DisplayWidget (QWidget * p); + virtual ~DisplayWidget (); + void setTrigger (TrigerSettings * ts); + void TriggerValues (int n); + void TimeBaseRange (int n); + void MarkerChanged (bool); + void saveSettings (QSettings & setting); + void restSettings (QSettings & setting); + + void resizeEvent (QResizeEvent * event) override; + void paintEvent (QPaintEvent * event) override; + void wheelEvent (QWheelEvent * event) override; + void mousePressEvent(QMouseEvent * event) override; + + public slots: + void DispChannels (QVector, QVector); + signals: + void SettingsChanged (int n); + protected: + void drawBackground (); + void reloadData (); + void reloadMatrix (const QSize & sz); +}; + +#endif // DISPLAYWIDGET_H diff --git a/V203/usb/scope/software/helpers.cpp b/V203/usb/scope/software/helpers.cpp new file mode 100644 index 0000000..252fe0f --- /dev/null +++ b/V203/usb/scope/software/helpers.cpp @@ -0,0 +1,47 @@ +#include +#include +#include "helpers.h" + +char * strip_eol (char * str) { + char * ptr = str; + for (;;) { + const char c = * ptr; + if (c == '\0') break; + else if (c == '\r') * ptr = '\0'; + else if (c == '\n') * ptr = '\0'; + ptr++; + } + return str; +} +std::string ing_fmt (const double par, const unsigned dnum) { + const char * suffixes [] = {"a", "f", "p", "n", "μ", "m", " ", "k", "M", "G", "T", "P", "E"}; + bool sign = false; + double value = par; + if (par < 0.0) { + value = - par; + sign = true; + } + if (value < 1.0e-18) return std::string ("0"); + if (value > 1.0e+18) return std::string ("hafo"); + + const double dlog = log10 (value) + 18.0; + double fractional, integer; + fractional = modf (dlog, & integer); + const double mult = pow (10.0, double (dnum - 1)); + double avgf = pow (10.0, fractional); // do mezí 1.0 až 9.9999... + avgf *= mult; + avgf = round (avgf); // a teď zaokrouhli + avgf /= mult; + div_t dt = div (int(integer), 3); + avgf *= pow (10.0, double (dt.rem)); + // printf("%g:%g (%f) [%d:%d]{%s}\n", integer, fractional, avgf, dt.quot, dt.rem, suffixes[dt.quot]); + + const unsigned buflen = 64; + char buffer [buflen]; + const int r = snprintf(buffer, buflen, "%g%s", avgf, suffixes [dt.quot]); + buffer [r] = '\0'; + + std::string result (buffer, r); + if (sign) result = std::string("-") + result; + return result; +} diff --git a/V203/usb/scope/software/helpers.h b/V203/usb/scope/software/helpers.h new file mode 100644 index 0000000..d337a7f --- /dev/null +++ b/V203/usb/scope/software/helpers.h @@ -0,0 +1,8 @@ +#ifndef HELPERS_H +#define HELPERS_H +#include + +extern char * strip_eol (char * str); +extern std::string ing_fmt (const double par, const unsigned dnum = 3u); + +#endif // HELPERS_H diff --git a/V203/usb/scope/software/ico.png b/V203/usb/scope/software/ico.png new file mode 100644 index 0000000000000000000000000000000000000000..e34e845f45d56fa9e6e5172f5720dca174987375 GIT binary patch literal 1545 zcmV+k2KM=hP)s9t#K!gBnG=000H3Nkl-=5Z>zQklIEJ&j^UwLt|Nj1;KD~C5!2pN| zfwS%>0SqGYKDK^{NHVxlH(ghGA|U}lIROO>Edq9k15cBHkZ`S*0RTt>1ZDd|DK$MF z!9?-(Lq|rM$dRLo;iJX|L&jg|Oy)#TlFW(m7qT0!4IedzA8TO`jqm#Mhc_h{AwATT zh9jX3F)+5tZQEl#qiv;()fA??!|CmDBQ0wsrzwm?!a*-1dI%uNYYyJ+Jo!q`?#J5? z9q&LeE6mD8G@-rwr;D5B0FsC{&s+akqH@E!cCK#LRLeR;Y~LO08%x|X&vJA75)^9rj1pi0@BQvP?9y9TgqGMB%qwy@m7W(Ra;NLJ{2K(EQ6dg63*f$?C^IApEY8%=Pu})!svL@l`G41{MY~m|*--u=V|JoE^l0162c?iMHedn7R_o&TBnc=jSl7j>6 zTT`DbdD6YVNi%Tvqtaj@dEr#~%eSusIQEzBum8ThfhrC>-SN-Y76J4_P^8Rt<1%Xla**v>*&?*Rz-K9)4@V=wc-}%8e36t{laRa<_;cCZnY0wn+Fdk zFHKjrKHM)&R|gBpV7afO>yF+?{vpFFE9RbKssDa@E2N7ZqlTBoj=qRT*PPp>uS8mOt;e-zx8;gRvS=2~H7Lig>AnRR#gh(FDK#_V*(^Pu~|Xl^(L#?)VnGAsZn zmvtTb5{z@B66%a*|dERByKlf*;mc zN*;fsPr01PhOz_t^xum3iD&L~<&i&Tk35$<_;f}HdhfZ*9{2bPawE~bzfC))QZCDo zxF?C;p&O%MAdTdC6%nW7-(lkv9NTK=rBayd4 vGf{K7Ciw5N@YeVB!obazNzClUfI$BTgMn}zRyAn<00000NkvXXu0mjf#w_Tw literal 0 HcmV?d00001 diff --git a/V203/usb/scope/software/main.cpp b/V203/usb/scope/software/main.cpp new file mode 100644 index 0000000..d55a291 --- /dev/null +++ b/V203/usb/scope/software/main.cpp @@ -0,0 +1,9 @@ +#include "main.h" + +int main (int argc, char *argv[]) { + // QApplication::setStyle("plastique"); + QApplication a (argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/V203/usb/scope/software/main.h b/V203/usb/scope/software/main.h new file mode 100644 index 0000000..d758843 --- /dev/null +++ b/V203/usb/scope/software/main.h @@ -0,0 +1,11 @@ +#ifndef _MAIN_H_ +#define _MAIN_H_ +#include +#include "mainwindow.h" +#include + +/** + * + * */ + +#endif // _MAIN_H_ diff --git a/V203/usb/scope/software/mainwindow.cpp b/V203/usb/scope/software/mainwindow.cpp new file mode 100644 index 0000000..06a3a8e --- /dev/null +++ b/V203/usb/scope/software/mainwindow.cpp @@ -0,0 +1,151 @@ +#include +#include +#include +#include +#include "mainwindow.h" +#include "ui_mainwindow.h" +/* +#undef qDebug +#define qDebug(p, ...) +*/ +static const char * TrigerChannelTexts [] = { + "Channel A", "Channel B" +}; +static const char * TrigerModeTexts [] = { + "Auto", "Normal", "Single" +}; +static const char * TrigerEdgeTexts [] = { + "Rising", "Faling" +}; +static const char * TrigerSelectTexts [] = { + "Trig.Value", "Trig.Offset", "Marker A", "Marker B", "Time Zoom" +}; +static const char * TimeBaseTexts [] = { + "2μs", "5μs", "10μs", "20μs", "50μs", "100μs", "200μs","500μs", + "1ms", "2ms", "5ms", "10ms", "20ms", "50ms", "100ms", "200ms", + "500ms", "1s", +}; + +MainWindow::MainWindow (QWidget * parent) + : QMainWindow (parent), firmware(parent) { + ui = new Ui_MainWindow; + ui->setupUi (this); + ui->radio_a->setChecked(true); + ui->Display->setTrigger (firmware.getTrigger()); + for (auto & e: TrigerModeTexts) ui->comboMode ->addItem(QString(e)); + for (auto & e: TrigerChannelTexts) ui->comboChannel ->addItem(QString(e)); + for (auto & e: TrigerEdgeTexts) ui->comboRissing ->addItem(QString(e)); + for (auto & e: TrigerSelectTexts) ui->comboValue ->addItem(QString(e)); + for (auto & e: TimeBaseTexts) ui->comboTimeRange->addItem(QString(e)); + + setWindowTitle ("Scope"); + setWindowIcon (QIcon(":ico")); +//setWindowFlags(Qt::Window | Qt::FramelessWindowHint); + connect (&firmware, SIGNAL (Channels_received(QVector, QVector)), + ui->Display, SLOT (DispChannels (QVector, QVector))); + connect (&firmware, SIGNAL (PaketTriggered()), this, SLOT(PaketTriggered())); + + connect (ui->Display, SIGNAL(SettingsChanged(int)), &firmware, SLOT(SettingChanged(int))); + connect (ui->buttonStart,SIGNAL(pressed()), this, SLOT(Started())); + connect (ui->actionQuit, SIGNAL(triggered(bool)), this, SLOT(close())); + connect (ui->radio_a, SIGNAL(toggled(bool)), this, SLOT(MarkerChanged(bool))); + connect (ui->actionExport_Image, SIGNAL(triggered(bool)), this, SLOT(ExportImage (bool))); + connect (ui->actionSaveSet, SIGNAL(triggered(bool)), this, SLOT(SaveSettings(bool))); + connect (ui->actionRestSet, SIGNAL(triggered(bool)), this, SLOT(RestSettings(bool))); + + connect (ui->comboMode, SIGNAL(activated(int)), this, SLOT(SendTrigerMode(int))); + connect (ui->comboRissing, SIGNAL(activated(int)), this, SLOT(SendTrigerEdge(int))); + connect (ui->comboChannel, SIGNAL(activated(int)), this, SLOT(SendTrigerChan(int))); + connect (ui->comboValue, SIGNAL(activated(int)), this, SLOT(TriggerValues (int))); + connect (ui->comboTimeRange, SIGNAL(activated(int)), this, SLOT(TimeBaseRange (int))); + + ui->comboTimeRange->setCurrentIndex(6); + ui->buttonStart->setEnabled(false); +} + +MainWindow::~MainWindow () { + delete ui; +} +void MainWindow::Started() { + firmware.Start(); +} +void MainWindow::MarkerChanged(bool b) { + ui->Display->MarkerChanged (b); +} + +void MainWindow::TriggerValues (int n) { + ui->Display->TriggerValues(n); +} +void MainWindow::SendTrigerMode (int n) { + if (n == TRIGER_MODE_SINGLE) ui->buttonStart->setEnabled(true); + else ui->buttonStart->setEnabled(false); + firmware.SendTrigerMode(n); +} +void MainWindow::SendTrigerEdge (int n) { + firmware.SendTrigerEdge(n); +} +void MainWindow::SendTrigerChan (int n) { + firmware.SendTrigerChan(n); +} +void MainWindow::TimeBaseRange (int n) { + firmware.SendBaseRange(n); + ui->Display->TimeBaseRange(n); +} +void MainWindow::PaketTriggered () { + statusBar()->showMessage(QString("TRIGER"), 500); +} + +void MainWindow::ExportImage (bool) { + QWidget * pw = ui->Display; + QString fileName = QFileDialog::getSaveFileName (this, + tr ("Save File"), ".", "Images Files(*.png)"); + if (!fileName.isEmpty()) { + QImage img (pw->width(), pw->height(), QImage::Format_ARGB32_Premultiplied); + QPainter painter(&img); + pw->render (&painter); + img.save (fileName, "png", 0); + } +} +void MainWindow::SaveSettings(bool) { + qDebug ("Save Settings"); + QSettings setting ("Kizarm", "Scope"); + setting.beginGroup("Trigger"); + const TrigerSettings * const ts = firmware.getTrigger(); + setting.setValue("Value", ts->value); + setting.setValue("Offset", ts->offset); + setting.setValue("Mode", ts->mode); + setting.setValue("Channel", ts->channel); + setting.setValue("Edge", ts->rising); + setting.endGroup(); + setting.beginGroup("Markers"); + ui->Display->saveSettings (setting); + setting.endGroup(); +} +void MainWindow::RestSettings(bool) { + qDebug ("Restore Settings"); + QSettings setting ("Kizarm", "Scope"); + setting.beginGroup("Trigger"); + TrigerSettings ts; + ts.value = setting.value ("Value").toInt(); + ts.offset = setting.value ("Offset").toInt(); + ts.mode = static_cast (setting.value ("Mode").toInt()); + ui->comboMode->setCurrentIndex(ts.mode); + ts.channel = static_cast (setting.value ("Channel").toInt()); + ui->comboChannel->setCurrentIndex(ts.channel); + ts.rising = setting.value ("Edge").toBool(); + ui->comboRissing->setCurrentIndex(ts.value ? 1 : 0); + memcpy (firmware.getTrigger(), & ts, sizeof(TrigerSettings)); + setting.endGroup(); + + SendTrigerMode (ts.mode); + firmware.SettingChanged(MOVE_VALUE); + firmware.SettingChanged(MOVE_OFSET); + + setting.beginGroup("Markers"); + ui->Display->restSettings (setting); + const int n = setting.value("TimeBaseIndex").toInt(); + TimeBaseRange(n); + ui->comboTimeRange->setCurrentIndex(n); + setting.endGroup(); +} + diff --git a/V203/usb/scope/software/mainwindow.h b/V203/usb/scope/software/mainwindow.h new file mode 100644 index 0000000..ebaaade --- /dev/null +++ b/V203/usb/scope/software/mainwindow.h @@ -0,0 +1,33 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "datasource.h" + +class Ui_MainWindow; + +class MainWindow : public QMainWindow { + Q_OBJECT + +public: + MainWindow (QWidget *parent = 0); + ~MainWindow (); +protected: +public slots: + void SendTrigerMode (int n); + void SendTrigerEdge (int n); + void SendTrigerChan (int n); + void TriggerValues (int n); + void TimeBaseRange (int n); + void ExportImage (bool); + void MarkerChanged (bool); + void SaveSettings (bool); + void RestSettings (bool); + void PaketTriggered (); + void Started (); +private: + Ui_MainWindow * ui; + DataSource firmware; +}; + +#endif // MAINWINDOW_H diff --git a/V203/usb/scope/software/mainwindow.ui b/V203/usb/scope/software/mainwindow.ui new file mode 100644 index 0000000..c3b8536 --- /dev/null +++ b/V203/usb/scope/software/mainwindow.ui @@ -0,0 +1,238 @@ + + + MainWindow + + + + 0 + 0 + 966 + 660 + + + + MainWindow + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + + + + + + Trigger + + + + + + + + + + + + + + + + + + Time Base + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 121 + 61 + + + + Markers + + + + + 10 + 30 + 106 + 25 + + + + + QLayout::SetMinimumSize + + + 0 + + + + + + + + + :/time:/time + + + + + + + + + + + :/volt:/volt + + + + + + + + + + + Item to move: + + + Qt::AlignCenter + + + + + + + + + + Start + + + + + + + + + + + + + 0 + 0 + 966 + 22 + + + + + Menu + + + + + + + + + + + + Quit + + + Alt+X + + + + + Export Image + + + + + Save Settings + + + Ctrl+S + + + + + Restore Settings + + + Ctrl+R + + + + + + DisplayWidget + QWidget +
displaywidget.h
+ 1 +
+
+ + + + +
diff --git a/V203/usb/scope/software/src.pro b/V203/usb/scope/software/src.pro new file mode 100644 index 0000000..c1d842b --- /dev/null +++ b/V203/usb/scope/software/src.pro @@ -0,0 +1,41 @@ +#CONFIG += qt debug +QT += core gui +QT += serialport +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport +QMAKE_CXXFLAGS += -std=c++14 + +TARGET = osciloscope +TEMPLATE = app + + +SOURCES = main.cpp +SOURCES += mainwindow.cpp +SOURCES += helpers.cpp +SOURCES += datasource.cpp +SOURCES += displaywidget.cpp + +HEADERS = mainwindow.h +HEADERS += helpers.h +HEADERS += datasource.h +HEADERS += structures.h +HEADERS += displaywidget.h + +FORMS = mainwindow.ui + +#INCLUDEPATH += +#LIBS += +RESOURCES += src.qrc + +MOC_DIR = moc +OBJECTS_DIR = obj +DESTDIR = ../bin + +unix { + DEFINES += UNIX +} + +win32 { + DEFINES += QT_DLL WIN32 +} + + diff --git a/V203/usb/scope/software/src.qrc b/V203/usb/scope/software/src.qrc new file mode 100644 index 0000000..7038ae3 --- /dev/null +++ b/V203/usb/scope/software/src.qrc @@ -0,0 +1,8 @@ + + + + ico.png + time.png + volt.png + + diff --git a/V203/usb/scope/software/structures.h b/V203/usb/scope/software/structures.h new file mode 120000 index 0000000..693584d --- /dev/null +++ b/V203/usb/scope/software/structures.h @@ -0,0 +1 @@ +../firmware/structures.h \ No newline at end of file diff --git a/V203/usb/scope/software/time.png b/V203/usb/scope/software/time.png new file mode 100644 index 0000000000000000000000000000000000000000..24a73b04424d67274258b68c4afa459557814782 GIT binary patch literal 606 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=hEVFt%hmI|q0=J1Z0v<)>xlq%tsQ zOst(~>v7mYqV4{&X<{oiwlsG*x4lYn3^@|GI-rGXVOYl1gHL{O`$|m`5qCeh^}&Pl zNmmaZjSOcGcNF-;SXf#ZR5DR$zc-g|(w@gV+Q0An{`8%-Lq^!ltZzNctI{N2rZ}B> z7IyT3wi8D}Lz+w4GCOro&5FMD)jxjJi9e6LS6bhlAp$-ECA_$})?W4Lx| zB|M36;MGbw&i{{ne|5H)y84|Z1xy@!ZhSk=A#jH^iud+EyKQf;aXzSLTeqce|2390 zJJ0-*EjI)&?B|V%DK|;w-*JbLZGOqk39={sy*0!dzZ?HrCL^$uwd(qveOu2L{$fa3 z5x!FT!2bHUgGKN%Kn^#RX{|Dk;|S1C?r|p8d2h$pPQSSSHj?2l$uzQ znxasiS(2gP?&%v4-pD5oRAlDq;usQf`0crkf(!}*hc~qREw3|cXlinkJFwi($nwgP i$`{|_f64$=B7p;J>r7beq#St~L4uyHelF{r5}E*8rt-i5 literal 0 HcmV?d00001 diff --git a/V203/usb/scope/software/volt.png b/V203/usb/scope/software/volt.png new file mode 100644 index 0000000000000000000000000000000000000000..b62b8346da69db13e81e896c62a5deefb4db0099 GIT binary patch literal 595 zcmV-Z0<8UsP)EX>4Tx04R}tkv&MmKp2MKrj{0|D71r!Lx$>PK~%(1t5Adrp;l10C8=2pevD?$ihfM!HyramW0DR_>rd-(W%7w1{tb$^b&e8FUZPb8jYx?vG-5KnJf zI_G`jC@af(;&b9LgDyz?$aTf#H_j!81)do-GU<8ZD6v@TVx@~&*-(k6h+}zGqkJLj za-Q=RXSG^q?R)YUh6~!tGS_JhBZ);UL4pVcbyQG=g*fdRDJIf%p78JwIev*;3b`s^ z<2)x+##|RMI1zHW;{yw(t)(PN$2ClS@zt#k1K1pwM zwAc~QzYSbmcQj=WxZD8-pLE%f9m!8iC>DYDGy0|+5V-|<*WA9f&vE(yWT;oE8{ps& z7%fruy2rai-F^GFrrp0E5wdcCk*;Le00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru=LiK56(44xa`*rM02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{001jVL_t(o!|m5G0RR99!?1yW&me