ATtiny13A用のI2C通信部コードをAVRアセンブラの勉強がてら
アセンブラに書き直してました(まだ汚いコードですが..)
動作はこの前のC言語バージョンとほぼ同じです(たぶん)。
前回、ATtiny13Aで作成した時計のスケッチのサイズは1000バイトだったのですが、
I2C接続部をアセンブラすることで884バイト(116バイトのダイエット)になりました。
初めてのAVRアセンブラですが、奇跡的にそれなりに動いているようです。
これなら、時計の時刻設定機能も追加できるかも。取りあえずソースを公開します。
ダウンロード tinyI2C.zip (2.6K)
(関数名は C言語バージョンの各関数名の頭にtinyをつけています)
解凍後、スケッチ保存用のディレクトリの\librariesに入れて利用できます。


アセンブラの学習で参考にしたサイト
・えるむ by ChaN - アセンブラ関数の書き方(avr-gcc)
・AVR Wiki - avrgccでアセンブラを使う
・弘前大学教育学部 AVRマイコンで学ぶ コンピュータの仕組み
・AVR.jp tiny13Aのデータシート、AVR命令セット、AVRGCCでのアセンブリとCの混合など
(直リンク禁止のためトップページのみリンクを貼ります)
アセンブラの開発環境はarduino IDEを使っています。
ライブラリ登録した(libraries)ソースはアセンブラ(拡張子.S)もコンパイルしてくれるので
テキストエディタでコードを作成すればOKなわけです。
ヘッダーファイル
#ifndef _TINYI2C_H_
#define _TINYI2C_H_
#include <avr/io.h>
#define T100_US ((F_CPU/1000000)*100/10)
#define T200_US ((F_CPU/1000000)*200/10)
#define T1_MS ((F_CPU/1000000)*1000/10)
#define T5_MS ((F_CPU/1000000)*5000/10)
#define T10_MS ((F_CPU/1000000)*10000/10)
#define T15_MS ((F_CPU/1000000)*15000/10)
#define T20_MS ((F_CPU/1000000)*20000/10)
extern "C" {
int16_t sum(int16_t, int16_t);
void delay_10cyc(uint16_t);
void blink(unsigned char);
void tinyI2C_DATA_HI(void);
void tinyI2C_DATA_LO(void);
void tinyI2C_CLOCK_HI(void);
void tinyI2C_CLOCK_LO(void);
void tinyI2C_WriteBit(unsigned char);
void tinyI2C_Stop(void);
void tinyI2C_Start(void);
void tinyI2C_Init(void);
void tinyI2C_Write(unsigned char c);
unsigned char tinyI2C_ReadBit(void);
unsigned char tinyI2C_Read(unsigned char ack);
}
#define delay_100us() delay_10cyc(T100_US)
#define delay_200us() delay_10cyc(T200_US)
#define delay_1ms() delay_10cyc(T1_MS)
#define delay_5ms() delay_10cyc(T5_MS)
#define delay_10ms() delay_10cyc(T10_MS)
#define delay_15ms() delay_10cyc(T15_MS)
#define delay_20ms() delay_10cyc(T20_MS)
#endif
ライブラリソース
#include <avr/io.h>
;delay用設定
#if F_CPU == 9600000UL
#define T100_US 96
#define T200_US 192
#elif F_CPU == 8000000UL
#define T100_US 80
#define T200_US 160
#elif F_CPU ==16000000UL
#define T100_US 160
#define T200_US 320
#endif
;-------------------------------------------------------;
;アセンブラ関数 void delay_10cyc(uint16_t t)
; 10サイクル x tの時間待ちを行う
; 引数 t(r25:r24)
; 本関数はr24,r25の変更を行う
; 0でも10サイクル時間待ち
; 実際の呼び出しでは rcall+ret-1 =5サイクル加算される
; 呼び出し側で引数で1引くこと
;--------------------------------------------------------;
.global delay_10cyc
.func delay_10cyc
counter = 24
delay_10cyc:
loop: ; 10サイクルのループ
nop
nop
nop
nop
nop
nop
sbiw counter,1 ; 1づつ減算
brne loop ; 0でループ抜け
ret
.endfunc
;-------------------------------------------------------;
;アセンブラ関数 void _delay_100us(void)
; 100usの時間待ちを行う
; 本関数はr24,r25の変更を行う
; 呼び出し時のサイクル数の補正済み(20サイクル)
;-------------------------------------------------------;
.global delay_100us
.func delay_100us
delay_100us:
ldi r25, 0 ; delay_100us
ldi r24, T100_US-2
rcall delay_10cyc
ret
.endfunc
;-------------------------------------------------------------------;
;I/OポートのON/OFF
;void blink(uint8_t led_val)
;Lチカ 動作確認用
;-------------------------------------------------------------------;
;
#ifdef test_port
#define LED 5
#define DDR_LED (DDRB-0x20)
#define PORT_LED (PORTB-0x20)
led_val = 24
.global blink
.func blink
blink:
cpi led_val,0
brne reset_hi
sbi DDR_LED, LED
cbi PORT_LED,LED
ret
reset_hi:
sbi DDR_LED, LED
sbi PORT_LED,LED
ret
.endfunc
#endif
;-------------------------------------------------------------------;
; tinyI2Cライブラリ
;-------------------------------------------------------------------;
;
#define I2C_DDR (DDRB-0x20)
#define I2C_PIN (PINB-0x20)
#define I2C_PORT (PORTB-0x20)
#define I2C_CLK 3
#define I2C_DAT 4
;-------------------------------------------------------------------;
; 関数 void I2C_DATA_HI(void)
;-------------------------------------------------------------------;
;
.global tinyI2C_DATA_HI
.func tinyI2C_DATA_HI
tinyI2C_DATA_HI:
cbi I2C_DDR ,I2C_DAT
sbi I2C_PORT,I2C_DAT
ret
.endfunc
;
;-------------------------------------------------------------------;
; 関数 void I2C_DATA_LO(void)
;-------------------------------------------------------------------;
;
.global tinyI2C_DATA_LO
.func tinyI2C_DATA_LO
tinyI2C_DATA_LO:
sbi I2C_DDR ,I2C_DAT
cbi I2C_PORT,I2C_DAT
ret
.endfunc
;-------------------------------------------------------------------;
; 関数 void I2C_CLOCK_HI(void)
;-------------------------------------------------------------------;
;
.global tinyI2C_CLOCK_HI
.func tinyI2C_CLOCK_HI
tinyI2C_CLOCK_HI:
cbi I2C_DDR ,I2C_CLK
sbi I2C_PORT,I2C_CLK
ret
.endfunc
;-------------------------------------------------------------------;
; 関数 void I2C_CLOCK_LO(void)
;-------------------------------------------------------------------;
;
.global tinyI2C_CLOCK_LO
.func tinyI2C_CLOCK_LO
tinyI2C_CLOCK_LO:
sbi I2C_DDR ,I2C_CLK
cbi I2C_PORT,I2C_CLK
ret
.endfunc
;-------------------------------------------------------------------;
; 関数 void tinyI2C_WriteBit(uint8_t c)
; 引数 r24: 書き込みデータ
; r25:r23 内容破壊される
;-------------------------------------------------------------------;
.global tinyI2C_WriteBit
.func tinyI2C_WriteBit
value_c = 24
tinyI2C_WriteBit:
cpi value_c, 0
breq IC2_DATA_LO ; 0なら0出力へ
IC2_DATA_HI:
rcall tinyI2C_DATA_HI ; I2C_DATA_HI()
rjmp STEP2
IC2_DATA_LO:
rcall tinyI2C_DATA_LO ; I2C_DATA_LO()
STEP2:
rcall tinyI2C_CLOCK_HI
L1:
sbis I2C_PIN,I2C_CLK ; I2C_CLK=1待ち
rjmp L1
mov r23, value_c
rcall delay_100us ; delay_100us
rcall tinyI2C_CLOCK_LO
rcall delay_100us ; delay_100us
cpi r23, 0
breq STEP3
rcall tinyI2C_DATA_LO
STEP3:
rcall delay_100us ; delay_100us
ret
.endfunc
;-------------------------------------------------------------------;
; 関数 void tinyI2C_Stop(void)
; r25:r24 内容破壊される
;
;-------------------------------------------------------------------;
.global tinyI2C_Stop
.func tinyI2C_Stop
tinyI2C_Stop:
rcall tinyI2C_CLOCK_HI
rcall delay_100us ; delay_100us
rcall tinyI2C_DATA_HI
rcall delay_100us ; delay_100us
ret
.endfunc
;-------------------------------------------------------------------;
; 関数 void tinyI2C_Start(void)
; r25:r24 内容破壊される
;-------------------------------------------------------------------;
.global tinyI2C_Start
.func tinyI2C_Start
tinyI2C_Start:
in r25,I2C_DDR
andi r25, ~((1 << I2C_DAT) | (1 << I2C_CLK));
out I2C_DDR,r25
rcall delay_100us ; delay_100us
rcall tinyI2C_DATA_LO
rcall delay_100us ; delay_100us
rcall tinyI2C_CLOCK_LO
ret
.endfunc
;-------------------------------------------------------------------;
; 関数 void tinyI2C_Init(void)
; r25 内容破壊される
;-------------------------------------------------------------------;
.global tinyI2C_Init
.func tinyI2C_Init
tinyI2C_Init:
in r25, I2C_PORT
andi r25,~((1 << I2C_DAT) | (1 << I2C_CLK));
rcall tinyI2C_CLOCK_HI
rcall tinyI2C_DATA_HI
ret
.endfunc
;-------------------------------------------------------------------;
; 関数 void tinyI2C_Write(uint8_t data)
; 引数: r24 :data
; r25:r21 内容破壊される
;-------------------------------------------------------------------;
.global tinyI2C_Write
.func tinyI2C_Write
data = 22
count= 21
tinyI2C_Write:
mov data, r24
ldi count, 8
LOOP: ;8ビット書き込みループ
lsl data
ldi r24,0
rol r24
rcall tinyI2C_WriteBit
dec count
brne LOOP
ret
.endfunc
;-------------------------------------------------------------------;
; 関数 uint8_t tinyI2C_ReadBit(void)
;
; 戻り値 r24: 読み取りビット
; r25,r23:r22 内容破壊される
;-------------------------------------------------------------------;
.global tinyI2C_ReadBit
.func tinyI2C_ReadBit
tinyI2C_ReadBit:
rcall tinyI2C_DATA_HI
rcall tinyI2C_CLOCK_HI
LOOP1:
sbis I2C_PIN,I2C_CLK ; I2C_CLKのHI待ち
rjmp LOOP1
rcall delay_100us ; delay_100us
in r22,I2C_PIN
rcall tinyI2C_CLOCK_LO
rcall delay_100us ; delay_100us
bst r22,I2C_DAT ;r22のI2C_DATビットをr24の0ビットに設定
ldi r24,0
bld r24,0
ret
.endfunc
;------------------------------------------------------------------;
; 関数 unsigned char tinyI2C_Read(unsigned char ack)
;
; 引数 r24: ack
; 戻り値 r24: 取得データ
; r25:r19 内容破壊される
;------------------------------------------------------------------;
value_ack = 21 ;引数用
value_res = 19 ;戻り値用
count = 20 ;ループ用
.global tinyI2C_Read
.func tinyI2C_Read
tinyI2C_Read:
mov value_ack, r24 ;
ldi count,8
ldi value_res,0
LOOP4: ;8ビット分データ取得ループ
rcall tinyI2C_ReadBit ;r24に読み込みビットを返す
lsl value_res ;左シフト
add value_res,r24 ;読んだビットを挿入
dec count
brne LOOP4
STEP4: ;value_ack が1ならtinyI2C_WriteBit(0)
cp value_ack,0 ; 0ならtinyI2C_WriteBit(1)
breq STEP5
ldi r24,0
rjmp STEP6
STEP5:
ldi r24,1
STEP6:
rcall tinyI2C_WriteBit ; ackの送信
mov r24, value_res
ret
.endfunc
;--------------------------------------------------------------------;
最近のコメント