mirror of
https://github.com/rvtr/ctr_mcu.git
synced 2025-06-18 16:45:33 -04:00

git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_mcu@602 013db118-44a6-b54f-8bf7-843cb86687b1
661 lines
15 KiB
C
661 lines
15 KiB
C
/* ========================================================
|
||
簡易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 )
|
||
{
|
||
// 1文字の時は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文字書きます。
|
||
|
||
【注】
|
||
1) スレーブがウェイトコンディションを出すことは禁止です。
|
||
その場合でもエラー検出などできません
|
||
2) len < sizeof(iic_send_work) でないとDMA 使いません。(RAM不足)へぼい。
|
||
|
||
【備考】
|
||
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 /* || len <= iic_send_work */ )
|
||
{
|
||
// 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 );
|
||
}
|
||
|
||
|
||
|
||
|
||
/********************************************//**
|
||
DMA1転送終了割り込み
|
||
|
||
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;
|
||
}
|
||
|