Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
STM32 ArduinoによるNTSCビデオ出力
//
// 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