2011/09/26
■ 筐体温度を測る
そもそもこのCPUの発熱は小さいが、サーバー用途ならシステム温度のトレンドを見たいので、コンソール用のシリアルポート経由でマイコンを用いて筐体の温度を測ることにしよう。
マイコンには今までPICマイコンを愛用していたが、今回はルネサスR8C/M12Aを使った(R5F2M122ANDD)。秋月で100円で売っているこれはプログラムメモリ2KB品なので注意。温度測定は抵抗とサーミスタの分圧をマイコンのA/Dで読み、近似式で温度変換する。サーミスタは石塚電子製103AT。回路図
プログラム開発はHEW+R8C/M16C C/C++コンパイラ+E8aデバッガ
E8aでデバッグ中。
稼動中の様子。サーミスタは本体にキッチンアルミテープで貼り付けている。
■ R8Cのソースコード
DreamPlugよりシリアルポートから文字列「#t_e_m_p.」をマイコンへ送信すると、マイコンからセ氏温度を10倍した値を返す。A/D値→温度変換は3次の近似式。驚くかな、floatが使える。コードサイズぎりぎり。
#include <string.h> #include "sfr_r8m12a.h" // *** 定義 typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned short DWORD; typedef unsigned char BOOL; #define BUFFERSIZE 8 // UART受信バッファ数 WORD timer1; // 100Hzタイマーカウンター1 WORD timer2; // 100Hzタイマーカウンター2 const char *magic="#t_e_m_p."; // 合致文字列 BYTE magic_len; // 合致文字列長 char rx_buff[BUFFERSIZE]; // UART受信バッファ BYTE rx_len; // UART受信バッファ長 WORD ntc(WORD x); // NTCサーミスタによる温度変換 void bin2dec(char *s, WORD x); // bin → dec変換 WORD adc(void); // A/D変換 char getc(void); // 受信バッファから1文字取り出し void putc(char c); // 1文字送信 void puts(char *s); // 1行送信 void main(void) { WORD ad; // A/D値 // *** ポート初期化 pd1_7=1; // P1_7 出力 p14sel0=1; // P1_4 TXD p14sel1=0; p15sel0=1; // P1_5 RXD p15sel1=0; // *** クロック設定 prc0=1; // プロテクトOFF locodis=0; // 低速オンチップオシレータ発振開始 hocoe=1; // 高速オンチップオシレータ発振開始 asm("nop"); // 発信待ち(適当) asm("nop"); asm("nop"); asm("nop"); asm("nop"); hscksel=1; // オンチップオシレータ選択 phisel=0x00; // 分周なし frv1=fr18s0; // φ=18.432MHz補正レジスタ frv2=fr18s1; scksel=1; // 高速クロック選択 prc0=0; // プロテクトON // *** パワーコントロール msttrb=0; // RBアクティブ (0でアクティブとは...) mstuart=0; // UARTアクティブ mstad=0; // A/Dアクティブ // *** RB2タイマーの設定 tstop_trbcr=1; // RB2強制停止 tstop_trbcr=0; // RB2動作 tstart_trbcr=0; // タイマー停止 trbmr=0b01010000; // count source supply, count source = f32, timer mode trbpr=36-1; // 18.432MHz/32/36/160=100Hz trbpre=160-1; trbir=0b10000000; // RB2割り込み許可 ilvlc0=1; // 割り込みレベル1 ilvlc1=0; tstart_trbcr=1; // タイマースタート // *** A/D設定 admod=0; // A/D clk=f8 単発モード adinsel=0; // AN0/グループ0 // *** UART設定 u0mr=0b00000101; // 8n1 u0c0=0b00010000; // LSBファースト,CMOS出力,デジタルフィルタON,カウントソース=f1 u0brg=9; // 115200bps u0rrm_u0c1=0; // 連続受信モード禁止 u0tie=0; // 送信割り込み禁止 u0rie=1; // 受信割り込み許可 ilvl90=0; // 割り込みレベル2 ilvl91=1; te_u0c1=1; // 送信許可 re_u0c1=1; // 受信許可 // *** 割り込み許可→メインループ asm("LDIPL #0"); // 割り込み優先レベル0(この値よりも高いレベルの割り込みが受け付けられる) asm("nop"); asm("FSET I"); // 割り込み開始 magic_len=strlen(magic); // 合致文字列長 ad=adc(); // A/D値を求める timer1=timer2=0; for (;;) { char c, s[16]; if ( timer1 >= 2 ) { // A/D開始 ad=(adc()+ad+1)/2; // 移動平均 timer1=0; } c=getc(); if ( c ) { // 受信文字あり int n; for ( n=0; n < magic_len-1; n++ ) s[n]=s[n+1]; // 1文字シフト s[magic_len-1]=c; s[magic_len]=0; if ( !strcmp(magic,s) ) { // 合致文字列? bin2dec(s,ntc(ad)); // A/D値出力 puts(s); bzero(s,sizeof(s)); // 文字列消去 } timer2=0; } if ( timer2 >= 50 ) { // 0.5秒経過で文字列消去 bzero(s,sizeof(s)); timer2=0; p1_7=1; // LED消灯 } } } WORD ntc(WORD x) // NTCサーミスタによる温度変換 戻り値 ℃*10 { float a=x; // y = -4.614E-07x3 + 6.409E-04x2 - 3.860E-01x + 9.769E+01 return ((-4.614e-6*a+6.409e-3)*a-3.860)*a+9.769e+2; // 10倍にする } void bin2dec(char *s, WORD x) // bin → dec変換 { s[0]=(x/100)%10+'0'; s[1]=(x/10)%10+'0'; s[2]=x%10+'0'; s[3]=0; } WORD adc() // A/D変換 { adst=1; // 変換開始 while ( !adf ); // 変換待ち return ad0; } char getc() // 受信バッファから1文字取り出し 戻り値 文字があれば文字, 文字がなければ 0 { static BYTE ptr; // バッファポインタ if ( rx_len ) { // 受信データあり BYTE c; c=rx_buff[ptr&(BUFFERSIZE-1)]; ptr++; rx_len--; return c; } else return 0; } void putc(char c) // 1文字送信 { while ( !ti_u0c1 ); // 送信バッファ空? u0tbh=0; u0tbl=c; } void puts(char *s) // 1行送信 { while ( *s ) { putc(*s++); } putc('\r'); putc('\n'); } #pragma interrupt _uart0_receive(vect=18) void _uart0_receive(void) // UART受信割り込み { static BYTE ptr; // バッファポインタ BYTE t; WORD tmp; t=u0rif; u0rif=0; // 割り込みクリア tmp=u0rb; if ( tmp & 0xf000 ) { // 受信エラー復帰 te_u0c1=0; // 送信停止 re_u0c1=0; // 受信停止 u0mr=0; // SIO無効 u0mr=0b00000101; // 8n1 te_u0c1=1; // 送信許可 re_u0c1=1; // 受信許可 } else { // 受信OK if ( rx_len < 8 ) { rx_buff[ptr&(BUFFERSIZE-1)]=tmp & 0xff; ptr++; rx_len++; } p1_7=0; // LED点灯 } } #pragma INTERRUPT _timer_rb2(vect=24) void _timer_rb2(void) // RB2割り込み { BYTE t; t=trbif_trbir; trbif_trbir=0; // 割り込みクリア timer1++; timer2++; }
■ DreamPlug側のプログラム(perl版)
/etc/inittabのT0:23:respawn:/sbin/getty 115200 ttyS0の行をコメントアウトし、libdevice-serialport-perlをインストールして、perlでシリアルポートを使えるようにする。
#!/usr/bin/perl use Device::SerialPort; # 温度読み込み my $port=new Device::SerialPort('/dev/ttyS0') || die; $port->user_msg(1); $port->user_msg(1); $port->error_msg(1); $port->baudrate(115200); $port->databits(8); $port->parity("none"); $port->stopbits(1); $port->handshake("none"); $port->read_const_time(50); $port->read_char_time(0); my $magic='#t_e_m_p.'; # 合致文字列 $port->write($magic); my ($len,$sys)=$port->read(8); $port->close; ($len >= 4) || die; # 数字3文字+\n $sys =~ /(\d{3})/; $sys=$1/10; print "sys=$sys\n";
2011/09/09
■ ファイルシステムをext2にする
SDの書き込み速度は3MB/sぐらいしかないのと、書き込み回数を減らしたいため、ファイルシステムをext2にする。
ジャーナリング無しのため最悪ディスク破壊の可能性があるが、確率は低いと考えてよしとする(一応UPSがあるので、瞬停は防げる。定期的なバックアップの励行)。