add scope

This commit is contained in:
Kizarm 2024-10-21 10:26:41 +02:00
parent ffd68b990c
commit 5b32ee9eae
31 changed files with 1912 additions and 0 deletions

8
.gitignore vendored
View file

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

View file

@ -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<size_t> (& ADC1.RDATAR_DR_ACT_DCG);
// 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 = 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();
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,21 @@
#include <stdint.h>
#include <stdarg.h>
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<n; i++) d[i] = s[i];
return dest;
}
void *memset (void *s, int c, size_t n) {
char *p = (char *) s;
int i;
for (i=0; i<n; i++) p[i] = c;
return s;
}

View file

@ -0,0 +1,29 @@
#include "main.h"
/** USB osciloskop.
* Původně to byl projekt pro STM32L452, ale i na CH32V203 to celkem funguje.
* AD převodník zde je pomalejší, pro 1 kanál snese max 1 Mbps, zde 2 kanály,
* tedy 500kbps max, s tím, že doba vzorkování je ta nejmenší, tedy vstupy se
* musí krmit z co nejmenší impedance (výstup externího OZ). Dál nejde omezit
* počet bitů na 10, musí zůstat 12.
* Komunikace po USB CDC je v hexadecimálním formátu - písmenka lze snadno
* číst a propustnost celkem vyhovuje - data 2*12 bitů zaberou 6 bytů, celkem
* nic by se stejně neušetřilo, binárně by se předávaly stejně 4 byty.
* Je zde aplikován trigger, zhruba jako normálního osciloskopu, nastavuje se
* povely po USB, stejně jako časová základna. Formát povelů je patrný ze
* zdrojáků.
* Bylo jen potřeba dopsat kontinuální běh pro časy vzorkování větší než 1ms.
* Zobrazování za úkol aplikace v Qt5 v adresáři software.
* */
static SampleRing ring;
static AdcClass adc (ring);
static cdc_class cdc;
int main () {
cdc.init();
adc.Init();
ring += cdc;
for (;;) {
ring.pass ();
}
return 0;
}

View file

@ -0,0 +1,8 @@
#ifndef MAIN_H
#define MAIN_H
#include "adcscope.h"
#include "cdc_class.h"
#include "samplering.h"
#endif // MAIN_H

View file

