2014年10月16日木曜日

Si5338 の試作回路と制御プログラム

回路図は慣れないDesignSpark PCB で書いています。部品ライブラリを自作しましたが、やはり違和感が残りますね。
Si5338 は変換基板、MCU は例のごとく ATmega328P で aitendo のマイコン基板を使っています。本来は、慎重に取り組むべき電源を手持ち部品でいい加減に組んだため発振で苦しみました。次回は、ちゃんとした電源を使わなければと痛感です。

試験回路図


AVRのメモリ使用量はこんな感じです。


プログラム ソースは main.c と register_map.h の2つに分割しました。
main.c は、やりたいことを単純に書き連ねたものです。特にエラー処理は、どう書いて良いかわからなかったのでばっさり省略。参考ベースで見て下さい。
register_map.h は、ClockBuilder Desktop Software が吐き出すヘッダファイルで、構造体がよくわからないので単純な配列として扱えるよう冒頭部分を少しアレンジしたものです。


main.c

#define F_CPU 8.000E6  // マスタクロック8MHz

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/twi.h>
#include <util/delay.h>
#include <register_map.h>

void init_devices(void);
void onERROR(uint8_t i);
void init_si5338(void);
void write_Si5338(uint8_t addr, uint8_t data);
uint8_t read_Si5338(uint8_t addr);
void reg_update_Si5338(uint8_t addr, uint8_t data, uint8_t mask);
void wait_ms(uint16_t t);



void init_devices(void)
{

// ポート設定 0:入力, 1:出力
DDRB = 0b00000000;
DDRC = 0b0000000;
DDRD = 0b10000000;

// 出力ポート初期化
// 入力ポート、1:プルアップ
PORTB = 0b00000000;
PORTC = 0b0000000;
PORTD = 0b00011111;


// I2C SCL周波数: 100kHz
// 100kHz = 8MHz/(16+2*TWBR*4^PRESCALER)
TWBR = 0x20;
}


