dポイントプレゼントキャンペーン実施中!

H8/3664Fを使ってA/D変換した信号を配列に一時的に格納し,その後その信号を処理する
プログラムを作成したのですが,A/D変換を行ってくれません。
いろいろ試した結果,配列を定義するとA/D変換を行ってくれないようです。

どなたかこの現象の原因と対策方法についてご存じないでしょうか。

A 回答 (5件)

DATAが1000とか4000 とか、これが無茶です。


H8/3664F は RAMが2KBしかないのですから。

例えば
#define DATA 1050
int A[DATA];
と書いた時点で、intが16bit(=2Byte)なら2 * 1050 = 2100 で既に2KB越えてます。intが32bitならその倍のRAMを使おうとしています。DATA=1007ならOKで、1008だとNGなのも、1007*2 = 2014 とスタックやその他のメモリ消費分をあわせてこのあたりがぎりぎりいっぱいの値だからだと思います。

さらに、ご質問に書かれている

> いろいろ試した結果,配列を定義するとA/D変換を行ってくれないようです。

というのも、配列の要素数が多すぎてRAMが不足していることによるものだと思います。

さて対策案です。

(a) 相関の計算をPCで行う
相関はどうしてもH8の中で計算しなくてはいけないのでしょうか?
PC側で計算すればすむことなら、大量のデータをH8の中にため込まずに少し測ってはPCに送り、PC側で計算してしまったほうが楽です。これなら、データ点数を5000点にしても10000点にしても、どうせ計算するのはPCですからH8のRAMが不足する心配や、H8の計算速度の心配をする必要がありません。

(b) データ点数を減らす
どうしてもH8の内部で相関を求める必要がある場合、データ点数は1000~4000点も本当に必要でしょうか?
計測対象信号の周波数の2倍の周期までサンプリングレートを落として、必要最低限のデータ点数を見直してみましょう。

(c) データ型に小さなものを使う
データ点数を減らしただけではまだ足りないのなら、AD変換した値を保存するデータ型を unsigned char にしてはどうでしょうか。H8のADコンバータの精度は10bitですが、最下位2bitはノイズレベルの値ですから多少目をつぶって良い部分だと思います。unsigned charなら8bitですから、16bitのデータ型の倍のデータ点数を扱えるようになります。

(d) マイコンをRAMの多いものに差し替える + 外部RAMを使う
それでもサンプリングしたデータが2KBを越えてしまうのなら、もっと大きなRAMを持っているH8を使ってはいかがでしょうか。例えば、H8/3052Fの内蔵RAMは8KBありますし、バスがあるので外にDRAMを増設すればKBではなくMB単位のRAMを使えるようになりますから、RAMの心配をする必要がなくなります。

秋月のH8キット一覧
http://akizukidenshi.com/catalog/renesas/

私がお勧めする順序で対策案を挙げてみました。
    • good
    • 0
この回答へのお礼

ご丁寧に何度もご指導いただきましてありがとうございました。
検討した結果。(a),(b),(c)は仕様上できないので(d)案を採用することにしました。

お礼日時:2008/10/25 18:46

AD変換の設定がスキャンモードになっていること、AN0,AN1が読み取り対象であることはわかりました。

こうしてみるとAD変換そのものには問題無いように見えます。

では何が悪いか考えようと思ったら、ANo.2のお礼に書かれているソースコードは、全体の一部抜粋のようですね。ご質問の雰囲気からして学生さんだと思いますが、ちゃんとコンパイルが通るソースコードを全部上げていただいた方が解決しやすいと思いますので、差し支えなければ見せてください。

> A/D変換を行ってくれません。

この場合、どのような値が入っていますか?

とりあえず相関の計算はちょっと置いておいて、AD変換して得られた加工前の値がどうなっているか、X,Yの値を全部シリアル通信でPCに送って確認してみてください。

