ロータリーエンコーダーと、LCD 制御

1.やっと、ロータリーエンコーダーで、周波数制御が可能に...

    10日ほど、

    この事が、うまく行かなくて、すっかり、はまりましたわ。

    液晶表示器の制御と、DDSの周波数設定の2つが、あるのですが、

    2つを、一気に、やらせても、2mSに収まりますので、

    タイマーXで、2mS間隔で、割り込ませ、割り込みプログラムに、書いたのですが、全然動かん!

void get_rot_int() //この中身は、大体 1.6mS位に収まる
{
    unsigned char index ;

    index = ( pos_former<<2 )+( A<<1 + B);
    freq += 100*dir[index];
    pos_former = A<<1+B; //今の状態を記憶
    

    // for LCD

    set_comma(freq ); //10MHzで、0.633mS費やす
    locate(1,4);
    lcd_write_str(result);
    lcd_write_str("Hz"); 
    locate(2,1);
    lcd_write_str("up_rot ");

    // for DDS
    set_terminals();
    DDS_data=28.63341769*freq; //DDS_data=設定値 
    data=(unsigned long)DDS_data; //data=DDS_dataを32ビット整数値に変換したもの
    send_data();

    locate(2,1);
    lcd_write_str(" ");
}    

   index = ( pos_former<<2 )+( A<<1 + B);

    これは、ロータリーエンコーダー用の配列 const signed char dir[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,-1,1,0 }; /* 回転方向テーブル */

    (後述)

    で、回転を調べるためのもの、です。

    A相を上位ビットに、B相を下位ビットに、見立てています。

    この中の、関数を、しらみつぶしに、消して、調べてみましたら、

    私の作った関数set_comma(freq ); が、おかしいねん......涙、涙

   char* set_comma(unsigned long x) //simulate=664uS
{
    unsigned char str[10]="";
    unsigned char str2[13]=""; 

    sprintf(str,"%10lu",x);

    for (i=0;i<10;i++)
    {
    strncat( str2,str+i,1);
    if( ( !(i % 3) ) && ( *(str+i) !=0x20 ) && (i !=9) ) strncat(str2, "." ,1);
    if( ( !(i % 3) ) && ( *(str+i) ==0x20 ) ) strncat(str2," ",1);
    }
    return str2;

}

   数値を 定型文字列に変換して、その文字列の先頭アドレスを、返す関数に、変えています。

    さらに調べると、ここやねん。

    sprintf(str,"%10lu",x);

    これは、コンパイラの、標準関数やねん。

    これを、調べると(renesas nc30uj.pdf)

※標準関数の幾つかは、その関数専用の大域変数を使用しています。関数が呼び出されて実行して

いる最中に割り込みが入り、割り込み処理プログラム中で同じ関数が呼び出された場合、最初に

呼び出された関数で使用していた大域変数の内容を書き換えることがあります。

リエントラント性がある関数(表中では○)は、このような大域変数の書き換えは発生しません。

リエントラント性がない関数(表中では×)を割り込み処理プログラムで使用するときは注意して

ください。

そんで、入出力関数(stdio.h)の全部が、あかんねん、リエントラン性が、ないねん。

sprintf 関数も、あかん... 大域変数(グローバル変数)を、壊すねんて....

そやけどね、DDS制御部分だけ、もしくは、LCD制御部分だけで、割り込み処理すると、動くんです。

この2つが、いっしょやと、あきませんねん。

このsprintf関数は、8MHZのクロックで、300uS以上、食いますねん。

こうなって来ましたら、もう、spritf関数と、同じ動作する関数を、作らないと、あきませんね、只今、検討中です。

 

2.どうやって、動いたか?

しゃあないから、DDS制御部分を、割り込み関数に書き、

LCD制御部分を、メイン関数の中に記述しました。

#pragma interrupt /B get_rot_int(vect=22)

void get_rot_int() 

{

    unsigned char index = 0;


    index = ( pos_former<<2 )+ (A<<1) + B;

    pos_former = (A<<1) + B; //今の状態を記憶 Aを上位、Bを下位とする


    freq +=1000*dir[index];

    set_terminals();
    DDS_data=28.633411*freq; //DDS_data=設定値 
    data=(unsigned long)DDS_data; //data=DDS_dataを32ビット整数値に変換したもの
    send_data();

}

