ctr_mcu/trunk/i2c_mcu.c
n2232 7e58fd13c4 typo修正
iic_mcu_read_a_byte() 失敗時、返値が不定だったのを 0xff に固定
市部も関数名・マクロ名を、体を表すよう変更(_snake向けからの輸入)

git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_mcu@597 013db118-44a6-b54f-8bf7-843cb86687b1
2014-01-07 01:22:23 +00:00

662 lines
15 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ========================================================
簡易I2C内蔵ペリフェラル使用通信
de JHL 藤田@開技
'09 Feb -
$Id$
======================================================== */
#ifndef _WIN32
#pragma sfr
#pragma di
#pragma ei
#pragma nop
#pragma inline // memcpy()をインライン展開する(の方が小さい!)
#endif
#include "incs_loader.h"
#include "i2c_mcu.h"
#include "loader.h"
#include "util_funcs.h"
#include "i2c_mcu_sub.h"
// ========================================================
// レジスタのビット名
// プリフィックスbだが、一部のビット名がレジスタ名にかぶるため...
// SMR0n
#define bCKS0 ( 1 << 15 )
#define bCCS0 ( 1 << 14 )
#define bSTS0 ( 1 << 8 )
#define bSIS0 ( 1 << 6 )
#define bMD0n2 ( 1 << 2 )
#define bMD0n1 ( 1 << 1 )
#define bMD0n0 ( 1 << 0 )
#define bSMR0n_FIXEDBIT ( 1 << 5 )
// SSR0n
#define bit_TSF0 6
#define PEF0 ( 1 << 1 )
// SIR0n
#define PECT0 ( 1 << 1 )
// SCR0n
#define TXE0 ( 1 << 15 )
#define RXE0 ( 1 << 14 )
#define SLC02 4
#define DLS02 0
#define TSF0 ( 1 << 6 )
// SOn
#define TAUS_MASK 0x0B0B;
// DMCn
#define DRS ( 1 << 6 )
// ========================================================
static void iic_mcu_send_st( );
// *subからしか呼ばない
void iic_mcu_send_re_st( );
void iic_mcu_send_sp( );
i2c_err iic_mcu_send_a_byte( u8 );
i2c_err iic_mcu_call_slave( u8 slave );
// ========================================================
bit iic_mcu_wo_dma;
volatile bit iic_mcu_busy;
static volatile bit iic_mcu_initialized;
static u8 iic_send_work[4];
static u8 *p_iic_send_wo_dma_dat;
static u8 iic_send_wo_dma_len;
// データエラーROHM製加速度センサ時リトライのため
/// とりあえず、DMA使用マルチバイトライト の時にしか機能しない
static u8 last_slave, last_reg_adrs, last_size;
i2c_err iic_mcu_result; // 一文字リードの時はデータを返す。
#ifdef i2c_timeout_test
extern bit i2c_mcu_time_out_error;
#endif
/********************************************//**
***********************************************/
void nop8()
{
// ここに来る call に3clk, return に 6clk
}
/********************************************//**
他の通信が終わるのを待つ。
タイムアウト有り
***********************************************/
static i2c_err iic_mcu_wait_free()
{
u16 tot = 0;
iic_mcu_start( );
while( 1 )
{
DI_wt_chk();
if( !iic_mcu_busy )
{
iic_mcu_busy = true;
EI();
break;
}
EI();
if( ++tot == 0 )
{
#ifdef i2c_timeout_test
i2c_mcu_time_out_error = true;
#endif
return( I2C_ERR_TIMEOUT );
}
}
return( I2C_ERR_OK );
}
/********************************************//**
スレーブからの 『1文字』 リード
返値がデータそのものです。
エラーコードは iic_mcu_result に入っています
***********************************************/
u8 iic_mcu_read_a_byte( u8 SLA, u8 adrs )
{
u8 dat;
iic_mcu_result = iic_mcu_read( SLA, adrs, 1, &dat );
if( iic_mcu_result != I2C_ERR_OK )
{
dat = 0xff;
}
return ( dat );
}
/********************************************//**
スレーブからのリード
【注】
スレーブがウェイトコンディションを出すことは禁止です。
その場合でもエラー検出などできません
***********************************************/
i2c_err iic_mcu_read( u8 slave, u8 adrs, u8 len, u8 * dest )
{
#if 1
if( iic_mcu_wait_free() != I2C_ERR_OK )
{
// (タイムアウト)
return( I2C_ERR_TIMEOUT );
}
#else
// 使用中なら帰る
#endif
// スタートコンディションとスレーブの呼び出し、レジスタアドレスの送信
if( iic_mcu_call_slave( slave ) != 0 )
{
iic_mcu_busy = false;
return ( I2C_ERR_NOSLAVE );
}
// レジスタアドレスの送信
iic_mcu_send_a_byte( adrs ); // 終わるまで帰ってこない
// 絶対にNAKが帰ってこない前提
// データ受信 //
iic_mcu_send_re_st( ); // リスタートコンディション
iic_mcu_send_a_byte( slave | 0x01 ); // 送信完了まで戻ってきません。
// データ受信
ST0 = 0x0004; // 受信モードに設定を変えるのでロジック停止
SCR02 = RXE0 | 1 << SLC02 | 7 << DLS02; // 受信設定
SS0 = 0x0004; // 通信待機
do
{
if( len == 1 )
{
SOE0 = 0x0000; // 最後のNAK
}
IICIF10 = 0;
SIO10 = 0xFF; // ダミーデータを書くと受信開始
while( IICIF10 == 0 )
{;} // 受信完了待ち
*dest = SIO10;
dest++;
len--;
}
while( len != 0 );
if( slave == IIC_SLA_CODEC )
{
codec_dummy_write();
}
iic_mcu_send_sp( );
IICIF10 = 0;
iic_mcu_busy = false;
return ( I2C_ERR_OK );
}
/********************************************//**
スレーブへ 『1バイト』 ライト
前の転送が終わるのを待って、ライトします。
返値 iic_mcu_write に同じ
***********************************************/
i2c_err iic_mcu_write_a_byte( u8 SLA, u8 adrs, u8 dat )
{
// 文字の時はDMAとか起動しないでさっさと終わらせる
if( iic_mcu_wait_free() != I2C_ERR_OK )
{
return( I2C_ERR_TIMEOUT );
}
// スタートコンディションとスレーブの呼び出し...
IICMK10 = 1;
if( iic_mcu_call_slave( SLA ) != I2C_ERR_OK )
{
iic_mcu_busy = false;
return( I2C_ERR_NOSLAVE );
}
iic_mcu_send_a_byte( adrs );
iic_mcu_send_a_byte( dat );
iic_mcu_send_sp( );
iic_mcu_busy = false;
return ( I2C_ERR_OK );
}
/********************************************//**
スレーブへライト
レジスタ adrs を先頭に、
*strから
len文字書きます。
【注】
スレーブがウェイトコンディションを出すことは禁止です。
その場合でもエラー検出などできません
DMA1を使用します。
***********************************************/
i2c_err iic_mcu_write( u8 slave, u8 adrs, u8 len, void * src )
{
if( iic_mcu_wait_free() != I2C_ERR_OK )
{
return( I2C_ERR_TIMEOUT );
}
#if 0 // rengeが真のマルチタスクになった暁には
// 使用中なら帰る
#endif
// スタートコンディションとスレーブの呼び出し...
IICMK10 = 1;
IICIF10 = 0;
if( iic_mcu_call_slave( slave ) != I2C_ERR_OK )
{
iic_mcu_busy = false;
return ( I2C_ERR_NOSLAVE );
}
IICIF10 = 0;
if( !iic_mcu_wo_dma )
{
// DMAを使用する通常//
// レジスタアドレスを送り、データの準備
memcpy( iic_send_work, src, 4 ); //バッファとして4バイトしか用意して無いため。
// リトライ時のため
last_slave = slave;
last_reg_adrs = adrs;
last_size = len;
// DMAセット
while( DST1 )
{;}
DEN1 = 1;
DSA1 = (u8)( &SIO10 );
DRA1 = (u16)( &iic_send_work[0] );
DBC1 = len;
DMC1 = DRS | 8; // RAM -> SFR, 8bit, IRQ, IIC10
DMAIF1 = 0;
DMAMK1 = 0;
DST1 = 1; // DEN1 = 1から2clk以上開け
SIO10 = adrs; // 書きっぱなし! 割り込みが発生してDMAスタート
// 残りは割り込みルーチン内で
}
else
{
// DMAを使用しない //
// レジスタアドレスの送信
IICMK10 = 0;
SIO10 = adrs;
iic_send_wo_dma_len = len;
p_iic_send_wo_dma_dat = (u8*)src;
// 残りは割り込みルーチン内で
}
return ( I2C_ERR_OK );
}
/********************************************//**
DMA転送終了割り込み
IIC_mcu の送信完了コールバック関数のようなもの
DMA転送が終わっただけで、I2Cの転送は終わってません
  割り込み中などで、DMA1の処理が遅延した場合、
IIC10の割り込みの準備ができずに、割り込みを発生させられなくなる
恐れがあります。また、回避方法も特にありません。
 そのため、DMA仕様の差異は、最後のバイトは送信完了を
フラグのポーリングで確認します。
***********************************************/
__interrupt void int_dma1( )
{
static bit in_retry;
EI();
// 最後のバイトの送信完了待ち
while( ( SSR02L & TSF0 ) != 0 )
{
u16 i = 0;
if( ++i == 0 ) // タイムアウト?
{
break;
}
}
// ストップコンディション発行
// iic_mcu_send_sp(); // ISR中で外の関数を呼ぶのは都合が悪い汎用レジスタ待避が発生するので展開
{
ST0 = 0x0004;
SOE0 = 0; // 受信の時はもっと前に「も」設定してる。(NACK出力)
SO0 = 0x0000 | TAUS_MASK; // SCL
nop8(); //. もう何も怖くない。外の関数呼ぶ
/*
NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP();
*/
SO0 = 0x0400 | TAUS_MASK; // SCL
nop8();
/*
NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP();
*/
SO0 = 0x0404 | TAUS_MASK;
}
IICMK10 = 1;
// データの途中で NAK だったら、一度だけリトライする。
/// 手抜き実装
if( SIR02 != 0 )
{
SIR02 = SSR02;
if( !in_retry )
{
in_retry = true;
IICIF10 = 0;
iic_mcu_call_slave( last_slave ); // ここでNAKは今ンケア
while( DST1 ){;}
DRA1 = (u16)( &iic_send_work[0] ); // 自動インクリメントされてしまっているので再セット
DBC1 = last_size; // 自動デクリメントされてしまっているので再セット
// ほかの設定は前回のまま
DMAIF1 = 0;
DMAMK1 = 0;
DST1 = 1;
SIO10 = last_reg_adrs; // 書きっぱなし! 割り込みが発生してDMAスタート
return;
// おしまい
}
else
{
// エラー二度目。もう知らない
dbg_nop();
// そのまま終了処理へ
}
}
// 正常終了
in_retry = false;
DMAMK1 = 1;
DEN1 = 0;
iic_mcu_busy = false;
}
/********************************************//**
IIC MCUのバイト送出完了割り込み
※DMA使用時は使用されません。
 他の割り込み処理中でDMAの割り込みにすぐ飛ばない場合、
 IIC割り込みのセットが間に合わず困ることがあります。
***********************************************/
__interrupt void int_iic10( )
{
EI();
if( iic_send_wo_dma_len != 0 ) // まだ送信しきっていない?
{
SIO10 = *p_iic_send_wo_dma_dat;
p_iic_send_wo_dma_dat ++;
iic_send_wo_dma_len --;
return;
// おしまい //
}
// 最後のバイト送信完了、ストップコンディション発行
IICMK10 = 1;
// iic_mcu_send_sp(); // ISR中で外の関数を呼ぶのは都合が悪い汎用レジスタ待避が発生するので展開
{
ST0 = 0x0004;
SOE0 = 0; // 受信の時はもっと前に「も」設定してる。(NACK出力)
SO0 = 0x0000 | TAUS_MASK; // clear SCL
NOP(); NOP(); NOP(); NOP(); // NOP8等も呼んではだめ
NOP(); NOP(); NOP(); NOP();
SO0 = 0x0400 | TAUS_MASK; // set SCL
NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP();
SO0 = 0x0404 | TAUS_MASK; // set CSL adn SDA
}
iic_mcu_wo_dma = false;
iic_mcu_busy = false;
}
/********************************************//**
スレーブの呼び出し
 スレーブアドレスを呼んで、ACKの確認。
ACK                  I2C_ERR_OK
 NACK → ストップコンディションを出す。 I2C_ERR_NOSLAVE
***********************************************/
i2c_err iic_mcu_call_slave( u8 slave )
{
iic_mcu_send_st( );
/*
// dbg
if( SSR02 != 0 )
{
P1.5 = P1.0 = P1.3 = 1;
}
*/
SIR02 = SSR02; // NAKエラーのフラグクリア
if( iic_mcu_send_a_byte( slave ) != I2C_ERR_OK )
{
iic_mcu_send_sp( );
return ( I2C_ERR_NOSLAVE ); // 指定のスレーブがいない
}
return ( I2C_ERR_OK );
}
/********************************************//**
ほんとに1バイト書くのみ
書き終わるまで帰りません
***********************************************/
i2c_err iic_mcu_send_a_byte( u8 dat )
{
IICMK10 = 1;
IICIF10 = 0;
SIO10 = dat;
while( IICIF10 == 0 )
{
// NOP( );
} // 通信中
if( SSR02 != 0 ) // 何らかエラー発生?
{
SIR02 = SSR02; // エラークリア
return( I2C_ERR_NAK );
}
return( I2C_ERR_OK );
}
/********************************************//**
スタートコンディションを発行
ソフトウェア制御
***********************************************/
static void iic_mcu_send_st( )
{
SO0 &= ~0x0004; // SDA
nop8();
SO0 &= ~0x0400; // SCL
SOE0 = 0x0004; // ハード制御へ
SCR02 = TXE0 | 1 << SLC02 | 7 << DLS02; // 送信許可、データは8ビット単位
SS0 = 0x0004; // 通信待機
}
/********************************************//**
リスタート発行
***********************************************/
void iic_mcu_send_re_st( )
{
ST0 |= 0x0004;
SO0 |= 0x0400 | TAUS_MASK; // ( SDA = H ), SCL -> H
nop8();
SOE0 &= ~0x0004; // ( SCL = H ), SDA -> L
nop8();
iic_mcu_send_st( );
}
/********************************************//**
ストップコンディション発行
この前に、「最後のバイトの送受信」の時に前準備が必要です。
***********************************************/
void iic_mcu_send_sp( )
{
ST0 = 0x0004;
SOE0 = 0; // 受信の時はもっと前に「も」設定してる。(NACK出力)
SO0 = 0x0000 | TAUS_MASK; // SCL
nop8();
SO0 = 0x0400 | TAUS_MASK; // SCL
nop8();
SO0 = 0x0404 | TAUS_MASK;
}
/********************************************//**
ペリフェラルモジュールの初期化
***********************************************/
void iic_mcu_start( )
{
if( iic_mcu_initialized )
{
return;
}
iic_mcu_busy = true;
// DST1 = 0;
I2C_PU_on();
// DEN1 = 0; // DST1 = 0 から 2clkもしくは、DSTn==0をポーリングしてから
wait_ms( 1 ); // 立ち上がるのに50us位かかる
SAU0EN = 1;
nop8();
SPS0 = 0x0000; // シリアルユニットのクロック0。(8M/2)/1
SMR02 = bSMR0n_FIXEDBIT | bMD0n2; // 簡易I2Cに設定
SDR02 = 10 << 9; // ボーレート設定 8M/1/(x+1)/2
SO0 = 0x0404 | TAUS_MASK; // 最初はHH
iic_mcu_wo_dma = false;
// バスのリセット
{
IICIF10 = 0;
IICMK10 = 1;
iic_mcu_send_st();
SIO10 = 0xFF;
while( IICIF10 == 0 ){;} // 通信中
iic_mcu_send_sp();
SIR02 = SSR02;
}
iic_mcu_busy = false;
iic_mcu_initialized = true;
}
/********************************************//**
モジュールの停止
再度使うときは初期化が必要
***********************************************/
void iic_mcu_stop( )
{
while( iic_mcu_busy )
{;} // DMA動作中はもう少し待つ
iic_mcu_send_re_st( ); // SCL,SDAをLLにする
I2C_PU_off();
SAU0EN = 0;
iic_mcu_initialized = false;
}