mirror of
https://github.com/rvtr/ctr_mcu.git
synced 2025-06-19 09:05:48 -04:00

最大音量を-4db対応(adc) お知らせLED対応(i2c_ctr) I2C_mで、書き込みデータが化ける? 巻き戻し GYROのCSが機能していなかった(PM5の設定忘れ) todo: 現状、温度無視して充電させてます git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_mcu@132 013db118-44a6-b54f-8bf7-843cb86687b1
617 lines
15 KiB
C
617 lines
15 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"
|
||
|
||
|
||
|
||
// ========================================================
|
||
// レジスタのビット名
|
||
// プリフィックス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_bus_status; // 一文字リードの時はデータを返す。
|
||
// ステータスが必要ならこっちを呼んで
|
||
|
||
|
||
|
||
// ========================================================
|
||
void nop8()
|
||
{
|
||
// 実は nop11 位なのだが
|
||
}
|
||
|
||
|
||
static err iic_mcu_is_ready()
|
||
{
|
||
if( iic_mcu_initialized == 0 )
|
||
{
|
||
#ifdef _debug_
|
||
iic_mcu_start( );
|
||
#else
|
||
return( ERR_ERR );
|
||
while( 1 )
|
||
{
|
||
NOP();
|
||
}
|
||
#endif
|
||
}
|
||
|
||
while( iic_mcu_busy )
|
||
{
|
||
NOP( );
|
||
}
|
||
iic_mcu_busy = 1;
|
||
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_bus_status = ERR_OK;
|
||
}
|
||
else
|
||
{
|
||
iic_mcu_bus_status = ERR_NOSLAVE;
|
||
}
|
||
return ( dat );
|
||
|
||
#else
|
||
iic_mcu_is_ready();
|
||
|
||
// スタートコンディションとスレーブの呼び出し、レジスタアドレスの送信
|
||
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 );
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
/* ========================================================
|
||
スレーブからのリード
|
||
0 正常終了
|
||
1 スレーブが応答しない
|
||
2 バスが誰かに占有されていてタイムアウト
|
||
3 意味不明エラー
|
||
【注】
|
||
スレーブがウェイトコンディションを出すことは禁止です。
|
||
その場合でもエラー検出などできません
|
||
======================================================== */
|
||
err iic_mcu_read( u8 slave, u8 adrs, u8 len, u8 * dest )
|
||
{
|
||
|
||
#if 1
|
||
if( iic_mcu_is_ready() != ERR_SUCCESS )
|
||
{
|
||
return( ERR_ERR );
|
||
}
|
||
;
|
||
#else
|
||
// 使用中なら帰る
|
||
if( iic_mcu_initialized == 0 ){
|
||
return(0x80);
|
||
}
|
||
if( iic_mcu_busy != 0 ){
|
||
return( 3 );
|
||
}
|
||
#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 )
|
||
{
|
||
// ラッパー
|
||
static u8 temp; // 書きっぱなしで終了を見ずに関数を抜ける(可能性が高い)のでstatic
|
||
|
||
#if 0
|
||
|
||
// これをしないと、立て続けに書いたときに前のデータを破壊してしまう
|
||
while( iic_mcu_busy )
|
||
{
|
||
NOP( );
|
||
}
|
||
|
||
temp = dat;
|
||
iic_mcu_wo_dma = 1;
|
||
return ( iic_mcu_write( SLA, adrs, 1, &temp ) );
|
||
#else
|
||
// 1文字の時はDMAとか起動しないでさっさと終わらせる
|
||
iic_mcu_is_ready();
|
||
|
||
// スタートコンディションとスレーブの呼び出し...
|
||
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, void * src )
|
||
{
|
||
if( iic_mcu_is_ready() != ERR_SUCCESS )
|
||
{
|
||
return( ERR_ERR );
|
||
}
|
||
#if 0
|
||
// 使用中なら帰る
|
||
if( iic_mcu_initialized == 0 ){
|
||
return(0x80);
|
||
}
|
||
if( iic_mcu_busy != 0 ){
|
||
return( 3 );
|
||
}
|
||
#endif
|
||
|
||
// スタートコンディションとスレーブの呼び出し...
|
||
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 = &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 );
|
||
}
|
||
|
||
|
||
|
||
|
||
/* ========================================================
|
||
DMA1転送終了割り込み
|
||
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( )
|
||
{
|
||
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に設定
|
||
#ifdef _OVERCLOCK_
|
||
// todo
|
||
// SDR02 = 12 << 9; // ボーレート設定 8M/1/(x+1)/2
|
||
SDR02 = 13 << 9; // ボーレート設定 (8M/2)/1/(x+1)/2
|
||
#else
|
||
SDR02 = 5 << 9; // ボーレート設定 (8M/2)/1/(x+1)/2
|
||
#endif
|
||
|
||
SO0 = 0x0404 | TAUS_MASK; // 最初はHH
|
||
iic_mcu_busy = 0;
|
||
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_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;
|
||
}
|