请选择 进入手机版 | 继续访问电脑版
凡是官网购买产品,均可以获得积分,(积分可以用来下载技术资料)还可以获得精致技术支持。零知实验室可提供全套电子方案定制服务,了解详情请咨询客服。           
查看: 1399|回复: 3

零知开源小项目-简易示波器

[复制链接]
发表于 2019-9-30 11:44:38 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
用零知标准板和LCD显示屏制作一个简易示波器

一、工具原料所需材料如下:

O-Scope.jpg


二、硬件接线
将LCD显示屏的针脚对好零知标准板的引脚直插进去即可使用


三、工程代码
主要代码如下所示:
[C] 纯文本查看 复制代码
/**********************************************************
*    文件: O-Scope.ino      by 零知实验室([url]www.lingzhilab.com[/url])
*    -^^- 零知开源,让电子制作变得更简单! -^^-
*    时间: 2019/09/27 16:21
*    说明: 
************************************************************/

#include <Adafruit_TFTLCD_8bit_STM32.h>
#include <Adafruit_GFX.h>
#include <SPI.h>
#include <RTClock.h>
#include <libmaple/pwr.h>
#include <libmaple/scb.h>
#include <time.h>
#include "SerialCommand.h"

Adafruit_TFTLCD_8bit_STM32 TFT;
RTClock rt (RTCSEL_LSE); //初始化
uint32 tt;

#define PORTRAIT 0
#define LANDSCAPE 1
#define BKP_REG_BASE   (uint32_t *)(0x40006C00 +0x04)  //定义RTC寄存器的基地址
#define TZ    "UTC+1"
#define TEST_WAVE_PIN PB1  //测试信号 PB1 PWM 500 Hz 
#define BOARD_LED PC13  //板载灯
#define GRATICULE_COLOUR 0x07FF
#define ANALOG_MAX_VALUE 4096
#define TRIGGER_POSITION_STEP ANALOG_MAX_VALUE/32

const int8_t analogInPin = A5;   //模拟输入引脚,
float samplingTime = 0;
float displayTime = 0;

// 定义信号显示位置的变量
uint16_t signalX ;
uint16_t signalY ;
uint16_t signalY1;
int16_t xZoomFactor = 1;
int16_t yZoomFactor = 100; //缩放(%)
int16_t yPosition = 0 ;

boolean triggerHeld = 0 ;  //开启或关闭臊扫描

unsigned long sweepDelayFactor = 1;
unsigned long timeBase = 200;  //时基(单位:us)

int16_t myWidth ;
int16_t myHeight ;

boolean notTriggered ;

int32_t triggerValue = 2048;  //默认触发位置

int16_t retriggerDelay = 0;
int8_t triggerType = 2;

uint16_t triggerPoints[2];  //触发点数组

boolean serialOutput = false;//样本串行输出-默认关闭状态

SerialCommand sCmd;  //串口命令实例

#define serial_debug Serial2  

# define maxSamples 1024*6 //1024*6
uint32_t startSample = 10; //10
uint32_t endSample = maxSamples ;

uint32_t dataPoints32[maxSamples / 2];
uint16_t *dataPoints = (uint16_t *)&dataPoints32;

uint16_t dataPlot[320]; //max(width,height) for this display


volatile static bool dma1_ch1_Active;
#define ADC_CR1_FASTINT 0x70000 //快速交替模式

