2010年2月28日日曜日

活用Bパーツ:pH ガラス電極 Electrode

pH electrode 使用の pH メーター(酸苦計?)

HORIBA の pH ガラス電極を使う回路です.オペアンプ1個と-5Vジェネレータ1個.あとはすべてコンデンサと抵抗器.安定化されたDC9Vをつなぐのが前提なら,負電圧ジェネレータはなくていいかも.
ウェブ検索してみつけた pHduino や phorpduino を参考に,回路とコードをいじったものです.温度補正をかけていませんが3点校正でもほぼ線形でなんだか正しく動くようです.半固定抵抗は2段目アンプの入力オフセットのためだけです.1.00 V が -IN に入るよう調整して,二度とさわりません.A/D の読みは pH 4.01 から 9.18 で 500 カウント以上の幅がでて振り切れていない(ICL7660 の出力が -4.5 V しか出ていないので上は 850 付近が限界)のでいいんでしょう.補正はすべてソフトウェアで.
ライブラリのディレクトリに pHduino の firmware ディレクトリをコピーしておき,スケッチ pHelectrode.zip を動かした.amp2 の +In を analog1 につないでおく.
Eagle は助かりますがインターフェースがCAD風?でWindows 版と同じになっていて妙なので Macintosh で使うとあたりまえにできそうな編集操作ができないのでつらい.

オペアンプ TL074 は2個ずつアンプを使ってチャネルを2つ作れます.BNCコネクタも2つ 3.5 プラグジャックとともに Vanilla Shield にくっつけてみました.CH2は,少し簡単にして,抵抗などを差して作れることを確認しましたが使っていません.似たものであるORP 電極用にリザーブされています.

温度センサがなんだかわかりません,ネガティブ係数で抵抗値が 45 k Ohm ぐらいのもののようです.反応が pH センサ自体よりおそく,係数ぎめがめんどくさいのでまじめにやっていません.学生実験かよ.

テストのために 0.01 まで出していますが,海水水槽のための測定は pH 0.1 精度,7.8 から 8.5 の範囲でよいのです.表示は試薬の色でもLEDでだせばじゅうぶん.

2010年2月13日土曜日

活用Bパーツ:SHT-75 気温湿度 Temperature & Humidity センサ

表面実装ロボット用のパッケージに入っているので,取り出す.ピン間隔が1.25mmでそのままではブレッドボードにも Arduino のピンソケットにも刺しづらい.(1)SOIC8からDIP8への変換基盤につけたり,(2)サンハヤトのプリント基板シールのSOIC用などにつけてから小さなユニバーサル基板につけて,ピンヘッダをつけて使う.



4つのピンすべて Arduino のピンソケットに差し込んで使えるかと思ったら調子が悪かった.5V の Vcc はちゃんと与え, DATA 端子と Vcc の間にR1プル・アップ抵抗 10k Ohm をつけたところ,それらしく気温と湿度が読めだした.

ファームウェア(プログラム,スケッチ)は,Arduino Home の Playground にある SHT-11? のライブラリをそのまま,ピン番号だけ変更して.

活用Bパーツ:SCP1000-D01 気圧 barometic pressure センサ

Sparkfun の SCP1000-D01 気圧 barometic pressure センサボードの Arduino Duemilanove での使い方.
SCP1000-D01 は電源電圧 3.3V 仕様のSPI接続センサで,電源の質や入力の電圧レベルに敏感なようだ.入荷したのは rev. C とよばれるもの.

