ctr_mcu/trunk/i2c_mcu.c
N2232 bcefe4d511 ■0.21
いつの間にか使われ方が変わって長手意を表していない変数などを一部名前変更
一部エラーコードで0を返しなどしてた。ERR_SUCCESSなどを返すように修正
ADCのノイズフィルタを改良
ヒステリシス+四捨五入を追加
Volテーブルを更新。最大音量を-10dbに
TWLからの音量設定を無視→反映、スライダの設定と後着優先になるように修正
 Volのポーリング書き込み廃止、CODECリセット時のために強制セットコマンド追加(command.4)
 そのつもりがなかったので修正量が割とあった。
Vol書き込み時、ベリファイ、一度だけリトライするようにした。発生頻度からすれば良かろう。評価中
バッテリー補正パラメータ更新
I2C_mにライトコマンドがきた直後に次の通信が来ると対応出来ずにバスが衝突(ウェイトコンディション理解してくれないから...)してしまっていた。
 結果:一瞬BL消えや突然の電源断
 一時的にスレーブアドレスを変えてNAKを返し、リトライしてもらうことにした。評価中。
電池残量ゼロ時のパターンをとりあえず高速点滅をプリセットにした。
 交換した電池が0や、完全放電などでMCUがリセットされてSoCからパターンをもらってない場合にLEDが青赤とも消灯になりユーザーが心配するため
お知らせLEDのフルカラー化の両対応コードが間違えていてめちゃめちゃになっていたのを修正
お知らせLEDフルカラー判定を誤ることがあった。マージンを増やした。
白箱を実機と誤判定していた。(FPGAの準備がまだ)判定方法を変更
本体設定や無線スイッチでWiFiを切ったときはフェードなしに。すぱっと変化した方がかっこいい
電源OFFにするとき、3DとWiFiはすぱっと消す。電源とお知らせはフェード(以前のまま)
スリープ期間が極短いとSoC.SLP_OのH期間を取り逃す事があった。
 症状:スリープに入ると電源断以外受け付けなくなる
 I2Cで予告してもらう。
歩数計のログポインタ進めるタイミング、秒レジスタ追加。
割り込み禁止区間の調整
電池残量ICとの通信・通信後のケアなど修正
電池残量0での強制電源断復活
.bin,.hexをリポジトリに追加

git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_mcu@198 013db118-44a6-b54f-8bf7-843cb86687b1
2010-06-30 05:50:16 +00:00