void setup()
{
#if defined BOARD_LED
  pinMode(BOARD_LED, OUTPUT);
  digitalWrite(BOARD_LED, HIGH);
  delay(1000);
  digitalWrite(BOARD_LED, LOW);
  delay(1000);
#endif

  serial_debug.begin(9600);
  adc_calibrate(ADC1);
  adc_calibrate(ADC2);
  setADCs (); //

//设置串行命令
  sCmd.addCommand("timestamp",   setCurrentTime);          //根据unix时间戳设置当前时间
  sCmd.addCommand("date",        serialCurrentTime);       // 显示RTC的当前时间
  sCmd.addCommand("sleep",       sleepMode);               // 使系统进入睡眠状态
  sCmd.addCommand("s",   toggleSerial);                    // 打开/关闭串行样本输出
  sCmd.addCommand("h",   toggleHold);                      // 开启/关闭触发
  sCmd.addCommand("t",   decreaseTimebase);                // 将时基降低10倍
  sCmd.addCommand("T",   increaseTimebase);                // 将时基提高10倍
  sCmd.addCommand("z",   decreaseZoomFactor);              // 缩小
  sCmd.addCommand("Z",   increaseZoomFactor);              // 放大
  sCmd.addCommand("r",   scrollRight);                     // 从右开始屏幕跟踪
  sCmd.addCommand("l",   scrollLeft);                      // 从左开始屏幕跟踪
  sCmd.addCommand("e",   incEdgeType);                     // 递增触发沿类型0 1 2 0 1 2等
  sCmd.addCommand("y",   decreaseYposition);               // 向下移动跟踪
  sCmd.addCommand("Y",   increaseYposition);               // 向上移动跟踪
  sCmd.addCommand("g",   decreaseTriggerPosition);         // 向下移动触发位置
  sCmd.addCommand("G",   increaseTriggerPosition);         // 向上移动触发位置
  sCmd.addCommand("P",   toggleTestPulseOn);               // 将测试引脚从高阻抗输入切换为方波输出
  sCmd.addCommand("p",   toggleTestPulseOff);              // 将测试引脚从方波测试切换到高阻抗输入

  sCmd.setDefaultHandler(unrecognized);                    // 其他未知命令处理
  sCmd.clearBuffer();

  timer_set_period(Timer3, 1000);
  toggleTestPulseOn();

  pinMode(analogInPin, INPUT);

  TFT.begin(0x9341);
  clearTFT();
  TFT.setRotation(PORTRAIT);
  myHeight   = TFT.width() ;
  myWidth  = TFT.height();
  TFT.setTextColor(GREEN, BLACK) ;

  TFT.setRotation(LANDSCAPE);
  clearTFT();
  showCredits();
  delay(1000) ;
  clearTFT();
  notTriggered = true;
  showGraticule();
  showLabels();
}

void loop()
{

  sCmd.readSerial();     // 读取并处理串口发送的命令
  if ( !triggerHeld  )
  {
    trigger(); //等待触发开启
    if ( !notTriggered )
    {
      blinkLED();

      takeSamples(); //取样
      
      TFTSamplesClear(BLACK); //清空前一个波形

      showGraticule(); //显示方框、坐标轴

      TFTSamples(GREEN);  //显示样本波形
      displayTime = (micros() - displayTime);
      
      showLabels();
      displayTime = micros();

    }
	else {
          showGraticule();
    }
    showTime();  //显示RTC时间
  }
  delay(retriggerDelay);
}

void showGraticule()
{
  TFT.drawRect(0, 0, myHeight, myWidth, GRATICULE_COLOUR);
//坐标轴上十个不同的分区(9个点).
  for (uint16_t TicksX = 1; TicksX < 10; TicksX++)
  {
    for (uint16_t TicksY = 1; TicksY < 10; TicksY++)
    {
      TFT.drawPixel(  TicksX * (myHeight / 10), TicksY * (myWidth / 10), GRATICULE_COLOUR);
    }
  }
  //水平和垂直中心线每格正方形5个刻度线
  for (uint16_t TicksX = 0; TicksX < myWidth; TicksX += (myHeight / 50))
  {
    if (TicksX % (myWidth / 10) > 0 )
    {
      TFT.drawFastHLine(  (myHeight / 2) - 1 , TicksX, 3, GRATICULE_COLOUR);
    }
    else
    {
      TFT.drawFastHLine(  (myHeight / 2) - 3 , TicksX, 7, GRATICULE_COLOUR);
    }

  }
  for (uint16_t TicksY = 0; TicksY < myHeight; TicksY += (myHeight / 50) )
  {
    if (TicksY % (myHeight / 10) > 0 )
    {
      TFT.drawFastVLine( TicksY,  (myWidth / 2) - 1 , 3, GRATICULE_COLOUR);
    }
    else
    {
      TFT.drawFastVLine( TicksY,  (myWidth / 2) - 3 , 7, GRATICULE_COLOUR);
    }
  }
}

