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

こんにちわ。
大学の研究で声の周波数をH8/3052で
示そうと考えています。周期を使ったプログラムですが、
声をA/D変換された電圧で、電圧が0になった所を0.125秒間で何回かカウントして
d = 0.1025 * 2 / count;//一周期分=半周期×2倍÷カウント(0になった数)
F=1/d (周波数=1/周期)を使い、ある範囲の周波数内になったらLEDが光るというプログラムを作りたいのですが、何も表示されません。皆様の知識をお借りしたいです。よろしくお願いします。プログラム乗せますのでご指摘お願いします。
//A/Dコンバータテストプログラム
//AN0からの入力電圧をLCDに表示

#include <3048F.h>
#include <io.h>


#define E_SIG 0x20
#define RS_SIG 0x10

void ioinit(void)
{
P5.DDR = 0xff;
}

void adinit(void)
{
AD.CSR.BIT.ADF = 0; //ADFフラグクリア
AD.CSR.BIT.SCAN = 0; //単一モード選択
AD.CSR.BIT.CKS = 1; //クロックセレクト
AD.CSR.BIT.CH = 0; //チャネルセレクト AN0単一モード
}
void wait(void)
{
int t = 1000;
while(t--);
}

int main(void)
{
int add; //変換された信号を記憶する
int i;
int t;
int b;
int d;
int f;
int v;
int count;
ioinit();
adinit();
wait(); //電源ONで15ms以上待ち
while(1)
{
AD.CSR.BIT.ADST = 1; //A/D変換スタート
while(AD.CSR.BIT.ADF == 0); //変換終了を待つ
add = AD.DRA >> 6; //AN0入力、6ビット右にシフト、大切
AD.CSR.BIT.ADF = 0; //フラグクリア
add=(int)((add*(5.0/1024.0)*1000.0)+0.5);//入力データを電圧値に換算
v=add;
while(b = 8500) //0.125s間
{
while(b--);
if(0==v)//0Vになったときカウント
{
count++;
}
}
d = 0.1025 * 2 / count;//半周期×2倍÷カウント(0になった数)
f = 1 / d;//F=1/T(周波数= 1 ÷ 周期)
}
wait();
if(4000<=f<=8000)//周波数Fが4000~8000Hzになったら実行
{
lcdxy2(); //2行目指定
dsp1g(" O K "); //2行目クリア
wait();

}
}
}

A 回答 (5件)

連投申し訳ありませんが, 気になったので追記いたします.



wait関数で減算させている初期値ですが1000で足りていますでしょうか?
コメントから15msec以上と読み取りましたが, 動作クロックとwait関数内のwhileループ部分の実行サイクル数の確認が必要です.

規定時間待つ為には, タイマ割り込みを利用してある程度正確な時間を取得するのが一般的ですが, 多少コーディングの難易度が上がります.
簡易的なビジーループを用いる場合は十分な時間が待てているかの確認が必須です.
300Hのコンパイラが今手元にないため概算ですが, 最適化を正しく抑制しておいた場合,
少なくとも"減算命令+比較判定+ジャンプ命令"は実施されますので5~7サイクル程度になるだろうと見積もりました.
実行サイクル数についてはアセンブラ出力から最終的な確認が必要です.
仮に動作クロックが1MHzだった場合でも, カウントダウンに5~7msec程度しか掛かりません.
3052の上限は50MHzだったはずですのでカウント値1000では最悪100usec程度しか待機しない可能性もあります.

対策として動作クロックに併せてカウント値を増やす場合には, さらに16bitのint型で表現できる値に収まるかの注意が必要です.
無難に volatile unsigned long 辺りに変更しておけば, 型の問題は起きないと思います.
    • good
    • 0

#2の方と同意見です.


提示のコードでは突っ込むべき所が多く, 問題点の抽出は難しいです.
各モジュール(ドライバ)単位で動作確認を行い, 問題点の切り分けを行うのがデバッグの第一歩です.

どなたもまだ挙げていない問題点が, wait関数の実装にあります.
開発環境が不明なので断言はできませんが, 現状のカウント値の減算によるビジーループでは機能しない可能性が高いでしょう.
変数tの減算によりループを実装していますが, 最終的な値が0になることが明白であるためコンパイラの最適化によって減算処理が省略されてしまいます.
(最適化の精度や, コンパイルオプション, 変数の割付位置等によりどう扱われるかは提示の情報からは見極めできません)

コンパイラの最適化の対象外とさせるため, 初期化を伴う変数の定義時にvolatileを付加する必要があります.
組み込みの世界では知ってて当然の知識として扱われるため, 注意が必要です. 
"volatile"でweb検索してみてください. 私がここに記述するよりも良いヒントが多く見つかると思います.


//-- sample
void wait(void)
{
volatile int t = 1000;
while(t--);
}
    • good
    • 0

ちょっと補足です。


