kopia lustrzana https://github.com/dgatf/Oscilloscope
Update to v0.3
rodzic
78d468c5e0
commit
6b27a3a53d
|
@ -1,6 +1,6 @@
|
|||
# Oscilloscope Serial
|
||||
|
||||
An oscilloscope application that reads the values at serial port generated by an Arduino board and based on the value of the analog pin A0
|
||||
An oscilloscope application that reads the values at serial port generated by an Arduino board and based on the value of the analog pin A0 (A5 for Leonardo)
|
||||
|
||||
<p align="center"><img src="./images/img1.png" width="600"><br>
|
||||
<i>Main window</i><br><br></p>
|
||||
|
@ -17,7 +17,7 @@ Flash _oscilloscope_. In the Oscilloscope app configure the serial port. By defa
|
|||
|
||||
You can connect any signal up to 5 volts to the analog pin A0. For signals over 5V a voltage divider is needed.
|
||||
|
||||
For ATmega boards it is defined a PWM signal at pin OC1B. To test the Oscilloscope app connect pin OC1B to pin A0
|
||||
For ATmega boards it is defined a PWM signal at pin OC1B. To test the Oscilloscope app connect pin OC1B to pin A0/A5
|
||||
Adjust PWM signal with PWM_FREQ and PWM_DUTY. Default is 1000Hz, 50% duty.
|
||||
|
||||
Pin OC1B location:
|
||||
|
@ -43,6 +43,9 @@ Supported OS:
|
|||
|
||||
## Change log:
|
||||
|
||||
v0.3
|
||||
- Minor fixes
|
||||
|
||||
[v0.2](https://github.com/dgatf/Oscilloscope/releases/tag/v0.2)
|
||||
- Upgrade to qml gui
|
||||
- Add csv export
|
||||
|
|
|
@ -1,31 +1,209 @@
|
|||
#define serial Serial
|
||||
|
||||
/* PWM signal */
|
||||
#define PWM_FREQ 1000 // HZ (1000HZ=1ms)
|
||||
#define PWM_FREQ 20000 // HZ (1000HZ=1ms)
|
||||
#define PWM_DUTY 0.5 // %/100
|
||||
|
||||
/* Capture buffer */
|
||||
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__)
|
||||
#define BUFFER_LEN 1500
|
||||
#endif
|
||||
#if defined(__AVR_ATmega32U4__)
|
||||
#define BUFFER_LEN 2000
|
||||
#endif
|
||||
#if defined(__AVR_ATmega2560__)
|
||||
#define BUFFER_LEN 5000
|
||||
#endif
|
||||
|
||||
/* Data bits */
|
||||
#define DATABITS 8 // 8, (4), 1
|
||||
|
||||
/* ADC Prescaler */
|
||||
#define PRESCALER 32 // 32, 16, 8, (4)
|
||||
|
||||
/* Mode */
|
||||
#define REALTIME 0
|
||||
#define CAPTURE 1
|
||||
#define MODE REALTIME // REALTIME, CAPTURE
|
||||
|
||||
#define MS_TO_COMP(SCALER) (F_CPU / (SCALER * 1000.0))
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
volatile bool isBufferFull = false;
|
||||
volatile uint8_t buffer[BUFFER_LEN];
|
||||
|
||||
ISR(ADC_vect) {
|
||||
void (*ADC_vect_handlerP)() = NULL;
|
||||
|
||||
ISR(ADC_vect)
|
||||
{
|
||||
if (ADC_vect_handlerP)
|
||||
ADC_vect_handlerP();
|
||||
/*static uint16_t contByte = 0;
|
||||
buffer[contByte] = ADCH;
|
||||
if (contByte == BUFFER_LEN)
|
||||
{
|
||||
isBufferFull = true;
|
||||
contByte = 0;
|
||||
ADCSRA &= ~_BV(ADIE);
|
||||
|
||||
}
|
||||
contByte++;*/
|
||||
}
|
||||
|
||||
/* Capture */
|
||||
|
||||
void capture_8b()
|
||||
{
|
||||
static uint16_t contByte = 0;
|
||||
buffer[contByte] = ADCH;
|
||||
if (contByte == BUFFER_LEN)
|
||||
{
|
||||
isBufferFull = true;
|
||||
contByte = 0;
|
||||
ADCSRA &= ~_BV(ADIE);
|
||||
|
||||
}
|
||||
contByte++;
|
||||
}
|
||||
|
||||
void capture_4b()
|
||||
{
|
||||
static uint16_t contByte = 0;
|
||||
static bool newByte = false;
|
||||
if (newByte)
|
||||
{
|
||||
buffer[contByte] |= (ADCH & 0xF0);
|
||||
contByte++;
|
||||
}
|
||||
else
|
||||
buffer[contByte] = ADCH >> 4;
|
||||
if (contByte == BUFFER_LEN)
|
||||
{
|
||||
isBufferFull = true;
|
||||
contByte = 0;
|
||||
ADCSRA &= ~_BV(ADIE);
|
||||
|
||||
}
|
||||
newByte = !newByte;
|
||||
}
|
||||
|
||||
void capture_1b()
|
||||
{
|
||||
static uint16_t contByte = 0;
|
||||
static uint8_t data = 0;
|
||||
static uint8_t contBit = 0;
|
||||
data |= (ADCH & 0x80);
|
||||
contBit++;
|
||||
if (contBit == 8)
|
||||
{
|
||||
buffer[contByte] = data;
|
||||
contBit = 0;
|
||||
contByte++;
|
||||
}
|
||||
if (contByte == BUFFER_LEN)
|
||||
{
|
||||
isBufferFull = true;
|
||||
contByte = 0;
|
||||
ADCSRA &= ~_BV(ADIE);
|
||||
}
|
||||
data >>= 1;
|
||||
}
|
||||
|
||||
/* Realtime */
|
||||
|
||||
void realtime_8b()
|
||||
{
|
||||
serial.write((uint8_t)ADCH);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
serial.begin(1000000);
|
||||
void realtime_4b()
|
||||
{
|
||||
static uint8_t data = 0;
|
||||
static bool sendByte = false;
|
||||
if (sendByte)
|
||||
serial.write(data | (ADCH & 0xF0));
|
||||
else
|
||||
data = ADCH >> 4;
|
||||
sendByte = !sendByte;
|
||||
|
||||
/* ADC init
|
||||
Capture time: F_CPU / ADC_PRESCALER / 13 ADC TICKS:
|
||||
Prescaler 32: 1.6M / 32 / 13 = 76923 -> 1 / 38461 = 26 us
|
||||
Prescaler 16: 1.6M / 16 / 13 = 76923 -> 1 / 76923 = 13 us (this is too fast for some devices)
|
||||
}
|
||||
|
||||
void realtime_1b()
|
||||
{
|
||||
static uint8_t data = 0;
|
||||
static uint8_t cont;
|
||||
data |= (ADCH & 0x80);
|
||||
cont++;
|
||||
if (cont == 8)
|
||||
{
|
||||
serial.write(data);
|
||||
data = 0;
|
||||
cont = 0;
|
||||
}
|
||||
data >>= 1;
|
||||
}
|
||||
|
||||
void sendData()
|
||||
{
|
||||
for (uint16_t i = 0; i < BUFFER_LEN; i++) {
|
||||
while (!serial.availableForWrite());
|
||||
serial.write(buffer[i]);
|
||||
}
|
||||
isBufferFull = false;
|
||||
delay(2000);
|
||||
ADCSRA |= _BV(ADIE);
|
||||
|
||||
}
|
||||
|
||||
void setup() {
|
||||
/*
|
||||
ADC init
|
||||
Capture time: F_CPU / ADC_PRESCALER / 13 ADC TICKS: (1 byte at 1000000 = 10us)
|
||||
Prescaler 32: 1.6M / 32 / 13 = 38461 -> 1 / 38461 = 26 us -> realtime 8 data bits
|
||||
Prescaler 16: 1.6M / 16 / 13 = 76923 -> 1 / 76923 = 13 us -> realtime 4 data bits
|
||||
Prescaler 8: 1.6M / 8 / 13 = 153846 -> 1 / 153846 = 6.5 us -> capture
|
||||
Prescaler 4: 1.6M / 4 / 13 = 307692-> 1 / 307692 = 3.25 us -> too fast
|
||||
Prescaler 2: 1.6M / 2 / 13 = 615385-> 1 / 615385 = 1.62 us -> too fast
|
||||
*/
|
||||
switch (PRESCALER)
|
||||
{
|
||||
case 4:
|
||||
ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS1);
|
||||
break;
|
||||
case 8:
|
||||
ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS1) | _BV(ADPS0);
|
||||
break;
|
||||
case 16:
|
||||
ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2);
|
||||
break;
|
||||
case 32:
|
||||
ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS0);
|
||||
break;
|
||||
}
|
||||
ADMUX = _BV(REFS0) | _BV(ADLAR);
|
||||
//ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2); // prescaler 16
|
||||
ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS0); // prescaler 32
|
||||
ADCSRB = 0;
|
||||
ADCSRA |= _BV(ADSC);
|
||||
|
||||
if (MODE == REALTIME)
|
||||
{
|
||||
serial.begin(1000000, SERIAL_8N1);
|
||||
if (DATABITS == 8)
|
||||
ADC_vect_handlerP = realtime_8b;
|
||||
if (DATABITS == 4)
|
||||
ADC_vect_handlerP = realtime_4b;
|
||||
if (DATABITS == 1)
|
||||
ADC_vect_handlerP = realtime_1b;
|
||||
}
|
||||
if (MODE == CAPTURE)
|
||||
{
|
||||
serial.begin(115200, SERIAL_8N1);
|
||||
if (DATABITS == 8)
|
||||
ADC_vect_handlerP = capture_8b;
|
||||
if (DATABITS == 4)
|
||||
ADC_vect_handlerP = capture_4b;
|
||||
if (DATABITS == 1)
|
||||
ADC_vect_handlerP = capture_1b;
|
||||
}
|
||||
|
||||
/* PWM signal is available at pin OC1B. To test the Oscilloscope app connect pin OC1B to pin A0
|
||||
Adjust PWM signal with PWM_FREQ and PWM_DUTY. Default: PWM 1000Hz, 50% DUTY
|
||||
Pin OC1B:
|
||||
|
@ -38,36 +216,25 @@ void setup() {
|
|||
#else
|
||||
DDRB |= _BV(DDB2);
|
||||
#endif
|
||||
DDRB |= _BV(DDB2);
|
||||
|
||||
TCCR1A = _BV(WGM11) | _BV(WGM10);
|
||||
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11); // scaler 8
|
||||
TCCR1A |= _BV(COM1B1);
|
||||
|
||||
// PWM signal
|
||||
OCR1A = 1000.0 / PWM_FREQ * MS_TO_COMP(8);
|
||||
OCR1B = PWM_DUTY * OCR1A;
|
||||
|
||||
/* // sinus signal (1Hz)
|
||||
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // scaler 1
|
||||
OCR1A = 1000.0 / 38000 * MS_TO_COMP(8);
|
||||
OCR1B = PWM_DUTY * OCR1A;*/
|
||||
|
||||
//TIMSK1 = OCIE1A;
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
if (isBufferFull)
|
||||
sendData();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Interval between analog readings (for non ATmega boards. It has to be the same as defined in the Oscilloscope settings */
|
||||
#define INTERVAL 200// microseconds (us). Minimun is 200
|
||||
|
||||
void setup() {
|
||||
serial.begin(1000000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static uint16_t ts = 0;
|
||||
if ((uint16_t)micros() - ts > INTERVAL)
|
||||
{
|
||||
ts = micros();
|
||||
uint8_t val = analogRead(A0) >> 2;
|
||||
serial.write(val);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -91,7 +91,11 @@ Item {
|
|||
ListElement { text: "5ms/d"; value: 5000 }
|
||||
ListElement { text: "1ms/d"; value: 1000 }
|
||||
ListElement { text: "500μs/d"; value: 500 }
|
||||
ListElement { text: "400μs/d"; value: 400 }
|
||||
ListElement { text: "300μs/d"; value: 300 }
|
||||
ListElement { text: "200μs/d"; value: 200 }
|
||||
ListElement { text: "100μs/d"; value: 100 }
|
||||
ListElement { text: "50μs/d"; value: 50 }
|
||||
}
|
||||
currentIndex: 2
|
||||
onCurrentValueChanged: oscilloscope.setHDiv(currentValue)
|
||||
|
|
|
@ -110,7 +110,7 @@ Item {
|
|||
ListElement { text: "500000" }
|
||||
ListElement { text: "1000000" }
|
||||
}
|
||||
currentIndex: 7
|
||||
currentIndex: 8
|
||||
}
|
||||
Label { text: "Data bits" }
|
||||
ComboBox {
|
||||
|
@ -139,7 +139,26 @@ Item {
|
|||
SpinBox {
|
||||
id: interval
|
||||
Layout.fillWidth: true
|
||||
value: 26
|
||||
value: 2600
|
||||
from: 0
|
||||
to: 100 * 100
|
||||
editable: true
|
||||
|
||||
property int decimals: 2
|
||||
property real realValue: value / 100
|
||||
|
||||
validator: DoubleValidator {
|
||||
bottom: Math.min(interval.from, interval.to)
|
||||
top: Math.max(interval.from, interval.to)
|
||||
}
|
||||
|
||||
textFromValue: function(value, locale) {
|
||||
return Number(value / 100).toLocaleString(locale, 'f', interval.decimals)
|
||||
}
|
||||
|
||||
valueFromText: function(text, locale) {
|
||||
return Number.fromLocaleString(locale, text) * 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,9 @@ Oscilloscope::Oscilloscope(qreal pixelRatio)
|
|||
, pixmap(new QPixmap(pmWidth, pmHeight))
|
||||
, paint(new QPainter(pixmap))
|
||||
, serial(new QSerialPort())
|
||||
, timer(new QTimer())
|
||||
{
|
||||
connect(timer, &QTimer::timeout, this, &Oscilloscope::timeOut);
|
||||
}
|
||||
#else
|
||||
Oscilloscope::Oscilloscope(qreal pixelRatio)
|
||||
|
@ -39,7 +41,8 @@ Oscilloscope::Oscilloscope(qreal pixelRatio)
|
|||
jclass classId = env->FindClass("org/qtproject/qt/UsbSerialInterface");
|
||||
usbSerial = QAndroidJniObject(classId);
|
||||
JNINativeMethod methods[] { {"sendBuffer", "([B)V", reinterpret_cast<void *>(sendBuffer)}
|
||||
, {"sendStatus", "(ILjava/lang/String;)V", reinterpret_cast<void *>(sendStatus)} };
|
||||
, {"sendStatus", "(ILjava/lang/String;)V", reinterpret_cast<void *>(sendStatus)}
|
||||
};
|
||||
env->RegisterNatives(classId, methods, 2);
|
||||
}
|
||||
#endif
|
||||
|
@ -65,7 +68,7 @@ QPixmap Oscilloscope::requestPixmap(const QString &id, QSize *size, const QSize
|
|||
}
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
void Oscilloscope::openSerialPort(QString portName, QString baudrate, QString dataBits, QString parity, QString interval)
|
||||
void Oscilloscope::openSerialPort(QString portName, QString baudrate, QString dataBits, QString parity, QString intervalStr)
|
||||
{
|
||||
connect(serial, &QSerialPort::readyRead, this, &Oscilloscope::readData);
|
||||
serial->setPortName(portName);
|
||||
|
@ -74,11 +77,11 @@ void Oscilloscope::openSerialPort(QString portName, QString baudrate, QString da
|
|||
serial->setParity((QSerialPort::Parity)parity.toUInt());
|
||||
serial->setStopBits(QSerialPort::StopBits::OneStop);
|
||||
serial->setFlowControl(QSerialPort::FlowControl::NoFlowControl);
|
||||
this->interval = interval.toUInt();
|
||||
interval = intervalStr.toUInt() / 100.0;
|
||||
if (serial->open(QIODevice::ReadOnly)) {
|
||||
status = connected;
|
||||
emit sendStatusConn(status);
|
||||
QString message("Connected (" + portName + " " + baudrate + "bps " + interval + "μs)");
|
||||
QString message("Connected (" + portName + " " + baudrate + "bps " + QString::number(interval) + "μs)");
|
||||
emit sendMessage(message);
|
||||
} else {
|
||||
status = disconnected;
|
||||
|
@ -96,9 +99,9 @@ void Oscilloscope::openSerialPort(QString portName, QString baudrate, QString da
|
|||
parity_android = parity.toUInt();
|
||||
jstring name = env->NewStringUTF(portName.toStdString().c_str());
|
||||
usbSerial.callMethod<void>
|
||||
("openConnection"
|
||||
, "(Landroid/content/Context;Ljava/lang/String;IIII)V"
|
||||
, QtAndroid::androidContext().object(), name, baudrate.toUInt(), dataBits.toUInt(), 1, parity_android);
|
||||
("openConnection"
|
||||
, "(Landroid/content/Context;Ljava/lang/String;IIII)V"
|
||||
, QtAndroid::androidContext().object(), name, baudrate.toUInt(), dataBits.toUInt(), 1, parity_android);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -119,12 +122,34 @@ void Oscilloscope::closeSerialPort()
|
|||
emit isPausedChanged(false);
|
||||
}
|
||||
|
||||
void Oscilloscope::timeOut()
|
||||
{
|
||||
timedOut = true;
|
||||
isPixmapSent = false;
|
||||
isDataStart = true;
|
||||
processData(data);
|
||||
}
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
void Oscilloscope::readData()
|
||||
{
|
||||
QByteArray data = serial->readAll();
|
||||
if (!isPaused)
|
||||
processData(data);
|
||||
if (isRealTime) {
|
||||
data = serial->readAll();
|
||||
if (!isPaused) {
|
||||
processData(data);
|
||||
}
|
||||
} else {
|
||||
if (!isPaused) {
|
||||
timer->setSingleShot(true);
|
||||
timer->start(500);
|
||||
if (timedOut) {
|
||||
data.clear();
|
||||
timedOut = false;
|
||||
}
|
||||
data.append(serial->readAll());
|
||||
timedOut = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Oscilloscope::readData(const QByteArray &data)
|
||||
|
@ -173,6 +198,10 @@ QStringList Oscilloscope::fillPortsInfo()
|
|||
QStringList list;
|
||||
for (const QSerialPortInfo& info : infos) {
|
||||
list.append(info.portName());
|
||||
qDebug() << info.description();
|
||||
qDebug() << info.manufacturer();
|
||||
qDebug() << info.serialNumber();
|
||||
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
@ -180,9 +209,9 @@ QStringList Oscilloscope::fillPortsInfo()
|
|||
QStringList Oscilloscope::fillPortsInfo()
|
||||
{
|
||||
QAndroidJniObject drivers = usbSerial.callObjectMethod
|
||||
("drivers"
|
||||
, "(Landroid/content/Context;)Ljava/util/List;"
|
||||
, QtAndroid::androidContext().object());
|
||||
("drivers"
|
||||
, "(Landroid/content/Context;)Ljava/util/List;"
|
||||
, QtAndroid::androidContext().object());
|
||||
QStringList list;
|
||||
int size = drivers.callMethod<jint>("size");
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
@ -211,101 +240,129 @@ void Oscilloscope::processData(const QByteArray &data)
|
|||
quint16 width = pmWidth;
|
||||
paint->setPen(QPen(QColor(signalColor), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
float xDelta = width * interval / 1000.0 / timeRange;
|
||||
for (quint16 i = 0; i < data.size(); i++) {
|
||||
// Find V limits
|
||||
if ((quint8)data[i] > maxValue) {
|
||||
maxValue = data[i];
|
||||
}
|
||||
if ((quint8)data[i] < minValue) {
|
||||
minValue = data[i];
|
||||
}
|
||||
// Find triggers and signal properties
|
||||
if ((quint8)data[i] - prevValue > 0) { // rising
|
||||
deltaRising += (quint8)data[i] - prevValue;
|
||||
deltaFalling = 0;
|
||||
}
|
||||
if ((quint8)data[i] - prevValue < 0) { // falling
|
||||
deltaFalling += prevValue - (quint8)data[i];
|
||||
deltaRising = 0;
|
||||
}
|
||||
prevValue = data[i];
|
||||
if (((float)deltaRising) / 0xFF * 5 > triggerValue / 1000.0) {
|
||||
if ((triggerType == rising || triggerType == none) && triggerCont - tsRising > 2) {
|
||||
isTriggered = true;
|
||||
rawFreq = triggerCont - tsRising;
|
||||
rawDuty = triggerCont - tsFalling;
|
||||
rawFreqAvg = 0.1 * rawFreq + 0.9 * rawFreqAvg;
|
||||
rawDutyAvg = 0.1 * rawDuty + 0.9 * rawDutyAvg;
|
||||
}
|
||||
deltaRising = 0;
|
||||
deltaFalling = 0;
|
||||
tsRising = triggerCont;
|
||||
}
|
||||
if (((float)deltaFalling) / 0xFF * 5 > triggerValue / 1000.0) {
|
||||
if (triggerType == falling && triggerCont - tsFalling > 2) {
|
||||
isTriggered = true;
|
||||
rawFreq = triggerCont - tsFalling;
|
||||
rawDuty = triggerCont - tsRising;
|
||||
rawFreqAvg = 0.1 * rawFreq + 0.9 * rawFreqAvg;
|
||||
rawDutyAvg = 0.1 * rawDuty + 0.9 * rawDutyAvg;
|
||||
}
|
||||
deltaRising = 0;
|
||||
deltaFalling = 0;
|
||||
tsFalling = triggerCont;
|
||||
}
|
||||
triggerCont++;
|
||||
// Capture
|
||||
if (isCapturing) {
|
||||
captureBuffer->append(data[i]);
|
||||
}
|
||||
// Draw new value
|
||||
if (isTriggered == true || triggerType == none) {
|
||||
float newxPos = oldxPos + xDelta;
|
||||
qint16 newyPos = height + yZeroV - getYVal(data[i]);
|
||||
// End of pixmap
|
||||
if (newxPos > width) {
|
||||
if (triggerType != none) {
|
||||
isTriggered = false;
|
||||
deltaRising = 0;
|
||||
deltaFalling = 0;
|
||||
}
|
||||
emit sendPixmap();
|
||||
static quint16 contX = 0;
|
||||
|
||||
if (pendingExport) {
|
||||
savePixmap(filename);
|
||||
pendingExport = false;
|
||||
if (isDataStart) {
|
||||
oldxPos = 0;
|
||||
maxValue = 0;
|
||||
minValue = 0;
|
||||
drawBackground();
|
||||
paint->setPen(QPen(QColor(signalColor), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
isDataStart =false;
|
||||
isTriggered = false;
|
||||
}
|
||||
for (quint16 contByte = 0; contByte < data.size(); contByte++) {
|
||||
for (quint16 contSubByte = 8 / dataBits; contSubByte > 0; contSubByte--) {
|
||||
quint8 value = (data[contByte] << (contSubByte - 1) * dataBits) & ~(0xFF >> dataBits);
|
||||
//quint8 value = data[contByte];
|
||||
// Find V limits
|
||||
if (value > maxValue) {
|
||||
maxValue = value;
|
||||
}
|
||||
if (value < minValue) {
|
||||
minValue = value;
|
||||
}
|
||||
// Find triggers and signal properties
|
||||
if (value - prevValue > 0) { // rising
|
||||
deltaRising += value - prevValue;
|
||||
deltaFalling = 0;
|
||||
}
|
||||
if (value - prevValue < 0) { // falling
|
||||
deltaFalling += prevValue - value;
|
||||
deltaRising = 0;
|
||||
}
|
||||
prevValue = value;
|
||||
if (((float)deltaRising) / 0xFF * 5 > triggerValue / 1000.0) {
|
||||
if ((triggerType == rising || triggerType == none) && triggerCont - tsRising > 2) {
|
||||
isTriggered = true;
|
||||
rawFreq = triggerCont - tsRising;
|
||||
rawDuty = triggerCont - tsFalling;
|
||||
rawFreqAvg = 0.1 * rawFreq + 0.9 * rawFreqAvg;
|
||||
rawDutyAvg = 0.1 * rawDuty + 0.9 * rawDutyAvg;
|
||||
}
|
||||
if (isCapturing) {
|
||||
isCapturing = false;
|
||||
saveCsv(filename);
|
||||
delete captureBuffer;
|
||||
deltaRising = 0;
|
||||
deltaFalling = 0;
|
||||
tsRising = triggerCont;
|
||||
}
|
||||
if (((float)deltaFalling) / 0xFF * 5 > triggerValue / 1000.0) {
|
||||
if (triggerType == falling && triggerCont - tsFalling > 2) {
|
||||
isTriggered = true;
|
||||
rawFreq = triggerCont - tsFalling;
|
||||
rawDuty = triggerCont - tsRising;
|
||||
rawFreqAvg = 0.1 * rawFreq + 0.9 * rawFreqAvg;
|
||||
rawDutyAvg = 0.1 * rawDuty + 0.9 * rawDutyAvg;
|
||||
}
|
||||
if (pendingCsv) {
|
||||
isCapturing = true;
|
||||
pendingCsv = false;
|
||||
captureBuffer = new QByteArray(timeRange/interval+1, 0);
|
||||
}
|
||||
|
||||
if (pendingPause) {
|
||||
pendingPause = false;
|
||||
isPaused = true;
|
||||
emit isPausedChanged(true);
|
||||
return;
|
||||
}
|
||||
drawBackground();
|
||||
paint->setPen(QPen(QColor(signalColor), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
newxPos = 0;
|
||||
maxValue = 0;
|
||||
minValue = 0;
|
||||
deltaRising = 0;
|
||||
deltaFalling = 0;
|
||||
tsFalling = triggerCont;
|
||||
}
|
||||
triggerCont++;
|
||||
// Capture
|
||||
if (isCapturing) {
|
||||
captureBuffer->append(value);
|
||||
}
|
||||
// Draw new value
|
||||
else {
|
||||
paint->drawLine(newxPos + width * hAdjust, newyPos + height * vAdjust, oldxPos + width * hAdjust, oldyPos + height * vAdjust);
|
||||
if (isTriggered == true || triggerType == none) {
|
||||
float newxPos = contX * xDelta;
|
||||
contX++;
|
||||
qint16 newyPos = height + yZeroV - getYVal(value);
|
||||
// End of pixmap
|
||||
if (newxPos > width) {
|
||||
if (triggerType != none) {
|
||||
isTriggered = false;
|
||||
deltaRising = 0;
|
||||
deltaFalling = 0;
|
||||
}
|
||||
emit sendPixmap();
|
||||
if (pendingExport) {
|
||||
savePixmap(filename);
|
||||
pendingExport = false;
|
||||
}
|
||||
if (isCapturing) {
|
||||
isCapturing = false;
|
||||
saveCsv(filename);
|
||||
delete captureBuffer;
|
||||
}
|
||||
if (pendingCsv) {
|
||||
isCapturing = true;
|
||||
pendingCsv = false;
|
||||
captureBuffer = new QByteArray(timeRange/interval+1, 0);
|
||||
}
|
||||
|
||||
if (pendingPause) {
|
||||
pendingPause = false;
|
||||
isPaused = true;
|
||||
emit isPausedChanged(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isRealTime) {
|
||||
isPixmapSent = true;
|
||||
newxPos = 0;
|
||||
maxValue = 0;
|
||||
minValue = 0;
|
||||
contX = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
drawBackground();
|
||||
paint->setPen(QPen(QColor(signalColor), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
newxPos = 0;
|
||||
maxValue = 0;
|
||||
minValue = 0;
|
||||
contX = 0;
|
||||
}
|
||||
// Draw new value
|
||||
else {
|
||||
paint->drawLine(newxPos + width * hAdjust, newyPos + height * vAdjust, oldxPos + width * hAdjust, oldyPos + height * vAdjust);
|
||||
}
|
||||
oldxPos = newxPos;
|
||||
oldyPos = newyPos;
|
||||
}
|
||||
oldxPos = newxPos;
|
||||
oldyPos = newyPos;
|
||||
}
|
||||
}
|
||||
if (!isRealTime && !isPixmapSent)
|
||||
emit sendPixmap();
|
||||
}
|
||||
|
||||
qint16 Oscilloscope::getYZeroV()
|
||||
|
@ -331,8 +388,8 @@ void Oscilloscope::drawBackground()
|
|||
for (quint8 i = 0; i < 6; i++) { // horizontal lines
|
||||
paint->drawLine(0, height * i / 6, width, height * i / 6);
|
||||
}
|
||||
for (quint8 i = 0; i < 10; i++) { // vertical lines
|
||||
paint->drawLine(width * i / 10, 0, width * i / 10, height);
|
||||
for (quint8 i = 0; i < 11; i++) { // vertical lines
|
||||
paint->drawLine(width * i / 11, 0, width * i / 11, height);
|
||||
}
|
||||
QFont font = paint->font();
|
||||
font.setPixelSize(14 * pixelRatio);
|
||||
|
@ -404,8 +461,7 @@ void Oscilloscope::exportCsv(QUrl url)
|
|||
void Oscilloscope::saveCsv(QString filename)
|
||||
{
|
||||
QFile data(filename);
|
||||
if(data.open(QFile::WriteOnly | QFile::Truncate))
|
||||
{
|
||||
if(data.open(QFile::WriteOnly | QFile::Truncate)) {
|
||||
QTextStream output(&data);
|
||||
output << "Interval\t" + QString::number(interval) + "\n";
|
||||
for (quint16 i=0; i < captureBuffer->size(); i++) {
|
||||
|
@ -416,11 +472,11 @@ void Oscilloscope::saveCsv(QString filename)
|
|||
data.close();
|
||||
}
|
||||
|
||||
void Oscilloscope::setIsPaused(const bool value) {
|
||||
void Oscilloscope::setIsPaused(const bool value)
|
||||
{
|
||||
if (value == true)
|
||||
pendingPause = true;
|
||||
else
|
||||
{
|
||||
else {
|
||||
isPaused = false;
|
||||
pendingPause = false;
|
||||
emit isPausedChanged(false);
|
||||
|
|
|
@ -16,59 +16,132 @@
|
|||
#ifndef OSCILLOSCOPE_H
|
||||
#define OSCILLOSCOPE_H
|
||||
|
||||
#include <QSerialPort>
|
||||
#include <QSerialPortInfo>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QPainter>
|
||||
#include <QQuickImageProvider>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QSerialPort>
|
||||
#include <QSerialPortInfo>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <QAndroidJniObject>
|
||||
#include <QAndroidJniEnvironment>
|
||||
#include <QAndroidJniObject>
|
||||
#include <QtAndroid>
|
||||
#endif
|
||||
|
||||
class Oscilloscope : public QObject, public QQuickImageProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
//Q_PROPERTY(bool isPaused READ getIsPaused WRITE setIsPaused NOTIFY isPausedChanged)
|
||||
|
||||
// Q_PROPERTY(bool isPaused READ getIsPaused WRITE setIsPaused NOTIFY
|
||||
// isPausedChanged)
|
||||
|
||||
public:
|
||||
enum TriggerType { none, rising, falling };
|
||||
enum Status { connected, disconnected, permission_not_granted, error_connecting, error_opening, connection_lost, driver_not_found, permission_requested };
|
||||
enum Status {
|
||||
connected,
|
||||
disconnected,
|
||||
permission_not_granted,
|
||||
error_connecting,
|
||||
error_opening,
|
||||
connection_lost,
|
||||
driver_not_found,
|
||||
permission_requested
|
||||
};
|
||||
Q_ENUM(Status)
|
||||
Q_ENUM(TriggerType)
|
||||
Oscilloscope(qreal pixelratio);
|
||||
~Oscilloscope();
|
||||
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override;
|
||||
Q_INVOKABLE void openSerialPort(QString portName, QString baudrate, QString dataBits, QString parity, QString interval);
|
||||
QPixmap requestPixmap(const QString &id, QSize *size,
|
||||
const QSize &requestedSize) override;
|
||||
Q_INVOKABLE void openSerialPort(QString portName, QString baudrate,
|
||||
QString dataBits, QString parity,
|
||||
QString interval);
|
||||
Q_INVOKABLE void closeSerialPort();
|
||||
Q_INVOKABLE QStringList fillPortsInfo();
|
||||
Q_INVOKABLE void setBackgroundColor(const QString &color) { backgroundColor = color; drawBackground(); emit sendPixmap(); }
|
||||
Q_INVOKABLE void setGridColor(const QString &color) { gridColor = color; drawBackground(); emit sendPixmap(); }
|
||||
Q_INVOKABLE void setSignalColor(const QString &color) { signalColor = color; drawBackground(); emit sendPixmap(); }
|
||||
Q_INVOKABLE void setTextColor(const QString &color) { textColor = color; drawBackground(); emit sendPixmap(); }
|
||||
Q_INVOKABLE void setVDiv(const quint16 value) { vDiv = value; voltRange = value * 6 / 1000.0; yZeroV = getYZeroV(); drawBackground(); emit sendPixmap(); }
|
||||
Q_INVOKABLE void setHDiv(const quint16 value) { hDiv = value; timeRange = value * 11 / 1000.0; drawBackground(); emit sendPixmap(); }
|
||||
Q_INVOKABLE void setTriggerType(const quint16 value) { triggerType = (TriggerType)value; }
|
||||
Q_INVOKABLE void setTriggerValue(const quint16 value) { triggerValue = value; }
|
||||
Q_INVOKABLE void setVAdjust(const qint16 value) { vAdjust = 6/voltRange * value / 100.0; drawBackground(); emit sendPixmap(); }
|
||||
Q_INVOKABLE void setHAdjust(const qint16 value) { hAdjust = 11/timeRange * value / 100.0 * 0.25; drawBackground(); emit sendPixmap(); }
|
||||
Q_INVOKABLE void setBackgroundColor(const QString &color)
|
||||
{
|
||||
backgroundColor = color;
|
||||
drawBackground();
|
||||
emit sendPixmap();
|
||||
}
|
||||
Q_INVOKABLE void setGridColor(const QString &color)
|
||||
{
|
||||
gridColor = color;
|
||||
drawBackground();
|
||||
emit sendPixmap();
|
||||
}
|
||||
Q_INVOKABLE void setSignalColor(const QString &color)
|
||||
{
|
||||
signalColor = color;
|
||||
drawBackground();
|
||||
emit sendPixmap();
|
||||
}
|
||||
Q_INVOKABLE void setTextColor(const QString &color)
|
||||
{
|
||||
textColor = color;
|
||||
drawBackground();
|
||||
emit sendPixmap();
|
||||
}
|
||||
Q_INVOKABLE void setVDiv(const quint16 value)
|
||||
{
|
||||
vDiv = value;
|
||||
voltRange = value * 6 / 1000.0;
|
||||
yZeroV = getYZeroV();
|
||||
drawBackground();
|
||||
emit sendPixmap();
|
||||
}
|
||||
Q_INVOKABLE void setHDiv(const quint16 value)
|
||||
{
|
||||
hDiv = value;
|
||||
timeRange = value * 11 / 1000.0;
|
||||
drawBackground();
|
||||
emit sendPixmap();
|
||||
}
|
||||
Q_INVOKABLE void setTriggerType(const quint16 value)
|
||||
{
|
||||
triggerType = (TriggerType)value;
|
||||
}
|
||||
Q_INVOKABLE void setTriggerValue(const quint16 value)
|
||||
{
|
||||
triggerValue = value;
|
||||
}
|
||||
Q_INVOKABLE void setVAdjust(const qint16 value)
|
||||
{
|
||||
vAdjust = 6 / voltRange * value / 100.0;
|
||||
drawBackground();
|
||||
emit sendPixmap();
|
||||
}
|
||||
Q_INVOKABLE void setHAdjust(const qint16 value)
|
||||
{
|
||||
hAdjust = 11 / timeRange * value / 100.0 * 0.25;
|
||||
drawBackground();
|
||||
emit sendPixmap();
|
||||
}
|
||||
Q_INVOKABLE void exportImage(QUrl filename);
|
||||
Q_INVOKABLE void exportCsv(QUrl filename);
|
||||
Q_INVOKABLE bool getIsPaused() { return isPaused; }
|
||||
Q_INVOKABLE bool getIsPaused()
|
||||
{
|
||||
return isPaused;
|
||||
}
|
||||
Q_INVOKABLE void setIsPaused(const bool value);
|
||||
Q_INVOKABLE QString getVersion() { return APP_VERSION; }
|
||||
Q_INVOKABLE QString getVersion()
|
||||
{
|
||||
return APP_VERSION;
|
||||
}
|
||||
#ifdef Q_OS_ANDROID
|
||||
static Oscilloscope *instance() { return m_instance; }
|
||||
static Oscilloscope *instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void isPausedChanged(bool isPaused);
|
||||
void sendPixmap();
|
||||
void sendStatusConn(Status status);
|
||||
void sendMessage(QString message, quint16 duration=0);
|
||||
void sendMessage(QString message, quint16 duration = 0);
|
||||
#ifdef Q_OS_ANDROID
|
||||
void receiveBuffer(const QByteArray &buffer);
|
||||
void receiveStatus(int status, const QString &msg);
|
||||
|
@ -76,8 +149,8 @@ signals:
|
|||
|
||||
private:
|
||||
enum Edge { min, max };
|
||||
quint16 pmHeight = 400; //230;
|
||||
quint16 pmWidth = 800; //520;
|
||||
quint16 pmHeight = 400;
|
||||
quint16 pmWidth = 800;
|
||||
quint8 maxValue = 0;
|
||||
quint8 minValue = 0;
|
||||
quint16 rawFreq = 0;
|
||||
|
@ -88,7 +161,7 @@ private:
|
|||
bool pendingCsv = false;
|
||||
bool pendingPause = false;
|
||||
QString filename;
|
||||
quint16 interval = 26;
|
||||
float interval = 26;
|
||||
quint16 vDiv = 1000;
|
||||
quint16 hDiv = 1000;
|
||||
float voltRange = 1000 * 6 / 1000.0;
|
||||
|
@ -125,9 +198,18 @@ private:
|
|||
void savePixmap(QString filename);
|
||||
void saveCsv(QString filename);
|
||||
|
||||
quint8 dataBits = 8;
|
||||
bool isRealTime = true;
|
||||
bool isPixmapSent = false;
|
||||
bool isDataStart = false;
|
||||
QTimer *timer;
|
||||
QByteArray data;
|
||||
bool timedOut = false;
|
||||
|
||||
public slots:
|
||||
#ifndef Q_OS_ANDROID
|
||||
void readData();
|
||||
void timeOut();
|
||||
#else
|
||||
void readData(const QByteArray &data);
|
||||
#endif
|
||||
|
|
Ładowanie…
Reference in New Issue