void setADCs ()
{
  //  const adc_dev *dev = PIN_MAP[analogInPin].adc_device;
  int pinMapADCin = PIN_MAP[analogInPin].adc_channel;
  adc_set_sample_rate(ADC1, ADC_SMPR_1_5); 
  adc_set_sample_rate(ADC2, ADC_SMPR_1_5);

  //  adc_reg_map *regs = dev->regs;
  adc_set_reg_seqlen(ADC1, 1);
  ADC1->regs->SQR3 = pinMapADCin;
  ADC1->regs->CR2 |= ADC_CR2_CONT;
  ADC1->regs->CR1 |= ADC_CR1_FASTINT;
  ADC1->regs->CR2 |= ADC_CR2_SWSTART;

  ADC2->regs->CR2 |= ADC_CR2_CONT;
  ADC2->regs->SQR3 = pinMapADCin;
}


void trigger()
{
  notTriggered = true;
  switch (triggerType) {
    case 1:
      triggerNegative() ;
      break;
    case 2:
      triggerPositive() ;
      break;
    default:
      triggerBoth() ;
      break;
  }
}

void triggerBoth()
{
  triggerPoints[0] = analogRead(analogInPin);
  while(notTriggered){
    triggerPoints[1] = analogRead(analogInPin);
    if ( ((triggerPoints[1] < triggerValue) && (triggerPoints[0] > triggerValue)) ||
         ((triggerPoints[1] > triggerValue) && (triggerPoints[0] < triggerValue)) ){
      notTriggered = false;
    }
    triggerPoints[0] = triggerPoints[1]; //analogRead(analogInPin);
  }
}

void triggerPositive() {
  triggerPoints[0] = analogRead(analogInPin);
  while(notTriggered){
    triggerPoints[1] = analogRead(analogInPin);
    if ((triggerPoints[1] > triggerValue) && (triggerPoints[0] < triggerValue) ){
      notTriggered = false;
    }
    triggerPoints[0] = triggerPoints[1]; //analogRead(analogInPin);
  }
}

void triggerNegative() {
  triggerPoints[0] = analogRead(analogInPin);
  while(notTriggered){
    triggerPoints[1] = analogRead(analogInPin);
    if ((triggerPoints[1] < triggerValue) && (triggerPoints[0] > triggerValue) ){
      notTriggered = false;
    }
    triggerPoints[0] = triggerPoints[1]; //analogRead(analogInPin);
  }
}

void incEdgeType() {
  triggerType += 1;
  if (triggerType > 2)
  {
    triggerType = 0;
  }

//  serial_debug.println(triggerPoints[0]);
//  serial_debug.println(triggerPoints[1]);
//  serial_debug.println(triggerType);
}

void clearTFT()
{
  TFT.fillScreen(BLACK);
}

void blinkLED()
{
#if defined BOARD_LED
  digitalWrite(BOARD_LED, LOW);
  delay(10);
  digitalWrite(BOARD_LED, HIGH);
#endif

}

void takeSamples ()
{

  dma_init(DMA1);
  dma_attach_interrupt(DMA1, DMA_CH1, DMA1_CH1_Event);

  adc_dma_enable(ADC1);
  dma_setup_transfer(DMA1, DMA_CH1, &ADC1->regs->DR, DMA_SIZE_32BITS,
                     dataPoints32, DMA_SIZE_32BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT));
  dma_set_num_transfers(DMA1, DMA_CH1, maxSamples / 2);
  dma1_ch1_Active = 1;
  //  regs->CR2 |= ADC_CR2_SWSTART;
  dma_enable(DMA1, DMA_CH1); // 启用通道并开始传输
  //adc_calibrate(ADC1);
  //adc_calibrate(ADC2);
  samplingTime = micros();
  while (dma1_ch1_Active);
  samplingTime = (micros() - samplingTime);

  dma_disable(DMA1, DMA_CH1); //传输结束,禁用DMA和连续模式
  // regs->CR2 &= ~ADC_CR2_CONT;

}

