1/fゆらぎ8bitマイコンへの挑戦

結論としては、短いループでの実装までしか到達しておらず、完璧なプログラムにできていません。
また時間が出来た時に再挑戦したいと思っております。

やりたい事のイメージ

こちら、P5.jsで実装した色変化の様子です。

上記は、stdlibから取ってきたランダム関数を元にして、間欠カオス法で1/fゆらぎを実装しています。
それだけではチラつきがちょっと目にキツいので、チラつきをあえて遅くして、フェードで色変化させています。

上記をC言語で書くとだいたい8kW(ワード数)くらい必要になります。
これを実際のジュエリーに使うマイコン、PIC10F222の512Wに収めなくてはなりません。

プログラム容量を約1/10にする挑戦です。
お、恐ろしい。。

やったこと色々

ランダム関数を作る

まずstdlibを捨てるのはPICマイコン使いであれば基本中の基本ですね。
彼(誰)を捨てるとなると、ランダム関数を自作しなくてはなりません。

自分でも書ける簡単なランダム関数を探しておりましたら、疑似乱数発生アルゴリズムのxorshiftがあるということでこちらを採用させていただきました。
発見のきっかけとなったサイト↓

でもこれでは変数が多く、容量の小さいマイコン(宣言できる変数がめっちゃ少ない)では使えません。
で、8bit用で探してこちらに辿り着きました。

ここはシンプルですがすごい。
プログラムのみならず、周期や初期値の検証までしてくれています。

こちらの8bit用を採用しました。

1/fゆらぎを作る

ネットに転がっている間欠カオス法のソースコードは、0~1の値を使った小数で処理するものばかりです。
こことか。

そう、float型を使わなくてはならない。
そんなの1個も入るわけがない。(1個なら入るかもw)

なので数式を256倍にして、int型に入るようにいじります。
ここで型変換による誤差が大分生じる気もしますが、致し方なし。

で、ここまで頑張った。

ランダム関数が8bit仕様なのがいけないのか、別の要因か、かなり短い周期で同じ光り方を繰り返しています。
シードを定期的に変えるようにすれば色々な光り方ができそうな気がするのですが、残念、もうこれ以上変数が入りません。

上の動画のソースコードです。

/*
 * File:   f1Fluctuation.c
 * Hard:   Projou(pic10f222)
 * Author: denkijoshi
 *
 * Created on 2020/06/06
 */

#include <xc.h>

#define MHz 000000 // for delay_ms(x) definition
#define _XTAL_FREQ 4MHz // for delay_ms(x) definition

//LED port define
#define port_red GP0
#define port_green GP2
#define port_blue GP1

//LED polarity setting. Anode common
#define off 1
#define on 0

//Chip configuration
//file:///C:/Program%20Files/Microchip/xc8/v1.36/docs/chips/12f222.html
// Watchdog Timer Enable bit: WDT disabled
// Code protection bit: Code protection off
// Master Clear Pull-up Enable bit: Pull-up enabled
// GP3/MCLR Pin Function Select bit: GP3/MCLR pin function is GP3
// Internal Oscillator Frequency Select bit: 4 MHz
#pragma config WDTE = OFF, CP = OFF, MCPU = ON, MCLRE = OFF, IOSCFS = 4MHZ

//Parameter setting
unsigned char pwm_red;
unsigned char f1Fluctuation();
unsigned int rl = 234;

char rnd_xorshift(){
	rl=rl^(rl<<3);
	rl=rl^(rl>>4);
	rl=rl^(rl<<1);
	rl=rl&0xff;
	return(rl);
}

void main(void) {
    OPTION   = 0b00010111;
    TRISGPIO = 0b11111000;
    GPIO = 0b11111111;
    ANS1 = ANS0 = 0; //digital port set

    while(1){
        pwm_red = f1Fluctuation();
        unsigned char outcnt = 10;
        while(outcnt --){
            for (unsigned char count=1; count!=255; count++){ //pwm
                if (count < pwm_red) port_red = on; else port_red = off;
                if (count < 10) port_green = on; else port_green = off;
                port_blue = off;
            }    
        }
    }
}

unsigned char f1Fluctuation(){
    static unsigned int x = 256;
    if(x < 128){ x = x + 2 * x * x / 256;} 
    else { x = x - 2 * (256 - x) * (256 - x) / 256;}
    if(x < 5 || x > 255){ x = rnd_xorshift();}
    return((unsigned char)(x));
}

ランダム関数だけの方が意外とそれっぽい

上記の失敗1/fよりも、ランダム関数だけの方が意外と炎に近い感じでした。
一応そちらの動画とソースコードも。

/*
 * File:   f1Fluctuation.c
 * Hard:   Projou(pic10f222)
 * Author: denkijoshi
 *
 * Created on 2020/05/29, 9:26
 */


#include <xc.h>

#define MHz 000000 // for delay_ms(x) definition
#define _XTAL_FREQ 4MHz // for delay_ms(x) definition

//LED port define
#define port_red GP0
#define port_green GP2
#define port_blue GP1

