ガンマ補正をPICで実装する(実装編)

通信(USART / RS-485 / DMX)

はじめに

調光カーブとは?(理論編)では、
実際のDMX値のフェードと、
人間の目で見たときに感じる調光との
ギャップについて解説しました。

今回は、Pythonでガンマテーブルを生成し、
PIC16F1938へ実装する方法について解説します。

PIC16Fでガンマ計算をさせる?

受信したDMX値に対して、
毎回ガンマ補正を計算することもできます。

しかし、ガンマ補正には指数関数の計算が必要です。

PIC16Fのような8bitマイコンでは、
このような浮動小数点演算は処理負荷が高くなります。

そこで今回は、あらかじめPythonで、
DMX値0~255に対応する256個のガンマ値を計算し、
PICのFlashへテーブルとして保存しておく方法を採用します。

実行時は計算ではなく配列を参照するだけなので、高速にガンマ補正を行えます。

Pythonで256段階の出力値を計算

C言語形式の gammaTable[256]を生成

PICのプログラムに貼り付けてへ書き込み

DMX値を配列の添字として参照

この流れで処理を行うようにしていきます。

Pythonでガンマテーブルを生成

まずは、第一ステップの
”Pythonで256個の値からなるガンマテーブルを作成”
です。

今回はVS CodeでPythonを実行し、
PICへ貼り付けるためのガンマテーブルを自動生成します。

python

# ガンマ値
gamma = 2.2

print("const unsigned char gammaTable[256] = {")

for i in range(256):
    value = round(pow(i / 255.0, gamma) * 255)
    end = "," if i != 255 else ""
    print(f"{value}{end}")

print("};")

round()で計算結果を0~255の整数に丸め、
PICへ貼り付けられるC言語の配列形式で
出力しています。

出力

c

const unsigned char gammaTable[256] = {
    0, 0, 0, 1, 1, 2,
    /*中略*/
    253, 254, 255
};

PICへ貼り付け

生成された配列をPIC側のCソースへ
貼り付け、つぎの流れで使用します。

c

/***********ガンマテーブルの宣言************/
const unsigned char gammaTable[256] = {
...
};

例えばDMX値が100なら、
gammaTable[100]に格納されている補正後の値が
PWMに代入されます。

c

/*********変数定義*************/
unsigned char pwm;

pwm = gammaTable[dmx];

c

/******PWMの出力*****/
pwm_apply(0,pwm); //1ch目にガンマ計算されたPWMが出力される

ガンマ補正はこのような形で、
難しい指数計算を毎回せず、
ガンマテーブルを1回参照するだけで、
補正後のPWM値を出力させることができます。

このコード一式を
「PIC16F1938で4ch DMX受信からオフセットをかけた4ch PWM出力」
に入れ込んでテストしました。

今回は1・2chに下限オフセット処理とガンマ補正を適用し、
3・4chはDMX値をそのままPWMへ出力して比較しました。

実際のコードと結果

下限オフセット処理で得られた0~255の値から、
ガンマテーブルを参照します。

unsigned char Apply_offset(unsigned char data_vol, unsigned char offset) {
    unsigned int span;
    unsigned int value;

    if (data_vol <= 2) return 0;
    if (data_vol >= 255) return 255;

    span = 255 - offset;

    value = offset + (((unsigned int) (data_vol - 2) * span) >> 8);

    if (value > 255) value = 255;
    
    return (unsigned char) gammaTable[value];

}

valueそのものを返す代わりに、
gammaTable[value]を返すことで、
下限オフセット処理ごの値にガンマ補正を
適応しています。

その結果を、2chずつに分けて
出力します

    unsigned char stored_offset[4];
    unsigned char write_val[4];

    for (char i = 0; i < 4; i++) {
        stored_offset[i] = EEPROM_Read(i);
    }

    while (1) {
        for (unsigned char i = 0; i < 4; i++) {
            if(i < 2){
                write_val[i] = Apply_offset(dimmer[i], stored_offset[i]);
            } else{
                write_val[i] = dimmer[i];
            }
            
            pwm_apply(i, write_val[i]);
        }
    }

これを実装したのが次の動画です。
左側のLED(オシロスコープの黄色波形)がガンマ補正ありの動きで、
右側のLED(オシロスコープの青色波形)がガンマ補正なしの動きとなります。


4chすべてを点灯すると動画では違いが分かりにくかったため、
今回は1ch目と4ch目のみを比較しています。

操作は、1ch目と4ch目のチャンネルフェーダをフルまで上げて、
マスターフェーダで操作しています。

ご覧のとおり、
ガンマ補正されている左のLEDは、
補正されていない右のLEDに比べて、
立上りがかなり緩やかになっているのが分かります。

また、補正されていない右のLEDは、
フェーダ操作の最初に一気に明るくなり、
後半から明るさの変化量が小さく感じます。
一方、左のLEDは、
フェーダの操作中ずっとLEDの明るさが
変化しているように感じます。

これがガンマ補正の効果です。

次回予告

次回は、DMX受信、下限オフセット、ガンマ補正など、これまで実装してきた処理を組み合わせ、調光アルゴリズムとしてまとめていきます。

コメント

タイトルとURLをコピーしました