ctr_mcu/trunk/i2c_mcu.c

477 lines
11 KiB
C
Raw 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 -
*******************************************************************************/
#pragma sfr
#pragma di
#pragma ei
#pragma nop
#pragma inline // memcpy()をインライン展開する
#include "incs_loader.h"
#include "i2c_mcu.h"
//#include <string.h>
bit iic_mcu_wo_dma;
volatile bit iic_mcu_busy;
volatile bit iic_mcu_initialized;
// 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 );
u8 iic_send_work[4];
u8* p_iic_send_wo_dma_dat;
u8 iic_send_wo_dma_len;
volatile u8 interrupted;
u8 dma_state;
/*****
スレーブからの 『1文字』 リード
返値がデータそのものです。
エラー判定ができません。
******************************************************************************/
u8 iic_mcu_read_a_byte( u8 SLA, u8 adrs ){
u8 dat;
iic_mcu_read( SLA, adrs, 1, &dat );
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 ){
DBG_M_n = 1;
NOP();
}
DBG_M_n = 0;
/*/
// 使用中なら帰る
if( iic_mcu_initialized == 0 ){
return(0x80);
}
if( iic_mcu_busy != 0 ){
return( 3 );
}
//*/
// スタートコンディションとスレーブの呼び出し、レジスタアドレスの送信
if( iic_mcu_call_slave( slave ) != 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; // 通信待機
while( len != 0 ){
if( len == 1 ){
SOE0 &= ~0x0004;
}
IICIF10 = 0;
SIO10 = 0xFF; // ダミーデータを書くと受信開始
while( IICIF10 == 0 ){;}
*dest = SIO10;
dest++;
len--;
}
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 ){
volatile static u8 temp;
while( iic_mcu_busy ){
DBG_M_n = 1;
NOP();
}
DBG_M_n = 0;
temp = dat;
return( iic_mcu_write( SLA, adrs, 1, &temp ) );
}
u8* p_src_work; // DMA送信バッファ
u8 len_work;
/******************************************************************************
スレーブへライト
レジスタ 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 ){
DBG_M_n = 1;
NOP();
}
DBG_M_n = 0;
/*/
// 使用中なら帰る
if( iic_mcu_initialized == 0 ){
return(0x80);
}
if( iic_mcu_busy != 0 ){
return( 3 );
}
//*/
// スタートコンディションとスレーブの呼び出し...
IICMK10 = 1;
if( iic_mcu_call_slave( slave ) != 0 ){
return( ERR_NAK );
}
iic_mcu_busy = 1;
dma_state = 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;
IICIF10 = 0;
SIO10 = adrs; // 書きっぱなし! 割り込みが発生してDMAスタート
}else{
// DMAを使用しない //
// レジスタアドレスの送信
IICIF10 = 0;
SIO10 = adrs;
IICMK10 = 0;
iic_send_wo_dma_len = len;
p_iic_send_wo_dma_dat = src;
// 残りは割り込みルーチン内で
}
dma_state = 1;
return( ERR_SUCCESS );
}
/******************************************************************************
DMA転送終了割り込み
IIC_mcu の送信完了コールバック関数のようなもの
DMA転送が終わっただけで、I2Cの転送は終わってません
******************************************************************************/
__interrupt void int_dma1(){
dma_state = 2;
DMAMK1 = 1;
DEN1 = 0;
IICMK10 = 0;
// 最後のバイト転送後、I2C割り込みが発生する
}
/**
IIC MCUのバイト送出完了割り込み
******************************************************************************/
__interrupt void int_iic10(){
dma_state = 3;
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;
}
}
// 共通(最終バイト送信完了)
IICMK10 = 1;
iic_mcu_send_sp();
iic_mcu_wo_dma = 0;
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 0
if( iic_mcu_send_a_byte( slave ) != 0 ){
iic_mcu_send_sp();
return( 1 ); // 指定のスレーブがいない / busy
}
#else
IICIF10 = 0;
SIO10 = slave;
while( IICIF10 == 0 ){
NOP();
} // 通信中
if( SSR02 != 0 ){
SIR02 = SSR02;
iic_mcu_send_sp();
return( ERR_NAK );
}
#endif
return( ERR_SUCCESS );
}
/**
I2C単機能API
ほんとに1バイト書くだけ。
書き終わるまで帰りません
******************************************************************************/
static err iic_mcu_send_a_byte( u8 dat ){
IICIF10 = 0;
SIO10 = dat;
while( IICIF10 == 0 ){
NOP();
} // 通信中
if( SSR02 != 0 ){
SIR02 = SSR02;
return( 1 ); // NAK
}
return( 0 );
}
/**
スタートコンディションを発行
自力でぱたぱたしなくてはならない
*******************************************************************************/
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(){
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;
}