A simple oscilloscope using Adafruit 2.8" TFT with touch. Runs on FRDM-KL25Z, FRDM-K22F, FRDM-K64F, NUCLEO-F411RE. 2 channel analog inputs with 4 trigger modes and time division.

Dependencies:   SPI_STMPE610 UniGraphic mbed vt100

My first attempt of implementing an oscilloscope using FRDM-KL25Z and Adafruit 2.8" TFT with touch.

FRDM-KL25Z と Adafruit 2.8" TFT with touch を使用して作ってみた最初のオシロスコープです。

On 25-Sep-2017, wrong calculation of trigger pos/val fixed.
On 25-Nov-2015, FRDM-K64F and NUCLEO-F411RE platforms were added.
On 26-Nov-2015, FRDM-K22F is added (tested).
Note: Now ch2 is A2 instead of A1!

25-Sep-2017, トリガー値と位置の計算の誤りを修正しました。
25-Nov-2015, FRDM-K64F と NUCLEO-F411RE でも動くようになりました。
26-Nov-2015, FRDM-K22F でも動作確認が出来ました。 ※一身上の都合で、アナログ入力の ch2 は A1 から A2 に変更しましたので、ご注意ください。m(_ _)m

/media/uploads/Rhyme/oscillo_top.jpg

This supports two analog inputs A0 for channel 1 and A2 for channel 2
and four trigger modes (NONE, RISE, FALL, LEVEL),
time division is 30us to 20ms per pixel which will make 300us/div to 200.0ms/div (as 10 pixels per div)

A0と A2 のアナログ2入力にて、
4種類のトリガーモード(NONE: フリーラン RISE: 立ち上がりエッジ、FALL: 立下りエッジ、LEVEL: レベル) 。
サンプリングは30us から 20ms, チャートの1メモリが10ピクセルなので、300us/dev から 200.0ms/div となっています。

The analog signals and gnd was picked at the pins in the picture above.
Note: now you need to pick A0 and A2 instead of A0 and A1.

信号は上記の写真のように、Adafruit のピンからつまみました。
変更により、A0とA2が入力となります。

/media/uploads/Rhyme/freerunmode.jpg

When powered on or reset, the program starts in Trigger Mode = NONE (Free Run Mode)

電源投入、リセット後 プログラムはトリガーモード NONE (フリーランモード) で起動します。

To change trigger mode and/or time division, press run/stop button.

トリガーモードを変えたり、サンプリング時間を変えるのには run/stop ボタンを押してください。

Each time you push Trig button, the trigger mode changes
NONE -> RISE -> FALL -> LEVEL ( -> NONE).

トリガーボタンを押すたびにトリガーモードは
NONE(フリーラン) -> RISE (立ち上がりエッジ) -> FALL (立下りエッジ) -> LEVEL (レベル)
と変化します。

When trigger mode is not NONE,
to specify the trigger position, level and channel, push inside the wave frame.
Then green trigger marks will be shown and value of trigger position and voltage will be printed.

トリガーモードが NONE でないときに、チャネルの枠内をタッチしますと、
その位置がトリガーのポジション、レベル、チャネルになります。 そして、緑色のトリガーマークが表示され、下部にはトリガーの具体的なポジションとボルテージが表示されます。

/media/uploads/Rhyme/usage_screen1.jpg

To change time division push "1/2" to Zoom Out (sampling interval will be bigger) or
"x2" to Zoom In (Sampling interval will be smaller).

サンプリングタイムを変更するのには
"1/2" ズームアウト (サンプリング間隔が長くなります) か
"x2" ズームイン (サンプリング間隔が短くなります。) を押してください。

/media/uploads/Rhyme/helps.jpg

On 7-Mar-2015 updated to UniGraphic library version!

2015年3月7日 UniGraphic ライブラリ使用版にアップデートしました。

/media/uploads/Rhyme/frdm-kl25z-ss.jpg FRDM-KL25Z
/media/uploads/Rhyme/frdm-k64f-ss.jpg FRDM-K64F
/media/uploads/Rhyme/nucleo-f411re-ss.jpg NUCLEO-F411RE