//LED polarity setting. Anode common
#define off 1
#define on 0

//Chip configuration
//file:///C:/Program%20Files/Microchip/xc8/v1.36/docs/chips/12f222.html
// Watchdog Timer Enable bit: WDT disabled
// Code protection bit: Code protection off
// Master Clear Pull-up Enable bit: Pull-up enabled
// GP3/MCLR Pin Function Select bit: GP3/MCLR pin function is GP3
// Internal Oscillator Frequency Select bit: 4 MHz
#pragma config WDTE = OFF, CP = OFF, MCPU = ON, MCLRE = OFF, IOSCFS = 4MHZ

//Parameter setting
unsigned char pwm_red;
unsigned int rl = 234;

char rnd_xorshift(){
	rl=rl^(rl<<3);
	rl=rl^(rl>>4);
	rl=rl^(rl<<1);
	rl=rl&0xff;
	return(rl);
}

void main(void) {
    OPTION   = 0b00010111;
    TRISGPIO = 0b11111000;
    GPIO = 0b11111111;
    ANS1 = ANS0 = 0; //digital port set

    while(1){
        pwm_red = rnd_xorshift();
        unsigned char outcnt = 10;
        while(outcnt --){
            for (unsigned char count=1; count!=255; count++){ //pwm
                if (count < pwm_red) port_red = on; else port_red = off;
                if (count < 10) port_green = on; else port_green = off;
                port_blue = off;
            }    
        }
    }
}

結局炎らしさは手書きした

1/fゆらぎに頼れないことがわかったので結局手書きしました。
強いオレンジ弱いオレンジをふわっと往復してからちょっとランダム、タイミングを少し変えてもうワンパターン。
こんな感じのものを手書きしました。

いつか、1/fに再挑戦しに帰ってきます。

/*
 * File:   illumination.c
 * Hard:   Projou(pic10f222)
 * Author: denkijoshi
 *
 * Created on 2020/05/29, 9:26
 */


#include <xc.h>

#define MHz 000000 // for delay_ms(x) definition
#define _XTAL_FREQ 4MHz // for delay_ms(x) definition

//LED port define
#define port_red GP0
#define port_green GP2
#define port_blue GP1

//LED polarity setting. Anode common
#define off 1
#define on 0

//Chip configuration
//file:///C:/Program%20Files/Microchip/xc8/v1.36/docs/chips/12f222.html
// Watchdog Timer Enable bit: WDT disabled
// Code protection bit: Code protection off
// Master Clear Pull-up Enable bit: Pull-up enabled
// GP3/MCLR Pin Function Select bit: GP3/MCLR pin function is GP3
// Internal Oscillator Frequency Select bit: 4 MHz
#pragma config WDTE = OFF, CP = OFF, MCPU = ON, MCLRE = OFF, IOSCFS = 4MHZ

//Parameter setting
unsigned char state;
int staytimer;
unsigned char pwm_red;
void change(unsigned char, unsigned char);
unsigned int rl = 234; //randam seed
char rnd_xorshift();
void fire(unsigned char);

void main(void) {
    OPTION   = 0b00010111;
    TRISGPIO = 0b11111000;
    GPIO = 0b11111111;
    ANS1 = ANS0 = 0;//digital port set

    pwm_red = 0;

    while(1){
        switch(state){
     
        //Programming change start.(Red,Green,Blue,Time[sec])

        case 0: change(240, 2); break;
        case 1: change(120, 2); break;
        case 2: change(240, 2); break;
        case 3: fire(1); break;
        case 4: change(240, 1); break;
        case 5: change(120, 1); break;
        case 6: change(240, 1); break;
        case 7: fire(2); break;
    
        //Programming change end.
        
        default: state=0;
        }
    }
}

void change(unsigned char red_value, unsigned char speed)
{
    if (pwm_red < red_value) pwm_red++; else if (pwm_red > red_value) pwm_red--;
    if(pwm_red == red_value) state++;

    while(speed--){
        for (unsigned char count=1; count!=255; count++){ //pwm
            if (count < pwm_red) port_red = on; else port_red = off;
            if (count < 10) port_green = on; else port_green = off;
            port_blue = off;
        }
    }
}

char rnd_xorshift(){
	rl=rl^(rl<<3);
	rl=rl^(rl>>4);
	rl=rl^(rl<<1);
	rl=rl&0xff;
	return(rl);
}

void fire(unsigned char cycle)
{
    pwm_red = rnd_xorshift();
    staytimer++;
    if(staytimer == cycle*60) { state++; staytimer = 0;}
    
    for (unsigned char count=1; count!=255; count++){ //pwm
        if (count < pwm_red) port_red = on; else port_red = off;
        if (count < 10) port_green = on; else port_green = off;
        port_blue = off;
    }
}

製品応用として

炎ゆらめく街灯にしてみました。
模型に使ってもらったり、作品に添えてもらったりなどしてもらえればなと想像しています。
充電池で出来ているので、配線の手間もなくちょこんと置けるので結構便利なんじゃないかしら。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です