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

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

1、工具原料

零知标准板(点击购买)
2.4寸直插式LCD显示屏(点击购买)

2、硬件连接

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

2、程序


											
	/**********************************************************
	*    文件: 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(点击下载)