5.0/1024.0 は、「小数点付きの定数は double」なので、
double で計算されます。
このため、
(5.0/1024.0)*1000.0)+0.5)
は、double 型の結果を返します。
さらに、
int * double は、int -> double への変換が発生します。
このため、
add*(5.0/1024.0)*1000.0)+0.5
は、全体で、double として計算されます。

>d = 0.1025 * 2 / count;
これは、* と / は、左 → 右 の順に結合するので、
(0.1025 * 2) / count
と結合します。
いずれも、double * int, double / int になるので、
全体として、double で計算はされます。

ただし、d が int である以上
1 / d は、意味を持たない結果になるでしょう。


>if(4000<=f<=8000)
これは、意図したとおりには動かないでしょうが、文法的には
間違っていません。
なので、コンパイルは動きます。
if ((4000 <= f) <= 8000)
と解釈されます。
この結果、
4000 <= f の真偽で、1 または 0 が返り、
最終的に、
if (1 <= 8000) か、(4000 <= f の場合)
if (0 <= 8000) になります(こちらは、4000 > f の場合)
いずれにしても、if (4000 <= f <= 8000) は、常に真
です。
    • good
    • 0

こんにちは。


ソースを拝見して申し上げたい事は山のようにありますが・・・。
まずはコーディングの事について。
整数型(int)の変数しかないのに、実数の計算があります。
>add=(int)((add*(5.0/1024.0)*1000.0)+0.5);
この中の、"5.0/1024.0"は、ゼロになっちゃうので、計算は成り立ちません。
同様に、
>d = 0.1025 * 2 / count;
>f = 1 / d;
も無理です。

あと、
>if(4000<=f<=8000)
コンパイルできていますか?

次に、研究と開発の手法についてです。
特に、こういう複雑なシステムについて言えますが、作っていきなり完動する事は、まず「あり得ない」と断言できます。
本件の場合、ざっと考えただけでも、
1.マイコンそのもの
2.LCD
3.A/Dコンバータ
4.LED
5.音声を電圧に変換する
6.周波数を測定する
といった数多くの要素があり、現在お困りの件でも、1~4をすべて動かす必要があります。
どこまで動いている事が確認できていますか?
いきなり全部、ではなく、一つ一つ確かめていかれる事をお勧めいたします。

あと、そもそもな話ですが、その方法論で本当にお望みの機能が実現できるのか、確認されていますか?
音声を電圧に変換した際に、どのような波形で入力されるのか、確かめていますか?
入力された電圧が、どのようなデータに変換されるか、確かめていますか?
マイコンの選定は、大丈夫ですか?
そのマイコンのA/Dコンバータの性能で、8kHzのゼロクロスを測定できるほどのサンプリングができる事は、確認できていますか?
    • good
    • 0

if(4000<=f<=8000) //周波数Fが4000~8000Hzになったら実行


記述が間違っているので、思った通りの動きはしません。

{
lcdxy2(); //2行目指定
dsp1g(" O K "); //2行目クリア
wait();
}
条件が合致したときに「表示」していますが、条件が合致しないときには何もしないので、一度表示されたら消えることがありません。

while(b = 8500) //0.125s間
{
while(b--);
if(0==v) //0Vになったときカウント
{
count++;
}
}
ここは、単なるミスなのか、または、非常に誤解を招きやすい記述をあえてしているのかどちらかですが、たとえば、
for(b = 8500; b; b--);
if(0==v) //0Vになったときカウント
{
count++;
}

とか、
for(b = 0; b <= 8500; b++);
if(0==v) //0Vになったときカウント
{
count++;
}

という動きを意図したのでしょうか?

また、count++;
が実行されるのは、(コメントが正しければ)最大でも 0.125s に一度です。
これから、
d = 0.1025 * 2 / count

f = 1/d = 1/(0.0125 * 2 / count)
= 1/0.0125/2 * count;
= 4.8 * count;

f が 4000 に到達するのは、(ゼロクリアされていると仮定すると)最短でも、104秒後

また、count は、インクリメントされるのみで、クリアも(初期化も)されていませんから、実際にはどうなるかわかりません。少なくとも、周波数を反映してはいません。

あと、前提条件として、
> 電圧が0になった所を0.125秒間で何回かカウントして
というのは、いろいろな点で不都合です。

・周波数をカウントするには、ゼロクロス点(上から下に横切る or 下から上に横切る)を数える必要がある。無音だったら、ゼロが連続するのではないか?
・そもそも、AD コンバータにはどういう信号が入っているのか? 本当に無音がゼロ?
・特に、「声」の場合ノイズ成分がとても多い。単純にゼロ点(とか、ゼロクロス点とか)をカウントすると、ノイズ成分のために、とんでもなく高い周波数がカウントされる。

など。

また、このソフトでは、そもそも、AD 変換が 0.125秒以上の間隔でしか動きませんが、入力される周波数の上限の2倍程度の頻度で動かす必要があります。
    • good
    • 0

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