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()関数が、文字通りメインの仕事をする場所。計算中は、タイマーをとめて、割り込み処理が行われないようにしている。こうしないと、計算が途中で止まったり、結果がおかしくなる。スタックの利用数の問題?割り込み処理中に、変な事をしてしまっているのか?