また、これも AD変換とは別の話なのですが、信号を計測するのにサンプリング時間が考慮されていないようですね。計測対象となる信号の周期はどれくらいですか? たぶん、サンプリング定理とかナイキスト周波数といえば何の事を言っているのかわかっていただけるかと思います。

> 相互相関の計算方法でi=0~DATAまでとしたとき
> X[i]*Y[i]の総和を計算するためオーバーフローするのではと考えたので

AD変換して得られる値は10bitなので10進数でいえば1024が最大値。この値を2乗しても高々1048576で、unsigned long など32bitのデータ型に入れておけばざっくり4000回くらい足してもオーバーフローしないんじゃないでしょうか。 unsigned longで扱える値の最大値は、limits.hの ULONG_MAXというマクロ定義をみて確認してください。

計算時間を気にしなくていいなら、浮動小数点使っても問題ないのでここは流していただいて結構です。浮動小数点演算は「すごく」遅いということだけ覚えておいてくださいね。

参考:H8における浮動小数点演算の速度
http://mobile-robots.way-nifty.com/daily_report/ …

この回答への補足

全体のプログラムはをあげるのは少々問題があるので問題がある部分を除いた物で
よろしいでしょうか。それでよろしいのであれば↓に記載します。
#include<3664.h>

#defineDATA  4000   //相関処理するデータ数//
#defineSR   31250   //サンプリングレート//
#defineL    99.79623//センサ間距離//

int flag1 = 0,flag2 = 0, d_num = 0, space1 = 0, space2 = 0;
double vh = 0,a1 = 0,b1 = 0,a2 = 0,b2 = 0;

typedef enum{
BR2400 = 207,
BR4800 = 103,
BR9600 = 51,
BR19200 = 25,
BR38400 = 12,
BR57600 = 8,
BR115200 = 3,
}BaudRate;

/*IO初期化*/
void IO_init(void)
{
IO.PCR8 = 0xff;
}

/*A/D変換器初期化*/
void AD_init(void)
{
AD.ADCSR.BIT.ADF = 0;//A/Dエンドフラグクリア//
AD.ADCSR.BIT.SCAN = 1;//スキャンモード選択//
AD.ADCSR.BIT.CKS = 1;//クロックセレクト:134ステート選択//
AD.ADCSR.BIT.CH = 1;//チャネルセレクト:CH0,CH1選択//
}

/*SCI初期化*/
void SCI_init(BaudRate b)
{
int i;
IO.PMR1.BIT.TXD = 1;
SCI3.SCR3.BYTE = SCI3.SMR.BYTE = 0;
SCI3.BRR = b;
for(i=0;i<3000;i++);
SCI3.SCR3.BYTE = 0x30;
SCI3.SSR.BYTE = 0x80;
}

/*タイマA初期化*/
void timerA_init(void)
{
TA.TMA.BYTE = 0x13;
IRR1.BIT.IRRTA = 0;
IENR1.BIT.IENTA = 0;
}

/*相関処理*/
void s_soukan(double X[DATA],double Y[DATA],int dt)
{
  ・
  ・   ←この部分で相互相関を行い,遅れ時間を算出しています
  ・
}

/*データ送信*/
void SCI3_out(int dt)
{
int i,j,k = dt,l,m=0;
for(i=1;i<=6;i++)
{
while(SCI3.SSR.BIT.TDRE == 0);
for(j=5;j>=i;j--)
{
k /= 10;
}
l = k/10;
l *= 10;
m = (k-l)+0x30;
SCI3.TDR = m;
}
SCI3.TDR = 0x0A;
}