604 lines
14 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"
// ========================================================
// レジスタのビット名
// プリフィックス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 0b0000101100001011;
// DMCn
#define DRS ( 1 << 6 )
// ========================================================
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_result; // 一文字リードの時はデータを返す。
// ステータスが必要ならこっちを呼んで
// ========================================================
void nop8()
{
// 実は nop11 位なのだが
}
static err iic_mcu_wait_free()
{
u16 tot = 0;
iic_mcu_start( );
while( 1 )
{
DI();
if( !iic_mcu_busy )
{
iic_mcu_busy = 1;
break;
}
EI();
if( ++tot == 0 )
{
return( ERR_ERR );
}
}
EI();
return( ERR_SUCCESS );
}
/* ========================================================
スレーブからの 『1文字』 リード
返値がデータそのものです。
======================================================== */
u8 iic_mcu_read_a_byte( u8 SLA, u8 adrs )
{
u8 dat;
#if 1
// ラッパー
if( iic_mcu_read( SLA, adrs, 1, &dat ) == ERR_SUCCESS )
{
iic_mcu_result = ERR_OK;
}
else
{
iic_mcu_result = ERR_NOSLAVE;
}
return ( dat );
#else
iic_mcu_wait_free();
// スタートコンディションとスレーブの呼び出し、レジスタアドレスの送信
if( iic_mcu_call_slave( SLA ) != 0 )
{
iic_mcu_result = 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 );
#endif
}
/* ========================================================
スレーブからのリード
0 正常終了
1 スレーブが応答しない
2 バスが誰かに占有されていてタイムアウト
3 意味不明エラー
【注】
スレーブがウェイトコンディションを出すことは禁止です。
その場合でもエラー検出などできません
======================================================== */
err iic_mcu_read( u8 slave, u8 adrs, u8 len, u8 * dest )
{
#if 1
if( iic_mcu_wait_free() != ERR_SUCCESS )
{
return( ERR_ERR );
}
#else
// 使用中なら帰る
#endif
// スタートコンディションとスレーブの呼び出し、レジスタアドレスの送信
if( iic_mcu_call_slave( slave ) != 0 )
{
iic_mcu_busy = 0;
return ( ERR_NOSLAVE );
}
// レジスタアドレスの送信
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 0
static u8 temp; // 書きっぱなしで終了を見ずに関数を抜ける可能性が高いのでstatic
// これをしないと、立て続けに書いたときに前のデータを破壊してしまう
while( iic_mcu_busy )
{
NOP( );
}
temp = dat;
iic_mcu_wo_dma = 1;
return ( iic_mcu_write( SLA, adrs, 1, &temp ) );
#else
// 文字の時はDMAとか起動しないでさっさと終わらせる
if( iic_mcu_wait_free() != ERR_SUCCESS )
{
return( ERR_ERR );
}
// スタートコンディションとスレーブの呼び出し...
IICMK10 = 1;
if( iic_mcu_call_slave( SLA ) != ERR_SUCCESS )
{
iic_mcu_busy = 0;
return ( ERR_NOSLAVE );
}
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, void * src )
{
if( iic_mcu_wait_free() != ERR_SUCCESS )
{
return( ERR_ERR );
}
#if 0
// 使用中なら帰る
#endif
// スタートコンディションとスレーブの呼び出し...
IICMK10 = 1;
IICIF10 = 0;
if( iic_mcu_call_slave( slave ) != ERR_SUCCESS )
{
iic_mcu_busy = 0;
return ( ERR_NOSLAVE );
}
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[0] );
DBC1 = len;
DMC1 = DRS | 8; // RAM -> SFR, 8bit, IRQ, IIC10
DMAIF1 = 0;
DMAMK1 = 0;
DST1 = 1;
SIO10 = adrs; // 書きっぱなし! 割り込みが発生してDMAスタート
// 残りは割り込みルーチン内で
}
else
{
// DMAを使用しない //
// レジスタアドレスの送信
IICMK10 = 0;
SIO10 = adrs;
iic_send_wo_dma_len = len;
p_iic_send_wo_dma_dat = src;
// 残りは割り込みルーチン内で
}
return ( ERR_SUCCESS );
}
/* ========================================================
DMA転送終了割り込み
IIC_mcu の送信完了コールバック関数のようなもの
DMA転送が終わっただけで、I2Cの転送は終わってません
  割り込み中などで、DMA1の処理が遅延した場合、
IIC10の割り込みの準備ができずに、割り込みを発生させられなくなる
恐れがあります。また、回避方法も特にありません。
 そのため、DMA仕様の差異は、最後のバイトは送信完了を
フラグのポーリングで確認します。
======================================================== */
__interrupt void int_dma1( )
{
u16 i = 0;
EI();
DMAMK1 = 1;
DEN1 = 0;
while( ( SSR02L & TSF0 ) != 0 )
{
if( ++i == 0 ) // タイムアウト?
{
break;
}
}
// iic_mcu_send_sp(); // ISR中で外の関数を呼ぶのは都合が悪いので展開
{
ST0 = 0x0004;
SOE0 = 0; // 受信の時はもっと前に「も」設定してる。(NACK出力)
SO0 = 0x0000 | TAUS_MASK; // SCL
nop8();
SO0 = 0x0400 | TAUS_MASK; // SCL
nop8();
SO0 = 0x0404 | TAUS_MASK;
}
IICMK10 = 1;
iic_mcu_busy = 0;
}
/* ========================================================
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; // SCL
nop8();
SO0 = 0x0400 | TAUS_MASK; // SCL
nop8();
SO0 = 0x0404 | TAUS_MASK;
}
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( iic_mcu_send_a_byte( slave ) != ERR_SUCCESS )
{
iic_mcu_send_sp( );
return ( ERR_NAK ); // 指定のスレーブがいない / busy
}
return ( ERR_SUCCESS );
}
/* ========================================================
ほんとに1バイト書くのみ
書き終わるまで帰りません
======================================================== */
static err iic_mcu_send_a_byte( u8 dat )
{
IICMK10 = 1;
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
nop8();
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
nop8();
SOE0 &= ~0x0004; // ( SCL = H ), SDA -> L
nop8();
iic_mcu_send_st( );
}
/* ========================================================
ストップコンディション発行
この前に、「最後のバイトの送受信」の時に前準備が必要です。
======================================================== */
static 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 = 1;
DST1 = 0;
NOP( ); // 2clkもしくは、DSTn==0をポーリング
NOP( );
DEN1 = 0;
I2C_PU_on();
wait_ms( 10 ); // 立ち上がるのに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 = 0;
// バスのリセット
IICIF10 = 0;
IICMK10 = 1;
iic_mcu_send_st();
SIO10 = 0xFF;
while( IICIF10 == 0 ){} // 通信中
iic_mcu_send_sp();
SIR02 = SSR02;
iic_mcu_busy = 0;
iic_mcu_initialized = 1;
}
/* ========================================================
モジュールの停止
再度使うときは初期化が必要
======================================================== */
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 = 0;
}