Renesas Synergy™

FAQ 1007812 : テーブル参照と補間処理(プログラム例以外は共通、プログラム例は78K0)

マイコンを制御用で使用する場合には、通常は、何らかの入力情報の加工(入力情報から計算)を行ない、制御量を決めて出力することになります。



[はじめに]
制御を行なうためのマイコンで必要とされる演算(計算)処理は比較的簡単なものから非常に複雑なものまで、用途に応じて大きく変わります。複雑な計算を演 算だけで、決められた時間内に処理しようとすると、マイコンには非常に高い演算能力が要求されることがあります。その対応のためだけに演算能力を高くする のはあまり実用的とはいえません。そこで、用いられるのがテーブル参照です。

[テーブル参照の考え方]
テーブル参照は与えられた入力(x)に対して、あらかじめ結果を計算しておき、その結果を表(テーブル)で準備しておくものです。計算そのものはある程度 の範囲の入力値に対して実行しておく必要があるので、手間はかかります。しかし、PCやその他のコンピュータを利用することで比較的簡単にできます。



テーブルを作成する場合にはその大きさに注意する必要があります。上に例のように繰り返しの場合には全ての入力値の範囲でテーブルを作成しておく必要はな く、繰り返される部分を代表する範囲だけのテーブルを作成すれば十分です。その上で、入力される値を繰り返しの周期で割った余りやその等を用いてテーブル を参照すれば済みます。

[テーブル参照の使い方]
テーブル参照では、入力データを元にして、テーブルの中でその先頭から欲しいデータがある場所までアドレスの差(これをオフセット値と呼びます)を計算し ます。欲しいデータがバイト幅のデータであれば、入力データはそのままオフセット値として使えますし、16ビット幅のデータの場合には入力データを2倍す ることでオフセット値となります。このオフセット値をテーブルの先頭アドレスと加算すれば、欲しい値が格納されたメモリのアドレスを得ることができます。 そのアドレスのメモリを読み出すことで、欲しい演算結果が得られます。

テーブル参照命令
4ビットマイコンの75XLではテーブル参照専用命令(MOVT XA,@PCDEやMOVT XA,@PCXA)がありましたが、78K0ではテーブル参照命令と明記された命令はありません。それでは78K0でテーブル参照はできないのでしょうか?
実は、78K0ではROMもRAMも同一のメモリ空間に配置されているので、わざわざ専用の命令を使用する必要がないのです。テーブル参照で便利な命令と しては、ベースト・インデックス・アドレッシング命令があります。これはMOV A,[HL+B]やMOV A,[HL+C]と言った命令で、HLレジスタにテーブルの先頭アドレス、BまたはCレジスタにオフセット値を設定して実行することで、先頭アドレスにオ フセット値が加算され、そのメモリの値をAレジスタに読み込みます。


[テーブル参照の注意点]
テーブル作成の際に問題になるのは計算の精度や、準備するデータ数です。あまり精度を欲張りすぎるとテーブルが大きくなり、メモリを圧迫してしまいます。全てをテーブル参照で処理するのではなく、最後に補間処理を行なうことで、テーブルを小さくできます。補間処理を参照してください。

[補間処理]
テーブル参照で大まかな結果を得て、細かな部分は簡単な比例計算で求めるものです。テーブルとして準備するデータはある程度大きな間隔の分で済むので、テーブルの大きさを数分の1に抑えられます。



例えば、データ量を1/16にすることを考えると、入力データの下位4ビットが0000に対応するデータだけをテーブルとして準備しておきます。そこで、以下の処理を行なうと補間処理を行なうことができます。

入力データを1/16(右に4ビットシフト)した値をオフセット値としてテーブルを参照します。上の図でAの部分のデータが得られます。
次にテーブル上の次のデータ(上の図でBのデータ)との差を計算します。

これを1/16して、入力データの下位4ビットと乗算を行なえば、A点からの増分を得ることができます。(このままの順序で計算すると、丸め誤差が発生するので、計算の順序を変えて、乗算を先に計算する必要があります。)
この増分をテーブルから得られたA点の値に加算する。


基本的には加算と乗算やシフト程度ですむので、それほど時間はかからずに処理できるはずです。ただし、②で 計算した誤差には符号を含むので、その後の計算が面倒になります。そこで、実際の処理では加重平均を取るのが簡単です。これらの処理が等価であることは次 のように式を変形できることから分かります。ここで、mは分割数でnが入力データの下位ビットで示される数値とします。



この最後の式でわかるように、分割数から入力の下位を引いたものとテーブルから得られたデータAを乗算したものと、入力の下位とテーブルの次のデータBを乗算したものを加えて分割数で割っても同じ結果になります。
上の例では下位4ビットで示されるオフセット分だけテーブル上の次のデータ(Bのデータ)を加算し、残りはAの部分のデータを加算して1/16することになります。

