2008/04/16(水)section記法のすけるとん

sectionのアレは以下のナニを見ればなんとなくわかるかも.カテゴリ記述は、'[]'で囲んだ文字列を置換してくれそうな記述があった.. titleごとに区切れそうですね.

以下のコードを参照すると, 解析手段が見えてきます.息抜きにperlのcodeを見ているだけですよ! 休憩時間...ね(笑

\lib\Satsuki\TextParser\Satsuki.pm (830,1):

# ■[02] table/リストのブロック処理、セクション処理
sub section {

# メモ忘れていたので遅ればせながら書き込み. オーバヒート気味だし帰るか....

2008/04/15(火)描画アルゴリズム

下調べの段階。昔も線分描画を作ったので流用可能ですが、再度調べてまとめておきたいと思います。といいつつ、ググって貼り付けるだけという酷い話で(ぉ


Fussyのホームページで詳しく解説されています。*1 X68erのようですねぇ。やはりこの辺の基礎がしっかりしているのには納得がいきますネ。パワーユーザばかりの印象があります。1人だけゲーマーとして持っている人を見た記憶がありますが('A`

とりあえず、補完のためコードだけ抜粋して流用したいと思います... dotを打つ処理が重いので、その点に関しては実装時に最適化をしてやる必要があります。とくにブレンド処理なんかを考えている場合は要注意です. VRAM外付け or 別デバイスのためにR/Wが遅いケースが致命傷です。マイコンのメモリに余裕があれば仮想VRAMとして割り当て、定期的に全画面転送したほうが無難かもしれません。

「Bresenhamの線分発生アルゴリズム」のサンプル・プログラム

void line( int x0, int y0, int x1, int y1, unsigned int col )
{
  int i;

  int dx = ( x1 > x0 ) ? x1 - x0 : x0 - x1;
  int dy = ( y1 > y0 ) ? y1 - y0 : y0 - y1;
  int sx = ( x1 > x0 ) ? 1 : -1;
  int sy = ( y1 > y0 ) ? 1 : -1;

  /* 傾きが1以下の場合 */
  if( dx >= dy ) {
    int E = -dx;
    for( i = 0 ; i <= dx ; i++ ) {
      pset( x0, y0, col );
      x0 += sx;
      E += 2 * dy;
      if( E >= 0 ) {
        y0 += sy;
        E -= 2 * dx;
      }
    }
  /* 傾きが1より大きい場合 */
  } else {
    int E = -dy;
    for( i = 0 ; i <= dy ; i++ ) {
      pset( x0, y0, col );
      y0 += sy;
      E += 2 * dx;
      if( E >= 0 ) {
        x0 += sx;
        E -= 2 * dy;
      }
    }
  }
}

クリッピング処理のサンプル・プログラム

/* 端点分類コード */
enum Edge {
  LEFT = 1,
  RIGHT = 2,
  TOP = 4,
  BOTTOM = 8,
};

/* 座標定義用構造体 */
typedef struct Coord
{
  int x;
  int y;
} Coord;

/* クリッピングエリア */
Coord Min, Max;

/*
  calc_seq_code : 端点分類コードを求める

  Coord c : 座標値

  戻り値 : 端点分類コード
*/
int calc_seq_code( Coord c )
{
  int code = 0;
  if( c.x < Min.x ) code += LEFT;
  if( c.x > Max.x ) code += RIGHT;
  if( c.y < Min.y ) code += TOP;
  if( c.y > Max.y ) code += BOTTOM;

  return( code );
}

/*
  calc_midP : 中点分割法による交点の算出メインルーチン

  int a0, int b0, int a1, int b1 : 線分の座標
  int clip_a : クリッピングする座標

  クリッピング対象は bの方になる。

  戻り値 : クリッピング結果
*/
int calc_midP( int a0, int b0, int a1, int b1, int clip_a )
{
  /* もう一方の端点がウィンドウの辺上にある場合の対処 */
  if ( a0 == clip_a ) return( b0 );
  if ( a1 == clip_a ) return( b1 );

  /*
    線分の始点と終点を求める
     (必ず sa < ea となるようにする)
  */
  int sa, sb, ea, eb;
  if ( a0 < a1 ) {
    sa = a0, sb = b0;
    ea = a1, eb = b1;
  } else {
    sa = a1, sb = b1;
    ea = a0, eb = b0;
  }

  int ca, cb;
  do {
    ca = ( sa + ea ) >> 1;
    cb = ( sb + eb ) >> 1;
    if ( ca < clip_a ) {
      sa = ca;
      sb = cb;
    } else {
      ea = ca;
      eb = cb;
    }
  } while ( ca != clip_a );

  return( cb );
}

/*
  calc_midP_x : 中点分割法による交点の算出(左右の辺)

  Coord c0, c1 : クリッピング前の線分座標
  int clip_x : 左(または右)の辺のX座標
  Coord* c : クリッピング後の座標

  戻り値 : クリッピング成功 >0 , 線分は完全に不可視 =0
*/
int calc_midP_x( Coord c0, Coord c1, int clip_x, Coord* c )
{
  int cy = calc_midP( c0.x, c0.y, c1.x, c1.y, clip_x );

  /* エリア外の場合はFalseを返す */
  if ( ( cy < Min.y ) || ( cy > Max.y ) ) return( 0 );

  c->x = clip_x;
  c->y = cy;

  return( 1 );
}

/*
  calc_midP_y : 中点分割法による交点の算出(上下の辺)

  Coord c0, c1 : クリッピング前の線分座標
  int clip_y : 上(または下)の辺のY座標
  Coord* c : クリッピング後の座標

  戻り値 : クリッピング成功 >0 , 線分は完全に不可視 =0
*/
int calc_midP_y( Coord c0, Coord c1, int clip_y, Coord* c )
{
  int cx = calc_midP( c0.y, c0.x, c1.y, c1.x, clip_y );

  /* エリア外の場合はFalseを返す */
  if ( ( cx < Min.x ) || ( cx > Max.x ) ) return( 0 );

  c->x = cx;
  c->y = clip_y;

  return( 1 );
}

/*
  calc_intsec_x : 数学的手法による交点の算出(左右の辺)

  Coord c0, c1 : クリッピング前の線分座標
  int clip_x : 左(または右)の辺のX座標
  Coord* c : クリッピング後の座標

  戻り値 : クリッピング成功 >0 , 線分は完全に不可視 =0
*/
int calc_intsec_x( Coord c0, Coord c1, int clip_x, Coord* c )
{
  int cy = ( c1.y - c0.y ) * ( clip_x - c0.x ) / ( c1.x - c0.x ) + c0.y;

  /* エリア外の場合はFalseを返す */
  if ( ( cy < Min.y ) || ( cy > Max.y ) ) return( 0 );

  c->x = clip_x;
  c->y = cy;

  return( 1 );
}

/*
  calc_intsec_y : 数学的手法による交点の算出(上下の辺)

  Coord c0, c1 : クリッピング前の線分座標
  int clip_y : 上(または下)の辺のY座標
  Coord* c : クリッピング後の座標

  戻り値 : クリッピング成功 >0 , 線分は完全に不可視 =0
*/
int calc_intsec_y( Coord c0, Coord c1, int clip_y, Coord* c )
{
  int cx = ( c1.x - c0.x ) * ( clip_y - c0.y ) / ( c1.y - c0.y ) + c0.x;

  /* エリア外の場合はFalseを返す */
  if ( ( cx < Min.x ) || ( cx > Max.x ) ) return( 0 );

  c->x = cx;
  c->y = clip_y;

  return( 1 );
}

/*
  クリッピング後の座標を求める

  int code : 端点分類コード
  Coord c0, c1 : 線分の座標
  Coord* c : クリッピング後の座標

  戻り値 : クリッピング成功 >0 , 線分は完全に不可視 =0
*/
int calc_clipped_point( int code, Coord c0, Coord c1, Coord* c )
{
  /* ウィンドウの左端より外側にある */
  if ( ( code & LEFT ) != 0 )
    if ( calc_intsec_x( c0, c1, Min.x, c ) )
    /* if ( calc_midP_x( c0, c1, Min.x, c ) ) */
      return( 1 );

  /* ウィンドウの右端より外側にある */
  if ( ( code & RIGHT ) != 0 )
    if ( calc_intsec_x( c0, c1, Max.x, c ) )
    /* if ( calc_midP_x( c0, c1, Max.x, c ) ) */
      return( 1 );

  /* ウィンドウの上端より外側にある */
  if ( ( code & TOP ) != 0)
    if ( calc_intsec_y( c0, c1, Min.y, c ) )
    /* if ( calc_midP_y( c0, c1, Min.y, c ) ) */
      return( 1 );

  /* ウィンドウの下端より外側にある */
  if ( ( code & BOTTOM ) != 0 )
    if ( calc_intsec_y( c0, c1, Max.y, c ) )
    /* if ( calc_midP_y( c0, c1, Max.y, c ) ) */
      return( 1 );

  return( 0 );  /* クリッピングされなかった場合、線分は完全に不可視 */
}

/*
  クリッピングメインルーチン

  Coord* c0 : 始点の座標
  Coord* c1 : 終点の座標

  戻り値 :
   >0 ... クリッピングされた
   0  ... クリッピングの必要なし
   <0 ... 線分は完全に不可視
*/
int clipping( Coord* c0, Coord* c1 )
{
  int code0, code1; /* 端点分類コード */

  code0 = calc_seq_code( *c0 );  /* 始点の端点分類コードを求める */
  code1 = calc_seq_code( *c1 );  /* 終点の端点分類コードを求める */

  /* 端点分類コードがどちらも0000ならばクリッピングは必要なし */
  if ( ( code0 == 0 ) && ( code1 == 0 ) ) return( 0 );

  /* 始点・終点の端点分類コードの論理積が非0ならば線分は完全に不可視 */
  if ( ( code0 & code1 ) != 0 ) return( -1 );

  /* 始点のクリッピング */
  if( code0 != 0 )
    if ( ! calc_clipped_point( code0, *c0, *c1, c0 ) )
      return( -1 );

  /* 終点のクリッピング */
  if( code1 != 0 )
    if ( ! calc_clipped_point( code1, *c0, *c1, c1 ) )
      return( -1 );

  return( 1 );
}

*1 : ほかのアルゴリズム説明に読み浸ってしまいました(^^; なんとも懐かしいやら, 説明が旨いやら... なんとなくやったけど忘れてるなぁ、というのが正直なところ... otz

2008/04/15(火)タイトル記法(アスタリスク)

コメントをいただいたので動作確認*1

*1 : ローカルで確認済み, コチラも記録しておきましょう. 強の日記は汚くなります

空白有で*が使える

* 空白有で*が使える

ただし, 記法との間の半角スペースも正しく適用され、タグとの間に隙間が空いています.コッチのほうが見やすい気がするのでかまやしないのですが...

記法とか、下手なスペースが入ると動作が変わるものと認識していたので、スペース挿入という考えが浮かばなかったですわ.

ついで, カテゴリ振り分けテスト. これはさすがに後で消すかな....

[工作][LCD] 秋月ドットマトリクス液晶(128x64)

タグテストだけだともったいないので。秋月のdot matrix液晶、64x64x2(128x64)の白黒ですが、6809系I/F(?)の液晶ですね.キャラクタ液晶と同じインタフェースなので, 一番下のインタフェース関数は流用できます.

128x64なので, 1KBのRAMを消費すればバックバッファを作ることができます.逆に作らずに処理をすると... 読み込み時間が遅くてタマラン状態になりそうですね.

mega128くらいは持ってきたほうが良さそうですねぇ...


[adiary][雑談] hnsからの移行

下記を満たしたとき、本格移行を考える。というか、すでに移行しているような気がするのだけれど(笑

  • 記法の理解、複数カテゴリが使えるかどうか。
  • コードを読もうと思えば頑張って読めるか。
  • さらに別の日記システムに移動させるときにexportが容易かどうか。
  • hnsからのコンバータの作成(元のタグからの置換で済むはずなので, エイヤで作れるはず...)

[adiary] タイトル記法に関すること

実行結果等メモしておく。wiki用には、結局、新たに"日記を書く"操作が必要なのだと思う。このタイトルだけwikiに登録なんてことができるなら、一日にpostする回数が大量になったりはしないのだけれど。

# 編集も大変になりそうだけれどね...

結果
見てのとおりなわけですが... カテゴリとは違う扱いになるんですね.見出し検索へのリンクになる.

hnsも検索だったので、等価といえば等価か. せっかくカテゴリ一覧が出るので, もったいない気はするけれども. wiki, タグ, カテゴリの、どれをキーにして記事を作っていくのが良いか、方針を決めてしまわないと、書いていく途中で変更するのは難儀ですね。

ん~。どうしたものかw

2008/04/14(月)記法の謎

見出し記法(*)にて、*文字が表示されない?

sub title(?)に '*'が使えない???

以下いずれもダメだった...

	**fname_04_\*.c
	**{fname_07_*.c}
	**fname_09_{*}.c

※ファイル名はfnameではないですが, 仕事のアレなので置換しました:)


Windows PCにlocalでhttpd, adiaryを入れて, 記法になれるようにしつつ, 作業日誌にしています.
pukiwikiでやっていたけれども, 同じ項目上書きで時間軸方向の流れが読めなかったため,
日記にしてみました。

ただ、最終的には結果だけをまとめてみたいので、"+wiki"というのに惹かれて
adiaryを試しているのですが...

wikiのトピックに期待して記述していかないと, 結局グダグダになるようですね.把握.

それはそうとして, 分類にてネタが重複するケースも想定していただけると助かりますねぇ.
内部管理で可能なら対応いただけるといいのですが.
タイトルのケツに'[タグ]'という感じで連ねておこうかとは考えていますけどね-.

2008/04/13(日)キャラクタ液晶を使う

キャラクタLCDライブラリを使う

自作のLCDライブラリもありますが、フルアセンブラということもあり、WinAVRで即使用可能なものをググってみました。やっぱりありますよねぇ・・・。

あきぼう氏のコードを流用

あきぼうのAVRで遊ぶ日々を参照しましょう。AVRのLCD操作関数から、lcd.h, lcd.cとを持ってきます。

lcd.hで、使用する環境に合わせて変更するべき箇所があります。適当に抜粋します。

//#define LCD_RW_PORT	PORTB	// Port contains RS-pin
//#define LCD_RW_DDR	DDRB	// Port contains RS-pin
//#define LCD_RW		1		// portbitNo connected to LCD-RS
#define N_LINE	4
#define N_COL   20
#define lcd_4
//#define lcd_nostring

これ以外に、LCD接続ポートの設定があります。上記を引用したのは、RW信号を未使用とした設定だったからです。あと、データ信号が4本使用時のため。コメントアウトされているところに、F_CPUをMakefileで定義するように記述されています。WinAVRを入れたディレクトリのavr/include/avr/util/delay.h を見るとわかりますが、空回りでウェイトを作っています。割り込み処理が多い場合は、規定時間をより多く取ってしまいそうですね。本当はfree run coutnerでスマートに作りたいところですが... まぁいいでしょう.

上記シンボルたちが何者か、と、lcd.cをさっと眺めて見ましたが、4bit data時には結構冗長かもしれませんねぇ。自作コードをgccにポートしたほうが良いかもしれませんね。余力があればもってきましょう。

ポーリングしてねぇ(confirm)

順番にやればいいのにざっとフレームを作ろうとして敗退。実機動作確認に至らず。時間待ちナシのAD変換できたら順次回すことにして、表示タイミングとチャタリング除去にタイマを使う方向で検討中。無論、AD変換精度向上のために、sleep命令は使用することにしますよ...。

で、先ほどwarning/errorなし状態にできたのですが・・・。lcd.cは、ウェイトで逃げてますね('A`
RW信号使ってないとは思っていましたが、コード自体も検証されていない様子。エラー出るしー。やっぱり自前の持ってきたほうがよさそうだわ。ぁ-どうすっかな。とりあえずコレでやり過ごして、その後持ってきたほうがいいかな。どんどんと完成が遠のいているのがよくわかるぜw

計算方法

固定少数とかなんだとか....。今回ADW(10bit)と、Vrefが2.5Vということで、綺麗に整数演算を済ますことができないと思われます。仕方がないので、可能な限り有効桁数を持って生きつつも演算量を減らすことを考えたいと思うます。
おおまかな考え方だけ転記しておきます。適宜、読み替えたりしてください。もちろんADWは複数読み出してはいけませんよ。また、右詰めを想定しています。

	i = v / r
	v = 2.5  * (ADW / 1024)
	i = 2.5 / 1024 * ADW / 1 [A]
	i = 2.5 *1000 / 1024 * ADW [mA]
		=> 0x9C4 * ADW >> 10
		0x9C4 = ((0x27 << 6) | 0x04)

	i = ((0x27 * ADW) << 6 + (0x04 * ADW)) >> 10
		(0x27 * ADW) >> 4 + (0x04 * ADW) >> 10
		(0x27 * ADW) >> 4 + (ADW >> 8)

uint16_tだと16bit精度あるので, 10bit x 6bitまでならばoverflowしない、ということを用いています。uint16_t * uint16_tだと32bitの結果になるので、コードサイズが肥大化するのは目に見えていますから・・・。こういった制約がわかっている状況では、コードに素直に書く前に自力で展開してやることも必要ですネ。

ついでにr=10のときの数式も張っておきます。

R=10
	i = v / r
	v = 2.5 * (ADW / 1024)
	i = 2.5 / 1024 * ADW / 10 [A]
	i = 2.5 *100 / 1024 * ADW [mA]
		=> 0x0FA * ADW >> 10
		0x0FA = ((0x03 << 6) | 0x2A)
	i = ((0x03 * ADW) << 6 + (0x2A * ADW)) >> 10
		= ((0x03 * ADW) << 6 >> 10) + (0x2A * ADW) >> 10
		= ((0x03 * ADW) >>4) + (0x2A * ADW) >> 10