FR60 マイコン基板 (10) -- 0 円 48 MHz オシレータ (2)

OSDC のドットクロック用 PLL 出力で USB 用の 48 MHz クロックを生成するプログラムを作り、実際に 48 MHz が得られることを確認しました。
プログラムリストを下に示します。

/*******************************************/
/*   PPG/OSD setup for 48 MHz generation   */
/*******************************************/

#include "_fr.h"

#define PPGE1_PPGE0    (0x03)
#define HSYNC_DIV      (440)
#define HSYNC_WIDTH    (33)
#define HSYNC_EDGE     (HSYNC_DIV-HSYNC_WIDTH)
#define OSD_HTOTAL     (1056)
#define OSD_VTOTAL     (626)
#define PPG_STRG_MASK  (0x4000)

//#pragma section CODE=code2,attr=CODE,locate=0x088000

void main()
{
  unsigned short trig0, trig1;

// Enable PPG0/PPG1 for HSYNC/VSYNC

  IO_PORT.IO_PFR3.byte |= PPGE1_PPGE0;

// PPG0 for VGA HSYNC 
//   20 MHz / 440 = 45.455 kHz

  IO_PCSR0 = HSYNC_DIV ; // fake HSYNC divisor
// HSYNC pulse width = 33 / 20 MHz = 1.65 us
  IO_PDUT0 = HSYNC_EDGE-1; 
  IO_PCN0.hword = 0x8001; // CKS=0 (phi), OSEL=1

// PPG1 for VGA VSYNC 
// (1.25 MHz/55)/(626/2)=45.455 kHz/626=72.611 Hz

  IO_PCSR1 = (55*(OSD_VTOTAL/2))-1;
// VSYNC pulse width = 2 / 45.455 kHz = 44 us
  IO_PDUT1 = (55*(OSD_VTOTAL-2)/2-1);
  IO_PCN1.hword = 0x8801; // CKS=2 (phi/16), OSEL=1

// Start HSYNC/VSYNC

  trig0 = (IO_PCN0.hword | PPG_STRG_MASK);
  trig1 = (IO_PCN1.hword | PPG_STRG_MASK);
  IO_PCN0.hword = trig0; // PPG0 software trigger
  IO_PCN1.hword = trig1; // PPG1 software trigger
  IO_PCSR0 = HSYNC_DIV-1 ; // HSYNC=20MHz/440=45.455 kHz
  IO_PDUT0 = HSYNC_EDGE-1; // pulse width = 33/20 MHz = 1.65 us

// OSDC dot clock PLL setup for 48 MHz generation

  IO_OSDC.IO_OSD_DCLKC1.hword = 0x0001; // DCLKO ON
  IO_OSDC.IO_OSD_DCLKC2.hword = OSD_HTOTAL-1; // dot clock=1056*45.455 kHz = 48 MHz
  IO_OSDC.IO_OSD_DCLKC3.hword = 0x009b; // VCO2 operate

// OSDC activation

  IO_OSDC.IO_OSD_ACT2.hword = 0x0011; // OSD ON, DAC ON
  IO_OSDC.IO_OSD_ACT1.hword = 0x0011; // PLL ON, analog ON

// End of 48 MHz dot clock setup

  while(1){}
}

簡単のためにプログラムを main() 関数の中に置いてますが、この部分を独立した関数としても構いません。
このプログラムが実行される前に、外部バスインターフェースの初期化が済んでいることを仮定しています。 
具体的には「startup.asm」中で実行済みであることを仮定しています。
OSD のサンプルプログラムで使用されている「startup.asm」には外部バスインターフェース初期化のコードが含まれています。
周辺クロックは 20 MHz で、目的の周波数は 48 MHz ですから、N 分周後に M 逓倍した比率 M/N が 2.4 になるような M と N を選べば、20 MHz クロックから 48 MHz を得ることができます。
単に 48 MHz を得るだけでなく、グラフックモードとしても使えるように、20 MHz を PPG0 で 440 分周して 45.455 kHz の水平同期信号としています。
ついでに、PPG1 で 626 分周して 72.611 Hz の垂直同期信号も作成しています。
これは、後から水平/垂直同期信号どうしのタイミングを合わせるのには手間を要するので、あらかじめタイミングを合わせた同期信号を発生させておくことにしました。
PPG0/PPG1 のコントロールレジスタ PCN0/PCN1 を変数 trig0/trig1 に読み出して、ソフトウェアトリガビットを立ててから PCN0/PCN1 に書き戻している部分がそれです。
PCN0/PCN1 は別のレジスタですから、実際には、両者の動作開始タイミングは 50 ns 程度ですが、ズレてしまいます。
そのズレを補正するために、PPG0 側の PWM 周期を本来の値よりズレの分だけ大きく設定しておき、PPG0, PPG1 の順にトリガをかけ、スタートさせます。
スタート直後に、PPG0 の本来の PWM 周期を PCSR0 に設定します。 PWM 周期設定後はデューティー値も PDUT0 に設定する必要があるので、再度設定します。
このようにして、水平/垂直同期のタイミングをぴったりに合わせることができます。
OSDC に関しては、必要最小限の設定だけをしています。 画像表示させるためには、さらに設定が必要です。
OSDC では、VCO2 を使い、PPG0 で発生した 45.455 kHz を PLL で 1056 倍して 48 MHz を生成しています。
コメントアウトしてある #pragma ディレクティブは、HEX ファイルしかない既存のプログラムに、このプログラムを組み込む場合に使います。 その辺は次回以降の記事で説明します。