プログラム例で は参考として、テーブルのデータを1/32に圧縮した場合を示しています。通常はここまでのデータ圧縮は必要なく、1/2に圧縮しても十分な場合もありま す。1/2圧縮では、補間処理としては単純な前後の値の平均で処理でき、プログラムを簡単にできるので、処理速度は速くなります。トータルのメモリ使用量 も単なるテーブル参照よりはるかに小さくできます。ただし、テーブルのデータ量が多くなるので、1/2圧縮の例は省きます。

補間による誤差について
補間処理を行なった場合の誤差について考えてみます。ここでは、sin0~sin(π/2)の範囲について10ビット分解能(1024分割する)で、結果を最大1023(=sin(π/2))とします。詳しい計算結果は省きますが、

256分割にして2ビット分の補間処理を行なった場合の誤差は最大で0.005
128分割にして3ビット分の補間処理を行なった場合の誤差は最大で0.019
64分割にして4ビット分の補間処理を行なった場合の誤差は最大で0.077
32分割にして5ビット分の補間処理を行なった場合の誤差は最大で0.31

となり、いずれの場合にも10ビット分解能の場合に対しては十分小さな誤差(0.5ビット以下)に収まっています。10ビット分のテーブルを用いた場合には2048バイトの容量が必要ですが、それを64バイトまで小さくしても十分な精度が得られることになります。


[プログラム例]
sin0~sinπ/2を10ビット精度で求めるプログラム例を示します。入力値としては、0~π/2 の範囲を1024分割して0~1023の10ビットで示すものとします。なお、入力パラメータからオフセット値を計算する部分で#00111110bとの 論理積をとっている部分を#01111110bに変更し、テーブルを拡張すれば0~2047の11ビットまで対応できるようになり、sin 0~sinΠまでの値を得ることができます。
このプログラムでは、入力パラメータ及び結果の格納用に16ビットの変数(CONVDATA)、それに引き続いた8ビット変数(WORK1)を作業用に使用し、これらをショート・ダイレクト・アドレッシング領域に確保します。

WORK00          DSEG    SADDRP
CONVDATA:
        DS      2               ;入力パラメータと結果の戻り領域
WORK01          DSEG    SADDR
WORK1:  DS      1


プログラムはサブルーチンとしており、大きくは4つの部分(3つはプログラム本体で、1つは内部のサブルーチン)から構成されます。

(1)最初の部分は入力パラメータからテーブル参照を行なう部分です。テーブルとしては1/32に圧縮したデータを格納しておきます。そのため、テーブル のオフセット値を求めるために、入力データを1/32しますが、データ長が10ビットであり2バイト必要なことから、これを2倍にすることになります。つ まり、入力パラメータを1/16すればテーブル参照のオフセット値となります。ここでは入力の下位10ビットのみが意味をもちます。そこで、上位4ビット は無視して、78K0の命令のデイジット・ローテート命令(ROR4 [HL])を使用して4ビット右回転することで1/16しています。このようにして得られたオフセット値(8ビット以下の値です)にテーブルの先頭アドレ スを加算することで、テーブル上のデータのアドレスを得ることができます。テーブルを200Hのように下位アドレスが00から配置してあれば、加算する必 要はなく、単にオフセットアドレスを下位アドレスに代入するだけでも処理できます(ここの例では一応、加算して求めています)。ここまでの部分は1/16 する処理を除けば通常のテーブル参照と同じです。

CONVSUB:
        PUSH    AX
        PUSH    HL
        MOVW    AX,CONVDATA     ;Aに上位、Xに下位
        MOVW    HL,#CONVDATA
        ROR4    [HL]            ;Aの下位4ビットと[HL]を1/16する
        MOV     A,[HL]
        AND     A,#00111110b    ;オフセット値を得る
        ADD     A,#LOW TABLE    ;下位アドレスを計算
        MOV     L,A
        MOV     A,#0
        ADDC    A,#HIGH TABLE   ;上位アドレスを計算
        MOV     H,A             ;HLはテーブルの対象を指す
        MOV     A,[HL]          ;下位バイトを読み出す
        MOV     CONVDATA,A      ;下位バイトの初期値としてセット
        MOV     A,[HL+1]
        MOV     CONVDATA+1,A    ;上位バイトのセット


(2)次は補間のための計算です。ここでは加重平均をとるためテーブルの値と次の値に重みを付けて積算します。補間が必要ない場合には既に出力パラメータ としてテーブル参照した結果がセットされているので、処理を終了します。補間を行なう場合には、補間データだけ次のデータを加算(実際には次のデータと乗 算)し、32から補間データを引いた数だけテーブルのデータを加算します。実際の計算はサブルーチンで処理します。ここでは、計算のための必要なパラメー タを準備しておき、必要な回数だけサブルーチンをコールします。計算は16ビット×8ビット+16ビットとなるので、処理を簡単にするため、必要なパラ メータはスタックにいれておき、HLレジスタを積算データのインデックスで使用しています。なお、積算した結果は16ビットに収まりますが、処理の共通化 のため、1バイトを作業用で使用しています。この作業用の1バイトはデータとしては意味がないのですが、初期値として0を入れてあります。

        MOV     A,X
        AND     A,#00011111b    ;補間のためのデータを得る
        BZ      $EXITS          ;補間不要なら終了