void main()
{
......

    while(1)
    {

    if( (ex_freq - freq ) != 0 )
    {
    result = set_comma(freq ); //10MHzで、0.633mS費やす

    set_terminals();
    locate(1,2);
    lcd_write_str(result);

    ex_freq = freq;
    }

}

そやけど、このプログラムは、spritf関数を入れたせいで、えらい、気難しいプログラムに、なってしもうた。

と言うのは、

プッシュSWを推した時の、処理を、関数で処理すると、全く、動かん!

例えば、メインの所で

void main()
{
......

    while(1)
    {
    if((!up) up_sw_pushed();


    if( (ex_freq - freq ) != 0 )
    {
    result = set_comma(freq ); //10MHzで、0.633mS費やす

    set_terminals();
    locate(1,2);
    lcd_write_str(result);

    ex_freq = freq;
    }

}

と、書いておいて、この関数up_sw_pushed()で、処理しようと、すると、全く動かん!

void up_sw_pushed() //pull_upされている
{
 
    for(i=0;i<20000;i++); //チャタリング防止用
    if( up ) return;
    for(i=0;i<20000;i++); //チャタリング防止用
i    f( up ) return;
     if(lcd_pos_former == 0 )lcd_pos_former = 14;
     pos = lcd_pos_former;
     locate(1,pos);
    ..............

ところが、メイン関数のなかで、この関数の中身を、記述すると、問題なく、動作するんです。

void main()
{
......

    while(1)
    {
        if((!up) 

        {     
        for(i=0;i<20000;i++); //チャタリング防止用
        if( up ) return;
        for(i=0;i<20000;i++); //チャタリング防止用
        if( up ) return;
         if(lcd_pos_former == 0 )lcd_pos_former = 14;
         pos = lcd_pos_former;
         locate(1,pos);
        ..............

        }    

        if( (ex_freq - freq ) != 0 )
        {
        result = set_comma(freq ); //10MHzで、0.633mS費やす

        set_terminals();
        locate(1,2);
    lcd_write_str(result);
    
        ex_freq = freq;
        }

    ]

}

ほんま、訳わからんように、なって来ましたわいのわい。

 

3.秋月のロータリーエンコーダー(EC16B)を使う

    どの端子が、A、Bで、Gがどれか? 説明書を見ても、わからんかったで....

    有難い事に、趣味の電子工作に、書いて下さってます、(いつも、有難う御座います。)

    さて、このロータリーエンコーダを使うと、

    私のプログラムでは、ワンクリックで、4HZ 回転します。

    これは、まずいで...

    やっぱ、ワンクリックで、1HZ(又は、その10倍数)で、ないと.....

    やっぱ、理屈が、解ってないと....

 

4.ロータリーエンコーダの理屈等

    R8C/15の、端子を、こんな風に使ってます。


//for AD9851

#define RESET p3_4 
#define D7 p3_3
#define W_CLK p1_0
#define FQ_UD p1_1
#define high 1 
#define low 0

// for LCD display

#define E p1_3
#define R_W p1_2
#define RS p4_5

//for Push_SW

#define up p4_7
#define down p4_6

void up_sw_pushed(void);
void down_sw_pushed(void);

//for rotary encoder
#define A p3_5
#define B p3_7

ロータリーエンコーダで使うA、B端子は、外付けで、pull_upしています。

ロータリーエンコーダの理論は、ELMさんのHPが、全てを、語って下さってます、いつも、ほんとに有難う御座います。

pull_upしてますので、A,Bの端子を、逆に、接続しています。

ですので、右回りの時

ですので、EC16Bのワンクリックで、4つの相が、できることになります。

ワンクリックで、1Hzだけ、回転させるには、この4つの内、1つだけ、採用すればよいのです。

私は、3→2の変化だけを、採用しました。

ですので、回転テーブルの位置は

   3 x 4 +2 = 14

ここに、右回りですから +1の数値を、書きます。(配列は、0から始まってるから、15番目ですよ!、私は、また、間違えた!)

同様の考察で、左回りの時

わたしは、3→1 を、採用しました。

したがって、配列の位置は

3 x 4 + 1 = 13

配列のテーブルの位置は 13番 (14番目の位置!)ここに、-1を書き込む。

従いまして、この配列 dir は

const signed char dir[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,-1,1,0 }; /* 回転方向テーブル */

これで、ワンクリックで、1Hz(乃至、その10倍等)の増減が、できることになりました。

ちょっと、もったいないかな....

最初にお見せした画像は、1クリックで、1KHzの回転に、しています。

まだまだ、未完成ですね。

 

H.17.11.25