読者です 読者をやめる 読者になる 読者になる

Lynx-EyEDの電音鍵盤 新館

広帯域制御屋の駄文とか

inPulse watchでマンデルブロのジュリア集合で遊ぶ

積んどくだけの『高級積みARM基板』にならないようにちょっと遊んでいます。
定番のマンデルブロ集合演算です。C言語での開発で、inPulseのAPIも揃ってるので開発は容易ですが、つまづいた点を少し。

  • タスクはOSが管理している

Pulse OSというOSが管理しています。結構時間にシビアです。5秒に1回、メインルーチンの

void main_app_loop()

にリターンする必要があります。サブルーチンで永延と(5秒以上)作業してるとWDTによりリセットされてしまいます。
サブルーチンも無駄なループを避けるとか、変数をstaticにするとか、これらを考慮した記述に書き換えます。

  • RGBデータはcolor24_tという構造体に格納

なのでRGBそれぞれ8bitずつ。例えば赤を255にしたければ

color24_t * color;
color->red = 255;

みたいな感じ。あとはAPI(pulse_draw_point24())にこのポインタを投げるだけでOLEDに表示してくれる 

pulse_draw_point24(bg_color);

フラクタル描画は以前MARY-DUINOで作った時のコードを書き換えています。
http://www45.atwiki.jp/marykiban/pages/51.html

■使い方(Mac OSX)

  • inPulse SDKをまだDLしてないならここからダウンロード。解凍すると、[pulse_sdk]フォルダが出来るのでデスクトップとか、使いやすい任意の場所に移動する。
  • このエントリの最後に貼付けてるソースコードをまるっとコピーして[pulse_app2.c]などどいう名前で保存。*1
  • pulse_app2.cを pulse_sdk/src/にコピー。くわえて、pulse_sdkフォルダにTerminal.appで移動
$ cp examples/featured_apps/Psychedelic\ Clock/include/app_resources.h src/

app_resources.hはPsychedelic Clockのものを使用。

  • MacとinPulse watchとのペアリング終わっているならL2CAPServerを起動する
$ java  -d32 -jar ./tools/L2CAPServer/L2CAPServer.jar 
  • [command ⌘ ]+[T]で新しいウィンドウタブで
$   python compileandload.py -d 00:50:xx:xx:xx:xx

これでしばらく待つとコンパイルとデプロイが完了するはず
f:id:Lynx-EyED:20111217005443j:image
ぬーん

■ソースコード*2

/**
 * Mandelbrot's Julia set. (z_{n+1} = z_{n}^2 + C)
 **/

#include <pulse_os.h>
#include <pulse_types.h>
#include "app_resources.h"

// This is a text box widget that will define the bounds of where to render the text
struct PWTextBox text_box;

// Text widget that stores required information about rendering the text
// It must be initialized before it can be used
struct PWidgetTextDynamic text_widget;

// Text buffer to store text to be rendered
char text_buffer[16];

volatile int count;
volatile float mag;
volatile float offsetx;
volatile float offsety;

color24_t bg_color = {255, 0, 0, 0};

void complex_mult(float ar,float ai,float br,float bi,float *cr,float *ci) {
    // C = A * B
    *cr = ar * br - ai * bi;
    *ci = ar * bi + ai * br;
}

float complex_abs2(float r,float i) {
    float ar,ai;
    complex_mult(r,i,r,-i,&ar,&ai);
    return ar;
}

void psychedlic_background()
{
    //pulse_set_draw_window(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); // indicate drawing field
    static int x;
    static int y;
    //for(int y = 0; y < SCREEN_HEIGHT; y++)
    //{
        pulse_set_draw_window(0, y, SCREEN_WIDTH - 1, y); // indicate drawing field
        for(x = 0; x <SCREEN_WIDTH ; x++)
        {
            
            float cr = (x - 64) / 64. / mag + offsetx;
            float ci = (y - 64) / 64. / mag + offsety;
            float zr = 0;
            float zi = 0;
            int t;
            for(t = 0; t < 512; t++)
            {
                if(complex_abs2(zr,zi) > 4) break;
                complex_mult(zr,zi,zr,zi,&zr,&zi);
                zr += cr;
                zi += ci;
            }
            cycle_color(&bg_color, t, 64);
            pulse_draw_point24(bg_color);
        }
    y++;
    
    if(y >= SCREEN_HEIGHT) 
    {
        y = 0;
        mag = mag * 1.2;
        if(count++ > 100)
        {
            offsetx = -0.756423894274328;
            offsety = 0.064179410646170;
            mag = 1;
            count = 0;
        }  
    }
}