void write_Si5338(uint8_t addr, uint8_t data)
{

const uint8_t I2C_ADDRESS = 0x70; // 7b1110000

// Send START condition
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// 開始条件が正常に送られたかどうかTWSRを検査
if(!(((TWSR & 0xF8) == TW_START)||((TWSR & 0xF8) == TW_REP_START))){onERROR(1);}

TWDR = (I2C_ADDRESS << 1);

// 送信実行
TWCR = (1<<TWINT) | (1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// ACKが返ってきているか確認
if((TWSR & 0xF8) != TW_MT_SLA_ACK){onERROR(1);}

TWDR = addr;

// 送信実行
TWCR = (1<<TWINT) | (1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// ACKが返ってきているか確認
if((TWSR & 0xF8) != TW_MT_DATA_ACK){onERROR(1);}

TWDR = data;

// 送信実行
TWCR = (1<<TWINT) | (1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// ACKが返ってきているか確認
if((TWSR & 0xF8) != TW_MT_DATA_ACK){onERROR(1);}

// I2C "STOP"
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);

}


uint8_t read_Si5338(uint8_t addr)
{

const uint8_t I2C_ADDRESS = 0x70; // 7b1110000

///// ★読み出しレジスタの指定★ /////
// Send START condition
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// 開始条件が正常に送られたかどうかTWSRを検査
if(!(((TWSR & 0xF8) == TW_START)||((TWSR & 0xF8) == TW_REP_START))){onERROR(1);}

TWDR = (I2C_ADDRESS << 1);

// 送信実行
TWCR = (1<<TWINT) | (1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// ACKが返ってきているか確認
if((TWSR & 0xF8) != TW_MT_SLA_ACK){onERROR(1);}

TWDR = addr;

// 送信実行
TWCR = (1<<TWINT) | (1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// ACKが返ってきているか確認
if((TWSR & 0xF8) != TW_MT_DATA_ACK){onERROR(1);}

// I2C "STOP"
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);


///// ★データの受信★ /////
// Send START condition
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// 開始条件が正常に送られたかどうかTWSRを検査
if(!(((TWSR & 0xF8) == TW_START)||((TWSR & 0xF8) == TW_REP_START))){onERROR(1);}

TWDR = (I2C_ADDRESS << 1) | 0x01; 

// 送信実行
TWCR = (1<<TWINT) | (1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// ACKが返ってきているか確認
if((TWSR & 0xF8) != TW_MR_SLA_ACK){onERROR(1);}

// スレーブのデータを受信するまで待機
while(!((TWCR & 0x80) & (1<<TWINT)));

// NACK送信実行
TWCR = (1<<TWINT) | (1<<TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & (1<<TWINT)));

// I2C "STOP"
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);

return TWDR;
}


void reg_update_Si5338(uint8_t addr, uint8_t data, uint8_t mask)
{

uint8_t curr_val, clear_curr_val, clear_new_val, combined;

if (mask != 0x00) {

if (mask == 0xFF) {

write_Si5338(addr, data);

} else {

curr_val = read_Si5338(addr);
clear_curr_val = (curr_val & (~ mask));
clear_new_val = data & mask;
combined = clear_curr_val | clear_new_val;
write_Si5338(addr, combined);

}

}

}


void onERROR(uint8_t i) {

// ランプ点灯するだけ
PORTD |= _BV(7);

}


void init_si5338(void)
{
uint16_t num_regs;
uint16_t n;
uint8_t curr_val;

// Step1 Disable Outputs
// Set OEB_ALL = 1; reg230[4]
reg_update_Si5338(230, 0x10, 0x10);

// Step2 Pause LOL
// Set DIS_LOL = 1; reg241[7]
reg_update_Si5338(241, 0x80, 0x80);

// Step3 Write "Register Map"
// Register Map 行数
num_regs = 349;

for (n = 0; n < num_regs; n++) {
// Register Address: Reg_Data[n][0]
// Data: Reg_Data[n][1]
// Mask: Reg_Data[n][2]
reg_update_Si5338(Reg_Data[n][0], Reg_Data[n][1], Reg_Data[n][2]);
}

// Step4 Validate input clock status
// Loss of Signal on Input Clock from IN1,2 or IN3
// LOS_CLKIN status; reg218[2]
do{
curr_val =read_Si5338(218);
} while(bit_is_set(curr_val, 2));

// Step5 Configure PLL for locking
//Set FCAL_OVRD_EN = 0; reg49[7]
reg_update_Si5338(49, 0x00, 0x80);

// Step6 Initiate Locking of PLL
//Set SOFT_RESET = 1; reg246[1]
reg_update_Si5338(246, 0x02, 0x02);

// Step7 Wait 25 ms
wait_ms(25);

// Step8 Restart LOL
//Set DIS_LOL = 0; reg241[7]
//Set reg 241 = 0x65
reg_update_Si5338(241, 0x65, 0xFF);

// Step9 Confirm PLL lock status
// PLL Loss of Lock Asserts when the two PFD inputs have a frequency difference > 1000ppm
// PLL_LOL status; reg218[4]
do{
curr_val =read_Si5338(218);
} while(bit_is_set(curr_val, 4));

// Step10 Copy FCAL values to active registers
// Copy registers as follows:
// 237[1:0] to 47[1:0]
// 236[7:0] to 46[7:0]
// 235[7:0] to 45[7:0]
// Set 47[7:2] = 000101b
curr_val = read_Si5338(237);
reg_update_Si5338(47, 0x14 | (curr_val & 0x03), 0x03);
curr_val = read_Si5338(236);
reg_update_Si5338(46, curr_val, 0xFF);
curr_val = read_Si5338(235);
reg_update_Si5338(45, curr_val, 0xFF);

// Step11 Set PLL to use FCAL values
//Set FCAL_OVRD_EN = 1; reg49[7]
reg_update_Si5338(49, 0x80, 0x80);

// Step12 If using down-spread:
// Set MS_RESET=1: reg 226[2]=1
// Wait 1 ms
// Set MS_RESET=0: reg 226[2]=0
// -> NOT USE

// Step13 Enable Outputs
// Set OEB_ALL = 0; reg230[4]
reg_update_Si5338(230, 0x00, 0x10);

}


void wait_ms(uint16_t t) { //1ms

while (t--) _delay_ms(1); 
}


int main(void)
 {

init_devices();
init_si5338();

SMCR &= ~(0x0E); // Idle
SMCR &= 0x01; // Sleep Enable

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



register_map.h

uint8_t Reg_Data[349][3] = {
{  0,0x00,0x00},
{  1,0x00,0x00},
{  2,0x00,0x00},
{  3,0x00,0x00},

~ 後は同じ・・・


2 件のコメント:

  1. はじめまして。
    同じくFMトランスミッターを作ろうとしているものです。
    ESRが小さくても発振することがあるんですね。衝撃です。
    http://rs-components.jp/techinfo/onepoint/0801_mlcc.html
    気をつけよう…

    返信削除
    返信
    1. そうなんです。LDOと高性能なMLCCのよくある組み合わせで起きることがあります。デジタル回路なら発振しても気がつかない場合も。知らぬが仏というやつかもしれません。

      削除