熱帯魚水槽の温度ロガー回路の製作
→
改善版 補正値を不揮発メモリに保存する機能を追加したバージョン を作りました!
今年の夏は(2010)とても暑いです.私は金魚とグッピーとミナミヌマエビを飼っているんですが、放置すると部屋の温度が37度にもなる日がありまして、これはすでにお風呂の温度なので、熱帯魚のグッピーといえどもさすがにのぼせて死んでしまうんじゃないかと心配です.
今年は、水槽のフタをくりぬいて、12cmのCPUファンを一日中まわして対処しました.これで、たぶん30度を超えることはなくなった気がします.
といっても「気がします」だけで、昼のうだるような暑さの時間帯にいったい水槽は何度になっているかはわからないので、水槽の温度ロガーを製作しました.
目標仕様
●金魚水槽+グッピー水槽+部屋の温度を同時に測る
●USBインターフェース
●PC側のアプリはターミナルソフトだけでよい
●温度はテキストファイルでHDDに保存される
●測定インターバルは、最短1秒、最長1分程度までターミナルソフトから設定できること
●温度センサの誤差を、ターミナルソフトから補正できること
回路概要
●回路図はこちら
(別ウインドウ)
●温度センサには、秋月電子で入手できる、LM61CZを3ヶつかいます
(4ヶで\200) (pdfはこちら)
●LM61CIZの外形は、フツーのトランジスタサイズです
(下写真)
●接続は適当な電源へ接続するだけ (下写真)
●出力電圧が温度によって動くわけですが、10mV/度です.25度で850mV
です
(下表)
●3つのLM61CIZを同時に温度測定したところ、あまり精度はよくありません.1度以上ばらついていましたので、なにも補正せずに水温計としてつかうのはムリでした.ソフトウエアで補正する必要があります.
●ADCのフルスケールは、VDDで決まります.一方USBの5V電源は、ホストPCによってばらつくと思われます.すると、せっかく補正したとしても、ホストPCを変えると温度測定値がズレてしまう恐れがあります.そこで、STM8Mの電源は、レギュレータの3.3Vにします.レギュレータは、FT232RLのレギュレータをつかいます.
●STM8Sの内蔵UARTのTX/RXを、FT232RLでUSBに変換し、ホストPCに接続します.
ソフト概要
visual studio
2008 のプロジェクトフォルダは、こちらをダウンロード (zip) してください.
●ST Visual
Development(STVD)のprojectフォルダをアーカイブした(lzh)ファイルをここにおきます.
●3つのLM61CIZを、STM8Sの、ポート
PB2,PB6,PB7 に接続します.ADCのポートでいうと、AIN2,AIN6,AIN7
に接続することになります.
●ADCは、自動的に10個の入力ポートを切り替えてAD変換する、scan
modeで動かします.
●ADCは、0.1秒後との割り込みでAD変換します.
●表示インターバルが5秒ごとであれば、50回のAD変換データを平均した値を温度として表示します.
●UARTは、STM8Sのソフトウエア実装例からパクったcodeを流用します.
●UARTの接続緒元は、
115200bps 7bit parity1bit フロー制御ナシ
●STM8S内蔵タイマは、
TIM2--->ADC0.1Sec割り込み用
TIM3--->UART割り込み用
●ターミナルソフトから設定できるコマンドは、
i 5 enter ---> 表示interval
5秒ごと
t 20 21 22 enter
---> 表示時刻のスタート時刻を、20日21時22分0秒
に設定する
v
enter ---> AD変換データを毎回表示する (もう一度 v enter
と入力すると表示しなくなる)
h2 -0.5
enter --->
AIN2の温度表示を-0.5する補正をかける
h6
0.2 enter --->
AIN6の温度表示を+0.2する補正をかける
h7
0 enter --->
AIN7の温度表示に補正をかけない
●ターミナルソフトの画面表示はこうなります
日 時 分
秒 温度
温度 温度
DHMS 000 00 00 34 TEMP(AIN2,AIN6,AIN7) 24.13 24.13 28.27
DHMS
000 00 00 37 TEMP(AIN2,AIN6,AIN7) 24.13 24.12 28.30
DHMS 000 00 00
40 TEMP(AIN2,AIN6,AIN7) 24.18 24.09 28.40
DHMS 000 00 00 43
TEMP(AIN2,AIN6,AIN7) 24.10 24.08 28.28
DHMS 000 00 00 46
TEMP(AIN2,AIN6,AIN7) 24.16 24.14 28.28
DHMS 000 00 00 49
TEMP(AIN2,AIN6,AIN7) 24.10 24.08
28.24
●このような表示結果をlogに落としたいときは、ターミナルソフトのlog機能をつかってHDDに記録します
●デフォルトで、インターバル1秒、補正ナシ、日付0で温度測定を開始します.なので、logを記録開始するまえに、次のようなコマンドで設定する必要があります.
t 20 21
22 -->
測定開始日時の設定
i 5 --> インターバル5秒
h2
-0.5 --> AIN2補正-0.5度
h6
0.2 -->
AIN6補正+0.2度
h7
0 -->
AIN7補正ナシ
●温度推移をグラフに描きたいなら、excelでlogを加工して描けばよいでしょう
実物の写真
STM8S
discovery
にLM61CIZをひとつじか付けし、2つのLM61CIZを1mぐらい配線で延長して取り付けました.
FT232RLは、出来合いの基板が手元にあったので、それを両面接着剤で取り付けました.写真の左上の子基板がそれです.
1mぐらい延長したLM61CIZは、センサの足がむき出しだと、電解質的な水槽の水に触れて誤動作すると予想されるので、シリコン接着剤でコーティングしました.このコーティングは、温度センサの誤動作防止だけでなく、金魚さんとエビさんの保護のためにも重要とおもわれます.特にエビさんは、銅が水中にあるとそれだけで死んでしまうほど毒性に弱い生き物なんです.
また、シリコン接着剤は、ホルムアルデヒドのような有害物質を含まない製品を使う必要があります.さもないとエビさんが死んでしまいます.キッチン用途のシリコン接着剤などは、防カビ材配合と表記されています.そういうのはエビさんにとって猛毒なんです.私が使ったシリコン接着剤は、この写真の製品です.#55388が製品番号のようです.
ソースコード説明
main.c のキモのところを説明
#include
"stdlib.h"
温度計算を浮動小数点計算するので linkerの設定は こちらのB に留意してください
#include
"hyperterminal.h"
UARTを動かすサブルーチンが入っています
●CLK_ClockSwitchConfig ( CLK_SWITCHMODE_AUTO,
CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE
);
外部16MHzを使うように切り替えます.
●UARTの割り込みはTIM3をつかいます
TIM3_DeInit();
TIM3_TimeBaseInit(TIM3_PRESCALER_1,0xFFFF);
TIM3_ITConfig(TIM3_IT_UPDATE,
ENABLE);
●ADCの割り込みはTIM2を使います.0.1秒ごとに割り込みます.源発信が16MHzで、プリスケーラで512分周し、さらにタイマカウンタで3125分周すると10Hzになるというからくりです.
TIM2_DeInit();
TIM2_TimeBaseInit(TIM2_PRESCALER_512,3125-1);
// 16000000/512/3125=10Hz
TIM2_ITConfig(TIM2_IT_UPDATE,
ENABLE);
TIM2_Cmd(ENABLE);
●GPIOの出力を定義
割り込みモニタ程度のインジケータなのでさほど重要ではないです
GPIO_Init(GPIOD, 0b00000001,
GPIO_MODE_OUT_PP_HIGH_FAST); // LED
GPIO_Init(GPIOE, 0b00001000,
GPIO_MODE_OUT_PP_LOW_FAST); // int monitor
●ADCをscan
modeに設定します.データシートを読んでもそうかかれているとは思えなかったんですが、CONTINUOUSで、かつCH[3:0]=15に設定しないとscan
modeがうまく動かなかったので、理由もわからずにこうしました.まぁ、おまじないと思ってつかうしかないだろう.
ADC1_DeInit();
ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, 15,
ADC1_PRESSEL_FCPU_D2, ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_LEFT,
ADC1_SCHMITTTRIG_CHANNEL7,
DISABLE);
ADC1_DataBufferCmd(ENABLE);
ADC1_ScanModeCmd(ENABLE);
●UARTの設定 11520bps
7bit parity1bit
フロー制御ナシ
UART2_DeInit();
UART2_Init((u32)115200,
UART2_WORDLENGTH_8D, UART2_STOPBITS_1, UART2_PARITY_ODD,
UART2_SYNCMODE_CLOCK_DISABLE,
UART2_MODE_TXRX_ENABLE);
●enableInterrupts();
割り込み許可
●メインループ
while (1) {
UART_Menu(); }
ホストPCからターミナルソフト経由でコマンドを受け付けるルーチン
ADC割り込みルーチンのキモのところを説明
●@far @interrupt void
Tim2Update_isr(void)
0.1秒ごとに割り込み処理するルーチン
{
● TIM2_ClearFlag(TIM2_FLAG_UPDATE);
次回の割り込み準備のためTIM2割りl込みフラグをクリアするおまじない
●if(cnt>(INTERVAL*10)){
たとえばintervalが5秒だったら、割り込み50回ごとに表示する処理
now_sec =
(u8)((int_cnt/10)
% 60); 現在秒
now_min =
(u8)((int_cnt/600) %
60); 現在分
now_hour=
(u8)((int_cnt/36000) % 24); 現在時刻
now_date=
(u8)((int_cnt/864000));
現在日付
●AD変換データを、温度に換算する式
mV =
(float)adc_data_sum2/(float)INTERVAL/10/65535*3333; temp2 = ( mV - 850 ) /
10 + 25 + HOSEI2;
mV =
(float)adc_data_sum6/(float)INTERVAL/10/65535*3333; temp6 = ( mV - 850 ) /
10 + 25 + HOSEI6;
mV =
(float)adc_data_sum7/(float)INTERVAL/10/65535*3333; temp7 = ( mV - 850 ) /
10 + 25 + HOSEI7;
●SerialPutString(a);
ターミナルソフトへ、1行の文字列を送信する関数
●ADC1_StartConversion();
scan
modeでAD変換せよというおまじない
●各温度センサのAD変換データを、積算する
(あとで積算回数で割る)
adc_data2=ADC1_GetBufferValue(2);
adc_data_sum2+=adc_data2;
adc_data6=ADC1_GetBufferValue(6);
adc_data_sum6+=adc_data6;
adc_data7=ADC1_GetBufferValue(7);
adc_data_sum7+=adc_data7;
●割り込みインジケータLEDを光らせる
GPIO_WriteReverse(GPIOD,GPIO_PIN_0); //
LED
UART処理ルーチンのキモのところを説明
STMicronのUART実装例をパクったので、詳細は知らないんです.
なので、キモのところではありませんが、ターミナルソフトのコマンドメニュー画面を説明します.
●void
UART_Menu(void)
ホストPCとターミナルソフトを通じて通信するルーチン
●GetInputString(choice);
ホストPCからコマンドを1行受診する関数
●fieldnumber =
separate_line(choice);
コマンドを分離する関数
●コマンドを解釈し、変数にパラメータを格納する
if(
fieldnumber==1 && Field[0][0]=='v'){ (VERBOSE=='v') ? (VERBOSE='s') :
(VERBOSE='v'); goto NEXT;}
else if(fieldnumber==2
&& Field[0][0]=='i'){ INTERVAL = (int)atof(Field[1]); goto
NEXT;}
else if(fieldnumber==4 &&
Field[0][0]=='t'){
DATE =
(int)atof(Field[1]);
HOUR =
(int)atof(Field[2]);
MIN =
(int)atof(Field[3]);
SEC =
0;
int_cnt = (u32)DATE*24*60*60*10 +
(u32)HOUR*60*60*10 + (u32)MIN*60*10 +
(u32)SEC*10;
goto NEXT;
}
else if(fieldnumber==2 && Field[0][0]=='h'
&& Field[0][2]==0){
if(Field[0][1]=='2') HOSEI2 =
atof(Field[1]);
補正値AIN2
if(Field[0][1]=='6') HOSEI6 =
atof(Field[1]);
補正値AIN6
if(Field[0][1]=='7') HOSEI7 =
atof(Field[1]);
補正値AIN7
}
NEXT: ;
hyperterminal.c stm8s_it.c
STMicroのUARTサンプルプログラムからパクりましたので、内容知らない.
stm8_interrupt_vector.c
●extern @far
@interrupt void Tim2Update_isr(void);
main.cで定義した関数をexternしておきます.
●{0x82, Tim2Update_isr}, /* irq13
*/
タイマ2の割り込み信号はIRQ13に接続されていますので、割り込みルーチンを、IRQ13にヒモ付けしておきます.
●{0x82,
(interrupt_handler_t)TIM3_UPD_OVF_BRK_IRQHandler }, /* irq15
*/
タイマ3の割り込みはIRQ15に接続されていますので、このようにヒモづけしておきます
stm8s.h
/* #define STM8S208
*/
/* #define STM8S207 */
#define
STM8S105
これのコメントを外す
/* #define STM8S103 */
/* #define STM8S903
*/
stm8s_conf.h
#define _ADC1 (1)
#define _CLK
(1)
FWライブラリをつかうペリフェラルのコメントを外す
#define _GPIO (1)
#define _TIM2 (1)
#define
_TIM3
(1)
それぞれに対応する、.cと.hを、FWライブラリフォルダから、コピーして、projectに追加してください.