フォト
2025年4月
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      
無料ブログはココログ

« Arduino IDE 1.0.6 では行番号が表示できりようになった | トップページ | 秋月電子の「お楽しみ袋」を買ってみた »

2014年11月18日 (火)

ATtiny13AでI2C接続キャラクタLCDを利用する(3)

ATtiny13A用のI2C通信部コードをAVRアセンブラの勉強がてら
アセンブラに書き直してました(まだ汚いコードですが..)
動作はこの前のC言語バージョンとほぼ同じです(たぶん)。

前回、ATtiny13Aで作成した時計のスケッチのサイズは1000バイトだったのですが、
I2C接続部をアセンブラすることで884バイト(116バイトのダイエット)になりました。
初めてのAVRアセンブラですが、奇跡的にそれなりに動いているようです。

これなら、時計の時刻設定機能も追加できるかも。取りあえずソースを公開します。

ダウンロード tinyI2C.zip (2.6K)
(関数名は C言語バージョンの各関数名の頭にtinyをつけています)
解凍後、スケッチ保存用のディレクトリの\librariesに入れて利用できます。

Dscn3244

01

アセンブラの学習で参考にしたサイト
えるむ 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

;--------------------------------------------------------------------;

« Arduino IDE 1.0.6 では行番号が表示できりようになった | トップページ | 秋月電子の「お楽しみ袋」を買ってみた »

arduino」カテゴリの記事

AVR」カテゴリの記事

ATtiny13A」カテゴリの記事

表示器制御関連」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック


この記事へのトラックバック一覧です: ATtiny13AでI2C接続キャラクタLCDを利用する(3):

« Arduino IDE 1.0.6 では行番号が表示できりようになった | トップページ | 秋月電子の「お楽しみ袋」を買ってみた »