int main(void)
{
int i=0,dt = DATA;
double vhb, X[DATA], Y[DATA];
IO_init();
SCI_init(BR19200);
AD_init();
timerA_init();
d_num = 0;
while(1)
{
IO.PDR8.BYTE = 0x01;
if(IRR1.BIT.IRRTA == 1)
{
IRR1.BIT.IRRTA = 0;
AD.ADCSR.BIT.ADST = 1;
while(AD.ADCSR.BIT.ADF == 0);
AD.ADCSR.BIT.ADF = 0;
X[i] = ((AD.ADDRA & 0xFFC0) >> 6)/1000;
Y[i] = ((AD.ADDRB & 0xFFC0) >> 6)/1000;
i++;
}
if(i>=DATA)
{
IO.PDR8.BYTE = 0x00;
s_soukan(X,Y,dt);
SCI3_out(dt);
d_num++;
i = 0;
if(dt != DATA)
{
vhb = vh;
vh = L*3600/1000000/dt*SR;
if(vh >= vhb)
flag1 = 0;
else
flag1 = 1;
}
}
}
}

> A/D変換を行ってくれません。

この場合、どのような値が入っていますか?

↑については時数の関係でお礼の部分に記載します。

補足日時:2008/10/25 13:34
    • good
    • 0
この回答へのお礼

>> A/D変換を行ってくれません。

>この場合、どのような値が入っていますか?

↓のようなプログラムを作成し,動作確認をしました。
#include<3664.h>

#define DATA 1050

typedef enum{
 BR2400 = 207,
 BR4800 = 103,
 BR9600 = 51,
 BR19200 = 25,
 BR38400 = 12,
 BR57600 = 8,
 BR115200 = 3,
}BaudRate;

void IO_init(void)
{
 IO.PCR8 = 0xff;
}

void timerA_init(void)
{
 IRR1.BIT.IRRTA = 0;
 TA.TMA.BYTE = 0x17;
 IRR1.BIT.IRRTA = 0;
 IENR1.BIT.IENTA = 0;
}

void AD_init(void)
{
 AD.ADCSR.BIT.ADF = 0;//A/Dエンドフラグクリア//
 AD.ADCSR.BIT.SCAN = 0;//スキャンモード選択//
 AD.ADCSR.BIT.CKS = 1;//クロックセレクト:134ステート選択//
 AD.ADCSR.BIT.CH = 0;//チャネルセレクト:CH0,CH1選択//
}

void SCI_init(BaudRate b)
{
 int i;
 IO.PMR1.BIT.TXD = 1;
 SCI3.SCR3.BYTE = SCI3.SMR.BYTE = 0;
 SCI3.BRR = b;
 for(i=0;i<3000;i++);
 SCI3.SCR3.BYTE = 0x30;
 SCI3.SSR.BYTE = 0x80;
}

void SCI_out(int A[DATA])
{
 int h,i,j=0,k=0,l=0;
 for(l=0;l<=DATA-1;l++)
 {
  for(h=0;h<=10;h++)
  {
   j=A[l];
   for(i=h;i<10;i++)
   {
    j /=10;
   }
   k=j/10;
   j=j-k*10+0x30;
   while(SCI3.SSR.BIT.TDRE == 0);
   SCI3.TDR = j;
  }
  while(SCI3.SSR.BIT.TDRE == 0);
  SCI3.TDR = 0x0A;
 }
 while(SCI3.SSR.BIT.TDRE == 0);
 SCI3.TDR = 0x0A;
}

int main(void)
{
 int A[DATA],i=0;
 AD_init();
 SCI_init(BR19200);
 while(1)
 {
for(i=0;i<=DATA-1;i++)
  {
   AD.ADCSR.BIT.ADST = 1;
   while(AD.ADCSR.BIT.ADF == 0);
   AD.ADCSR.BIT.ADF = 0;
   A[i] = (AD.ADDRA>>6)&0x03FF;
}
  if(i >= DATA)
   SCI_out(A);
 }
return 0;
}

このとき,DATAの個数が1007個まではPCにデータが正しく送信されてきます。
しかし,1008個になるとデータが00000031010 や 0000000000/ のような
おかしなデータが送信されるようになります。(正しい時は00000001023)
さらに,DATAの個数を1050以上にするとPCにデータが送信されなくなってしまいました。

