用零知标准板和LCD显示屏制作一个简易示波器
零知标准板(点击购买)
2.4寸直插式LCD显示屏(点击购买)
将LCD显示屏的针脚对好零知标准板的引脚直插进去即可使用
/**********************************************************
* 文件: 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(点击下载)