@ -0,0 +1,182 @@
#include "samplering.h"
static const char * const hexstr = "0123456789ABCDEF";
uint32_t SampleRing::Up (const char * data, const uint32_t len) {
for (unsigned n=0; n<len; n++) {
const char c = data [n];
switch (c) {
case '$':
rcvd_counter = 0;
rcvd_status = RCVD_DATA;
break;
case '\r':
case '\n':
CmdReceived ();
rcvd_counter = 0;
rcvd_status = RCVD_IDLE;
break;
default:
if (rcvd_status == RCVD_DATA) {
rcvd_buffer [rcvd_counter] = c;
rcvd_counter += 1u;
}
break;
}
}
return len;
}
void SampleRing::CmdReceived() {
if (rcvd_counter == 0u) return;
unsigned result = 0u;
for (unsigned n=0; n<rcvd_counter; n++) {
const char c = rcvd_buffer [n];
result *= 16;
if ((c >= '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<DESTINATION> (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<TRIGGER_CMD> (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<TRIGER_MODE> (new_mode_union.bits.mode);
if (m_settings.mode != new_mode) m_settings.mode = new_mode;
const ADC_CHANNELS new_channel = static_cast<ADC_CHANNELS>(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<DATA_HALF_LEN; n++) {
const DATA_BLOCK & sample = data [n];
ring_buffer [t_head].common_data = sample.common_data;
t_head += 1u;
t_head &= RING_MSK;
t_lenght += 1u;
const bool compare = m_settings.rising != (sample.channels[m_settings.channel] > 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);
}

View file

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

View file

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

View file

@ -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<const unsigned N> 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<const uint8_t*> (&str_1),
reinterpret_cast<const uint8_t*> (&str_2),
reinterpret_cast<const uint8_t*> (&str_3),
};

View file

@ -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 <stdint.h>
/******************************************************************************/
/* 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_ */

View file

@ -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<len; i++) {
const char c = data [i];
switch (c) {
case '$':
state = StateHeader;
packet_cnt = 0;
break;
case '#':
parse_header();
state = StateData;
packet_cnt = 0;
break;
case '\r':
case '\n':
parse_packet();
state = StateIdle;
packet_cnt = 0;
break;
default:
packet_buf [packet_cnt] = c;
packet_cnt += 1;
if (packet_cnt >= (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<int> ChA, ChB;
if (state != StateData) return;
bool ok = false;
int k=0;
for (int n=0; n<packet_cnt; n+=3) {
char buf [4];
memcpy (buf, packet_buf+n, 3);
buf[3] = '\0';
QString s (buf);
const unsigned sample = s.toUInt(&ok, 16);
if (ok) {
if (k & 1) {
ChB.push_back (sample);
} else {
ChA.push_back (sample);
}
}
k += 1;
}
bool sok = true;
if (ChA.size() != (int) header.bits.pack_len) sok = false;
if (ChB.size() != (int) header.bits.pack_len) sok = false;
if (sok) {
if (trigerSettings.mode == TRIGER_MODE_SINGLE) {
if (catching) {
catching = false;
emit Channels_received (ChA, ChB);
}
} else {
emit Channels_received (ChA, ChB);
}
} else {
qDebug ("packet error: ChA=%d, ChB=%d, size=%d", ChA.size(), ChB.size(), header.bits.pack_len);
}
}
void DataSource::SendTrigerMode(int n) {
trigerSettings.mode = static_cast<TRIGER_MODE> (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<ADC_CHANNELS> (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<MOVE_ITEMS>(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));
}

View file

@ -0,0 +1,52 @@
#ifndef DATASOURCE_H
#define DATASOURCE_H
#include <QObject>
#include <QSerialPort>
#include <QVector>
#include <QPoint>
#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<int>, QVector<int>);
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

View file

@ -0,0 +1,277 @@
#include <QPainter>
#include <QResizeEvent>
#include <QPaintEvent>
#include <QWheelEvent>
#include <QSettings>
#include <math.h>
#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<MOVE_ITEMS>(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<int> cha, QVector<int> 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<QPointF> 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<Alen; n++) {
const double x = (double (n));
const double y = ( ACopy [n]);
va [n] = QPointF (x,y);
}
ChA = QPolygonF (va);
for (int n=0u; n<Blen; n++) {
const double x = (double (n));
const double y = ( BCopy [n]);
vb [n] = QPointF (x,y);
}
ChB = QPolygonF (vb);
const int l = Alen > 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; x<xe; x+=dx) {
const QLineF yline (x, yb, x, ye);
p.drawLine (m_forward.map(yline));
}
const double dv = 0.5;
const double dy = dv / STEP_Y;
for (double y=yb; y<ye; y+=dy) {
const QLineF xline (xb, y, xe, y);
p.drawLine (m_forward.map(xline));
}
// triger
QPointF trg (m_ts->offset, 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();
}

View file

@ -0,0 +1,58 @@
#ifndef DISPLAYWIDGET_H
#define DISPLAYWIDGET_H
#include <QWidget>
#include <QVector>
#include <QPoint>
#include <QImage>
#include <QMatrix>
#include "structures.h"
/**
*/
class QSettings;
class DisplayWidget : public QWidget {
Q_OBJECT
QVector<int> ACopy;
QVector<int> 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<int>, QVector<int>);
signals:
void SettingsChanged (int n);
protected:
void drawBackground ();
void reloadData ();
void reloadMatrix (const QSize & sz);
};
#endif // DISPLAYWIDGET_H

View file

@ -0,0 +1,47 @@
#include <cstdio>
#include <cmath>
#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;
}

View file

@ -0,0 +1,8 @@
#ifndef HELPERS_H
#define HELPERS_H
#include <string>
extern char * strip_eol (char * str);
extern std::string ing_fmt (const double par, const unsigned dnum = 3u);
#endif // HELPERS_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

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

View file

@ -0,0 +1,11 @@
#ifndef _MAIN_H_
#define _MAIN_H_
#include <QApplication>
#include "mainwindow.h"
#include <qstyle.h>
/**
*
* */
#endif // _MAIN_H_

View file

@ -0,0 +1,151 @@
#include <QDesktopWidget>
#include <QFileDialog>
#include <QPainter>
#include <QSettings>
#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<int>, QVector<int>)),
ui->Display, SLOT (DispChannels (QVector<int>, QVector<int>)));
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<TRIGER_MODE> (setting.value ("Mode").toInt());
ui->comboMode->setCurrentIndex(ts.mode);
ts.channel = static_cast<ADC_CHANNELS> (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();
}

View file

@ -0,0 +1,33 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#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

View file

@ -0,0 +1,238 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>966</width>
<height>660</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="DisplayWidget" name="Display" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupTriger">
<property name="title">
<string>Trigger</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QComboBox" name="comboRissing"/>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="comboMode"/>
</item>
<item row="0" column="0">
<widget class="QComboBox" name="comboChannel"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBase">
<property name="title">
<string>Time Base</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QComboBox" name="comboTimeRange"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupMarkers">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>121</width>
<height>61</height>
</size>
</property>
<property name="title">
<string>Markers</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>106</width>
<height>25</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="radio_a">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="src.qrc">
<normaloff>:/time</normaloff>:/time</iconset>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="radio_b">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="src.qrc">
<normaloff>:/volt</normaloff>:/volt</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="labelA">
<property name="text">
<string>Item to move:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboValue"/>
</item>
<item>
<widget class="QPushButton" name="buttonStart">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>966</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuMenu">
<property name="title">
<string>Menu</string>
</property>
<addaction name="actionQuit"/>
<addaction name="actionExport_Image"/>
<addaction name="actionSaveSet"/>
<addaction name="actionRestSet"/>
</widget>
<addaction name="menuMenu"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
<property name="shortcut">
<string>Alt+X</string>
</property>
</action>
<action name="actionExport_Image">
<property name="text">
<string>Export Image</string>
</property>
</action>
<action name="actionSaveSet">
<property name="text">
<string>Save Settings</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="actionRestSet">
<property name="text">
<string>Restore Settings</string>
</property>
<property name="shortcut">
<string>Ctrl+R</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>DisplayWidget</class>
<extends>QWidget</extends>
<header location="global">displaywidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="src.qrc"/>
</resources>
<connections/>
</ui>

View file

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

View file

@ -0,0 +1,8 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/" >
<file alias="ico">ico.png</file>
<file alias="time">time.png</file>
<file alias="volt">volt.png</file>
</qresource>
</RCC>

View file

@ -0,0 +1 @@
../firmware/structures.h

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B