;
;       補間(加重平均)のための準備
;       (スタックに計算データを入れておく)
;
        MOV     WORK1,#0        ;上位データを初期化
        MOV     X,A             ;補間データをXレジスタにセット
        MOV     A,[HL+3]        ;次のデータの上位をセット
        PUSH    AX              ;計算データをセーブ
        MOV     A,[HL+2]        ;次のデータの下位をセット
        PUSH    AX              ;計算データをセーブ
        MOV     A,#31
        SUB     A,X             ;32に対する残り数を計算
        MOV     X,A
        MOV     A,[HL+1]        ;テーブルの上位データをセット
        PUSH    AX              ;計算データをセーブ
        MOV     A,[HL]          ;テーブルから下位データを
        MOVW    HL,#CONVDATA    ;ポインターを設定
;
;       テーブルの検索データ分の積算を行なう
;
        CALL    !SEKIWA         ;下位データ演算
        POP     AX
        INCW    HL
        CALL    !SEKIWA         ;上位の計算と結果の積算
;
;       補間データから次のデータ重み分の積算を行なう
;
        POP     AX
        DECW    HL
        CALL    !SEKIWA
        POP     AX
        INCW    HL
        CALL    !SEKIWA


(3)積算した結果を1/32して最終的な結果を計算します。ここまでの結果は10ビットと5ビットの乗算と同じであり、結果は16ビット以下に収まりま す。結果を切り捨てではなく四捨五入するために積算結果の0.5に相当する値を加算してから1/32処理を行ないます。この処理はこれまでの処理の結果、 HLがCONVDATA+1を指していることを利用しています。また、1/32するのに、4ビットの左回転を行ない、バイトをずらして考えることで1 /16し、その後さらに1/2処理を行います。

        MOV     A,CONVDATA      ;結果の下位をロード
        ADD     A,#00010000b    ;四捨五入のために0.5分を加算
        BNC     $NEXT
        INC     CONVDATA+1      ;上位に桁上げ
NEXT:   AND     A,#11100000b    ;未使用ビットをマスク
        ROR     A,1             ;結果を下位4ビットに
        ROR     A,1             ;A=00XXX000、CY=0
        ROR     A,1             ;A=000XXX00、CY=0
        ROR     A,1             ;A=0000XXX0、CY=0
        ROL4    [HL]            ;結果を1/16する
        RORC    A,1             ;さらに1/2する
        XCH     A,[HL]          ;上位3ビットと下位を交換
        RORC    A,1             ;下位も1/2する
        MOV     CONVDATA,A      ;結果を保存

EXITS:  POP     HL
        POP     AX
        RET


(4)実際に積算のための積和演算を行なう内部のサブルーチンです。A、Xレジスタに乗数と被乗数を設定し、HLで積算されるデータを指した状態でコールします。A、Xレジスタの内容を乗算した結果を[HL]及び[HL+1]に積算していきます。

;       8ビットの積和演算
;  (結果は16ビットに収まるものとする)
;
SEKIWA:
        MULU    X               ;重みの計算
        XCH     A,X             ;結果の上位と下位を交換
        ADD     A,[HL]          ;下位を積算
        MOV     [HL],A          ;結果をセーブ
        MOV     A,X             ;結果の上位をもってくる
        ADDC    A,[HL+1]        ;上位への桁上げ計算
        MOV     [HL+1],A        ;上位バイトをセーブ
        RET


(5) 0(0)~1024(=π/2)に対応したテーブルです。テーブルのデータとしては1024個のデータではなく、1/32に圧縮した(32個ごとのデータだけをとりだした)データです。
対象範囲を0(0)~2048(=π)に広げたい場合には、このテーブルに0~1022のデータを逆の順で追加します。

TAREA   CSEG    AT      200H
;
;       sin0~sin(π/2)を32分割したときの値を1023倍した値のテーブル

TABLE:  DW      0,      50,     100,    150
        DW      200,    249,    297,    345ソフトウェア
        DW      392,    437,    482,    526
        DW      568,    609,    649,    687
        DW      723,    758,    791,    822
        DW      851,    877,    902,    925
        DW      945,    963,    949,    992
        DW      1003,   1012,   1018,   1022
        DW      1023

 

2048(=π)以上への対応
Π以上への対応はテーブルやオフセットのマスクだけでは対応できません。これは結果の値の範囲(負の値をどのように表現するか)を見直す必要があるからです。
このプログラムでは結果の値の範囲として0~1023を扱えるようになっています。従って、sinπ/2~sin3π/2を0~1023で表す必要があります。具体的には、1(sinπ/2)を1023、0(sinπ)を512、-1(sin3π/2)を1で表現することで対応できるようになります。
負の数を2の補数で表現する場合には、このプログラムでは対応できませんので、このプログラムの外で符号を付ける(0から引く)ような処理を追加する必要があります。

 

 

他にご質問がございましたら、リクエストを送信してください