あけおめことよろ 新規プロジェクトの紹介

あけましておめでとう🌅⛩🎍

今年もよろしくお願いします。

最近新しいプロジェクトを始めました。MachiKaniaのハードでCコンパイラテキストエディタなどを乗せたネイティブコンパイラの開発をしています。

このシステムは編集、コンパイルや実行に必要な一通りのアプリケーションを搭載しています。ここで一連のフローを示します。

1、プログラム(.c)を作成

2、コンパイルする(.c→.s)

このコンパイルアセンブリリストを生成します。(.s)

3、アセンブリする。(.s→.o)

2、3はすべてのソースコードに対して行います。

4、リンクする(全ての.o→実行ファイル)

5、実行(a.outフォーマットローダを使う。実行に関して、ここで解説しています

実行時の各セクションも確認しましょう。

.text

実行コードが入ります。静的です。

.data

0以外の初期値を持つデータ(変数)がここに入ります。静的です。

.bss

0に初期化するデータ(変数)がここに入ります。実行前に初期化します。静的です。この領域はa.outフォーマットの中には入りません。dataのサイズを減らすためのセクションです。

.heap

heapに使います。下に向かって伸びます。動的に変わります。

stack

stackに使います。上に向かって伸びます。動的に変わります。

このstackを使い果たすとstackオーバーフローとなり、heapを侵食します。MMU(memory management unit)があればいろんなことできそうなんですがね、、まあないものはしょうがないです。

RetroBSDでは128kのRAMを搭載しているので様々なアプリをRAMで実行できるのですが、今回のプロジェクトは64kBをターゲットとしているので結構厳しいです。コンパイル時はFlashで実行します。(つまりシステムにビルドインする)動作も50MHz付近なのでshもFlashで動かす予定です。(スワップ実装するのも大変だから、、)

ファイルシステムもFatファイルシステムにしました(FatFS)。内蔵Flashのストレージも作ってます。(80kBだけど減るかも、、それと大きいものコンパイルするときはアセンブリリストが長くなり、容量足りなくなるから、そういう場合はSDカード用意してね)

ってことで現在テトリスまで動きました。本家のソースコードはほとんどいじってません。(PS2キーボード化と、乱数を実装と音声部削除)コンパイルはオフスクリーンで3秒位です。RAMは13kBくらいしか使っていないので残り45KBもあります。音声など実装できると思います。Tスピンなどもできるかもしれません。ハードドロップ、ホールドも欲しいなぁ。

http://www.ze.em-net.ne.jp/~kenken/tetris/index.html

操作はキーボードに移しました。

ソースコード

Hackadayにも載せました

SPIx3でNTSC

PIC32でNTSCシグナルの生成に成功(?)したのでメモ。

こちらがけんけんさんがやってくださったものです。よく書けてるのでこっち見てください。

PIC32MK でNTSCシグナルを生成するとき、普通だったらタイミングに沿ってCPUで出力します。ただ、リソースの問題で、CPUの使用率は60%を越えるため、アプリケーションに割ける時間が少なくなってしまいます。

今回はSPIという通信規格で、シグナルを生成することに成功しました。SPIは、本来クロックに従ってデータ出力をします。その際、1ビットずつシフトするため、負荷が理論上は1ドット16サイクル3バイト出力と、減るはずです。今はまだCで書いているため重いですが、最適化次第でさらに軽くなると思います。

仮に1ビットだとすると以下のように使います。

Read more

マイコンで3D表示(図解)

3D表示に関してここにまとめておきます。

1、座標変換

3DのCGの世界ではカメラを回すのではなく、世界を回します。また、世界を歪めます(透視投影)。しかし線形変換では透視は与えられないので別の方法を使います。

透視はzで割ることで与えられます。しかし実際にはzは重なり計算に用いられるので代わりにwを使います。そのため通常はカメラの位置は焦点になります。投影行列*カメラ行列が基本的な変形式となり、そこに必要に応じてオブジェクト回転などを入れます。

ここで透視投影と平行投影の違いを見てみましょう。

平行投影の場合、以下のような表示となります。物体の距離にかかわらず等しい大きさで描画されます。これはzの値を破棄することで投影することができます。

また、今回は透視投影をします。これは完全にカメラに視点をもたせ、そこからの空間の広がりを描画します。

ここで描画空間も確認しておきましょう。描画空間とはこの中の点を描画する、すなわちクリップする範囲を定めるための空間です。視点に近い方ではnearを用いて遠いところではfarでクリッピングします。

2,描画の方針

上の図でも説明がありますが、少ないメモリで3D計算をする場合はzバッファの値も少ないため、工夫して節約する必要があります。そのため、奥行きの情報、またピクセルの情報は一ライン分のみ保持して、一気に描画します。実際には、ピクセルの情報のみ2ライン分保持することもあります。(ダブルバッファリング)

zスパンの計算は普通に奥行きを計算して手前側を描画するだけに過ぎません。このため手前から描画することで二重の描画を防ぎ、速度を向上させることもできます。

3、三角形の形をあらかじめ計算

三角形を描くためには、あらかじめ辺の傾き(増加量)を計算しておくと高速です。

開始点vx1,vx2を一定の増加量ずつ足しあわせていきます。三角形には変曲点がありますのでその文を考慮しておきましょう。この場合ではvx2が途中で変化します。忘れずに実装しましょう。同様にzバッファやw補間、uv補間もします。

三角形内も補間したい場合は、まず辺で補間し、その両端を更に補間をかけます。いずれも増加量を予め計算しておくことで高速に求めることができます

テクスチャマッピングについて知るべきこと

テクスチャマッピングで何を注意する必要があるかというと歪みの補正です。下の図を見てください。

これを三角ポリゴンに分解して、以下のようになります。

気づいていらっしゃるかもしれませんが、手前の三角系のほうが、大きく表示されています。これは点を保管するだけでは、奥行きの情報まで補間しないと正しいテクスチャマッピングができないということを暗示しています。そのためマッピングの際は予め1/wを計算しておき、その値で描く要素をかけて、その1/wを補間した結果で割ることで補正できます。

https://github.com/elect-gombe/esp32_mmd

マイコンで3Dグラフィクス(2)

前の記事

ラインスキャン法による三角ポリゴン描画

うむ。こんにちは。ポリゴン描画にいよいよかかります。こいつがいちばんの難所で速度が出る出ないの境目がここにあります。今回はラインスキャンというCPUで描画するのに適した高速かつ穴のないレンダリングを目指します。

ラインスキャンと言う名の通り1行ずつ描画していきます。y方向に1行ずつ増えた時のxの増加量を65536倍して計算しておき、それに沿って描画を行います。zバッファのzの値も線型性を利用してx同様に増加量を計算しておきます。

描画開始点と終了点のそれぞれに対して増加量を計算しておきます。開始地点と終了地点がもとまれば後はzを補間をかけながらzテストを行い描画するだけです。何度も言いますがzは線型性を持つからこれでおkです。

ここでクリップについて考えましょう。今ここではいちばん上の頂点がはみ出しています。描画を開始するx座標は二点になります。たいていの場合はクリップはこれだけ考えておけばなんとかなります(笑

3Dのスキャンライン法では1行ずつ各ポリゴンを描画します。各ポリゴンは複数行にまたがることが多いので描画はその回数分だけ繰り返し行います。

最後に固定小数点の行方について考察します。固定小数点では以下のメリットがあります

  • まず早い。FPU無しでも早い。
  • そして加算誤差がない。

もちろんデメリットも

  • 誤差が問題になることが多い。特に16.16フォーマットの問題とか8.24だとオーバーフローも
  • 記述がめんどくさい。かけ算は64ビット計算してシフトする。割り算はあらかじめ繰り上げしておこなう。
  • 頭が痛くなる

ってところでしょうか?32.32フォーマットだと128ビット計算が必要ですね。これは非現実的です。

PCで実際に動作させたところ、80ポリゴンで6000fpsほどだったので1/200の性能だと仮定すると30fpsは出ると思います。

実際に測ってみると

  • MIPS32(PIC32)
  • 160x120
  • 20MHzくらい
  • 20ポリくらい

で20fpsくらいでした。このアルゴリズムの性質上描画する面積に比例して処理速度が増加するため、ドットを置くスピードに強く依存してしまうようです。

例えば60MHz、SPI液晶、など相手だったらかなりスムーズに動くと思います。ただそれでも解像度次第ですね。

あと気になってたんですがアルゴリズム次第ではもう少し上がりそうな気がします。例えば辺の傾きって辺を共有していると計算量が減るとか。その処理には割り算使ってるのでそこが重いんですよ。後は多角形に対応するとか?←これが一番効果ありそう。

マイコンで3Dグラフィクス(1)

マイコンで3D計算というと重すぎるのかと思いましたが以外といけるようなのでそのメモメモ📝

昔のDSとかPS1とかはFPUが付いていないので固定小数点演算でグラフィクスの処理を行いました。現在ではFPUもあるしハードウェアアクセラレータがありますしこんなことしなくてもハードウェアでポリゴンの描画までやってくれますが、やっていることは今も昔も変わりません。

フローを書きましょうね。そうです。だいたいこんな感じです。

オブジェクトの頂点リストをを変形、回転、移動する。それをカメラ行列でカメラの前に持ってきて投射行列を用いて投射する。

投射したら必要に応じて表面の向きの判定をし、面の法線ベクトルから面の輝度計算を行う。頂点リストを整理してy方向でソート後ライン毎に描画をする。

1、投射行列を考える。

A、オブジェクトを考える。

3Dではオブジェクトが置いてあってカメラが設置されてとか色々したいですよね。でも実際一つ一つ行いません。これには訳があって例えば回転ではsin,cosの計算が必要です。膨大な量のデータ一つ一つに対してここに変換しては時間がかかります。ここでは線型性を利用して合成関数をあらかじめ作って最後に変換します。

またこのとき4次元で物事を考えます。並行投影の場合はそこまで大きな恩恵はないのですが(あるけど)透視投影(視点を持った遠近法を適用した投影法)になるとこれが非常に大きな意味を持ちます。まあとにかく4次元です。

3次元だと拡大回転は表現できますが移動などに対して表現ができません。最初はそこで4次元にしたと思ってもらってもいいかな?

物体に対して[回転→拡大→移動]をして欲しい形に欲しい所に移動させたりします。行列では移動させる行列に拡大をかけて、回転をかけます。合成関数の変換なのでこれでおkです。逆にすると大変なことになります。

B、透視投影行列を考える

いよいよ投影座標を考えましょう。投影するためにはカメラもつけたい所ですね。そのためにカメラを移動するのではなく逆にオブジェクトを回転させるようにしましょう。先ほど行った操作と逆なのです逆行列を用意すればいいですね。最後に透視投射行列、これはもういいですね。アスペクト比とかを合わせるのに使います。画面外判定を楽にしたりもできます。投影後に隠面消去を行うためのz成分を求めるのにも使います。

そして頂点データを求め出した行列で変形、透視投射します。最後にw成分で割るのを忘れずに。こうして求め出したベクトル[x,y,z]は、x,yが画面上座標、zはzバッファに用います。

2、陰と明るさを求める

面と光の方向を考慮し、明るさを考えます。並行照明の場合が考えやすいでしょう。太陽を思い浮かべましょう。影は考えません。陰です。降り注ぐ方向と面の法線ベクトルとの内積でその角度を求めて色にかけて簡単に作れます。

‪透視投影が終わったらいよいよポリゴン描画します。私はメモリの都合上スキャンライン法を用いました。ポリゴン数が少ない場合はzバッファ法と大して速度は変わりません。アルゴリズムもほとんど同じで違う点はzバッファのサイズを削減しただけです。削減によってRAMサイズを縮小でき、マイコンの限られた残りのRAMをポリゴン保存やテクスチャなどに活用できます。

スキャンライン法では1ライン分のzバッファのみ保持します。そのためにポリゴン描画も同一ライン上にあるものを全て描画させます。そのためポリゴン描画アルゴリズムレンダリングと同様にスキャンライン法を用いると相性がとても良いでしょう。

長くなったので続きは後で書きます。次はスキャンライン法(多角形描画アルゴリズム)について解説します。

続く

 

I2Cの使い方 回路編

0、なぜI2C?

I2Cは2本の信号線によるシリアル通信の一種です。低速ではあるものの、多数のスレーブに接続できるためセンサーの接続やコントロールICの制御など様々なところで使われます。

I2Cは通信方式がシリアル通信の中でも特殊なため使用する前にその仕組みを理解する必要があります。

[caption id="attachment_4471" align="alignnone" width="474"]基本回路図 基本回路図[/caption]

この回路の1番の特徴はスレーブの数に基本的には制約がありません。マスターとしては普通はマイコンを使います。スレーブには例えばセンサーとか様々なデバイスを接続します。マスターには複数デバイスが使えますが通常は一つです。

で、なぜこのような便利な仕様になっているのかについて解説します。ポイントはオープンコレクタ接続(オープンドレイン)にあります。アイドル時(通信していない間)はプルアップ抵抗を通して2つの端子はHi出力になります。ここで、なんらかの原因でLo出力にしたい場合は何かのデバイスがLoを出力します。Hi出力にしたい場合はHiを出力ではなく、Z(ハイインピーダンス)に設定します。通常はプルアップを通してHi状態になります。それにより、LoとHiで電源が貫通することが防げます。(通常の機器はLoとLoで出力が競合しても壊れません。)

1、電源について

ここでポイントとなるのがハイインピーダンスとなるという点です。そのためスレーブには最大プルアップ抵抗につながれている電源の電圧がかかります。そのため複数電源が必要な回路もも場合によってはレベル変換なしにサポートすることができます。

ここで5Vと3V3のラインがあるとしましょう。もし3V3ラインのデバイスが5Vトレラントの場合はレベル変換は必要ありません。5Vの電源にプルアップしましょう。そうでない場合は諦めてレベル変換をしましょう。

例えばSTMマイコンではIOがトレラントになっているピンが多いのでそのピンを使えば回路が簡単になります。

2、プルアップ抵抗

プルアップ抵抗は通信に非常に大切です。この値を誤って設定すると信号伝達が正しく行えなくなる恐れがあります。

バス間には浮遊容量成分が存在します。それらを十分にドライブできる通信バス、例えばプッシュプルタイプのSPIなどにはプルアップ抵抗は通信に必要ないことがあるでしょう。しかし今ここではHiへのドライブは抵抗を通してのみ行われます。そのためプルアップ抵抗は速度に合わせて十分に余裕を持ってドライブできるように値を設定すべきです。

400kbpsで通信したいのであれば、2k2などを使いましょう。逆に速度が低速であれば通信時の消費電力を考えて5kをつけることもあるでしょう。デバイスが増えれば容量成分も増加します。抵抗値を下げる必要もあるかもしれません。波形を観測する必要も出てくるかもしれません。

まだ制限付きのXC32コンパイラ使ってたのか?

XC32コンパイラはMIPS32アーキテクチャコンパイルツールです。実はこれ、ソースコードがmicrochip社によって公開されてます。これはつまり、「自分でリビルドして制限解除してね」と言っていることになります。

簡単に説明するともともとXC32コンパイラではGPLのライブラリなどが使用されています。(LGPLではないよー)そのためソースコードを入手できるというわけです。 そのコンパイルした生成物も著作権GPLに則って再度公開もできます。

今回はリビルドされたchipkitの付属のコンパイラを使用します。ただしスタートアップルーチンや、リンカスクリプトは用意しましょう。コンパイラはMPLABxに登録もできます。が、そうでなくともMakefile書けばなくても使えます。

最適化でサイズは結構減ります。-mips16が半分くらいまで減って、 -Osをつけるとさらに減ります。 また、速度の最適化をかけるとコードサイズは増えることがありますが、そのぶん早くなります。

というわけで生成物をGithubに転がしておきます。見たい方などはどうぞ。