mirror of
https://github.com/rvtr/ctr_mcu.git
synced 2025-06-19 00:55:37 -04:00
561 lines
13 KiB
C
561 lines
13 KiB
C
/* ========================================================
|
||
簡易I2C(内蔵ペリフェラル使用)通信
|
||
de JHL 藤田@開技
|
||
'09 Feb -
|
||
======================================================== */
|
||
#pragma sfr
|
||
#pragma di
|
||
#pragma ei
|
||
#pragma nop
|
||
#pragma inline // memcpy()をインライン展開する
|
||
|
||
#include "incs.h"
|
||
#include "i2c_mcu.h"
|
||
|
||
|
||
|
||
// ========================================================
|
||
// SSR0n
|
||
#define bit_TSF0 6
|
||
// SIR0n
|
||
#define PECT0 ( 1 << 1 )
|
||
// SSR0n
|
||
#define PEF0 ( 1 << 1 )
|
||
|
||
#define TXE0 ( 1 << 15 )
|
||
#define RXE0 ( 1 << 14 )
|
||
#define SLC02 4
|
||
#define DLS02 0
|
||
#define TSF0 ( 1 << 6 )
|
||
|
||
#define DRS ( 1 << 6 )
|
||
|
||
#define TAUS_MASK 0b0000101100001011;
|
||
|
||
|
||
|
||
// ========================================================
|
||
static void iic_mcu_send_st();
|
||
static void iic_mcu_send_re_st();
|
||
static void iic_mcu_send_sp();
|
||
static err iic_mcu_send_a_byte( u8 );
|
||
static err iic_mcu_call_slave( u8 slave );
|
||
|
||
|
||
|
||
// ========================================================
|
||
bit iic_mcu_wo_dma;
|
||
volatile bit iic_mcu_busy;
|
||
volatile bit iic_mcu_initialized;
|
||
|
||
|
||
u8 iic_send_work[4];
|
||
u8* p_iic_send_wo_dma_dat;
|
||
u8 iic_send_wo_dma_len;
|
||
|
||
u8 iic_mcu_bus_status; // 一文字リードの時はデータを返す。
|
||
// ステータスが必要ならこっちを呼んで
|
||
|
||
|
||
|
||
/* ========================================================
|
||
スレーブからの 『1文字』 リード
|
||
返値がデータそのものです。
|
||
エラー判定ができません。
|
||
======================================================== */
|
||
u8 iic_mcu_read_a_byte( u8 SLA, u8 adrs ){
|
||
u8 dat;
|
||
|
||
if( iic_mcu_initialized == 0 ){
|
||
#ifdef _debug_
|
||
iic_mcu_start();
|
||
#else
|
||
while(1){};
|
||
#endif
|
||
}
|
||
|
||
while( iic_mcu_busy ){
|
||
NOP();
|
||
}
|
||
iic_mcu_busy = 1;
|
||
|
||
iic_mcu_bus_status = ERR_OK;
|
||
// スタートコンディションとスレーブの呼び出し、レジスタアドレスの送信
|
||
if( iic_mcu_call_slave( SLA ) != 0 ){
|
||
iic_mcu_bus_status = ERR_NOSLAVE;
|
||
iic_mcu_busy = 0;
|
||
return( 0 );
|
||
}
|
||
|
||
// レジスタアドレスの送信
|
||
iic_mcu_send_a_byte( adrs ); // 終わるまで帰ってこない
|
||
// if( err != ERR_SUCCESS )~
|
||
|
||
// データ受信 //
|
||
iic_mcu_send_re_st(); // リスタートコンディション
|
||
iic_mcu_send_a_byte( SLA | 0x01 ); // 送信完了まで戻ってきません。
|
||
|
||
ST0 = 0x0004; // 受信モードに設定を変えるのでロジック停止
|
||
SCR02 = RXE0 | 1 << SLC02 | 7 << DLS02; // 受信設定
|
||
SS0 = 0x0004; // 通信待機
|
||
|
||
SOE0 = 0x0000; // 1バイト送信なので、最後のNAKを送る
|
||
IICIF10 = 0;
|
||
SIO10 = 0xFF; // ダミーデータを書くと受信開始
|
||
|
||
while( IICIF10 == 0 ){ // 受信完了待ち
|
||
;}
|
||
dat = SIO10;
|
||
|
||
iic_mcu_send_sp();
|
||
IICIF10 = 0; // 後を濁さないこと
|
||
iic_mcu_busy = 0;
|
||
return( dat );
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
スレーブからのリード
|
||
0 正常終了
|
||
1 スレーブが応答しない
|
||
2 バスが誰かに占有されていてタイムアウト
|
||
3 意味不明エラー
|
||
【注】
|
||
スレーブがウェイトコンディションを出すことは禁止です。
|
||
その場合でもエラー検出などできません
|
||
======================================================== */
|
||
err iic_mcu_read( u8 slave, u8 adrs, u8 len, u8* dest ){
|
||
|
||
//*
|
||
// 使用中なら待つ
|
||
if( iic_mcu_initialized == 0 ){
|
||
#ifdef _debug_
|
||
iic_mcu_start();
|
||
#else
|
||
while(1){};
|
||
#endif
|
||
}
|
||
while( iic_mcu_busy ){
|
||
NOP();
|
||
}
|
||
/*/
|
||
// 使用中なら帰る
|
||
if( iic_mcu_initialized == 0 ){
|
||
return(0x80);
|
||
}
|
||
if( iic_mcu_busy != 0 ){
|
||
return( 3 );
|
||
}
|
||
//*/
|
||
|
||
iic_mcu_busy = 1;
|
||
// スタートコンディションとスレーブの呼び出し、レジスタアドレスの送信
|
||
if( iic_mcu_call_slave( slave ) != 0 ){
|
||
iic_mcu_busy = 0;
|
||
return( ERR_NAK );
|
||
}
|
||
|
||
// レジスタアドレスの送信
|
||
iic_mcu_send_a_byte( adrs ); // 終わるまで帰ってこない
|
||
// if( err != ERR_SUCCESS )~
|
||
|
||
// データ受信 //
|
||
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 );
|
||
|
||
iic_mcu_send_sp();
|
||
|
||
IICIF10 = 0;
|
||
iic_mcu_busy = 0;
|
||
return( ERR_SUCCESS );
|
||
}
|
||
|
||
|
||
|
||
|
||
/* ========================================================
|
||
スレーブへ 『1バイト』 ライト
|
||
前の転送が終わるのを待って、ライトします。
|
||
返値 iic_mcu_write に同じ
|
||
|
||
======================================================== */
|
||
err iic_mcu_write_a_byte( u8 SLA, u8 adrs, u8 dat ){
|
||
|
||
if( iic_mcu_initialized == 0 ){
|
||
#ifdef _debug_
|
||
iic_mcu_start();
|
||
#else
|
||
while(1){};
|
||
#endif
|
||
}
|
||
while( iic_mcu_busy ){
|
||
NOP();
|
||
}
|
||
iic_mcu_busy = 1;
|
||
#if 0
|
||
temp = dat;
|
||
return( iic_mcu_write( SLA, adrs, 1, &temp ) );
|
||
}
|
||
#else
|
||
// スタートコンディションとスレーブの呼び出し...
|
||
IICMK10 = 1;
|
||
if( iic_mcu_call_slave( SLA ) != 0 ){
|
||
iic_mcu_busy = 0;
|
||
return( ERR_NAK );
|
||
}
|
||
iic_mcu_send_a_byte( adrs );
|
||
iic_mcu_send_a_byte( dat );
|
||
iic_mcu_send_sp();
|
||
iic_mcu_busy = 0;
|
||
return( ERR_SUCCESS );
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
スレーブへライト
|
||
レジスタ adrs を先頭に、
|
||
*strから
|
||
len文字書きます。
|
||
|
||
0 正常終了
|
||
1 スレーブが応答しない
|
||
2 バスが誰かに占有されていてタイムアウト
|
||
3 前に指示された通信がまだ終わってない
|
||
【注】
|
||
スレーブがウェイトコンディションを出すことは禁止です。
|
||
その場合でもエラー検出などできません
|
||
DMA1を使用します。
|
||
******************************************************************************/
|
||
err iic_mcu_write( u8 slave, u8 adrs, u8 len, u8* src ){
|
||
//*
|
||
// 使用中なら待つ
|
||
if( iic_mcu_initialized == 0 ){
|
||
#ifdef _debug_
|
||
iic_mcu_start();
|
||
#else
|
||
while(1){};
|
||
#endif
|
||
}
|
||
while( iic_mcu_busy ){
|
||
NOP();
|
||
}
|
||
/*/
|
||
// 使用中なら帰る
|
||
if( iic_mcu_initialized == 0 ){
|
||
return(0x80);
|
||
}
|
||
if( iic_mcu_busy != 0 ){
|
||
return( 3 );
|
||
}
|
||
//*/
|
||
iic_mcu_busy = 1;
|
||
// スタートコンディションとスレーブの呼び出し...
|
||
IICMK10 = 1;
|
||
IICIF10 = 0;
|
||
if( iic_mcu_call_slave( slave ) != 0 ){
|
||
iic_mcu_busy = 0;
|
||
EI();
|
||
return( ERR_NAK );
|
||
}
|
||
|
||
IICIF10 = 0;
|
||
if( !iic_mcu_wo_dma ){
|
||
// DMAを使用する(通常)
|
||
|
||
// レジスタアドレスを送り、データの準備
|
||
memcpy( iic_send_work, src, 4 ); //バッファとして4バイトしか用意して無いため。
|
||
// DMAセット
|
||
while( DST1 ){;};
|
||
|
||
DEN1 = 1;
|
||
DSA1 = (u8)( &SIO10 );
|
||
DRA1 = (u16)iic_send_work;
|
||
DBC1 = len;
|
||
DMC1 = DRS | 8; // RAM -> SFR, 8bit, IRQ, IIC10
|
||
|
||
DMAIF1 = 0;
|
||
DMAMK1 = 0;
|
||
DST1 = 1;
|
||
|
||
SIO10 = adrs; // 書きっぱなし! 割り込みが発生してDMAスタート
|
||
// 残りは割り込みルーチン内で
|
||
}else{
|
||
// DMAを使用しない //
|
||
|
||
// レジスタアドレスの送信
|
||
SIO10 = adrs;
|
||
|
||
IICMK10 = 0;
|
||
iic_send_wo_dma_len = len;
|
||
p_iic_send_wo_dma_dat = src;
|
||
// 残りは割り込みルーチン内で
|
||
}
|
||
return( ERR_SUCCESS );
|
||
}
|
||
|
||
|
||
|
||
|
||
/* ========================================================
|
||
DMA1転送終了割り込み
|
||
IIC_mcu の送信完了コールバック関数のようなもの
|
||
注:DMA転送が終わっただけで、I2Cの転送は終わってません
|
||
======================================================== */
|
||
__interrupt void int_dma1(){
|
||
IICIF10 = 0;
|
||
DMAMK1 = 1;
|
||
DEN1 = 0;
|
||
IICMK10 = 1;
|
||
while(( SSR02L & TSF0 ) != 0 ){;
|
||
}
|
||
// 最後のバイト転送後、I2C割り込みが発生する
|
||
// ↓
|
||
|
||
// 共通(最終バイト送信完了)
|
||
IICMK10 = 1;
|
||
// ISR中で外の関数を呼ぶのは都合が悪いので展開
|
||
// iic_mcu_send_sp();
|
||
{
|
||
ST0 = 0x0004;
|
||
SOE0 = 0; // 受信の時はもっと前に「も」設定してる。(NACK出力)
|
||
SO0 = 0x0000 | TAUS_MASK; // SCL
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
SO0 = 0x0400 | TAUS_MASK; // SCL
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
SO0 = 0x0404 | TAUS_MASK;
|
||
}
|
||
iic_mcu_wo_dma = 0;
|
||
iic_mcu_busy = 0;
|
||
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
IIC MCUのバイト送出完了割り込み
|
||
======================================================== */
|
||
__interrupt void int_iic10(){
|
||
if( iic_mcu_wo_dma ){
|
||
// DMA使用せず、転送途中
|
||
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;
|
||
}
|
||
iic_mcu_wo_dma = 0;
|
||
}
|
||
// 共通(最終バイト送信完了)
|
||
IICMK10 = 1;
|
||
// ISR中で外の関数を呼ぶのは都合が悪いので展開
|
||
// iic_mcu_send_sp();
|
||
{
|
||
ST0 = 0x0004;
|
||
SOE0 = 0; // 受信の時はもっと前に「も」設定してる。(NACK出力)
|
||
SO0 = 0x0000 | TAUS_MASK; // SCL
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
SO0 = 0x0400 | TAUS_MASK; // SCL
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
SO0 = 0x0404 | TAUS_MASK;
|
||
}
|
||
iic_mcu_busy = 0;
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
スレーブの呼び出し
|
||
スレーブアドレスを呼んで、ACKの確認。
|
||
ACK → 返:0
|
||
NACK → ストップコンディションを出す。 返:1
|
||
======================================================== */
|
||
static err iic_mcu_call_slave( u8 slave ){
|
||
iic_mcu_send_st();
|
||
|
||
// SIR02 = SSR02; // NAKエラーのフラグクリア
|
||
if( iic_mcu_send_a_byte( slave ) != 0 ){
|
||
iic_mcu_send_sp();
|
||
return( ERR_NAK ); // 指定のスレーブがいない / busy
|
||
}
|
||
|
||
return( ERR_SUCCESS );
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
ほんとに1バイト書くのみ
|
||
書き終わるまで帰りません
|
||
======================================================== */
|
||
static err iic_mcu_send_a_byte( u8 dat ){
|
||
|
||
IICIF10 = 0;
|
||
SIO10 = dat;
|
||
while( IICIF10 == 0 ){
|
||
NOP();
|
||
} // 通信中
|
||
if( SSR02 != 0 ){
|
||
SIR02 = SSR02;
|
||
return( ERR_NAK );
|
||
}
|
||
return( ERR_SUCCESS );
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
スタートコンディションを発行
|
||
ソフトウェア制御
|
||
======================================================== */
|
||
static void iic_mcu_send_st(){
|
||
SO0 &= ~0x0004; // SDA
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
SO0 &= ~0x0400; // SCL
|
||
SOE0 = 0x0004; // ハード制御へ
|
||
|
||
SCR02 = TXE0 | 1 << SLC02 | 7 << DLS02; // 送信許可、データは8ビット単位
|
||
SS0 = 0x0004; // 通信待機
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
リスタート発行
|
||
======================================================== */
|
||
static void iic_mcu_send_re_st(){
|
||
ST0 |= 0x0004;
|
||
SO0 |= 0x0400 | TAUS_MASK; // ( SDA = H ), SCL -> H
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
SOE0 &= ~0x0004; // ( SCL = H ), SDA -> L
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
iic_mcu_send_st();
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
ストップコンディション発行
|
||
この前に、「最後のバイトの送受信」の時に前準備が必要です。
|
||
======================================================== */
|
||
static void iic_mcu_send_sp(){
|
||
ST0 = 0x0004;
|
||
SOE0 = 0; // 受信の時はもっと前に「も」設定してる。(NACK出力)
|
||
SO0 = 0x0000 | TAUS_MASK; // SCL
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
SO0 = 0x0400 | TAUS_MASK; // SCL
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
SO0 = 0x0404 | TAUS_MASK;
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
バスのリセット
|
||
(ストップコンディションが出せそうだったらすかさず出す。
|
||
======================================================== */
|
||
void iic2m_bus_reset(){
|
||
/*
|
||
u8 count;
|
||
for( count = 19; count != 0; count-- ){
|
||
iics_sda_H;
|
||
iics_scl_H;
|
||
PM1.1 = 1; // SDA
|
||
if( iics_sda != 0 ){
|
||
PM1.1 = 0;
|
||
iic_mcu_send_sp;
|
||
return;
|
||
}
|
||
PM1.1 = 0;
|
||
iics_scl_L;
|
||
}
|
||
return;
|
||
*/
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
ペリフェラルモジュールの初期化
|
||
======================================================== */
|
||
void iic_mcu_start(){
|
||
DST1 = 0;
|
||
DEN1 = 0;
|
||
I2C_PU = 1;
|
||
SAU0EN = 1;
|
||
NOP(); // 4clkあける
|
||
NOP();
|
||
NOP();
|
||
NOP();
|
||
SPS0 = 0x0000; // シリアルユニットのクロック0。(8M/2)/1
|
||
SMR02 = 0 << 15 | 0 << 14 | 0 << 7 | 0 << 5 | 1 << 4 | 1 << 2; // I2Cとそのクロックなど設定
|
||
SDR02 = 5 << 9; // ボーレート設定 (8M/2)/1/(x+1)/2
|
||
|
||
SO0 = 0x0404 | TAUS_MASK; // 最初はHH
|
||
iic_mcu_busy = 0;
|
||
iic_mcu_wo_dma = 0;
|
||
iic_mcu_initialized = 1;
|
||
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
モジュールの停止
|
||
再度使うときは初期化が必要
|
||
======================================================== */
|
||
void iic_mcu_stop(){
|
||
while( iic_mcu_busy ){;} // DMA動作中はもう少し待つ
|
||
iic_mcu_send_re_st(); // SCL,SDAをLLにする
|
||
I2C_PU = 0;
|
||
SAU0EN = 0;
|
||
iic_mcu_initialized = 0;
|
||
}
|