아두이노 나노 DIY 오실로스코프 (10Hz~50kHz, 128x64 LCD)
부품 및 준비물
| 부품 | 수량 |
| 아두이노 나노 R3 | 1 |
| 커패시터 100µF | 1 |
| ST7920 LCD 128x64 | 1 |
| 로터리 포텐셔미터 | 2 |
| 모멘터리 푸시버튼 | 3 |
필요 도구
사용 소프트웨어
프로젝트 개요
외부 DAC 칩 없이 아두이노 나노만으로 10Hz~50kHz 대역의 간이 오실로스코프를 제작합니다. 128x64 해상도의 ST7920 LCD에 파형을 표시하며, 측정 영역 96x64, 정보 표시 영역 32x64로 구성됩니다.
주요 기능
- 오토 트리거 (안정적인 파형 고정)
- 주사 속도: 0.02ms/div ~ 10ms/div (1-2-5 스텝, 9단계)
- 홀드 기능: 파형 및 파라미터 정지
- 수직 위치 조절 (50kΩ 포텐셔미터)
- 콘트라스트 조절 (10kΩ 포텐셔미터)
구성이 매우 단순하고 저렴하여 교육용이나 저주파 신호 테스트용으로 적합합니다. 원본 코드는 Wu Hanqing의 블로그에 공개되어 있으며, 하드웨어에 맞게 최소한만 수정했습니다.
소스 코드
U8glib 라이브러리 필요 (U8GLIB_ST7920_128X64_4X 사용)
#include <U8glib.h>
U8GLIB_ST7920_128X64_4X u8g(6, 5, 4); // SCK, MOSI, CS
int Input = A0;
int Key_add = 8;
int Key_sub = 9;
int Key_hold = 10;
int x, y, i, i1, i2, V_min, V_max, V_mid, t, t0, t1, sta, Key = 1, hold = 0;
long Freq;
float Vpp;
int Y[96];
int Buffer[192];
// 폰트 비트맵 데이터 (생략 - 원본 참조)
const uint8_t L[] PROGMEM = { ... };
void setup() {
pinMode(Key_add, INPUT); digitalWrite(Key_add, HIGH);
pinMode(Key_sub, INPUT); digitalWrite(Key_sub, HIGH);
pinMode(Key_hold, INPUT); digitalWrite(Key_hold, HIGH);
ADMUX = 0x60; // ADC 설정
ADCSRA = 0xe2; // 프리스케일러 등
u8g.setFont(u8g_font_5x7);
u8g.firstPage();
do { u8g.drawBitmapP(0, 0, 16, 64, L); } while (u8g.nextPage());
delay(2000);
}
void loop() {
sample();
Measure();
Transform();
Key_scan();
if (hold == 0) {
u8g.firstPage();
do { draw(); } while (u8g.nextPage());
}
}
void sample() {
for (i = 0; i < 192; i++) {
Buffer[i] = ADCH;
switch (Key) {
case 1: break;
case 2: delayMicroseconds(4); break;
case 3: delayMicroseconds(10); break;
case 4: delayMicroseconds(23); break;
case 5: delayMicroseconds(60); break;
case 6: delayMicroseconds(123); break;
case 7: delayMicroseconds(248); break;
case 8: delayMicroseconds(623); break;
case 9: delayMicroseconds(1247); break;
}
}
}
void Measure() {
V_max = Buffer[0]; V_min = Buffer[0];
for (i = 0; i < 192; i++) {
if (Buffer[i] > V_max) V_max = Buffer[i];
if (Buffer[i] < V_min) V_min = Buffer[i];
}
V_mid = (V_max + V_min) / 2;
Vpp = (V_max - V_min) * 5.0 / 255.0;
// 주파수 측정 (제로 크로싱)
for (i = 0; i < 97; i++) {
if (Buffer[i] < V_mid && Buffer[i+1] >= V_mid) { i1 = i; break; }
}
for (i = i1 + 1; i < 98 + i1; i++) {
if (Buffer[i] < V_mid && Buffer[i+1] >= V_mid) { i2 = i; break; }
}
t = i2 - i1;
Freq = (t > 0) ? 8000 / t : 0;
}
void Transform() {
for (sta = 0; sta < 96; sta++) {
if (Buffer[sta] < 128 && Buffer[sta+2] > 128) break;
}
for (i = 0; i < 96; i++) Y[i] = 63 - (Buffer[i+sta] >> 2);
}
void draw() {
for (x = 0; x < 95; x++) u8g.drawLine(x, Y[x], x, Y[x+1]);
u8g.drawFrame(0, 0, 97, 64);
u8g.drawLine(48, 0, 48, 63); // 중앙 세로선
u8g.drawLine(0, 32, 96, 32); // 중앙 가로선
for (x = 0; x < 96; x += 8) u8g.drawLine(x, 31, x, 33); // 눈금
for (y = 0; y < 64; y += 8) u8g.drawLine(47, y, 49, y);
for (x = 8; x < 96; x += 8) for (y = 8; y < 64; y += 8) u8g.drawPixel(x, y);
u8g.drawStr(98, 7, "MS/div");
u8g.drawStr(98, 23, "V/div");
u8g.drawStr(98, 30, "0.324");
u8g.drawStr(98, 40, "Vpp");
u8g.setPrintPos(98, 47); u8g.print(Vpp); u8g.drawStr(118, 47, "V");
u8g.drawStr(98, 55, "F(HZ)");
switch (Key) {
case 1: u8g.drawStr(98, 14, "0.02"); u8g.setPrintPos(98, 62); u8g.print(Freq * 50); break;
case 2: u8g.drawStr(98, 14, "0.05"); u8g.setPrintPos(98, 62); u8g.print(Freq * 20); break;
case 3: u8g.drawStr(98, 14, " 0.1"); u8g.setPrintPos(98, 62); u8g.print(Freq * 10); break;
case 4: u8g.drawStr(98, 14, " 0.2"); u8g.setPrintPos(98, 62); u8g.print(Freq * 5); break;
case 5: u8g.drawStr(98, 14, " 0.5"); u8g.setPrintPos(98, 62); u8g.print(Freq * 2); break;
case 6: u8g.drawStr(98, 14, " 1"); u8g.setPrintPos(98, 62); u8g.print(Freq); break;
case 7: u8g.drawStr(98, 14, " 2"); u8g.setPrintPos(98, 62); u8g.print(Freq / 2); break;
case 8: u8g.drawStr(98, 14, " 5"); u8g.setPrintPos(98, 62); u8g.print(Freq / 5); break;
case 9: u8g.drawStr(98, 14, " 10"); u8g.setPrintPos(98, 62); u8g.print(Freq / 10); break;
}
}
void Key_scan() {
if (digitalRead(Key_add) == LOW) { while (digitalRead(Key_add) == LOW); Key++; if (Key == 10) Key = 9; delay(10); }
if (digitalRead(Key_sub) == LOW) { while (digitalRead(Key_sub) == LOW); Key--; if (Key == 0) Key = 1; delay(10); }
if (digitalRead(Key_hold) == LOW) { while (digitalRead(Key_hold) == LOW); hold = ~hold; delay(10); }
}
회로도
참고 사항
- 입력 신호는 A0 핀으로 입력 (보호 회로 권장)
- 전문 계측기 수준은 아니나 교육/취미용으로 충분
- 사인파/구형파 발생기로 테스트 완료