2013/06/16

カメラがいい感じ

どうもKです。

秋月からカメラが届いたので早速映してみました。
中々いい画質で、操縦も楽になりそうです。
カメラの切り替え用の基板が欲しいところですね。 # 作らないと……

そういえば今日はI2Cに苦戦していましたのでそのメモでも書いておきますね。 # 低脳

* I2C動作しないメモ

SSP1IE = 0;
SSP1IF = 0;
SSP1CON2bits.SEN = 1; // スタートコンディションの発行
while(!SSP1IF); // 発行終了待ち←止まる

SSP1IFが1にならない。割り込みが起こってしまっているのでは? と思い、SSP1IFを割り込み内でクリアするようにしてもだめ。

“In Master mode, the SDAx
and SCKx pins must be configured as INPUT”
まじかよw 設定していなかったので再度設定し直すことに。

やっと次のステップに進めた。

続いてアドレスのマッチングは行われたようだが、送られてくるデータが違う。
IDを変更してIDが一致しないと動作しないことを確認した。

どうやら送られてきていたデータはIDだったようで、
IDが一致していればそのIDがそのままSSPBUFに格納される仕様だったようだ。 # 見落とし

そこでSSP1STATbits.D_nAを確認してみたらみごとに0(アドレス)が返ってきた。
1度空読みしてから再度SSP1IFをクリアすれば次の受信を待つそうだから、やってみることに。

……ずっとアドレスが送られ続ける。ボタンにチャタリング対策を施していないので、
紛らわしいため実装する。(ボタンを押したら待機するだけ)

どうやらデータの送信がうまくいっていないようだ。
> The MSSPx module generates an interrupt at
the end of the ninth clock cycle by setting the
SSPxIF bit.
初めのデータがかけていたのはスタートコンディション送信後に
SSP1IFをクリアして、その後アドレスを送信した後、何も待機せずにそのままデータを送ってしまったのが問題なようだ。

アドレスを送り終えたときにSSP1IFがセットされるため、そこまで待機する。
while(!SSP1IF);
を追加した。ついでにACKの確認も追加して、

SSP1IF = 0;
SSP1BUF = id << 1; // アドレス+W指定
while(!SSP1IF); // アドレス送信待ち

if(SSP1CON2bits.ACKSTAT){
    return; // 送信失敗
}
とした。

データの送信時も同様に、データを格納したらSSP1IFがセットされるまで待機し、それを繰り返すことに。
NO-ACKが返ってきたら再度データを送る仕様にした。

while(data < end_data){
    SSP1IF = 0;
    SSP1BUF = *data;
    while(!SSP1IF);
    if(SSP1CON2bits.ACKSTAT){
        continue; // 送信失敗
    }
    data++;
}

スレーブ側では、データの受信にアドレスかそうでないかで振り分けて判断した。
if(SSP1STATbits.D_nA){
    g_buffer[g_buffer_position++] = SSP1BUF;
}
else{
    data = SSP1BUF;
    g_buffer_position = 0;
}

ここで、空読みとして、適当なレジスタにSSP1BUFを読み込ませる処理があるが、これがないと受信されない。
なお、SSP1BUF = 0;のように適当な値を代入することでも動作が確認できたため、こちらを採用した。

また、SSP1CON3のストップコンディションの際の割り込みを有効にしていなかったため、
送信の終了判定が出来ず苦労していた。これも初期化時に有効にして、割り込み内でSSP1STATbits.Pをセンスして判定した。
結局次のようなプログラムになった。

if(SSP1STATbits.P){
    g_callback(g_buffer, g_buffer_position);
}
else{
    if(SSP1STATbits.D_nA){
        g_buffer[g_buffer_position++] = SSP1BUF;
    }
    else{
        SSP1BUF = 0;
        g_buffer_position = 0;
    }
}

ただしスレーブ受信のみに対応しており、送信の場合はR/Wフラグを確認して動作を割り振る必要がある。

スレーブが送信するプログラムはそのタイミングですでに送るデータが用意されているわけだから、
例えばマスターがデータをくれという命令を出して特定のデータを送信するといったデバイスが考えられる。

TPIP周辺回路では、TPIPから送られるシリアルデータをI2Cで各基板に送り届け、
入力が必要な基板では、必要なタイミングでデータを送るように命令し、取得する。

明日はスレーブ送信とマスタ受信を完成させる予定。
今現在としては、スレーブ送信要求が来たら予め設定した関数ポインタをコールバックし、
その内部で送信関数を使って送信するという流れを予定している。

結局次のようなヘッダファイルになりそうだ。

// マスターモード初期化
void I2C_initMasterMode(void);

// スレーブモード初期化(id)
void I2C_initSlaveMode(char id, void (*callback)(char *data, char size));

// マスターモード送信
void I2C_sendByMaster(char id, char *data, char size);

// マスターモード受信
void I2C_receiveToMaster(void);

// スレーブモード送信
void I2C_sendBySlave(char *data, char size);

// スレーブモード受信
void I2C_receiveToSlave(void);

* パケットにして送るメモ
ところでサーボの制御のためにデータをたくさん垂れ流していくわけだが、
シリアルデータを各基板に送らないといけないわけだから、そのシリアルデータには区切りが存在する。
その区切りを例えば0x7Eとする(フレームデリミタという)。

つまり、0x7E データ 0x7Eをひとかたまりのデータとして送りつける。
もしもデータ中に0x7Eがあった場合は混乱してしまうため、0x7Eを別のデータに変えて、
それが返られたことを示すエスケープオクテット0x7Dを前に挿入することにする。

0x7Eは上から3bit目を反転させて0x5Eとする。つまり、データ中の0x7Eは0x7D 0x5Eに置換される。
ところで0x7D 0x5Eが存在している場合、勝手に0x7Eに置換されてしまって困る。
そこでエスケープオクテット0x7Dもデータ中に存在している場合、
3bit目を反転させた0x5Dに置換して、その前に0x7Dを挿入することにする。
即ち0x7Dは0x7D 0x5Dに置換される。
するとデータ中には0x7Dがエスケープのためだけに存在することになる。
そして、0x7Eはフレームの開始と収量を表すためだけに存在することになる。

(例)
データ : 0x7E 0x7D 0x33 0x7D 0x7E
置換後 : 0x7D 0x5E 0x7D 0x5D 0x33 0x7D 0x5D 0x7D 0x5E

このような1フーレムのデータが来たら、それを適当に解釈して(例えば先頭がアドレス、とか)
他の基板にI2Cで転送する。もしもスレーブ側が送信を必要とする場合は、
そのような命令をフレーム内に記述して(例えばアドレスに送受信の要求を含めるとか)送り返して貰う。

あと明日こそバッテリー試験用基板も作ります;; # バイト終わるまでに出来るといいなー

とはいっても、
(1) PCからテスト要求をRS232Cで出す
(2) PICはそれを受信してテスト(サーボを上下に振り続ける)を開始する。
(3) PCから1秒おきにPICにデータ要求を出し、特定の値を取得する。
(4) PICはアナログ値を取得し、PCに送る。
(5) PCはその値がある値より小さくなったりしたときにテストを終了させる。
(6) PCは記録したデータをtext file等に出力して終了する。

まあできればグラフまで表示させればいいんですけどね。

0 件のコメント:

コメントを投稿