void TFTSamplesClear (uint16_t beamColour)
{
  for (signalX=1 ; signalX < myWidth - 2; signalX++)
  {
    TFT.drawLine (  dataPlot[signalX-1], signalX, dataPlot[signalX] , signalX + 1, beamColour) ;
  }
}


void TFTSamples (uint16_t beamColour)
{
  //计算第一个样本
  signalY =  ((myHeight * dataPoints[0 * ((endSample - startSample) / (myWidth * timeBase / 100)) + 1]) / ANALOG_MAX_VALUE) * (yZoomFactor / 100) + yPosition;
  dataPlot[0]=signalY * 99 / 100 + 1;
  
  for (signalX=1 ; signalX < myWidth - 2; signalX++)
  {
    //缩放以适合屏幕
    signalY1 = ((myHeight * dataPoints[(signalX + 1) * ((endSample - startSample) / (myWidth * timeBase / 100)) + 1]) / ANALOG_MAX_VALUE) * (yZoomFactor / 100) + yPosition ;
    dataPlot[signalX] = signalY1 * 99 / 100 + 1;
    TFT.drawLine (  dataPlot[signalX-1], signalX, dataPlot[signalX] , signalX + 1, beamColour) ;
    signalY = signalY1;
  }
}


void showLabels()
{
  TFT.setRotation(LANDSCAPE);
  TFT.setTextSize(1);
  TFT.setCursor(10, 190);
  // TFT.print("Y=");
  //TFT.print((samplingTime * xZoomFactor) / maxSamples);
  TFT.print(float (float(samplingTime) / float(maxSamples)));

  TFT.setTextSize(1);
  TFT.print(" uS/Sample  ");
  TFT.print(maxSamples);
  TFT.print(" samples ");
//  TFT.setCursor(10, 190);
//  TFT.print(displayTime);
  TFT.print(float (1000000 / float(displayTime)));
  TFT.print(" fps    ");
  TFT.setTextSize(2);
  TFT.setCursor(10, 210);
  TFT.print("0.3");
  TFT.setTextSize(1);
  TFT.print(" V/Div ");
  TFT.setTextSize(1);

  TFT.print("timeBase=");
  TFT.print(timeBase);
  TFT.print(" yzoom=");
  TFT.print(yZoomFactor);
  TFT.print(" ypos=");
  TFT.print(yPosition);
  //showTime();
  TFT.setRotation(PORTRAIT);
}

void showTime ()
{
  // Show RTC Time.
  TFT.setTextSize(1);
  TFT.setRotation(LANDSCAPE);
  if (rt.getTime() != tt)
  {
    tt = rt.getTime();
    TFT.setCursor(5, 10);
    if (rt.hour(tt) < 10) {
      TFT.print("0");
    }
    TFT.print(rt.hour(tt));
    TFT.print(":");
    if (rt.minute(tt) < 10) {
      TFT.print("0");
    }
    TFT.print(rt.minute(tt));
    TFT.print(":");
    if (rt.second(tt) < 10) {
      TFT.print("0");
    }
    TFT.print(rt.second(tt));
    TFT.print(" ");
    TFT.print(rt.day(tt));
    TFT.print("-");
    TFT.print(rt.month(tt));
    TFT.print("-");
    TFT.print(rt.year(tt));
    TFT.print(" "TZ" ");
    // TFT.print(tt);

  }
  TFT.setRotation(PORTRAIT);
}


完整工程代码及库文件: O-Scope示波器.zip (9.05 KB, 下载次数: 9)
回复

使用道具 举报

 楼主| 发表于 2019-9-30 12:15:19 | 显示全部楼层
回复

使用道具 举报

发表于 2019-10-28 11:50:06 | 显示全部楼层
学习学习,先收藏了再说
回复

使用道具 举报

发表于 2019-11-2 08:12:42 | 显示全部楼层
没币只能看看了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版| 小黑屋| 零知实验室 ( 粤ICP备17040594号-3 )

GMT+8, 2020-4-9 05:41 , Processed in 0.062174 second(s), 18 queries .

快速回复 返回顶部 返回列表