답변:
VGA 모니터로 Arduino Uno 출력에 대한 내 페이지 에는 다음과 같은 색상 막대를 생성하는 스케치를 포함하여 많은 이론이 있습니다.
단일 색상을 생성하는 것이 약간 더 간단합니다.이 스케치는 나를 위해했습니다.
/*
VGA colour video generation
Author: Nick Gammon
Date: 22nd April 2012
Version: 1.0
Connections:
D3 : Horizontal Sync (68 ohms in series) --> Pin 13 on DB15 socket
D4 : Red pixel output (470 ohms in series) --> Pin 1 on DB15 socket
D5 : Green pixel output (470 ohms in series) --> Pin 2 on DB15 socket
D6 : Blue pixel output (470 ohms in series) --> Pin 3 on DB15 socket
D10 : Vertical Sync (68 ohms in series) --> Pin 14 on DB15 socket
Gnd : --> Pins 5, 6, 7, 8, 10 on DB15 socket
*/
#include <TimerHelpers.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
const byte hSyncPin = 3; // <------- HSYNC
const byte redPin = 4; // <------- Red pixel data
const byte greenPin = 5; // <------- Green pixel data
const byte bluePin = 6; // <------- Blue pixel data
const byte vSyncPin = 10; // <------- VSYNC
const int horizontalBytes = 60; // 480 pixels wide
const int verticalPixels = 480; // 480 pixels high
// Timer 1 - Vertical sync
// output OC1B pin 16 (D10) <------- VSYNC
// Period: 16.64 ms (60 Hz)
// 1/60 * 1e6 = 16666.66 µs
// Pulse for 64 µs (2 x HSync width of 32 µs)
// Sync pulse: 2 lines
// Back porch: 33 lines
// Active video: 480 lines
// Front porch: 10 lines
// Total: 525 lines
// Timer 2 - Horizontal sync
// output OC2B pin 5 (D3) <------- HSYNC
// Period: 32 µs (31.25 kHz)
// (1/60) / 525 * 1e6 = 31.74 µs
// Pulse for 4 µs (96 times 39.68 ns)
// Sync pulse: 96 pixels
// Back porch: 48 pixels
// Active video: 640 pixels
// Front porch: 16 pixels
// Total: 800 pixels
// Pixel time = ((1/60) / 525 * 1e9) / 800 = 39.68 ns
// frequency = 1 / (((1/60) / 525 * 1e6) / 800) = 25.2 MHz
// However in practice, it we can only pump out pixels at 375 ns each because it
// takes 6 clock cycles to read one in from RAM and send it out the port.
const byte verticalBackPorchLines = 35; // includes sync pulse?
const int verticalFrontPorchLines = 525 - verticalBackPorchLines;
volatile int vLine;
volatile byte backPorchLinesToGo;
#define nop asm volatile ("nop\n\t")
// ISR: Vsync pulse
ISR (TIMER1_OVF_vect)
{
vLine = 0;
backPorchLinesToGo = verticalBackPorchLines;
} // end of TIMER1_OVF_vect
// ISR: Hsync pulse ... this interrupt merely wakes us up
EMPTY_INTERRUPT (TIMER2_OVF_vect)
void setup()
{
// disable Timer 0
TIMSK0 = 0; // no interrupts on Timer 0
OCR0A = 0; // and turn it off
OCR0B = 0;
// Timer 1 - vertical sync pulses
pinMode (vSyncPin, OUTPUT);
Timer1::setMode (15, Timer1::PRESCALE_1024, Timer1::CLEAR_B_ON_COMPARE);
OCR1A = 259; // 16666 / 64 µs = 260 (less one)
OCR1B = 0; // 64 / 64 µs = 1 (less one)
TIFR1 = bit (TOV1); // clear overflow flag
TIMSK1 = bit (TOIE1); // interrupt on overflow on timer 1
// Timer 2 - horizontal sync pulses
pinMode (hSyncPin, OUTPUT);
Timer2::setMode (7, Timer2::PRESCALE_8, Timer2::CLEAR_B_ON_COMPARE);
OCR2A = 63; // 32 / 0.5 µs = 64 (less one)
OCR2B = 7; // 4 / 0.5 µs = 8 (less one)
TIFR2 = bit (TOV2); // clear overflow flag
TIMSK2 = bit (TOIE2); // interrupt on overflow on timer 2
// prepare to sleep between horizontal sync pulses
set_sleep_mode (SLEEP_MODE_IDLE);
// pins for outputting the colour information
pinMode (redPin, OUTPUT);
pinMode (greenPin, OUTPUT);
pinMode (bluePin, OUTPUT);
} // end of setup
// draw a single scan line
void doOneScanLine ()
{
// after vsync we do the back porch
if (backPorchLinesToGo)
{
backPorchLinesToGo--;
return;
} // end still doing back porch
// if all lines done, do the front porch
if (vLine >= verticalPixels)
return;
PORTD = bit (5) | bit (6); // cyan (green + blue)
delayMicroseconds (27); // one scan line
PORTD = 0; // back to black
// finished this line
vLine++;
} // end of doOneScanLine
void loop()
{
// sleep to ensure we start up in a predictable way
sleep_mode ();
doOneScanLine ();
} // end of loop
@ChrisStratton이 제안했듯이 하드웨어 타이머는 큰 도움이됩니다.
나는 이것을 다음과 같이 연결했다.
TimerHelpers.h의 라이브러리 내에서 설명 타이머 , 사본은 다음과 같습니다 페이지 :
/*
Timer Helpers library.
Devised and written by Nick Gammon.
Date: 21 March 2012
Version: 1.0
Licence: Released for public use.
See: http://www.gammon.com.au/forum/?id=11504
Example:
// set up Timer 1
TCNT1 = 0; // reset counter
OCR1A = 999; // compare A register value (1000 * clock speed)
// Mode 4: CTC, top = OCR1A
Timer1::setMode (4, Timer1::PRESCALE_1, Timer1::CLEAR_A_ON_COMPARE);
TIFR1 |= bit (OCF1A); // clear interrupt flag
TIMSK1 = bit (OCIE1A); // interrupt on Compare A Match
*/
#ifndef _TimerHelpers_h
#define _TimerHelpers_h
#include <Arduino.h>
/* ---------------------------------------------------------------
Timer 0 setup
--------------------------------------------------------------- */
namespace Timer0
{
// TCCR0A, TCCR0B
const byte Modes [8] [2] =
{
{ 0, 0 }, // 0: Normal, top = 0xFF
{ bit (WGM00), 0 }, // 1: PWM, Phase-correct, top = 0xFF
{ bit (WGM01), 0 }, // 2: CTC, top = OCR0A
{ bit (WGM00) | bit (WGM01), 0 }, // 3: Fast PWM, top = 0xFF
{ 0, bit (WGM02) }, // 4: Reserved
{ bit (WGM00), bit (WGM02) }, // 5: PWM, Phase-correct, top = OCR0A
{ bit (WGM01), bit (WGM02) }, // 6: Reserved
{ bit (WGM00) | bit (WGM01), bit (WGM02) }, // 7: Fast PWM, top = OCR0A
}; // end of Timer0::Modes
// Activation
// Note: T0 is pin 6, Arduino port: D4
enum { NO_CLOCK, PRESCALE_1, PRESCALE_8, PRESCALE_64, PRESCALE_256, PRESCALE_1024, T0_FALLING, T0_RISING };
// what ports to toggle on timer fire
enum { NO_PORT = 0,
// pin 12, Arduino port: D6
TOGGLE_A_ON_COMPARE = bit (COM0A0),
CLEAR_A_ON_COMPARE = bit (COM0A1),
SET_A_ON_COMPARE = bit (COM0A0) | bit (COM0A1),
// pin 11, Arduino port: D5
TOGGLE_B_ON_COMPARE = bit (COM0B0),
CLEAR_B_ON_COMPARE = bit (COM0B1),
SET_B_ON_COMPARE = bit (COM0B0) | bit (COM0B1),
};
// choose a timer mode, set which clock speed, and which port to toggle
void setMode (const byte mode, const byte clock, const byte port)
{
if (mode < 0 || mode > 7) // sanity check
return;
// reset existing flags
TCCR0A = 0;
TCCR0B = 0;
TCCR0A |= (Modes [mode] [0]) | port;
TCCR0B |= (Modes [mode] [1]) | clock;
} // end of Timer0::setMode
} // end of namespace Timer0
/* ---------------------------------------------------------------
Timer 1 setup
--------------------------------------------------------------- */
namespace Timer1
{
// TCCR1A, TCCR1B
const byte Modes [16] [2] =
{
{ 0, 0 }, // 0: Normal, top = 0xFFFF
{ bit (WGM10), 0 }, // 1: PWM, Phase-correct, 8 bit, top = 0xFF
{ bit (WGM11), 0 }, // 2: PWM, Phase-correct, 9 bit, top = 0x1FF
{ bit (WGM10) | bit (WGM11), 0 }, // 3: PWM, Phase-correct, 10 bit, top = 0x3FF
{ 0, bit (WGM12) }, // 4: CTC, top = OCR1A
{ bit (WGM10), bit (WGM12) }, // 5: Fast PWM, 8 bit, top = 0xFF
{ bit (WGM11), bit (WGM12) }, // 6: Fast PWM, 9 bit, top = 0x1FF
{ bit (WGM10) | bit (WGM11), bit (WGM12) }, // 7: Fast PWM, 10 bit, top = 0x3FF
{ 0, bit (WGM13) }, // 8: PWM, phase and frequency correct, top = ICR1
{ bit (WGM10), bit (WGM13) }, // 9: PWM, phase and frequency correct, top = OCR1A
{ bit (WGM11), bit (WGM13) }, // 10: PWM, phase correct, top = ICR1A
{ bit (WGM10) | bit (WGM11), bit (WGM13) }, // 11: PWM, phase correct, top = OCR1A
{ 0, bit (WGM12) | bit (WGM13) }, // 12: CTC, top = ICR1
{ bit (WGM10), bit (WGM12) | bit (WGM13) }, // 13: reserved
{ bit (WGM11), bit (WGM12) | bit (WGM13) }, // 14: Fast PWM, TOP = ICR1
{ bit (WGM10) | bit (WGM11), bit (WGM12) | bit (WGM13) }, // 15: Fast PWM, TOP = OCR1A
}; // end of Timer1::Modes
// Activation
// Note: T1 is pin 11, Arduino port: D5
enum { NO_CLOCK, PRESCALE_1, PRESCALE_8, PRESCALE_64, PRESCALE_256, PRESCALE_1024, T1_FALLING, T1_RISING };
// what ports to toggle on timer fire
enum { NO_PORT = 0,
// pin 15, Arduino port: D9
TOGGLE_A_ON_COMPARE = bit (COM1A0),
CLEAR_A_ON_COMPARE = bit (COM1A1),
SET_A_ON_COMPARE = bit (COM1A0) | bit (COM1A1),
// pin 16, Arduino port: D10
TOGGLE_B_ON_COMPARE = bit (COM1B0),
CLEAR_B_ON_COMPARE = bit (COM1B1),
SET_B_ON_COMPARE = bit (COM1B0) | bit (COM1B1),
};
// choose a timer mode, set which clock speed, and which port to toggle
void setMode (const byte mode, const byte clock, const byte port)
{
if (mode < 0 || mode > 15) // sanity check
return;
// reset existing flags
TCCR1A = 0;
TCCR1B = 0;
TCCR1A |= (Modes [mode] [0]) | port;
TCCR1B |= (Modes [mode] [1]) | clock;
} // end of Timer1::setMode
} // end of namespace Timer1
/* ---------------------------------------------------------------
Timer 2 setup
--------------------------------------------------------------- */
namespace Timer2
{
// TCCR2A, TCCR2B
const byte Modes [8] [2] =
{
{ 0, 0 }, // 0: Normal, top = 0xFF
{ bit (WGM20), 0 }, // 1: PWM, Phase-correct, top = 0xFF
{ bit (WGM21), 0 }, // 2: CTC, top = OCR2A
{ bit (WGM20) | bit (WGM21), 0 }, // 3: Fast PWM, top = 0xFF
{ 0, bit (WGM22) }, // 4: Reserved
{ bit (WGM20), bit (WGM22) }, // 5: PWM, Phase-correct, top = OCR2A
{ bit (WGM21), bit (WGM22) }, // 6: Reserved
{ bit (WGM20) | bit (WGM21), bit (WGM22) }, // 7: Fast PWM, top = OCR2A
}; // end of Timer2::Modes
// Activation
enum { NO_CLOCK, PRESCALE_1, PRESCALE_8, PRESCALE_32, PRESCALE_64, PRESCALE_128, PRESCALE_256, PRESCALE_1024 };
// what ports to toggle on timer fire
enum { NO_PORT = 0,
// pin 17, Arduino port: D11
TOGGLE_A_ON_COMPARE = bit (COM2A0),
CLEAR_A_ON_COMPARE = bit (COM2A1),
SET_A_ON_COMPARE = bit (COM2A0) | bit (COM2A1),
// pin 5, Arduino port: D3
TOGGLE_B_ON_COMPARE = bit (COM2B0),
CLEAR_B_ON_COMPARE = bit (COM2B1),
SET_B_ON_COMPARE = bit (COM2B0) | bit (COM2B1),
};
// choose a timer mode, set which clock speed, and which port to toggle
void setMode (const byte mode, const byte clock, const byte port)
{
if (mode < 0 || mode > 7) // sanity check
return;
// reset existing flags
TCCR2A = 0;
TCCR2B = 0;
TimerHelpers.h
TCCR2A |= (Modes [mode] [0]) | port;
TCCR2B |= (Modes [mode] [1]) | clock;
} // end of Timer2::setMode
} // end of namespace Timer2
#endif
"Arduino VGA"를 빠르게 검색하면 많은 정보를 얻을 수 있습니다. 회로와 프로그래밍에는 몇 가지 변형이 있으며 해상도와 색상 깊이도 다릅니다.
나는 며칠 전에 이것을 찾고 있었고, 이것이 내가 가장 좋아하는 것입니다 (지금까지).
http://labdegaragem.com/profiles/blogs/gerando-sinal-vga-colorido-com-arduino-completo (포르투갈어이지만, 할 일에 대한 꽤 좋은 아이디어를 얻을 수 있습니다)
https://forum.arduino.cc/index.php?topic=320238.0 (전체 토론을 읽고 좋은 결과를 얻으십시오)
TV를 사용하는 것도 그럴듯한 옵션 인 경우 Arduino TV 출력 라이브러리를 확인하십시오. Arduino IDE에서 직접 설치할 수 있으며 데모가 좋습니다.
실제 이미지를 표시하지 않아도 Arduino에 메모리가 부족하고 (조잡한 의미를 제외하고) 대역폭이 부족하여 실질적으로 단순화합니다.
그러나 R, G 및 B 라인에 안정적인 아날로그 전압을 적용 할 수는 없습니다. 수평 및 수직 동기 신호를 구동해야 할뿐만 아니라, 화면의 활성 부분에 있지 않을 때 RGB 신호를 비워야합니다. 그렇지 않으면 모니터가 정상 전압을 "검정색"으로 가정하고 색상이 장치가 처음 연결되거나 활성화되면 잠깐 동안 깜박입니다.
Arduino에서 큰 직사각형의 컬러 필드를 생성하는 것은 다소 어려운 일이지만 불가능하지는 않습니다. 수평 및 "컬러 인 에이블"에 하드웨어 PWM 채널을 사용할 수 있으며 수직 측면에 대해 엄격하게 코딩 된 소프트웨어 카운터를 사용할 수 있습니다. 그런 다음 "컬러 인 에이블"을 사용하여 가변 가변 저항 네트워크를 게이트하여 특정 단일 관심 컬러를 설정할 수 있습니다.