void cycle_color(color24_t * color, int count, int base)
{
    //int r,g,b;
	int d = (count % base) * 256 / base;
	int m = (int)(d / 42.667);
    
	switch(m) {
		case 0: color->red=0;          color->green=6*d;          color->blue=255;         break;
		case 1: color->red=0;          color->green=255;          color->blue=255-6*(d-43);break;
		case 2: color->red=6*(d-86);   color->green=255;          color->blue=0;           break;
		case 3: color->red=255;        color->green=255-6*(d-129);color->blue=0;           break;
		case 4: color->red=255;        color->green=0;            color->blue=6*(d-171);   break;
		case 5: color->red=255-6*(d-214); color->green=0;         color->blue=255;         break;
		default: color->red=0;         color->green=0;            color->blue=0;           break;
	}
}

void print_time_into_text_buffer()
{
    struct pulse_time_tm current_time;
    pulse_get_time_date(&current_time);
    uint32_t hour = current_time.tm_hour;
    if(hour == 0)
    {
        hour = 12;
    }
    else if(hour > 12)
    {
        hour -= 12;
    }
    sprintf(text_buffer, "%d:%02d", current_time.tm_hour, current_time.tm_min);
}

void draw_clock()
{
    // Initialize the text box parameters
    text_box.top = 10;
    text_box.right = SCREEN_WIDTH;
    text_box.left = 0;
    text_box.bottom = SCREEN_HEIGHT;

    // Configure the text style
    // We want it to be centered both vertically and horizontally in the text box
    // and to truncate any text if it won&#39;t fit in the text box
    enum PWTextStyle style = (PWTS_TRUNCATE | PWTS_CENTER | PWTS_VERTICAL_CENTER);

    // Initialize the text widget
    pulse_init_dynamic_text_widget(&text_widget, text_buffer, FONT_CLOCKOPIA_32, COLOR_BLACK24, style);

    // Print the time from the RTC (real time clock) in to the text buffer
    print_time_into_text_buffer();

    text_widget.font.resource_id = FONT_CLOCKOPIA_32;

    // Set the font color to be psuedo-random
    text_widget.font.color = COLOR_BLACK24;

    // Render the text in the text box area
    pulse_render_text(&text_box, &text_widget);
}

// This function is called once after the watch has booted up
// and the OS has loaded
void main_app_init()
{
    
    
    count = 0;
    mag = 1;
    offsetx = -0.756423894274328;
    offsety = 0.064179410646170;
    
    

}

void main_app_handle_button_down()
{
    pulse_update_power_down_timer(100);
}

void main_app_handle_button_up()
{

}


// Main loop. This function is called frequently.
// No blocking calls are allowed in this function or else the watch will reset.
// The inPulse watchdog timer will kick in after 5 seconds if a blocking
// call is made.
void main_app_loop()
{
    psychedlic_background();
    //draw_clock();
}

// This function is called whenever the processor is about to sleep (to conserve power)
// The sleep functionality is scheduled with pulse_update_power_down_timer(uint32_t)
void main_app_handle_doz()
{

}

void main_app_handle_hardware_update(enum PulseHardwareEvent event)
{

}

*1:pulse_sdk/examples/featured_apps/Psychedelic Clock/pulse_app.cをフラクタル描画用に書き換えてるだけです

*2:ボタンを押したら演算を止めるとか、気の利いた事はしていないので電池を使い尽くすまで描画し続けるので注意