Duemilanove 328 は 5V 仕様.3.3V 出力を備えているが,入出力は落とさなければならない.
SPIはマスタ(Arduino側)出力が3本,入力が1本で方向が固定のため,そんなに難しくはない.
(1)出力の電圧を落とすには,クロックSCK,データアウトMOSI,チップセレクトCS を割り当てる13番,11番,10番にそれぞれ 10k Ohm の抵抗2本を直列でGNDの間につなぎ,その中点から約 2.5V を取り出し,SCP1000側につなぐ.
気を利かしたつもりで 5k + 10 k にして 3.3V ぐらいを取り出してつないでも,うまく動かない.
(2)MISO からの入力の電圧を上げるには,小型・低ゲート容量のパワーMOSFET を用意し,ゲートを3.3Vに,ソースをSCP1000のMISO に,ドレインを Arduino のMISOピン12番につなぎ,4.7k 〜 5.1k Ohm 程度の抵抗2本をゲート(3.3V)とソースの間,ドレインと5Vの間に入れる昇圧回を入れる.
これは I2C のレベル変換回路の転用.FET は5LN01SPでうまくいった,もう少し大きくて遅いものでも動くだろう.2SK2232 はムリではなかろうか(リードが太くてブレッドボードに刺さりにくい).
めんどくさければ,Sparkfun のロジックレベル変換ブレイクアウト・ボードを利用してください.

もしかしたら,出力は昇圧しなくても3.0Vぐらいあって Arduino は HIGH と認識できるかもしれない. 

2010年2月2日火曜日

HMC5843 3軸磁気センサ

Sparkfun から12ビットA/D内蔵のI2Cインターフェースの軸磁気センサ HMC5843 のブレイクアウトボードが発売された.ネット上で見つかる Arduino 用ライブラリコードはなんか動作があやしいので,というかメモリ節約のため? Wire ライブラリを使っていないので,結局自分で作った.
HMC5843.zip: Extract the folder and put it into library
最大 1 ミリガウスあたり 1620 カウントの分解能であるという...ほんとかいな

スケッチ HMC5843.ped
-----

#include
#include
#include

i2cLCD lcd = i2cLCD();
HMC5843 hmc = HMC5843();

void setup() {
  Wire.begin();
  lcd.begin();

  delay(50);
  hmc.begin(Gain1300);
}

int x,y,z;
void loop() {
  int tx, ty, tz;
  float cpmg;
  hmc.measured(tx, ty, tz);
  x = (tx + x)/2;
  y = (ty + y)/2;
  z = (tz + z)/2;
  cpmg = hmc.countsPerMilliGauss(Gain1300);
  lcd.setCursor(0,0);
  lcd.print((float)x/cpmg, 3);
  lcd.print(" ");
  lcd.print((float)y/cpmg, 3);
  lcd.print(" ");
  lcd.setCursor(0,1);
  lcd.print((float)z/cpmg, 3);
  lcd.print(" ");
  delay(200);
}

ATTiny2313 モニタ用 SerialSlave

シリアル端末で,RAM(レジスタ,I/Oポート)/プログラムメモリの読み書きができるモニタプログラム.ただしプログラムメモリの書き込みは,まだ「未実装.」
AVRはPCの値をレジスタのようにして読み出せないのだろうか?しょうがないので割り込み処理サブルーチンの最初にプッシュされたスタック領域から読んでいる.
gcc のレジスタ利用規則を理解するのがめんどくさそうなので,アセンブラで作ったが,関数の中のアドレスラベルなど,一切構造化できないので,久しぶりにめんどくさかった.AVRの命令体系には subi イミディエト値で減算はあるのに addi はない.
不思議なのでさがしたら,AVRフリークのBBS?で英語で
「イミディエト値なんだからマイナスにして足せばいいだろ」
「フラグの違いに注意しましょう」
「アホが!おまえの脳みそでもわかるように説明してやる」
などと盛り上がっていた.
RISCにはよくあるパターンらしい.なんでないかは,設計者に聞くしかないらしい.マクロで addi を作った.

ユーザーのプログラムに影響するからグローバルでメモリを使えないなあ,と思っていたが,SPをずらしてこそっと確保すればいいことに気がついた.

USARTにつないで機械語プログラミングの学習に使える教材まで,あと一歩?だ.プログラムメモリへの書き込みはバッファリングしないといけないので,めんどくさい.

;
;
.include "tn2313def.inc"

