Last active
June 27, 2020 06:24
-
-
Save Tamakichi/0c00fdaa66fac47a6bdbee162d2cd45d to your computer and use it in GitHub Desktop.
STM32 ArduinoによるNTSCビデオ出力
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Arduino STM32 NTSCビデオ出力 サンプル V2.0 | |
// Blue Pillボード(STM32F103C8)にて動作確認 | |
// 最終更新日 2017/02/17 たま吉さん | |
// | |
#include <SPI.h> | |
#include <misakiUTF16.h> // 美咲フォントライブラリ | |
#define gpio_write(pin,val) gpio_write_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, val) | |
#define CLK PB1 // 同期信号出力ピン | |
#define DAT PA7 // 映像信号出力ピン | |
#define SC_WIDTH 224 // 横解像度 | |
#define SC_HIGHT 192 // 縦解像度 | |
#define SYNC(V) gpio_write(CLK,V) // 同期信号出力 | |
#define VRAMSIZE (224*192/8) // ビデオ表示フレームバッファサイズ | |
int count=1; // 走査線を数える変数 | |
uint8_t vram[VRAMSIZE]; // ビデオ表示フレームバッファ | |
uint8_t* ptr; // ビデオ表示フレームバッファ参照用ポインタ | |
uint8_t flgHsync = 0; // 水平同期信号出力フラグ | |
uint8_t flgVideo = 0; // 映像信号出力フラグ | |
//垂直同期信号 | |
inline void vsync(){ | |
SYNC(0); | |
delay_us(27); | |
SYNC(1); | |
delay_us(5); | |
} | |
// フォント描画 | |
void drawFont(int x, int y, uint8_t* font) { | |
uint8_t* ptr = &vram[y*28*8+x]; | |
for (int i=0; i<8; i++) { | |
*ptr = *font; | |
ptr+=28; | |
font++; | |
} | |
} | |
// 文字列描画 | |
void drawText(int x, int y, char* str) { | |
uint8_t fnt[8]; | |
while(*str) { | |
if (x>=28) | |
break; | |
if (! (str = getFontData(fnt, str)) ) { | |
Serial.println("Error"); | |
break; | |
} | |
drawFont(x,y ,fnt); | |
x++; | |
} | |
} | |
// 画面クリア | |
void cls() { | |
memset(vram, 0, VRAMSIZE); | |
} | |
// DMA用割り込みハンドラ(データ出力をクリア) | |
void DMA1_CH3_handle() { | |
while(SPI.dev()->regs->SR & SPI_SR_BSY); | |
SPI.dev()->regs->DR = 0; | |
} | |
// DMAを使ったデータ出力 | |
void SPI_dmaSend(uint8_t *transmitBuf, uint16_t length) { | |
dma_setup_transfer( | |
DMA1,DMA_CH3, // SPI1用DMAチャンネル3を指定 | |
&SPI.dev()->regs->DR, // 転送先アドレス :SPIデータレジスタを指定 | |
DMA_SIZE_8BITS, // 転送先データサイズ : 1バイト | |
transmitBuf, // 転送元アドレス : SRAMアドレス | |
DMA_SIZE_8BITS, // 転送先データサイズ : 1バイト | |
DMA_MINC_MODE| // フラグ: サイクリック | |
DMA_FROM_MEM| // メモリから周辺機器、転送完了割り込み呼び出しあり | |
DMA_TRNS_CMPLT // 転送完了割り込み呼び出しあり | |
); | |
dma_set_num_transfers(DMA1, DMA_CH3, length); // 転送サイズ指定 | |
dma_enable(DMA1, DMA_CH3); // DMA有効化 | |
} | |
// タイマー割り込みハンドラ(走査線の処理) | |
void handle_video() { | |
SYNC(0); | |
flgHsync = 0; | |
flgVideo = 0; | |
if(count>=3 && count<=5){ | |
//垂直同期信 | |
vsync(); | |
vsync(); | |
} else if (count >35 && count <227) { | |
flgHsync = 1; //水平同期信号出力有効 | |
flgVideo = 1; //映像信号出力有効 | |
} else { | |
flgHsync = 1; //水平同期信号出力有効 | |
} | |
count++; | |
if(count>262) { | |
count=1; | |
ptr = vram; | |
} | |
} | |
// 水平同期信号出力 | |
void handle_hsync() { | |
if (flgHsync) | |
SYNC(1); | |
} | |
// ビデオ用データ表示(ラスタ出力) | |
void handle_vout() { | |
if (flgVideo) { | |
SPI_dmaSend((uint8_t *)ptr, 28); | |
ptr+=28; | |
} | |
} | |
void setup(){ | |
// ジッター防止のためタイマー割り込み優先度を上げる | |
nvic_irq_set_priority(NVIC_TIMER2, 0); // 割り込み優先レベル設定 | |
pinMode(CLK,OUTPUT); // 同期信号出力ピン | |
// SPIの初期化・設定 | |
SPI.begin(); | |
SPI.setBitOrder(MSBFIRST); | |
SPI.setDataMode(SPI_MODE3); | |
SPI.setClockDivider(SPI_CLOCK_DIV16); | |
SPI.dev()->regs->CR1 |=SPI_CR1_BIDIMODE_1_LINE|SPI_CR1_BIDIOE; // 送信のみ利用の設定 | |
// SPIデータ転送用DMA設定 | |
dma_init(DMA1); | |
dma_attach_interrupt(DMA1, DMA_CH3, &DMA1_CH3_handle); | |
spi_tx_dma_enable(SPI.dev()); | |
ptr = vram; | |
/// タイマ2の初期設定 | |
Timer2.pause(); // タイマー停止 | |
Timer2.setPrescaleFactor(3); // システムクロック 72MHzを24MHzに分周 | |
Timer2.setOverflow(1524); // カウンタ値1524でオーバーフロー発生 63.5us周期 | |
// +0.0us 更新時呼び出し 割込みハンドラの登録 | |
Timer2.attachInterrupt(TIMER_UPDATE_INTERRUPT, handle_video); | |
// +4.7us 水平同期信号出力用 割り込みハンドラ登録 | |
Timer2.setCompare(1, 112); | |
Timer2.setMode(1,TIMER_OUTPUTCOMPARE); | |
Timer2.attachInterrupt(1, handle_hsync); | |
// +9.4us 映像出力用 割り込みハンドラ登録 | |
Timer2.setCompare(2, 225); | |
Timer2.setMode(2,TIMER_OUTPUTCOMPARE); | |
Timer2.attachInterrupt(2, handle_vout); | |
Timer2.refresh(); // タイマーの更新 | |
Timer2.resume(); // タイマースタート | |
// 画面表示 | |
cls(); | |
drawText(3,3,"■■STM32ボードでNTSC出力テスト■■"); | |
drawText(7,6,"ねこにコ・ン・バ・ン・ワ"); | |
drawText(5,8,"解像度は224x192ドットです"); | |
drawText(6,12,"まだまだ色々と調整中です^^"); | |
} | |
void loop(){ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment