¿Cómo Funciona Un DSO?: Pero, Antes Que Nada, ¿Qué Es Un Osciloscopio?
¿Cómo Funciona Un DSO?: Pero, Antes Que Nada, ¿Qué Es Un Osciloscopio?
Acondicionamiento de la señal
Antes de que pueda ser medida por los diversos componentes de un osciloscopio, la
señal de entrada debe estar condicionada de alguna manera. Por lo general, esto
implica escalarlo y atenuarlo mediante un componente llamado frontend analógico. El
elemento de medición real de nuestro instrumento solo puede muestrear voltajes en
el rango de 0 a 3,3 V. Con el fin de ampliar este rango a un dominio útil, atenuamos la
señal de entrada a la mitad y la centramos en torno a 1,65 V. Esto se hace mediante
el uso de un divisor de voltaje y un amplificador operacional para crear un voltaje de
referencia y otro divisor y amplificador operacional para atenuar y amortiguar la
señal. Esto permite que el instrumento mida voltajes que van desde -3,3 V hasta +3,3
V. El esquema de la interfaz se puede encontrar en el archivo [Link].
GitHub - VanAn-nd/DIY-Oscilloscope-base-STM32F103C8T6
Channel: 2-CH
Input impedance: 1M
Mi propósito es hacer un osciloscopio simple para que todos puedan construirlo. Porque es difícil
obtener un amplificador operacional de gran ancho de banda en mi ubicación. Por supuesto,
puede usar uno mayor si lo tiene: AD8001, AD8065, AD8061, OPA2354, ... El resultado es bueno o
no dependiendo de su interfaz analógica...
2
3
Microcontrolador: STM32F030F4
Mini Oscilloscope : 9 Steps (with Pictures) - Instructables
La más pequeña que tengo es la STM32F030F4, que en Ebay suele venderse como
placa de
desarrollo. Dentro de este microcontrolador hay un ADC decente, puede hacer una
conversión en 1us. Los resultados de las conversiones se pueden leer desde un
registro, pero, y eso es muy importante aquí, también se transfieren a través de DMA
a cualquier lugar de la memoria haciendo que el proceso sea muy rápido y sin cargar
la cpu. En este microcontrolador se encuentran los temporizadores habituales,
dispone de SPI e I2C, un RTC, un USART y 2 Watchdogs. Por supuesto, no puede usar
todos los periféricos al mismo tiempo, ya que esta versión del STM32F030 tiene solo
20 pines y algunos de ellos se usan para cosas como energía, reinicio, cristal,
programación y selección de arranque. Para programar los microcontroladores
fabricados por ST Microelectronics se necesita una herramienta llamada STLink-V2,
disponible a través de Mouser, Farnell y otros, un clon barato de la misma está
disponible en Ebay y otros. La placa STM32F030F4 se vende por menos de 3 euros y
la STLink-V2 por un precio similar. Si tiene una placa de desarrollo oficial hecha por ST
Microelectronics, como una "placa Nucleo", hay un STLink-V2 en ella que se puede
quitar de ella. Eso es lo que hice y lo puse en una pequeña caja de plástico.
Especificaciones
La pantalla TFT tiene una resolución de 160 x 128 píxeles, lo que significa que el eje X
(base de tiempo) necesita 160 muestras ADC. El ADC necesita un mínimo de 1 us por
muestra y creo que 10 ondas de una señal de una pantalla tan pequeña está en el
límite de lo útil. Esto significa 16 píxeles por forma de onda. Como el ADC necesita
alrededor de 1us por muestra, eso es 16us, esa es una forma de onda con una
frecuencia de 62500 Hz. Eso no es mucho cuando estás acostumbrado a un
osciloscopio digital real de 100 MHz, pero es suficiente para audio, proyectos de LED y
más experimentos de baja frecuencia.
Al final ni siquiera pude conseguir que el ADC hiciera 1.000.000 de conversiones por
segundo, así que me conformé con 800.000. Por lo tanto, la base de tiempo sube a
40us/división. Con 5 divisiones en la pantalla, significa 200 us por pantalla completa.
Una forma de onda de 5 kHz llena toda la pantalla y creo que el límite de utilidad es
4
de alrededor de 50 kHz. En el extremo inferior, decidí que 200 ms/división (1
segundo/pantalla) era un buen momento, por lo que una señal de 1 Hz utiliza todo el
ancho de la pantalla.
El ADC convierte el voltaje en su entrada referenciado a 3.3V (Vdd). Puedes elegir las
resoluciones de 12, 10, 8 y 6 bits y cuanto más bajo vayas, más rápidas serán las
conversiones. Como la pantalla TFT tiene un eje Y (resolución vertical) de 128 píxeles
(7 bits), no es necesario que el ADC supere los 7 bits, pero no hay una resolución de 7
bits disponible, por lo que se establece en 8 bits. El bit menos significativo se
descartará con un desplazamiento a la derecha de 1 bit.
Se requiere atenuación para poder medir un poco más de 3.3V que el ADC es capaz
de hacer, amplificación si desea medir señales más pequeñas que eso, y sería bueno
poder medir un voltaje negativo también. Esto significa tener una fuente de
alimentación positiva y negativa o tener un desplazamiento agregado al voltaje de
entrada. Debido a que quería usar solo una batería y mantener el dispositivo lo más
simple posible, elegí el método de compensación. Esto significa que el terminal de
tierra de la entrada no está conectado a tierra en absoluto, está conectado al voltaje
de compensación, a esto lo llamo tierra virtual. También significa que tiene que usar
este osciloscopio alimentado por baterías.
El ancho de banda analógico de un osciloscopio debe ser lo más alto posible, como
regla general, al menos 10 veces la frecuencia que desea medir. Después de todo,
una onda cuadrada de solo 10 kHz tiene muchos armónicos y necesita tener al menos
un ancho de banda de 90 kHz para ver el noveno armónico. Dicho de otra manera, si
intentas ver una onda cuadrada de 10MHz en un osciloscopio de 10 MHz, verás muy
poca onda cuadrada, pero sobre todo una onda sinusoidal de 10 MHz. Aquí un ancho
de banda de 500kHz sería suficiente, pero más es mejor. Utilicé un amplificador
operacional con un producto de ganancia/ancho de banda de 10MHz un Microchip
MCP6021, por la sencilla razón de que tengo algunos en stock. Si desea usar otro,
recuerde que debe funcionar a 3.3V.
La última especificación que quiero es que la batería de iones de litio se pueda cargar
a través de una pequeña placa con un conector mini-usb. Al principio quería usar una
batería de iones de litio de tamaño 18650 pero era demasiado grande, ahora se usa
una pequeña batería rectangular, tiene las mismas dimensiones que la batería de un
teléfono (tonto) Samsung y otros utilizados en sus teléfonos, pero es el doble de
gruesa. Simplemente use cualquier batería de iones de litio que tenga y que le quede.
Siempre que el voltaje nominal sea de 3,7 voltios, está bien.
- Así que todos los honores de la biblioteca ST7735 son para los fabricantes, sean
quienes sean -
Los codificadores rotativos muy, muy malos En segundo lugar, hay que leer el
codificador rotatorio. Esto me causó la mayoría de los problemas, no es que no
supiera cómo hacerlo, es solo una simple señal de cuadratura. No, resultó que los 10
codificadores que compré (Ebay) eran de tan mala calidad que no solo necesité hacer
la eliminación de rebotes de software, sino también la eliminación de rebotes en el
hardware. Y aun así, no puedo girar la perilla demasiado rápido ya que perderá
pulsos. Normalmente hago solo derebotes de software, ya que no necesita nada
extra, ni resistencias pullup, ni condensadores, pero en este caso eran muy
necesarios.
6
ADC y DMA Lo más importante es la entrada de datos analógicos. Como dije, el ADC
hace una conversión de 8 bits en aproximadamente 1us. Se inicia mediante un
temporizador (TIM3) que funciona continuamente y envía pulsos al ADC de acuerdo
con la configuración de la base de tiempo actual. La velocidad más baja (200 ms/div --
> 1 segundo/pantalla) es de 160 Hz. Por lo tanto, el ADC realiza 160 conversiones por
segundo, llenando la pantalla de 160 píxeles en 1 segundo. La velocidad más alta es
de 800 kHz, por lo que la pantalla se llena en 160*(1/800.000) = 200us. ¡Ojalá la
pantalla y el software fueran tan rápidos! Entonces podrías tener una frecuencia de
actualización de 5 kHz. (cualquier visor analógico antiguo hace eso sin sudar)
En su lugar, los datos del ADC se transfieren a una matriz: adc_buffer[] en la memoria.
Esto se hace con DMA, lo que significa que la CPU del microcontrolador no es
necesaria para hacer eso, puede continuar con lo que sea que esté haciendo. Esto
hace que el almacenamiento de los datos sea muy sencillo y rápido. Cuando el DMA
está listo con el número programado de valores que necesita transportar, establece el
indicador TC (transmisión completa) y desencadena una interrupción. La interrupción
en sí misma no hace mucho, solo borra la bandera TC y establece una variable
llamada "token" como una señal para la rutina principal que le dice que hay datos
listos para ser mostrados. El ADC continúa con las conversiones y el DMA sigue
transportando esos resultados al adc_buffer[]. Por lo tanto, no importa lo que esté
sucediendo dentro del microcontrolador, hay un flujo interminable de valores que
entran en adc_buffer.
Como se dijo, la pantalla tiene 160 píxeles de ancho, por lo que solo se necesitan 160
valores para mostrar una forma de onda completa. El adc_buffer[] contiene en
realidad 320 muestras. Por lo tanto, el DMA almacena 320 valores en él antes de
desencadenar una interrupción TC. Esto se hace porque la activación se realiza en el
software. Y como es muy poco probable que el primer valor en el adc_buffer[] sea el
lugar donde debería estar el desencadenamiento. Tenemos que encontrar el lugar
donde está ese punto. Por lo tanto, se leen 320 valores y en los primeros 160 de ellos
se busca el punto de activación real.
El código
MAIN.C
debido a que las conversiones nunca se detienen, los valores en adc_buffer[] se
sobrescribirán continuamente, en la configuración de base de tiempo más rápida, esto
sucederá con todos los valores cada 400us. Para evitar mostrar datos incorrectos, lo
primero que hace la rutina principal es hacer una copia de adc_buffer[] en
display_buffer[]. Un poco más tarde también copia los mismos datos en
7
erase_buffer[]. Esto se hace porque borrar toda la pantalla lleva "una eternidad". Lo
que se hace ahora es: la forma de onda anterior (verde) se sobrescribe exactamente
con la misma forma de onda en negro en la siguiente ronda, justo antes de que se
muestre la nueva.
El resto del programa es más o menos cosmético, se muestra una cuadrícula de líneas
horizontales y verticales, los valores actuales de sensibilidad y base de tiempo. La
línea de cero voltios se muestra más brillante que todas las demás y se mueve con el
desplazamiento. Se muestra una pequeña línea donde está el nivel de disparo actual.
Lo que en este momento está activo en el codificador rotativo, la base de tiempo, el
desplazamiento o el nivel de activación, se muestra en amarillo.
MX_SPI1_Init No hay mucho que contar, excepto que funciona a 16MHz, lo cual es
bueno con la pantalla TFT ST7735.
MX_TIM14_Init Este produce una onda cuadrada en PB1 con una frecuencia de un
poco más de 100 kHz y un ancho de pulso variable. Un filtro de paso bajo hecho con
una resistencia de 1k y un condensador de 10uF transforma esto en el voltaje de
compensación para el amplificador operacional.
STM32F0XX_IT.C
9
10
Osciloscopio STM32 con exportación FFT y SD
[Link] - STM32 Oscilloscope with FFT and SD export - The Arduino code
Este no es solo otro osciloscopio STM32, es mío. Como tal, fue diseñado a mi gusto.
Exprimí la tasa de conversión máxima (2,57 Msps) de un solo ADC, utilicé el
controlador DMA incorporado para maximizar la velocidad de transferencia de datos y
lo hice exportar los datos adquiridos y calculados en una tarjeta SD. Fue construido
sobre una placa de propósito general y tiene un circuito de entrada flexible.
Osc
iloscopio STM32 - Señal cuadrada de 500 kHz, ciclo de trabajo del 50%. Izquierda:
forma de onda. Derecha: el espectro de potencia FFT.
Características técnicas:
Un canal de entrada
ADC de 2,57 Msps, que acepta frecuencias de señal de hasta 1,28 MHz
Calcula los valores
mínimos, máximos y medios
Análisis de espectro FFT
Detección de frecuencia fundamental
Exportación con tarjeta SD de la forma y el espectro
de la onda de señal
Función de congelación
Selección de la frecuencia de muestreo
11
Os
ciloscopio STM32 - Señal cuadrada de 1 MHz, 50% de ciclo de trabajo. Izquierda:
forma de onda. Derecha: el espectro de potencia FFT.
donde TS = 1,5 ciclos ADC, fADC = 36 MHz, CADC = 8 pF, RADC <= 1 kOhm y N es el
número efectivo de bits que almacenan datos sin errores.
Experimentos
han indicado que una señal de 250 kHz muestreada a 2,57 Msps, 1,8 Msps, 1,38 Msps
o incluso 529 ksps proporciona mediciones de amplitud similares, lo que lleva a creer
12
que RADC de hecho, es mucho más bajo que (tal vez la mitad de) el máximo
mencionado en las especificaciones y que el muestreo de señales a 2,57 MHz no
planteará problemas significativos.
Osciloscopio STM32 - señal cuadrada de 250 kHz, ciclo de trabajo del 50%. Se observó
un error de 20 mV a diferentes velocidades de muestreo. Izquierda: a 2,57 MSPS.
Derecha: a 529 kSPS.
El código
todavía usa la estructura y las bibliotecas de Arduino, excepto para las rutinas ADC,
DMA y FFT relacionadas con STM32. Se supone que un TFT NT35702 está enganchado
como se describe aquí, los pines de la tarjeta SD van al puerto SPI2 del STM32 y los
botones y entradas están conectados como se indica en la publicación anterior.
13
También se debe instalar la biblioteca SdFat para que el código de la tarjeta SD
funcione y agregar (arrastrando y soltando) los archivos de ensamblador
relacionados con el cr4_fft_1024_stm32 al proyecto IDE de Arduino.
#include <table_fft.h>
#include <cr4_fft_stm32.h>
14
static const float VCC_3_3 = 3.3; // volts
Adafruit_ILI9341_8bit_STM tft;
SdFat sd(2);
SdFile file;
uint8_t bk[SCREEN_HORIZONTAL_RESOLUTION];
uint16_t data16[BUFFER_SIZE];
uint32_t data32[BUFFER_SIZE];
uint32_t y[BUFFER_SIZE];
uint8_t time_base = 7;
uint16_t i, j;
uint8_t state = 0;
uint16_t maxy, avgy, miny;
// ------------------------------------------------------------------------------------
// The following section was inspired by [Link]
void setADCs() {
//
switch (DT_PRE[time_base]) {
//
case 0: rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_2); break;
case 1: rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_4); break;
case 2: rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_6); break;
case 3: rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_8); break;
default: rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_8);
}
switch (DT_SMPR[time_base]) {
15
//
case 0: adc_set_sample_rate(ADC1, ADC_SMPR_1_5); break;
case 1: adc_set_sample_rate(ADC1, ADC_SMPR_7_5); break;
case 2: adc_set_sample_rate(ADC1, ADC_SMPR_13_5); break;
case 3: adc_set_sample_rate(ADC1, ADC_SMPR_28_5); break;
case 4: adc_set_sample_rate(ADC1, ADC_SMPR_41_5); break;
case 5: adc_set_sample_rate(ADC1, ADC_SMPR_55_5); break;
case 6: adc_set_sample_rate(ADC1, ADC_SMPR_71_5); break;
case 7: adc_set_sample_rate(ADC1, ADC_SMPR_239_5); break;
default: adc_set_sample_rate(ADC1, ADC_SMPR_239_5);
}
adc_set_reg_seqlen(ADC1, 1);
ADC1->regs->SQR3 = PIN_MAP[CHANNEL_1].adc_channel;
ADC1->regs->CR2 |= ADC_CR2_CONT; // | ADC_CR2_DMA; // Set continuous mode and DMA
ADC1->regs->CR2 |= ADC_CR2_SWSTART;
}
uint16_t asqrt(uint32_t x) { //good enough precision, 10x faster than regular sqrt
//
int32_t op, res, one;
op = x;
res = 0;
one = 1 << 30;
while (one > op) one >>= 2;
while (one != 0) {
if (op >= res + one) {
op = op - (res + one);
res = res + 2 * one;
}
res /= 2;
one /= 4;
}
return (uint16_t) (res);
}
void export_to_sd() {
//
[Link](170, 20);
[Link](WHITE);
[Link](1);
[Link]("Writing to SD ...");
[Link](170, 20);
if ()) {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("No SD card detected");
return;
}
delay(500);
if (![Link]()) {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("File system init failed.");
return;
}
uint8_t index;
if () {
// no pre-exising folder structure
if () {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("Can't create folder");
return;
}
}
if () {
// no index file
index = 1;
if () {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("Can't create idx file");
return;
}
[Link](index);
if (![Link]() || [Link]()) {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("Idx file write error");
return;
}
[Link]();
} else {
//
if () {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("Can't open idx file");
return;
}
if () {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("Can't read idx file");
return;
17
}
if (![Link]() || [Link]()) {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("File write error");
return;
}
[Link]();
}
String s = "DSO/Exp";
s += index;
s += ".dat";
if (, O_CREAT | O_WRITE | O_EXCL)) {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("Can't data create file");
return;
}
[Link]("Time series");
for (uint16_t i = 0; i < BUFFER_SIZE; i ++) {
//
[Link](data16[i], DEC);[Link](", ");
}
[Link](" ");
[Link]("Fs: ");[Link](DT_FS[time_base]);[Link]("kHz");
[Link]("Spectrum");
for (uint16_t i = 0; i < BUFFER_SIZE/2; i ++) {
//
[Link](y[i], DEC);[Link](", ");
}
[Link](" ");
if (![Link]() || [Link]()) {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("File write error");
return;
}
[Link]();
s += ".img";
index ++;
if () {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("Can't create idx file");
return;
}
[Link](index);
if (![Link]() || [Link]()) {
//
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("Idx file write error");
return;
}
[Link]();
[Link](169, 19, 150, 9, BACKGROUND_COLOR);
[Link]("File write success");
delay(2000);
[Link](170, 19, 150, 9, BACKGROUND_COLOR);
}
void setup() {
//
[Link]();
[Link](3);
bPress[0] = false;
bPress[1] = false;
bPress[2] = false;
adc_calibrate(ADC1);
}
void loop() {
//
if (state == 0) {
//
[Link](BACKGROUND_COLOR);
[Link](15, 100);
[Link](YELLOW);
[Link](3);
[Link]("[Link]");
// analogWrite(TEST_SIGNAL, 127);
delay(1500);
[Link](BACKGROUND_COLOR);
state = 1;
}
if (state == 1) {
// init
state = 2;
}
if (state == 2) {
// buttons check
if (wasPressed(TIME_BUTTON, 0)) {
// toggling the time division modes
time_base ++;
if (trigger == 0) {
// spectrum
if (time_base <= 2) time_base = 3;
}
time_base = time_base % sizeof(DT_DT);
h = DT_DT[time_base];
bScreenChange = true;
19
}
if (wasPressed(TRIGGER_BUTTON, 1)) {
// toggling the trigger mode
trigger ++;
trigger = trigger % 4;
bScreenChange = true;
bTitleChange = true;
}
if (wasPressed(FREEZE_BUTTON, 2)) {
// toggling the freeze screen
freeze = (freeze > 0) ? 0 : 3;
bTitleChange = true;
}
if (freeze) {
// frozen screen
state = 5;
} else {
// live screen
state = 3;
}
}
if (state == 3) {
// acquisition
setADCs();
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_16BITS, data16,
DMA_SIZE_16BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT));
dma_set_num_transfers(DMA1, DMA_CH1, BUFFER_SIZE);
dma1_ch1_Active = 1;
dma_enable(DMA1, DMA_CH1); // enable the DMA channel and start
the transfer
state = 4;
}
if (state == 4) {
// display signal screen
if (bScreenChange) {
// massive change on screen
bScreenChange = false;
[Link](BACKGROUND_COLOR);
bTitleChange = true;
} else {
// clear previous wave
if (trigger == 0) {
// clear previous spectrum
for (i = 1; i < SCREEN_HORIZONTAL_RESOLUTION; i ++) {
//
[Link](
i,
bk[i],
i + 1,
bk[i + 1],
BACKGROUND_COLOR);
}
} else {
// clear previous time samples
20
for (i = 0, j = 0; j < SCREEN_HORIZONTAL_RESOLUTION; i ++, j += h2) {
//
[Link](
j,
bk[i],
j + h2,
bk[i + 1],
BACKGROUND_COLOR);
}
}
}
// re-draw the divisions
for (i = 0; i < SCREEN_HORIZONTAL_RESOLUTION; i += DIVISION_SIZE) {
//
for (j = SCREEN_VERTICAL_RESOLUTION; j > 13; j -= ((i == 160) ?
SUBDIVISION_SIZE : DIVISION_SIZE)) {
//
[Link](i - 1, j, i + 1, j, DIV_LINE_COLOR);
}
}
for (i = SCREEN_VERTICAL_RESOLUTION; i > 13; i -= DIVISION_SIZE) {
//
for (j = 0; j < SCREEN_HORIZONTAL_RESOLUTION; j += ((i == 120) ? SUBDIVISION_SIZE
: DIVISION_SIZE)) {
//
[Link](j, i - 1, j, i + 1, DIV_LINE_COLOR);
}
}
// draw current wave
if (trigger == 0) {
// display spectrum
uint16_t max_y = 0, max_x = 0;
uint16_t i_0, i_1;
bool hit_max = false;
for (i = 1; i < BUFFER_SIZE / 2; i ++) {
//
if (y[i] > max_y) {
//
max_y = y[i];
max_x = i;
}
}
max_y = max(max_y, EFFECTIVE_VERTICAL_RESOLUTION);
[Link](WHITE);
[Link](1);
for (i = 1; i < SCREEN_HORIZONTAL_RESOLUTION; i ++) {
//
i_0 = (int)((float)i * (float)BUFFER_SIZE / (float)SCREEN_HORIZONTAL_RESOLUTION
/ 2.0);
i_1 = (int)((float)(i + 1) * (float)BUFFER_SIZE /
(float)SCREEN_HORIZONTAL_RESOLUTION / 2.0);
if (hit_max) {
// was in the vicinity of max
i_0 = max_x;
hit_max = false;
} else if ((max_x <= i_1) && (i_0 <= max_x)) {
// is in the vicinity of max
if ((i_1 - max_x) <= (max_x - i_0)) {
//
hit_max = true;
i_1 = max_x;
} else {
//
i_0 = max_x;
21
}
}
bk[i] = SCREEN_VERTICAL_RESOLUTION - (10 + ((float)y[i_0] / (float)max_y) *
(float)(EFFECTIVE_VERTICAL_RESOLUTION - 10));
[Link](
i,
bk[i],
i + 1,
SCREEN_VERTICAL_RESOLUTION - (10 + ((float)y[i_1] / (float)max_y) * (float)
(EFFECTIVE_VERTICAL_RESOLUTION - 10)),
CH1_SIGNAL_COLOR);
if (i % DIVISION_SIZE == 0) {
//
float freq = ((float)i / (float)SCREEN_HORIZONTAL_RESOLUTION *
(float)DT_FS[time_base]) / 2.0;
[Link](i - (freq > 100 ? 8 : 5) - (freq > (int)freq ? 4 : 0),
SCREEN_VERTICAL_RESOLUTION - 7);
[Link](freq, 1);
}
}
// clear previous stats
[Link](7, 19, 150, 9, BACKGROUND_COLOR);
[Link](8, 20);
[Link](WHITE);
[Link](1);
String s;
s = "F: ";
s += (float)max_x / (float)BUFFER_SIZE * (float)DT_FS[time_base];
s += "kHz ";
s += (float)20 * log10(max_y);
s += "dB";
[Link](s);
} else {
// display time samples
uint16_t maxy = 0;
uint16_t miny = ADC_RESOLUTION;
uint32_t avgy = 0;
for (i = 1; i < BUFFER_SIZE; i ++) {
//
maxy = max(maxy, data16[i]);
miny = min(miny, data16[i]);
avgy += data16[i];
}
avgy /= BUFFER_SIZE;
for (i = 0, j = 0; j < SCREEN_HORIZONTAL_RESOLUTION; i ++, j += h) {
//
bk[i] = SCREEN_VERTICAL_RESOLUTION - (20 + (data16[i] * ADC_SCREEN_FACTOR));
bk[i + 1] = SCREEN_VERTICAL_RESOLUTION - (20 + (data16[i + 1] *
ADC_SCREEN_FACTOR));
[Link](
j,
bk[i],
j + h,
bk[i + 1],
CH1_SIGNAL_COLOR);
if (h > 1) [Link](j, bk[i] - 1, GREEN);
}
// clear previous stats
[Link](7, 19, 60, 9, BLUE);
[Link](8, 20);
[Link](WHITE);
[Link](1);
String s;
s = "Max: ";
22
s += (float)maxy / (float)ADC_RESOLUTION * VCC_3_3;
s += "V";
[Link](s);
state = 5;
}
if (state == 5) {
//
if (bTitleChange) {
// title change
bTitleChange = false;
[Link](0, 0, SCREEN_HORIZONTAL_RESOLUTION, 12, CH1_SIGNAL_COLOR);
[Link](8, 3);
[Link](BLUE);
[Link](1);
String s = "CH1 ";
s += .65;
s += "V ";
if (trigger == 0) {
// spectrum
s += (int)DT_FS[time_base];
s += "kHz ";
} else {
// time samples
s += DT_DIV[time_base];
s += "us ";
}
if (trigger == 1) {
// raising front trigger
s += "Raising ";
} else if (trigger == 2) {
// descending front trigger
s += "Falling ";
} else if (trigger == 3) {
// no trigger
s += "None ";
} else {
// spectrum scope
s += "Spectrum ";
}
[Link](s);
if (freeze) {
//
23
[Link](170, 3);
[Link](RED);
[Link](1);
[Link]("Freeze");
}
[Link](215, 3);
[Link](BLACK);
[Link](1);
[Link]("[Link]");
}
if (freeze == 3) {
//
freeze = 1;
export_to_sd();
bScreenChange = true;
}
}
delay(50);
state = 1;
}
La conclusión
Si la hay, es que un proyecto como este nunca está completo. Siempre falta algo, algo
parcialmente implementado o un truco que mantiene las cosas en su lugar. No
obstante, esto se convirtió en una herramienta independiente seria que ya me ayuda
a depurar otros proyectos basados en MCU o sondear señales de audio.
¡Quédate para estar al tanto de las novedades!
24
25