2015/11/07
■ Watch Dog Timer
ローダブルモジュールが使えればデバイスドライバorion_wdtを使うのがスマートだが、
ハードウエア直たたきでも問題ないだろう。
このページを参考にしてCで作成してみた。
http://plugcomputer.org/plugforum/index.php?topic=466.0
約21秒でtime upするWatch Dog Timerである。
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <inttypes.h> #include <sys/types.h> #include <sys/io.h> #include <sys/mman.h> typedef uint64_t u64; typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; typedef int64_t s64; typedef int32_t s32; typedef int16_t s16; typedef int8_t s8; main() { #define REG_SIZE 0xfff #define PAGE_SIZE (sysconf(_SC_PAGE_SIZE)) int mem_fd; u8 *preg; mem_fd=open("/dev/mem",O_RDWR|O_CREAT,0666); if ( mem_fd < 0 ) { puts("/dev/mem error."); return 1; } preg=(u8 *)malloc(REG_SIZE+(PAGE_SIZE-1)); // PAGE_SIZEでアラインメント if ( (u32)preg % PAGE_SIZE ) { preg+=(PAGE_SIZE-((u32)preg % PAGE_SIZE)); } preg=(u8 *)mmap((caddr_t)preg,REG_SIZE,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_FIXED, mem_fd,0xf1020000ul); *(u32 *)(preg+0x0324)=0xfffffffful; // カウンタ値(約21秒) *(u32 *)(preg+0x0300)|=0x10; // WDT enable *(u32 *)(preg+0x0108)=0x2; // WDT start for (;;) { *(u32 *)(preg+0x0324)=0xfffffffful; // カウンタ値リロード puts("WDT clear."); sleep(15); // 15秒毎にWDTクリア } }
これを gcc -O2 -o wdt_dp wdt_dp.c でコンパイルして、
./wdt_dp & と動かせばよい。(root必須)
2014/01/04
■ u-bootを書き換える
https://code.google.com/p/dreamplug/downloads/list
にある、
u-boot-DreamPlug.kwb
がu-boot F/Wである。これをダウンロードし、SDカードの第1パーティション(カーネルのあるパーティション)にu-boot.kwbと名前を変えてコピーする。
シリアルケーブをつなぎu-boot設定画面に入る。
設定が初期化されるので、printenv で設定を控えておく。
usb start
fatload usb 1:1 0x0800000 u-boot.kwb
sf probe 0
sf erase 0x0 0x60000
sf write 0x0800000 0x0 0x60000
reset
■ SDカードからの起動を早くする
net bootを試みるためにサーバーにアクセスするためタイムアウトの分だけ起動が遅くなる。
サーバーにアクセスしないように x_bootcmd_ethernet の設定を空にする。
setenv x_bootcmd_ethernet
saveenv
reset
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があるので、瞬停は防げる。定期的なバックアップの励行)。
2011/08/17
■ シリアル変換基板の製作
シリアル接続ぐらいはGuruPlug JTAGが無くても良いだろうと思っていたので、UARTの口にオシロをつないで信号を見る(ファーム屋の意地)。シリアルレベル変換の簡単な回路でPCと接続できた。部品はマルツパーツで手に入れた。回路図
難儀だったのは、Molex Picobladeの圧着で、工具はこれで、コンタクトは20本も買っていたが、圧着のコツをつかんだのは残り5本となってからだ。PCとはRS-232Cクロスケーブルで接続する。
2011/08/12
■ HDDをSDカードに変更
さらに省エネのため、HDDをSDカードにする。
SuperTalent SDHCカード 32GB class 10 SDHC32-C10が\3,500ほどで手に入った。
高級SSDでは無いので、プチフリーズはやむを得ないが、圧倒的な低消費電力サーバーとなったので、良しとしよう。