;.macro ldiwx
; ldi xh, high(@0)
; ldi xl, low(@0)
;.endmacro

.macro ldiaz
ldi zh, high(@0<<1)
ldi zl, low(@0<<1)
.endmacro

; Create an Add Immediate by subtracting the negative 
.macro   addi 
   subi   @0, -@1      ;subtract the negative of an immediate value 
.endmacro 


.macro reserve
sts tempByte, zl
in   zl, SPL
sts shadowSPL, zl
lds zl, tempByte
;
push zh
push zl
push r17
push r16
push r3
push r2
push r1
push r0
in r16, SREG
sts shadowSREG, r16
.endmacro

.macro restore
lds r16, shadowSREG
out SREG, r16
pop r0
pop r1
pop r2
pop r3
pop r16
pop r17
pop zl
pop zh
.endmacro

.equ     clock = 8000000          ;clock frequency
.equ     baudrate = 2*9600          ;choose a baudrate
;
.equ     ubrrval = (clock/(16*baudrate))-1

.def buf0 = r0
.def buf1 = r1
.def buf2 = r2
.def buf3 = r3


.cseg
;reset and interrupt vectors
.org 0x0000
rjmp reset
.org 0x0007
rjmp ISR_USART0_RX
; the end of interrupt vectors

.org 0x0013
reset:
ldi     r16, LOW(STACKBOTTOM)
out     SPL, r16     ;set spl
; ldi r16, (0<<<
; out DDRB, r16
init:
rcall USART_Init
sei
rjmp main


ISR_USART0_RX:
reserve
rcall assume_PC

rcall RX_hexbuf
sts   tempByte, r16 ; end char
rcall hexbuf_to_temp
;
; ldiaz HexPrefix
; rcall TX_message
lds r16, tempWord+1
rcall TX_r16
lds r16, tempWord
rcall TX_r16
ldiaz SPACECHAR
rcall TX_message

lds r16, tempByte
cpi r16, $52 ; R
breq read_progmem
cpi r16, $53 ; S
breq store_ram
load_ram:
; ldiaz Data_Prefix
; rcall TX_message
lds zl, tempWord
lds zh, tempWord+1
ld r16, Z+
rcall TX_r16
rjmp output_end

read_progmem:
; ldiaz HexPrefix
; rcall TX_message
lds zl, tempWord
lds zh, tempWord+1
lsl zl
rol zh
lpm r16, Z+
sts tempByte, r16
lpm r16, Z
rcall TX_r16
lds r16, tempByte
rcall TX_r16
rjmp output_end

store_ram:
lds zl, tempWord
lds zh, tempWord+1
rcall RX_hexbuf
rcall hexbuf_to_temp
lds r16, tempWord ; only low byte
st   Z, r16
; ldiaz Data_Prefix
; rcall TX_message
lds r16, tempWord
rcall TX_r16
rjmp output_end

output_end:
ldiaz CRLF
rcall TX_message
restore
reti

assume_PC:
clr zh
lds zl, shadowSPL
adiw zl,1
ld   r16, Z+
sts shadowPC, r16
ld   r16, Z
sts shadowPC+1, r16
ret

;
;r16_to_digit:
; cpi r16, $41
; brcs zero_nine
; andi r16, $4f
; subi r16, 7 ; after '9'
; rjmp aligned
;zero_nine:
; andi r16, $3f
;aligned:
; ret
;hex_to_digit end.




USART_Init:
ldi r17, HIGH(ubrrval)
ldi r16, LOW(ubrrval)
; Set baud rate
out UBRRH, r17
out UBRRL, r16
; Enable receiver and transmitter, receiver interrupt
ldi r16, (1<<<<
out UCSRB,r16
; Set frame format: 8data, 2stop bit
ldi r16, (1<<
out UCSRC,r16
ret

USART_Transmit:
; Wait for empty transmit buffer
sbis UCSRA,UDRE
rjmp USART_Transmit
; Put data (r16) into buffer, sends the data
out UDR,r16
ret

USART_Receive:
; Wait for data to be received
sbis UCSRA, RXC
rjmp USART_Receive
; Get and return received data from buffer
in r16, UDR
ret

USART_Flush:
sbis UCSRA, RXC
ret
in r16, UDR
rjmp USART_Flush

RX_string:
rcall USART_Receive
st   Z+, r16
subi r16, $2e
brne RX_string
ldi r16, $00
st   -Z, r16 ; place the terminal char
ret
;RX_string end.

RX_hexbuf:
ldi r16, $30
mov buf3, r16
mov buf2, r16
mov buf1, r16
mov buf0, r16
RX_hexbuf_receive:
rcall USART_Receive
cpi r16, $30
brcs RX_hexbuf_exit
cpi r16, $3a
brcs RX_hexbuf_cont
cpi r16, $41
brcs RX_hexbuf_exit
cpi r16, $47
brcs RX_hexbuf_cont
cpi r16, $61
brcs RX_hexbuf_exit
cpi r16, $67
brcs RX_hexbuf_cont
breq RX_hexbuf_exit
RX_hexbuf_cont:
mov   buf3, buf2
mov   buf2, buf1
mov   buf1, buf0
mov   buf0, r16
rjmp RX_hexbuf_receive
RX_hexbuf_exit:
ret
;RX_qbytes end.

hexbuf_to_temp:
mov r16, buf3
rcall hex_to_nibble
mov r17, r16
swap r17
mov r16, buf2
rcall hex_to_nibble
or   r17, r16
sts tempWord+1, r17
mov r16, buf1
rcall hex_to_nibble
mov r17, r16
swap r17
mov r16, buf0
rcall hex_to_nibble
or   r16, r17
sts tempWord, r16
ret

;
hex_to_nibble:
subi r16, $30
cpi r16, $10
brcs hex_to_nibble_exit
andi r16, $17
subi r16, 7
hex_to_nibble_exit:
ret
;hex_to_nibble end.

nibble_to_hexdec:
andi r16, $0f
cpi r16, 10
brcs PC+2
subi r16, -$7  ; addi r16, $7
;nibble_to_hexdec_2:
subi r16, -$30 ; addi r16, $30
ret

;TX_string:
; ld   r16, X+
; cpi r16, $00
; breq TX_string_done
; rcall USART_Transmit
; rjmp TX_string
;TX_string_done:
; ret
;TX_string end.

TX_r16:
push r16
swap r16
rcall nibble_to_hexdec  ;ntohex
rcall USART_Transmit
pop r16
rcall nibble_to_hexdec
rcall USART_Transmit
ret
;TX_bytehex end.


;ntohex:
; andi r16, $0f
; ldiaz Hex_Table
; add zl, r16
; clr r16
; adc zh, r16
; lpm r16, Z
; ret

TX_message:
lpm   r16, Z+
cpi r16, $00
brne PC+2
ret
rcall USART_Transmit
rjmp TX_message
;TX_message end.


;Hex_Table:
;.db "0123456789abcdef"
;
;Hex_Suffix:
;.db "H",0

Data_Prefix:
.db "$",0
HexPrefix:
.db "0x",0, 0

SPACECHAR:
.db " ",0
CRLF:
.db $0d, $0a, 0, 0


.org 0x200
main:
ldi r16, 0xff
out DDRB, r16
clr r16
loop:
out PORTB, r16
rcall wait
inc r16
rjmp loop

wait:
ldi r18, 10
wait_loop:
rcall wait01
dec r18
brne wait_loop
ret

wait01:
ldi r19, 100
wait01_loop:
rcall wait1m
dec r19
brne wait01_loop
ret

wait1m:
ldi r20, 250
wait1m_loop:
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
dec r20
brne wait1m_loop
ret


.dseg
.org 0x00d8
STACKBOTTOM:

.org 0x00d9
workspace:
tempByte: .byte 1
tempWord: .byte 2

shadowPC: .byte 2
shadowSREG: .byte 1
shadowSPL: .byte 1