お礼日時:2008/10/25 15:43

IO_init, AD_init の中身はどうなっていますか?



AD変換の動作モードが「単一モード」なのか「スキャンモード」なのか、AD_initのなかでどのように設定しているか見せてください。
予想としては、単一スキャンモードをつかっているのではないかと。

また、ADCの動作ではないのですが、

>   X[i] = ((AD.ADDRA & 0xFFC0) >> 6)/1000;
>   Y[i] = ((AD.ADDRB & 0xFFC0) >> 6)/1000;

これ考え直した方がいいかもしれません。というのは、
(AD.ADDRA & 0xFFC0) >> 6 で、AD変換された値が10bitで取得できますが、10bitで得られる値は最大1024で、これを1000で割っていますから割り算の結果(商)は 0 か、1 です。X, Y がdoubleで定義いされていても、X, Y に代入される前の計算は整数のままです。

明示的にdoubleにキャストするか、割る数を 1000.0 にすれれば計算が浮動小数点で行われますが、その前に 1000 で割る必要性についても少し説明してください。(私は、割る必要ないのでは?と思っています)

この回答への補足

A/D変換の初期化は
/*A/D変換器初期化*/
void AD_init(void)
{
 AD.ADCSR.BIT.ADF = 0;//A/Dエンドフラグクリア//
 AD.ADCSR.BIT.SCAN = 1;//スキャンモード選択//
 AD.ADCSR.BIT.CKS = 1;//クロックセレクト:134ステート選択//
 AD.ADCSR.BIT.CH = 1;//チャネルセレクト:CH0,CH1選択//
}
としているため,スキャンモードを選択しています。

また,1000で割ることは私の間違いで実際には浮動小数点で行おうと
考えていました。理由としては相互相関の計算方法でi=0~DATAまでとしたとき
X[i]*Y[i]の総和を計算するためオーバーフローするのではと考えたので
浮動小数点で扱おうと考えていました。

補足日時:2008/10/25 11:30
    • good
    • 0

では、具体的にどのような処理を行っているのか、AD変換に関連する部分だけでよいので書いてください。

また、使っている開発環境(コンパイラなど)も明記してくださいね。
    • good
    • 0
この回答へのお礼

抽象的に書いてしまい,申し訳ありませんでした。
処理の内容は
int main(void)
{
 int i=0;
double vhb, X[DATA], Y[DATA];
 IO_init();
 SCI_init(BR19200);
 AD_init();
 timerA_init();
 d_num = 0;
 while(1)
 {
  IO.PDR8.BYTE = 0x01;
  if(IRR1.BIT.IRRTA == 1)
  {
   IRR1.BIT.IRRTA = 0;
   AD.ADCSR.BIT.ADST = 1;
   while(AD.ADCSR.BIT.ADF == 0);
   AD.ADCSR.BIT.ADF = 0;
   X[i] = ((AD.ADDRA & 0xFFC0) >> 6)/1000;
   Y[i] = ((AD.ADDRB & 0xFFC0) >> 6)/1000;
   i++;
  }
  if(i>=DATA)
  {
   IO.PDR8.BYTE = 0x00;
   s_soukan(X,Y,dt); /*XとYの相互相関を行い遅れ時間を算出*/
   SCI3_out(dt);   /*算出した遅れ時間をPCに送信*/
   i = 0;
  }
 }
}
というように2つの信号を取得して,その信号の相互相関を行い,遅れ時間を
計算するプログラムを作成してマイコンに書き込んだんですがうまく
動きませんでした。

ちなみにコンパイラはGDL(GCC Developer Lite)のVer.2.0.1.8を使用しています。
ライタはGDLの設定方法が悪いためか,GDLでマイコンにライトできなかったため,
別のライタソフトのH8 writer Ver.0.37を使用してマイコンにライトしています。

お礼日時:2008/10/25 09:18

アルゴリズムと設定項目を説明してくれないと回答できません。

    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!