pi calculator
2009年7月12日
IC16F690をつかった、pi calculatorの完成形。結果は、
3.141592653589793238462643383279502884
となる。
以下は、3.141592653589793238462643まで計算したところ。
本当は、小数点以下36桁まで計算し表示するのだが、その計算に8分強かかる。フルの長さの動画がアップできなかった。
この計算に当たり、40桁分のバッファをPIC内に確保したが、はじめその確保の仕方を見つけるのに、ずいぶん手間取った。PIC16F690は、256バイトのRAMがある。10進数2桁に一バイトを割り当ており、計算させるのに7つのバッファを使ったので、合計140バイトのメモリが必要だ。これをそのまま、unsigned char buff[140];としても、割り当てできない。ところが、70バイトずつ2つに分けると、割り当てできる。最初、これがなぜだか分からなかったのであるが、いろいろ調べてみて分かった。PIC16F690は、256 byte のRAMを、96, 80, 80 bytes と、3つのバンクに分けて確保しており、連続で80 bytesを超えるバッファの確保が難しく、96 bytesを超えることはできないためだ。
その他、乗除算の際にかなりのRAMを使うようで、140バイトのバッファを確保するためにコードの見直しにずいぶん時間がかかった。
PICでのプログラミングに関して、もうひとつ気をつけないといけないのは、スタックの使い方。マニュアルでは、スタックは8段までとある。どうやら、スタック用のメモリと、ユーザ用のメモリ(256バイトのRAM)は別々に確保してあるらしく、スタックはアセンブラのCALL命令(Cでは、関数呼び出し)のみに使われているらしい。通常、Cでは、関数内のローカル変数などもスタック内に確保されるので、この辺りが異なる。では、PICCの場合、関数内のローカル変数はどのように確保されているかというと、どうやらスタティック変数のようにユーザRAMを使っているらしい。もしそうだとすると、関数の再帰呼び出しはできない(やりにくい)ので、注意が必要である。ここは、あとでゆっくり調べなければならない。
こういった理由で、関数内のローカル変数の使用もなるべく避け、可能な場合はグローバル変数を使うようにし、なるべく新たな変数の定義を行わないようなコードにした。なので、あまりきれいなコードにできないのは、仕方のないところ。256バイトのRAMを最大限に利用しようとすると、どうしてもこうなるだろう。
pi2.h
pi2.c
calc_pi.c
計算中は、タイマーをとめて、割り込み処理が行われないようにしている。こうしないと、計算が途中で止まったり、結果がおかしくなる。スタックの利用数の問題?割り込み処理中に、変な事をしてしまっているのか?
3.141592653589793238462643383279502884
となる。
以下は、3.141592653589793238462643まで計算したところ。
本当は、小数点以下36桁まで計算し表示するのだが、その計算に8分強かかる。フルの長さの動画がアップできなかった。
この計算に当たり、40桁分のバッファをPIC内に確保したが、はじめその確保の仕方を見つけるのに、ずいぶん手間取った。PIC16F690は、256バイトのRAMがある。10進数2桁に一バイトを割り当ており、計算させるのに7つのバッファを使ったので、合計140バイトのメモリが必要だ。これをそのまま、unsigned char buff[140];としても、割り当てできない。ところが、70バイトずつ2つに分けると、割り当てできる。最初、これがなぜだか分からなかったのであるが、いろいろ調べてみて分かった。PIC16F690は、256 byte のRAMを、96, 80, 80 bytes と、3つのバンクに分けて確保しており、連続で80 bytesを超えるバッファの確保が難しく、96 bytesを超えることはできないためだ。
その他、乗除算の際にかなりのRAMを使うようで、140バイトのバッファを確保するためにコードの見直しにずいぶん時間がかかった。
PICでのプログラミングに関して、もうひとつ気をつけないといけないのは、スタックの使い方。マニュアルでは、スタックは8段までとある。どうやら、スタック用のメモリと、ユーザ用のメモリ(256バイトのRAM)は別々に確保してあるらしく、スタックはアセンブラのCALL命令(Cでは、関数呼び出し)のみに使われているらしい。通常、Cでは、関数内のローカル変数などもスタック内に確保されるので、この辺りが異なる。では、PICCの場合、関数内のローカル変数はどのように確保されているかというと、どうやらスタティック変数のようにユーザRAMを使っているらしい。もしそうだとすると、関数の再帰呼び出しはできない(やりにくい)ので、注意が必要である。ここは、あとでゆっくり調べなければならない。
こういった理由で、関数内のローカル変数の使用もなるべく避け、可能な場合はグローバル変数を使うようにし、なるべく新たな変数の定義を行わないようなコードにした。なので、あまりきれいなコードにできないのは、仕方のないところ。256バイトのRAMを最大限に利用しようとすると、どうしてもこうなるだろう。
pi2.h
#include <pic.h> #define FIG 20 void show_all(unsigned char* set_num, unsigned char set_max); void wait(); void init();
pi2.c
#include "pi2.h" __CONFIG(INTIO & WDTDIS & PWRTDIS & MCLRDIS & UNPROTECT & BORDIS & IESODIS & FCMDIS); unsigned char i; unsigned char g_ledp=0; unsigned short g_num=1000; unsigned char g_dp=0; unsigned char led7(char num){ unsigned char ret=0xFF; if (num & 0x10) ret=0xF7; switch (num & 0x0F) { case 0: return ret & 0x28; case 1: return ret & 0x7B; case 2: return ret & 0x1C; case 3: return ret & 0x19; case 4: return ret & 0x4B; case 5: return ret & 0x89; case 6: return ret & 0x88; case 7: return ret & 0x2B; case 8: return ret & 0x08; case 9: return ret & 0x09; default: return 0xff; } } void show(unsigned char* num, unsigned char pos){ // Set the dot, first. switch(pos){ case 0: g_dp=0x02; break; case 1: g_dp=0x01; break; default: g_dp=0x00; } i=pos>>1; if (pos%2) g_num=(num[i]%10)*100 + num[i+1]; else g_num=num[i]*10+num[i+1]/10; } unsigned char g_shown=0; void show_all(unsigned char* set_num, unsigned char set_max){ static unsigned char *num=0, max=0, state=0; if (set_max) { num=set_num; max=set_max; state=0; g_shown=0; return; } if (!max) { g_num=1000; return; } if (state<6) { if (state%2==0) g_num=1000; else show(num,1); } else if (state < max+3) { show(num,state-5); } else if (state > max+5) { g_shown=1; state=3; } state++; } void wait(){ while(!g_shown); g_num=1000; PORTA=0x00; } // 10000 cycles @ 8 MHz / 4 == 5 ms #define WAIT_CYCLE (10000-23) #define TMR1H_INIT ((unsigned char)(((unsigned short)(65536-WAIT_CYCLE))>>8)) #define TMR1L_INIT ((unsigned char)(((unsigned short)(65536-WAIT_CYCLE))&0x00ff)) void interrupt int_function (void) { if (!TMR1IF) return; TMR1H=TMR1H_INIT; TMR1L=TMR1L_INIT; TMR1IF=0; static unsigned char show_counter=0; if (99 < ++show_counter) { show_counter=0; show_all(0,0); } if (++g_ledp==3) g_ledp=0; PORTA=0x00; if (g_num>999) return; switch (g_ledp) { case 0: i=(g_dp&0x01)?0x10:0; i=i | g_num / 100; PORTC=led7( i ); PORTA=0x04; break; case 1: i=(g_dp&0x02)?0x10:0; i=i | ( g_num / 10 ) % 10; PORTC=led7( i ); PORTA=0x10; break; case 2: default: i=(g_dp&0x04)?0x10:0; i=i | (g_num % 10); PORTC=led7( i ); PORTA=0x20; break; } } void init(){ // CLK=8 MHz OSCCON=0x70; // All output TRISA=0; TRISB=0; TRISC=0; // Non-analog mode ANSEL=0x00; ANSELH=0x00; // Scaler 1:1 T1CON=0x00; // Reset timer TMR1H=0x00; TMR1L=0x00; // Clear flag TMR1IF=0; // Enable interrupt TMR1IE=1; PEIE=1; GIE=1; // Stop timer, first TMR1ON=0; }main()以外のPIC機能は、ここに配置。
calc_pi.c
#include "pi2.h" unsigned char cache[FIG*4]; unsigned char cache2[FIG*3]; unsigned char* buff(unsigned char page){ if (page<4) return &cache[FIG*page]; page-=4; return &cache2[FIG*page]; } unsigned char i,c; unsigned short t; void copy(unsigned char* res, unsigned char* v1){ // round the value t=v1[FIG-1]+5; for (c=0;9<t;c+=10) t-=10; v1[FIG-1]=c; // check the value and copy c=0; for (i=FIG-1;i<FIG;i--) { t=v1[i]+c; if (i) { for (c=0;99<t;c++) t-=100; } res[i]=t; } } void clear(unsigned char* res){ for (i=0;i<FIG;i++) res[i]=0; } void add(unsigned char* res, unsigned char* v1, unsigned char* v2){ for (i=0;i<FIG;i++) res[i]=v1[i]+v2[i]; copy(res,res); } void mul2(unsigned char* res, unsigned char* v1, unsigned char v2){ c=0; for (i=FIG-1;i<FIG;i--) { t=v1[i]*v2+c; if (i) { for (c=0;99<t;c++) t-=100; } res[i]=t; } copy(res,res); } void mul(unsigned char* res, unsigned char* v1, unsigned char* v2){ unsigned char i; clear(&cache2); clear(&cache2[FIG]); for (i=FIG-1;i<128;i--) { mul2(&cache2[FIG*2],v1,v2[i]); add(&cache2[i],&cache2[i],&cache2[FIG*2]); } copy(res,&cache2); } void div(unsigned char* res, unsigned char* v1, unsigned char v2){ unsigned short c=0; for (i=0;i<FIG;i++) { c=c*100+v1[i]; cache2[i]=c/v2; c=c-cache2[i]*v2; } copy(res,&cache2); } void main(){ init(); unsigned char* num=buff(0); unsigned char* pi=buff(1); unsigned char* y=buff(2); unsigned char* t=buff(3); unsigned char n,x2=0; unsigned char x; clear(num); clear(pi); for (n=0;n<127;n++) { // Begin pi calculation process clear(y); y[0]=3; div(y,y,2*n+1); for (x=1;x<=n;x++) { clear(t); t[0]=x+n; div(t,t,x); div(t,t,16); mul(y,y,t); } add(pi,pi,y); for(x=0;x<FIG;x++){ if (num[x]!=pi[x]) break; } copy(num,pi); // Show the result on display if (1<x && x2<x) { x2=x; TMR1ON=1; show_all(num,x=x*2-2); wait(); TMR1ON=0; } // Terminate if reaching maximum depth. if (FIG<=x2) break; } TMR1ON=1; while(1); }main()関数が、文字通りメインの仕事をする場所。
計算中は、タイマーをとめて、割り込み処理が行われないようにしている。こうしないと、計算が途中で止まったり、結果がおかしくなる。スタックの利用数の問題?割り込み処理中に、変な事をしてしまっているのか?