view.cpp

Committer:
Rhyme
Date:
2017-09-25
Revision:
10:bd3d8d71ee73
Parent:
3:ea0c3cffa988

File content as of revision 10:bd3d8d71ee73:

/** mbed oscilloscope my implementation of a oscillo scope
 * Copyright (c) 2014, 2015 Motoo Tanaka @ Design Methodology Lab
 *
 * view.cpp
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "mbed.h"
#include <ILI9341.h>
#include "SPI_STMPE610.h"
#include "Arial12x12.h"
#include "Arial24x23.h"
#include "Arial28x28.h"
#include "Arial43x48_numb.h"
#include "vt100.h"
#include "TFTMenu.h"
#include "main.h"
#include "trig.h"
#include "view.h"

int pane_w = 200 ;
int pane_h = 70 ; // height per channel
int head_h = 20 ; // header height
int foot_h = 20 ; // footer height
int right_margin = 10 ;
int left_margin = 30 ;

void initTFT(void)
{
    //Configure the display driver
    TFT.BusEnable(true) ;
    TFT.FastWindow(true) ;
    TFT.background(Black);
    TFT.foreground(White);
    wait(0.01) ;
    TFT.cls();
    TFT.BusEnable(false) ;
}


void eraseChFrame(int ch)
{
    int x1, x2, y1, y2 ;
    x1 = left_margin ;
    x2 = left_margin + pane_w ;
    y1 = head_h + (pane_h + 5) * ch ;
    y2 = y1 + pane_h ;
    TFT.BusEnable(true) ;
    TFT.fillrect(x1,y1,x2, y2, Black) ;
    TFT.BusEnable(false) ;
}

void eraseGraphFrames(void)
{
    int ch ;
    TFT.BusEnable(true) ;
    for (ch = 0 ; ch < numAnalogIn ; ch++) {
        eraseChFrame(ch) ;
    }
    TFT.BusEnable(false) ;
}

void drawChFrame(int ch)
{
    int x1, x2, y1, y2,y_offset ;
    x1 = left_margin ;
    x2 = left_margin + pane_w ;
    y1 = head_h + (pane_h + 5) * ch ;
    y2 = y1 + pane_h ;
    TFT.BusEnable(true) ;
    TFT.set_font((unsigned char*) Arial12x12);
    TFT.background(Black) ;
    TFT.foreground(White) ;
    TFT.locate(5, y1+5) ;
    TFT.printf("3.0") ;
    TFT.locate(15, (y1+y2)/2) ;
    TFT.printf("V") ;
    TFT.locate(5, y2-10) ;
    TFT.printf("0.0") ;
    TFT.rect(x1,y1,x2,y2,Blue) ;
    y_offset = head_h + 1;
 
    for (int i = 0 ; i < pane_w ; i += 10) {
        x1 = left_margin + i ;
        x2 = x1 ;
        if ((i % 100) == 0) {
            y1 = head_h - 5 ;
        } else {
            y1 = head_h - 2 ;
        }
        y2 = head_h ;
        TFT.line(x1, y1, x2, y2, Blue) ;
        y1 = head_h + (pane_h + 5) * 2 - 5 ;
        if ((i % 100) == 0) {
            y2 = y1 + 5 ;
        } else {
            y2 = y1 + 2 ;
        }
        TFT.line(x1, y1, x2, y2, Blue) ;
    }

    for (float f = 0.0 ; f < vref ; f += 1.0) {
        y_offset = head_h + 1;
        y1 = ((pane_h - 2) * (1.0 - f/vref)) + y_offset ;
        y2 = y1 ;
        x1 = left_margin - 3 ;
        x2 = left_margin ;
        TFT.line(x1, y1, x2, y2, Blue) ;
        y_offset = head_h + (pane_h + 5) + 1;
        y1 = ((pane_h - 2) * (1.0 - f/vref)) + y_offset  ;
        y2 = y1 ;
        TFT.line(x1, y1, x2, y2, Blue) ;
    }
    TFT.BusEnable(false) ;
}

bool inChFrame(int ch, int x, int y)
{
    bool result = false ;
    int x1, x2, y1, y2 ;
    x1 = left_margin ;
    x2 = left_margin + pane_w ;
    y1 = head_h + (pane_h + 5) * ch ;
    y2 = y1 + pane_h ;
    if ((x1 < x)&&(x < x2)&&(y1 < y)&&(y < y2)) { // on 5-Mar-2015 '=' removed
        result = true ;
    }
    return( result ) ;
}

void plotCh_line(int turn, int ch, int index, int color)
{
    int x[2], y[2] ;
    int y_offset, data_pos, prev_pos ;
    
    y_offset = head_h + (pane_h + 5) * ch + 1;
    data_pos = (bor[turn] + index) % memLength ;
    prev_pos = (memLength + bor[turn] + index - 1) % memLength ;
    
    x[1] = left_margin + index + 1; 
    y[1] = ((pane_h - 2) * (1.0 - u2f(udata[turn][ch][data_pos]))) + y_offset ;

    if (index == 0) {
        x[0] = x[1] ;
        y[0] = y[1] ;
    } else {
        x[0] = left_margin + index ;
        y[0] = ((pane_h - 2) * (1.0 - u2f(udata[turn][ch][prev_pos]))) + y_offset ;
    }
    TFT.line(x[0],y[0],x[1],y[1], color) ;
}

void drawGraphFrames(void)
{
    TFT.BusEnable(true) ;
    for (int i = 0 ; i < numAnalogIn ; i++ ) {
        drawChFrame(i) ;
    }
    TFT.BusEnable(false) ;
}

void drawChart(void)
{
    int i, ch ;
    if (!frame_full) {
        return ;
    }
    if (mode == MODE_RUN) {
        timer.detach() ;
    }

    TFT.BusEnable(true) ;
    for (i = 0 ; i < memLength ; i++ ) {
        for (ch = 0 ; ch < numAnalogIn ; ch++ ) {
            plotCh_line(prev_page, ch, i, Black) ; // erase prev
            plotCh_line(page, ch, i, White) ;
        }
    }
    TFT.BusEnable(false) ;
    prev_page = page ;
    page = (page + 1)%2 ;
    sampling_status = ST_PRE_TRIG ;
    frame_full = false ;
    trig_index = 0 ;
    data_index = 0 ;
    if (mode == MODE_RUN) {
        timer.attach_us(&sampleAD, us_interval) ;
    }
}

void printMode(void)
{
    TFT.BusEnable(true) ;
    TFT.locate(80, 200) ;
    TFT.background(Black) ;

    if (mode == MODE_STOP) {
        TFT.foreground(Yellow) ;
        TFT.printf(" Stopped") ;
    } else if (mode == MODE_RUN) {
        TFT.foreground(White) ;
        TFT.printf(" Running") ;
    }
    TFT.BusEnable(false) ;
}

void printTimeDiv(void)
{
    char str[32] ;
    int ival, dotval ;
    if (us_interval < 100) { // 10 * us_interval < 1ms
        sprintf(str, " %3d us/div", 10 *us_interval) ;
    } else if (us_interval < 100000) { // 10 * us_interval >= 1ms
        ival = us_interval / 100 ;
        dotval = us_interval % 100 ;
        sprintf(str, " %3d.%0d ms/div", ival, dotval) ;
    }
    TFT.BusEnable(true) ;
    TFT.fillrect(65, 240, 175, 260, Black) ;
        
    TFT.background(Black) ;
    TFT.foreground(White) ;

    TFT.locate(65, 240) ;
    TFT.printf(str) ;
    TFT.BusEnable(false) ;
}

void printTrigMode(void)
{
    char str[60] ;
    TFT.BusEnable(true) ;
    TFT.fillrect(65, 280, 235, 310, Black) ;
 
    TFT.background(Black) ;
    TFT.foreground(White) ;

    if (trig_mode == TRIG_MODE_NONE) {
        sprintf(str, "%6s",
        trig_name[trig_mode]) ;
    } else {
        sprintf(str, "%6s ch%1d (%d, %.1f)",
        trig_name[trig_mode], trig_ch+1, trig_pos, trig_level) ;
    }
    TFT.locate(65, 280) ;
    TFT.printf(str) ;
    TFT.BusEnable(false) ;
}

void printTrigLevel(void)
{
    char str[16] ;
    sprintf(str, " %.1f", trig_level) ;
    TFT.BusEnable(true) ;
    TFT.locate(140, 280) ;
    TFT.background(Black) ;
    TFT.foreground(White) ;
    TFT.printf(str) ;
    TFT.BusEnable(false) ;
}

void eraseTrigMark(void)
{
    int x1, x2, y1, y2 ;
    TFT.BusEnable(true) ;
    x1 = left_margin - 3 ;
    x2 = left_margin + pane_w + 3 ;
    y1 = 1 ;
    y2 = head_h - 5 ;
    TFT.fillrect(x1,y1,x2, y2, Black) ;
    y1 = (head_h + (pane_h + 5)*2) + 5  ;
    y2 = y1 + 13 ;
    TFT.fillrect(x1,y1,x2, y2, Black) ;
    x1 = left_margin + pane_w + 1 ;
    x2 = x1 + 10 ;
    y1 = 0 ;
    y2 = head_h + (pane_h + 5) * (2) + 1;
    TFT.fillrect(x1, y1, x2, y2, Black) ;
    TFT.BusEnable(false) ;
}

void drawTrigPos()
{
    uint16_t color = Green ;
    int x1, x2, y1, y2 ;
    int y_offset ;
// draw down arrow
    TFT.BusEnable(true) ;
    x1 = left_margin + trig_pos + 1 ;
    x2 = x1 ;
    y1 = 5 ;
    y2 = head_h - 8 ;
    TFT.line(x1, y1, x2, y2, color) ;
    x1 = x2 - 3 ;
    y1 = y2 - 4 ;
    TFT.line(x1, y1, x2, y2, color) ;
    x1 = x2 + 3 ;
    TFT.line(x1, y1, x2, y2, color) ;
// draw up arrow
    x1 = x2 ;
    y1 = (head_h + (pane_h + 5)*2) + 5  ;
    y2 = y1 + 7 ;
    TFT.line(x1, y1, x2, y2, color) ;
    x2 = x1 - 3 ;
    y2 = y1 + 3 ;
    TFT.line(x1, y1, x2, y2, color) ;
    x2 = x1 + 3 ;
    TFT.line(x1, y1, x2, y2, color) ;
// draw level mark
    x1 = left_margin + pane_w + 3 ;
    x2 = x1 + 7 ;
    y_offset = head_h + (pane_h * (trig_ch+1)) +  (5 * trig_ch) - 1;
    y1 = y_offset - ((pane_h - 2) * (trig_level/vref)) ;
    y2 = y1 ;
    TFT.line(x1, y1, x2, y2, color) ;
    x2 = x1 + 3 ;
    y2 = y1 - 3 ;
    TFT.line(x1, y1, x2, y2, color) ;
    y2 = y1 + 3 ;
    TFT.line(x1, y1, x2, y2, color) ;
    TFT.BusEnable(false) ;
// printf("trig_ch = %d, trig level = %.2f, y1 = %d yoffset = %d\n", 
//         trig_ch, trig_level, y1, y_offset) ;
}

void drawTrigMark(void)
{
    eraseTrigMark() ;
    switch(trig_mode) {
    case TRIG_MODE_NONE:
        break ;
    case TRIG_MODE_RISE:
    case TRIG_MODE_FALL:   
    case TRIG_MODE_LEVEL:
        drawTrigPos() ;
        break ;
    default:
        printf("Unkown trigger mode %d\n\r",trig_mode) ;
        break ;
    }
}