2010年6月28日月曜日

Si570 "ANY-RATE"プログラマブル発振器(3)

制御プログラム
制御用として、AtmelのATtiny85という8ピンのマイコンを使いました。このマイコンには、USI(Universal Serial Interface)というシリアル通信機能が組み込まれていますが、汎用ゆえ使い方が難解です。そこで、がた老AVR研究所温度ロガーで公開されていたsoft_I2C.cの主要ルーチンをほぼそのまま利用させて頂きました。また、JimCom氏のソースコードも参考にさせて頂いています。この場をお借りしてお礼申し上げます。



メインルーチン Si570.c(一部、#includeに続く括弧を全角に変更しています)
#include <avr/io.h>
#include
avr/interrupt.h
#include stdlib.h
#include avr/sleep.h
#include avr/pgmspace.h
#include inttypes.h

#define  I2C_FUNCTIONS
#include ”soft_I2C.h”

/*
    PB5 I (Reset)
    PB4 I DIP SW 1-1
    PB3 I DIP SW 1-2
    PB2 I/O SCL/(ISP SCK)
    PB1 I/O (ISP MISO)
    PB0 I/O SDA/(ISP MOSI)
*/


uint8_t I2C_comd(uint8_t cmd)
{
    I2C_start_cond();
    if( I2C_status_flag != 1) return(I2C_status_flag);

    I2C_Master_send(Si570_ADR);
    if( I2C_status_flag != 1) return(I2C_status_flag);

    I2C_write(cmd);
    if(I2C_status_flag !=1) return(I2C_status_flag);
    return(1);
}


void I2C_put(uint8_t cmd, uint8_t data)
{
    if( !(I2C_status_flag = I2C_comd(cmd)) ) return;
    I2C_write(data);
    if(I2C_status_flag != 1) return;
    I2C_stop_cond();
}


void init_si570(void)
{
    // Freeze DCO
    I2C_put(137,0x10);

    // Set HSDIV, N1, RFREQ
    switch (((bit_is_clear(PINB,4))<<1) | (bit_is_clear(PINB,3))) {
        case 0:    // 122.880MHz

            I2C_put(7, 0x21);        // HSDIV=1, N1=7
            I2C_put(8, 0xC2);        // RFREQ=0x02B021DE77
            I2C_put(9, 0xB0);
            I2C_put(10, 0x21);
            I2C_put(11, 0xDE);
            I2C_put(12, 0x77);
            break;

        case 1:    // 73.728MHz

            I2C_put(7, 0xE1);        // HSDIV=7, N1=5
            I2C_put(8, 0x42);        // RFREQ=0x02A9404015
            I2C_put(9, 0xA9);
            I2C_put(10, 0x40);
            I2C_put(11, 0x40);
            I2C_put(12, 0x15);
            break;

        case 2:    // 61.440MHz

            I2C_put(7, 0x23);        // HSDIV=1, N1=15
            I2C_put(8, 0xC2);        // RFREQ=0x02B021DE77
            I2C_put(9, 0xB0);
            I2C_put(10, 0x21);
            I2C_put(11, 0xDE);
            I2C_put(12, 0x77);
            break;

        case 3:    // 12.288MHz

            I2C_put(7, 0xE8);        // HSDIV=7, N1=35
            I2C_put(8, 0xC2);        // RFREQ=0x02A9404015
            I2C_put(9, 0xA9);
            I2C_put(10, 0x40);
            I2C_put(11, 0x40);
            I2C_put(12, 0x15);
            break;

      default:    // 122.880MHz
            I2C_put(7, 0x21);        // HSDIV=1, N1=7
            I2C_put(8, 0xC2);        // RFREQ=0x02B021DE77
            I2C_put(9, 0xB0);
            I2C_put(10, 0x21);
            I2C_put(11, 0xDE);
            I2C_put(12, 0x77);
    }

    // UnFreeze DCO
    I2C_put(137, 0x00);

    // Set New Freq
    I2C_put(135, 0x40);
}

int main(void)
{

    //    出力ポート初期化
    //    入力ポート、1:プルアップ
    PORTB = 0b011101;

    //     ポート設定 0:入力, 1:出力
    DDRB = 0b000101;

    // USIインタフェースの初期化
    I2C_Master_init();

    // Si570の設定
    init_si570();

    while(1);    // 無限ループ
}


soft_I2C.c(変更箇所のみ抜粋
//#include <avr/iotn861.h>
//#define  I2C_FUNCTIONS

void I2C_Master_init()
{
//  USIPP = I2C_USIPOS;


soft_I2C.h(変更箇所のみ抜粋)
//#define RTC8564_ADR   0xA2   //8564 RTC
#define Si570_ADR   0xAA    // Si570 I2C Address: 0x55<<1
#define I2C_PORT      PORTB
#define I2C_PIN       PINB
#define I2C_DDR       DDRB
#define I2C_SDA       PB0
#define I2C_SCL       PB2

ところで、HSDIVとN1の分周比と、実際にレジスタに書き込む値とは若干異なっています。
HSDIV
3ビットで表現しますが、少し変則的です。
000 = 4
001 = 5
010 = 6
011 = 7
101 = 9
111 = 11

N1
7ビットで表現します。分周比から1を引いた値をセットします。
0000000 = 1
1111111 = 128

2 件のコメント:

  1. たまご様

    jr1pwzです。

    LAOさんの掲示板ではお世話になりました。

    先ほどMouserからメールが届き、Si570を送ったとのこと。数日中には届くらしいので、来たらいじり倒したいと思います。大変興味深いデバイスなので楽しみです。

    昔、このデバイスがちらちらネットに出始めた頃、グループ購入に参加しようとしたのですが、「相手にされず」に悔しい思いをしたことがありましたっけ。

    これからもこのブログを参考にさせていただきます。

    では

    返信削除
  2. 良かったですね!
    このデバイス、コマンドを送り込むだけで任意のクロックが取り出せるので、今までの苦労は何だったのかという気分になります。
    私は。高速DDSのクロックに使えないかなぁと、妄想を広げております。

    返信削除