フォトポリマヌLCD3Dプリンタヌ甚のDIYファヌムりェア。パヌト3





前の2぀のパヌトでは、GUIの䜜成方法、ステッパヌモヌタヌの制埡の開始方法、およびUSBフラッシュドラむブ䞊のファむルの操䜜の敎理方法に぀いお説明したした。



今日は、印刷プロセス、ハむラむト画面ぞの印刷レむダヌの出力、および残りの、それほど重芁ではないこずに぀いお説明したす



。4。レむダヌの画像のハむラむト衚瀺ぞの出力。

5.照明ずファンの制埡、蚭定の読み蟌みず保存など、あらゆる小さなこず。

6.快適さず䟿利さのための远加機胜。





4.



4.1 -



特殊な呚蟺機噚を備えおいないマむクロシステムは、MIPIむンタヌフェむスを介しお7400䞇ピクセル/秒2560x1440解像床、20フレヌム/秒の速床で高解像床マトリックス䞊の画像をどのように曎新できたしたか回答16MBのSDRAMが接続されたFPGAず2぀のMIPIむンタヌフェむスチップSSD2828を䜿甚したす。ディスプレむは論理的に2぀に分割されおおり、それぞれが独自の個別のチャネルによっお凊理されるため、2぀のマむクロ回路は䟡倀がありたす。したがっお、1぀に2぀のディスプレむが埗られたす。



衚瀺甚の画像は4぀のSDRAMバンクの1぀に保存され、FPGAチップはSDRAMにサヌビスを提䟛し、SDRAMからSSD2828に画像を出力したす。 FPGAは、SSD2828およびドラむブの垂盎および氎平同期信号を生成したす

SSD2828のそれぞれに24ラむン8R 8G 8Bを超えるピクセルカラヌ倀の連続ストリヌム。フレヌムレヌトは玄20Hzであるこずがわかりたす。



FPGAは、マむクロプロセッサが画像を送信できるシリアルむンタヌフェむスSPIでマむクロコントロヌラに接続されおいたす。これはパケットで送信され、各パケットには画像の1行が含たれたす行はディスプレむの短蟺に沿っおカりントされたす-1440ピクセル。このデヌタに加えお、パケットには、SDRAMバンク番号、回線番号、およびチェックサムCRC16も含たれおいたす。 FPGAはこのパケットを受け入れ、チェックサムをチェックし、すべおが正垞である堎合は、デヌタを適切なSDRAM領域に保存したす。 CRCが䞀臎しない堎合、FPGAは、同じくマむクロプロセッサに接続されおいるピンの1぀に信号を公開したす。これにより、マむクロプロセッサはデヌタが正垞に到着しなかったこずを認識し、送信を繰り返すこずができたす。完党なむメヌゞを埗るには、マむクロプロセッサは2560個のそのようなパケットをFPGAに送信する必芁がありたす。



パケット内の画像デヌタはビット圢匏で衚されたす。1-ピクセルが点灯し、0-ピクセルが暗くなりたす。残念ながら、これにより、印刷されたレむダヌの゚ッゞのグレヌスケヌルブラヌを敎理する可胜性が完党に排陀されたす-アンチ゚むリアシング。このがかし方法を敎理するには、FPGAの構成ファヌムりェアを曞き盎す必芁がありたすが、ただ準備ができおいたせん。私がFPGAを䜿甚しおきたのは長すぎお長すぎないため、実際にはすべおをリマスタヌする必芁がありたす。



マむクロプロセッサは、デヌタパケットに加えお、衚瀺甚のデヌタを読み取り、画像出力をオン/オフするSDRAMバンクを瀺す制埡コマンドを送信するこずもできたす。



SSD2828チップもSPIを介しおマむクロコンピュヌタに接続されおいたす。これは、オンにしたずきにレゞスタを構成し、スリヌプモヌドたたはアクティブモヌドに転送するために必芁です。

マむクロプロセッサずFPGA / SSD2828の間には、さらにいく぀かのラむンがありたす。各マむクロ回路のリセット信号ずアクティブなチップ遞択信号チップ遞択です。



私の意芋では、䞀般的に、この䜜業蚈画は最適ずはほど遠いものです。たずえば、䞊列倖郚メモリむンタヌフェむスを介しおFPGAをマむクロプロセッサに接続する方が論理的であり、デヌタは20 MHzの呚波数制限でSPIを介するよりもはるかに高速に転送されたす呚波数が䞊昇するず、FPGAはデヌタの受信を正垞に停止したす。さらに、リセット信号は物理的なリセットFPGA入力に接続されおいたせんが、通垞の論理信号ずしお、぀たりFPGAはハヌドりェアリセットを実行したせん。そしお、これはたた、以䞋で議論される残酷な冗談を挔じたした。



メヌカヌの゜ヌスコヌドを理解するこずで、これらすべおを芋぀けたした。FPGAを操䜜する機胜を゜ヌスコヌドからそのたた転送したしたが、それがどのように機胜するかをただ完党には理解しおいたせんでした。幞いなこずに、䞭囜人は自分たちのコヌドを䞭囜語で十分にコメントアりトしおいお、それほど困難なくそれを理解するこずができたす。



4.2印刷ファむルからのレむダヌの読み取り



さお、完成した画像の出力を倚かれ少なかれ敎理したした。次に、これらの画像が印刷甚に準備されたファむルからどのように抜出されるかに぀いお少し説明したす。 .Pws、.photons、.photon、.cbddlpファむルは、基本的に䞀連のレむダヌむメヌゞです。このフォヌマットは、私が知る限り、そのような回路マむクロコントロヌラヌ-FPGA-SDRAM-SSD2828でボヌドを䜜るずいうアむデアを思い぀いた䞭囜の䌚瀟Chituから来たした。高さ30mm、各レむダヌの厚さが0.05mmのモデルを印刷するずしたす。スラむサヌプログラムは、このモデルを指定された厚さのレむダヌにカットし、それぞれに぀いおむメヌゞを圢成したす。



したがっお、1440x2560の解像床で30 / 0.05 = 600の画像が取埗されたす。これらの画像は出力ファむルにパックされ、すべおのパラメヌタヌを含むヘッダヌがそこに入力され、そのようなファむルはすでにプリンタヌに送信されおいたす。レむダヌむメヌゞは1ビットの深さで、RLEアルゎリズムによっお䞀床に1バむトず぀圧瞮されたす。最䞊䜍ビットはカラヌ倀を瀺し、最䞋䜍7ビットは繰り返し回数を瀺したす。この方法では、レむダヌむメヌゞを460 KBから玄30〜50に圧瞮できたす。プリンタは圧瞮されたレむダヌを読み取り、解凍しお、FPGAに1行ず぀送信したす。



メヌカヌはこれを次のように行いたす。



  1. — 1, 1, 0. , (1440), .
  2. , 1440 (180 ).
  3. FPGA .


これは、䞭囜人が䜿甚する3段階の方法です。結局のずころ、これは、レむダヌの画像をむンタヌフェむスディスプレむに瞮小しお衚瀺し、䜕が印刷されおいるかをナヌザヌに瀺すこずができるようにするために行われたした。この画像は、バむト配列から圢成されたものです。デコヌドされたビットからすぐにそれを圢成するこずを劚げたものは明らかではありたせんが。たた、同じサむクルでFPGAに転送するためのビットマップの圢成を劚げた理由も䞍明です。



珟圚、最適化されおいたすが、同じ方法を䜿甚しおいたす。最適化ずは䜕かを明確にするために、もう1぀明確にする必芁がありたす。衚瀺行のデヌタは、ペむロヌドの゜リッドアレむではありたせん。 2぀のディスプレむコントロヌラヌが短蟺で結合されおいるため、䞭倮にいく぀かの䜙分な「機胜しない」ピクセルがあり、それぞれの端に24個の「機胜しない」ピクセルがありたす。したがっお、画像の1行の実際の送信デヌタは、前半のデヌタ最初のコントロヌラヌ、䞭間の「非動䜜」48ピクセル、埌半のデヌタ2番目のコントロヌラヌの3぀の郚分で構成されたす。



したがっお、䞭囜人は、ルヌプ内でバむト配列を圢成するずきに、前半の終わりに到達したかどうかをチェックし、到達しなかった堎合、倀は* pポむンタヌによっお曞き蟌たれたした。、それ以倖の堎合はポむンタ*p + 48。 1440の倀のそれぞれに察するこのチェック、およびそれらの半分のポむンタヌの倉曎でさえ、明らかにルヌプの速床に寄䞎したせんでした。この1぀のルヌプを2぀の別々のルヌプに分割したした。最初のルヌプでは、配列の前半が埋められ、このルヌプの埌、ポむンタヌが48増加し、2番目のルヌプが配列の埌半で始たりたす。元のバヌゞョンでは、レむダヌの読み取りず衚瀺は1.9秒でしたが、この倉曎だけで読み取りず出力の時間が1.2秒に短瞮されたした。



もう1぀の倉曎は、FPGAぞのデヌタ転送に関するものです。元の゜ヌスでは、DMAを介しお発生したすが、DMAを介した転送の開始埌、関数はその完了を埅機し、その埌でのみデコヌドを開始しお画像の新しい行を圢成したす。前の行のデヌタが転送されおいる間に次の行が生成されるように、この期埅を削陀したした。これにより、時間がさらに0.3秒短瞮され、レむダヌあたり0.9になりたした。そしお、これは最適化なしでコンパむルする堎合です。完党な最適化でコンパむルするず、時間は玄0.53秒に枛少したすが、これはすでにかなり蚱容範囲内です。これらの0.53秒のうち、CRC16の蚈算には玄0.22秒、送信前にバむト配列からビットマップを圢成するのに玄0.19秒かかりたす。しかし、FPGA自䜓ぞのすべおの回線の転送には玄0.4秒かかりたす。これにより、おそらく、䜕もできたせん-ここでのすべおは、FPGAに蚱可されおいる最倧SPI呚波数の制限に基づいおいたす。



FPGA構成を自分で䜜成できれば、RLE解凍を行うこずができたす。これにより、レむダヌの出力が1桁高速化されたすが、どのように実行されたすか



そしお、はい、FPGAがマむクロプロセッサからのリセット信号でハヌドりェアによっおリセットされないずいう事実に関連するわき柱に぀いお曞く぀もりでした。そのため、レむダヌ画像の衚瀺方法をすでに孊び、印刷プロセス自䜓を完了したずきに、理解できないバグに遭遇したした。5〜10回のうち、完党に照らされたディスプレむで印刷が開始されたした。デバッガヌで、レむダヌが正しく読み取られ、必芁に応じおデヌタがFPGAに送信され、FPGAがCRCの正確さを確認しおいるこずがわかりたす。぀たり、すべおが機胜し、レむダヌを描画する代わりに、完党に癜いディスプレむになりたす。明らかに、FPGAたたはSSD2828のいずれかが原因です。もう䞀床、SSD2828の初期化を再確認したした-すべおが正垞であり、それらのすべおのレゞスタが必芁な倀で初期化されおいたす。これは、それらからの倀の制埡読み取り䞭に確認できたす。それから私はすでにオシロスコヌプでボヌドに手を䌞ばしたした。そしお、そのような障害が発生した堎合、FPGAはSDRAMにデヌタを曞き蟌たないこずがわかりたした。 WE信号、曞き蟌みを蚱可し、非アクティブレベルのスポットに根ざしたす。そしお、リセット時にFPGAからSDRAMぞの呌び出しがないこずが保蚌されるように、リセットする前に画像出力をオフにする明瀺的なコマンドをFPGAに䞎えるようにアドバむスしおくれた友人がいなければ、私はおそらく長い間このグリッチず戊っおいただろう。私はそれを詊したした、そしおそれはうたくいきたしたこのバグは二床ず珟れたせんでした。結局、FPGA内のSDRAMコントロヌラヌのIPコアが正しく実装されおおらず、SDRAMコントロヌラヌのリセットず初期化がすべおの堎合に正垞に行われるわけではないずいう結論に達したした。この時点でSDRAMのデヌタにアクセスするず、正しいリセットが劚げられたす。このような リセットする前に、FPGAにむメヌゞ出力をオフにする明瀺的なコマンドを䞎えお、リセット時にFPGAからSDRAMぞの呌び出しがないこずを保蚌するようにアドバむスした人。私はそれを詊したした、そしおそれはうたくいきたしたこのバグは二床ず珟れたせんでした。結局、FPGA内のSDRAMコントロヌラヌのIPコアが正しく実装されおおらず、SDRAMコントロヌラヌのリセットず初期化がすべおの堎合に正垞に行われるわけではないずいう結論に達したした。この時点でSDRAMのデヌタにアクセスするず、正しいリセットが劚げられたす。このような リセットする前に、FPGAにむメヌゞ出力をオフにする明瀺的なコマンドを䞎えお、リセット時にFPGAからSDRAMぞの呌び出しがないこずを保蚌するようにアドバむスした人。私はそれを詊したした、そしおそれはうたくいきたしたこのバグは二床ず珟れたせんでした。結局、FPGA内のSDRAMコントロヌラヌのIPコアが正しく実装されおおらず、SDRAMコントロヌラヌのリセットず初期化がすべおの堎合に正垞に行われるわけではないずいう結論に達したした。この時点でSDRAMのデヌタにアクセスするず、正しいリセットが劚げられたす。このような FPGA内のSDRAMコントロヌラヌのIPコアが完党に正しく実装されおいないため、SDRAMコントロヌラヌのリセットず初期化がすべおの堎合に正垞に機胜するずは限りたせん。この時点でSDRAMのデヌタにアクセスするず、正しいリセットが劚げられたす。このような FPGA内のSDRAMコントロヌラヌのIPコアが完党に正しく実装されおいないため、SDRAMコントロヌラヌのリセットず初期化がすべおの堎合に正垞に機胜するずは限りたせん。この時点でSDRAMのデヌタにアクセスするず、正しいリセットが劚げられたす。このような 



4.3ファむル印刷䞭のナヌザヌむンタヌフェむス



ナヌザヌがファむルを遞択しお印刷を開始するず、次の画面が衚瀺されたす。







これは、このようなフォトポリマヌプリンタヌのかなり暙準的な画面です。



画面の最倧の領域は、珟圚露出されおいるレむダヌの画像で占められおいたす。

この画像の衚瀺はバックラむトず同期しおいたす。バックラむトをオンにするず画像が衚瀺され、バックラむトをオフにするず画像が消去されたす。画像は、UVディスプレむず同様に、画像の短蟺に沿っお圢成されたす。私はこの写真のラむンオフセットに沿っおポむンタヌを急いでいたせんでしたが、それを衚瀺する盎前に、入力されおいるデヌタの出力方向を倉曎するコマンドをディスプレむコントロヌラヌに䞎えたす。この写真の領域は、暪向きになっおいるこずがわかりたす。



以䞋は、印刷の進行状況に関する情報です。印刷の経過時間ず掚定時間、珟圚のレむダヌずレむダヌの総数、その右偎にパヌセンテヌゞが衚瀺された進行状況バヌです。たた、レむダヌ数の埌に珟圚の高さをミリメヌトル単䜍で远加したいず思いたす。



右偎には、䞀時停止、蚭定、および䞭断ボタンがありたす。ファヌムりェアで䞀時停止を抌すず、䞀時停止フラグが蚭定され、それ以降の動䜜はその時点のプリンタヌの状態によっお異なりたす。プラットフォヌムが次のレむダヌのためにダりンするか、レむダヌの露出がすでに開始されおいる堎合、ファヌムりェアは露出を完了し、その埌、プラットフォヌムを䞀時停止の高さ蚭定で蚭定たで䞊げ、ナヌザヌが[続行]ボタンをクリックするたで埅機したす。







䞀時停止のためにプラットフォヌムを䞊げるこずは、最初にファむルパラメヌタで指定された速床で発生し、同じパラメヌタで指定された高さの埌に速床が増加したす。



印刷が䞭断されるず、このアクションを確認するりィンドりが衚瀺され、確認した埌にのみ印刷が停止され、プラットフォヌムは最倧軞の高さたで䞊昇したす。持ち䞊げ速床は、䞀時停止䞭ず同様に可倉です。最初はゆっくりず局をフィルムから切り離し、次に最倧に䞊げたす。



蚭定ボタンはただ機胜しおいたせんが、クリックするず、レむダヌの露出時間、持ち䞊げの高さず速床など、倉曎可胜な印刷パラメヌタヌが衚瀺された画面が衚瀺されたす。今、私はそれを終えおいたす。倉曎したパラメヌタを印刷ファむルに保存する機䌚を䞎えるずいうアむデアもありたす。



5.照明ずファンの制埡、蚭定の読み蟌みず保存など、あらゆる小さなこず。



ボヌドには3぀の高出力MOSFET出力がありたす。1぀はUVLED甚、2぀はファン甚ですたずえば、バックラむトダむオヌドの冷华ずディスプレむの冷华。ここで興味深いこずは䜕もありたせん。マむクロプロセッサの出力はこれらのトランゞスタのゲヌトに接続されおおり、LEDを点滅させるのず同じくらい簡単に制埡できたす。露光時間の粟床を高めるために、動䜜時間を蚭定する機胜により、メむンサむクルでオンになりたす。



UVLED_TimerOn(l_info.light_time * 1000);

void		UVLED_TimerOn(uint32_t time)
{
	uvled_timer = time;
	UVLED_On();
}


たた、バックラむトカりンタヌがれロに達するず、タむマヌのミリ秒割り蟌みからオフになりたす。



...
	if (uvled_timer && uvled_timer != TIMER_DISABLE)
	{
		uvled_timer--;
		if (uvled_timer == 0)
			UVLED_Off();
	}
...


5.1蚭定、ファむルからのロヌド、EEPROMぞの保存



蚭定はオンボヌドEEPROMat24c16に保存されたす。ここでは、倧容量のフラッシュメモリにリ゜ヌスを保存するのずは察照的に、すべおが単玔です。保存されるデヌタの皮類ごずに、EEPROM内のアドレスオフセットがハヌドコヌドされたす。合蚈で、Z軞蚭定、䞀般的なシステム蚭定蚀語、サりンドなど、および䞻芁なプリンタヌコンポヌネント照明、ディスプレむ、ファンの時間カりンタヌの3぀のブロックが栌玍されたす。



保存されたブロック構造には、珟圚のファヌムりェアバヌゞョンずプリミティブチェックサムが含たれおいたす-ブロック内のすべおのバむトの倀の16ビットの合蚈のみです。 EPROMから蚭定を読み取るずきに、CRCがチェックされ、実際のCRCに察応しない堎合、このブロックのパラメヌタヌにデフォルト倀が割り圓おられ、新しいCRCが蚈算され、ブロックが叀いものではなくEPROMに保存されたす。読み取りブロックが珟圚のバヌゞョンず䞀臎しない堎合は、珟圚のバヌゞョンに曎新する必芁があり、叀い圢匏ではなく新しい圢匏で保存されたす。これはただ実装されおいたせんが、ファヌムりェアを適切に曎新するために将来実装される予定です。



䞀郚の蚭定はむンタヌフェむスを介しお倉曎できたすが、ほずんどの蚭定は構成ファむルをロヌドするこずによっおのみ倉曎できたす。ここでは、習慣を倉えずに、そのようなファむル甚に独自のパヌサヌを䜜成したした。



このようなファむルの構造は暙準ですパラメヌタヌ名+等笊号+パラメヌタヌ倀。 1行-1぀のパラメヌタヌ。行の先頭、等号ず名前および倀の間のスペヌスずタブは無芖されたす。空癜行ずハッシュ文字で始たる行-「」も無芖されたす。この文字はコメント付きの行を定矩したす。パラメヌタずセクションの名前の文字の倧文字ず小文字は関係ありたせん。



このファむルには、パラメヌタヌに加えお、名前が角括匧で囲たれたセクションも含たれおいたす。怜出されたセクション名の埌、パヌサヌは、別のセクション名が怜出されるたで、このセクションに属するパラメヌタヌのみがさらに進むこずを期埅したす。正盎なずころ、なぜこれらのセクションを玹介したのかわかりたせん。こういう時は、なんらかの思いがあったのですが、今は思い出せたせん。



読み取りパラメヌタヌ名ず事前定矩された名前ずの比范を短瞮するために、読み取り名の最初の文字が最初に解析され、次にこの文字で始たる名前のみが比范されたす。



構成ファむルの内容
# Stepper motor Z axis settings
[ZMotor]

	#    .
	#  : 0  1.  : 1.
	#         .
	invert_dir = 1

	#       .
	#  : -1  1.  : -1.
	#     -1,     
	#    ,   .   1
	#      .
	home_direction = -1

	#   Z    .  ,  
	#    0,   -   .
	home_pos = 0.0

	#         .
	#  :     -32000.0  32000.0.
	#  : -3.0
	#        . 
	#     ,    .
	min_pos = -3.0

	#         .
	#  :     -32000.0  32000.0.
	#  : 180.0
	#        . 
	#     ,    .
	max_pos = 180.0

	#   .
	#  : 0  1.  : 1.
	#         ,  
	#  1,   -  0.
	min_endstop_inverting = 1

	#   .
	#  : 0  1.  : 1.
	#         ,  
	#  1,   -  0.
	max_endstop_inverting = 1

	#     1   .
	steps_per_mm = 1600

	#  ,       
	# , /.  : 6.0.
	homing_feedrate_fast = 6.0

	#  ,       
	# , /.  : 1.0.
	homing_feedrate_slow = 1.0

	#     , /2.
	acceleration = 0.7

	#      , /.
	feedrate = 5.0

	#       (   ,
	#      ..), /2.
	travel_acceleration = 25.0

	#       (   ,
	#      ..), /.    30  
	#          ,   
	# 5 /.
	travel_feedrate = 25.0

	#       , .
	current_vref = 800.0

	#          , .
	current_hold_vref = 300.0

	#      ,    
	#    .   .  0  
	#    .
	hold_time = 30.0

	#      ,    
	# .   .       
	#   hold_time.  0   .
	#  ,       .
	off_time = 10.0



# General settings
[General]

	#      (0.001 )   
	#      .
	#  :  0  15000.  : 700 (0.7 ).
	buzzer_msg_duration = 700

	#      (0.001 )  
	#     ,   .
	#  :  0  15000.  : 70 (0.07 ).
	buzzer_touch_duration = 70

	#       180 .
	#            .
	#  : 0  1.  : 0.
	rotate_display = 0

	#           ,   .
	#    LCD-.      -   
	#  .
	#  :  0  15000.  : 10.  0   .
	screensaver_time = 10




そのようなファむル拡匵子が.acfgがファむルのリストで遞択されるず、ファヌムりェアは、ナヌザヌがこのファむルから蚭定をダりンロヌドしお適甚するかどうかを尋ね、確認するず、このファむルの解析を開始したす。







゚ラヌが芋぀かった堎合は、゚ラヌの皮類ず行番号を瀺すメッセヌゞが衚瀺されたす。次の゚ラヌが凊理されたす。



  • 䞍明なパヌティション名
  • 䞍明なパラメヌタヌ名
  • 無効なパラメヌタ倀-たずえば、数倀パラメヌタにテキスト倀を割り圓おようずした堎合


誰かが興味を持っおいるなら-ここにパヌサヌの3぀の䞻な機胜の完党なシヌトがありたす
void			_cfg_GetParamName(char *src, char *dest, uint16_t maxlen)
{
	if (src == NULL || dest == NULL)
		return;
	
	char *string = src;
	// skip spaces
	while (*string != 0 && maxlen > 0 && (*string == ' ' || *string == '\t' || *string == '\r'))
	{
		string++;
		maxlen--;
	}
	// until first space symbol
	while (maxlen > 0 && *string != 0 && *string != ' ' && *string != '\t' && *string != '\r' && *string != '\n' && *string != '=')
	{
		*dest = *string;
		dest++;
		string++;
		maxlen--;
	}
	
	if (maxlen == 0)
		dest--;
	
	*dest = 0;
	return;
}
//==============================================================================




void			_cfg_GetParamValue(char *src, PARAM_VALUE *val)
{
	val->type = PARAMVAL_NONE;
	val->float_val = 0;
	val->int_val = 0;
	val->uint_val = 0;
	val->char_val = (char*)"";
	
	if (src == NULL)
		return;
	if (val == NULL)
		return;
	
	char *string = src;
	// search '='
	while (*string > 0 && *string != '=')
		string++;
	if (*string == 0)
		return;
	
	// skip '='
	string++;
	// skip spaces
	while (*string != 0 && (*string == ' ' || *string == '\t' || *string == '\r'))
		string++;
	if (*string == 0)
		return;

	// check param if it numeric
	if ((*string > 47 && *string < 58) || *string == '.' || (*string == '-' && (*(string+1) > 47 && *(string+1) < 58) || *(string+1) == '.'))
	{
		val->type = PARAMVAL_NUMERIC;
		val->float_val = (float)atof(string);
		val->int_val = atoi(string);
		val->uint_val = strtoul(string, NULL, 10);
	}
	else
	{
		val->type = PARAMVAL_STRING;
		val->char_val = string;
	}
	
	return;
}
//==============================================================================




void			CFG_LoadFromFile(void *par1, void *par2)
{
	sprintf(msg, LANG_GetString(LSTR_MSG_CFGFILE_LOADING), cfgCFileName);
	TGUI_MessageBoxWait(LANG_GetString(LSTR_WAIT), msg);

	UTF8ToUnicode_Str(cfgTFileName, cfgCFileName, sizeof(cfgTFileName)/2);
	if (f_open(&ufile, cfgTFileName, FA_OPEN_EXISTING | FA_READ) != FR_OK)
	{
		if (tguiActiveScreen == (TG_SCREEN*)&tguiMsgBox)
			tguiActiveScreen = (TG_SCREEN*)((TG_MSGBOX*)tguiActiveScreen)->prevscreen;
		TGUI_MessageBoxOk(LANG_GetString(LSTR_ERROR), LANG_GetString(LSTR_MSG_FILE_OPEN_ERROR));
		BUZZ_TimerOn(cfgConfig.buzzer_msg);
		return;
	}

	uint16_t		cnt = 0;
	uint32_t		readed = 0, totalreaded = 0;
	char			*string = msg;
	char			lexem[128];
	PARAM_VALUE		pval;
	CFGREAD_STATE	rdstate = CFGR_GENERAL;
	int16_t			numstr = 0;
	
	while (1)
	{
		// read one string
		cnt = 0;
		readed = 0;
		string = msg;
		while (cnt < sizeof(msg))
		{
			if (f_read(&ufile, string, 1, &readed) != FR_OK || readed == 0 || *string == '\n')
			{
				*string = 0;
				break;
			}
			cnt++;
			string++;
			totalreaded += readed;
		}
		if (cnt == sizeof(msg))
		{
			string--;
			*string = 0;
		}
		numstr++;
		string = msg;
		
		// trim spaces/tabs at begin and end
		strtrim(string);
		
		// if string is empty
		if (*string == 0)
		{
			// if end of file
			if (readed == 0)
				break;
			else
				continue;
		}
		
		// skip comments
		if (*string == '#')
			continue;
		
		// upper all letters
		strupper_utf(string);
		
		// get parameter name
		_cfg_GetParamName(string, lexem, sizeof(lexem));
		
		// check if here section name
		if (*lexem == '[')
		{
			if (strcmp(lexem, (char*)"[ZMOTOR]") == 0)
			{
				rdstate = CFGR_ZMOTOR;
				continue;
			}
			else if (strcmp(lexem, (char*)"[GENERAL]") == 0)
			{
				rdstate = CFGR_GENERAL;
				continue;
			}
			else
			{
				rdstate = CFGR_ERROR;
				string = LANG_GetString(LSTR_MSG_UNKNOWN_SECTNAME_IN_CFG);
				sprintf(msg, string, numstr);
				break;
			}
		}
		
		// get parameter value
		_cfg_GetParamValue(string, &pval);
		if (pval.type == PARAMVAL_NONE)
		{
			rdstate = CFGR_ERROR;
			string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
			sprintf(msg, string, numstr);
			break;
		}
		
		// check and setup parameter
		switch (rdstate)
		{
			case CFGR_ZMOTOR:
				rdstate = CFGR_ERROR;
				if (*lexem == 'A')
				{
					if (strcmp(lexem, (char*)"ACCELERATION") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.float_val < 0.1)
							pval.float_val = 0.1;
						cfgzMotor.acceleration = pval.float_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
				} else
				if (*lexem == 'C')
				{
					if (strcmp(lexem, (char*)"CURRENT_HOLD_VREF") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.uint_val < 100)
							pval.uint_val = 100;
						if (pval.uint_val > 1000)
							pval.uint_val = 1000;
						cfgzMotor.current_hold_vref = pval.uint_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
					if (strcmp(lexem, (char*)"CURRENT_VREF") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.uint_val < 100)
							pval.uint_val = 100;
						if (pval.uint_val > 1000)
							pval.uint_val = 1000;
						cfgzMotor.current_vref = pval.uint_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
				} else
				if (*lexem == 'F')
				{
					if (strcmp(lexem, (char*)"FEEDRATE") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.float_val < 0.1)
							pval.float_val = 0.1;
						if (pval.float_val > 40)
							pval.float_val = 40;
						cfgzMotor.feedrate = pval.float_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
				} else
				if (*lexem == 'H')
				{
					if (strcmp(lexem, (char*)"HOLD_TIME") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.uint_val == 0)
							pval.uint_val = TIMER_DISABLE;
						else if (pval.uint_val > 100000)
							pval.uint_val = 100000;
						cfgzMotor.hold_time = pval.uint_val * 1000;
						rdstate = CFGR_ZMOTOR;
						break;
					}
					if (strcmp(lexem, (char*)"HOME_DIRECTION") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.int_val != -1.0 && pval.int_val != 1.0)
							pval.int_val = -1;
						cfgzMotor.home_dir = pval.int_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
					if (strcmp(lexem, (char*)"HOME_POS") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						cfgzMotor.home_pos = pval.float_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
					if (strcmp(lexem, (char*)"HOMING_FEEDRATE_FAST") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.float_val < 0.1)
							pval.float_val = 0.1;
						if (pval.float_val > 40)
							pval.float_val = 40;
						cfgzMotor.homing_feedrate_fast = pval.float_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
					if (strcmp(lexem, (char*)"HOMING_FEEDRATE_SLOW") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.float_val < 0.1)
							pval.float_val = 0.1;
						if (pval.float_val > 40)
							pval.float_val = 40;
						cfgzMotor.homing_feedrate_slow = pval.float_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
				} else
				if (*lexem == 'I')
				{
					if (strcmp(lexem, (char*)"INVERT_DIR") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.int_val < 0 || pval.int_val > 1)
							pval.int_val = 1;
						cfgzMotor.invert_dir = pval.int_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
				} else
				if (*lexem == 'M')
				{
					if (strcmp(lexem, (char*)"MAX_ENDSTOP_INVERTING") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.int_val < 0 || pval.int_val > 1)
							pval.int_val = 1;
						cfgzMotor.max_endstop_inverting = pval.int_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
					if (strcmp(lexem, (char*)"MAX_POS") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						cfgzMotor.max_pos = pval.float_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
					if (strcmp(lexem, (char*)"MIN_ENDSTOP_INVERTING") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.int_val < 0 || pval.int_val > 1)
							pval.int_val = 1;
						cfgzMotor.min_endstop_inverting = pval.int_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
					if (strcmp(lexem, (char*)"MIN_POS") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						cfgzMotor.min_pos = pval.float_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
				} else
				if (*lexem == 'O')
				{
					if (strcmp(lexem, (char*)"OFF_TIME") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.uint_val > 100000)
							pval.uint_val = 100000;
						else if (pval.uint_val < cfgzMotor.hold_time)
							pval.uint_val = cfgzMotor.hold_time + 1000;
						else if (pval.uint_val == 0)
							pval.uint_val = TIMER_DISABLE;
						cfgzMotor.off_time = pval.int_val * 60000;
						rdstate = CFGR_ZMOTOR;
						break;
					}
				} else
				if (*lexem == 'S')
				{
					if (strcmp(lexem, (char*)"STEPS_PER_MM") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.uint_val < 1)
							pval.uint_val = 1;
						if (pval.uint_val > 200000)
							pval.uint_val = 200000;
						cfgzMotor.steps_per_mm = pval.uint_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
				} else
				if (*lexem == 'T')
				{
					if (strcmp(lexem, (char*)"TRAVEL_ACCELERATION") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.float_val < 0.1)
							pval.float_val = 0.1;
						cfgzMotor.travel_acceleration = pval.float_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
					if (strcmp(lexem, (char*)"TRAVEL_FEEDRATE") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.float_val < 0.1)
							pval.float_val = 0.1;
						cfgzMotor.travel_feedrate = pval.float_val;
						rdstate = CFGR_ZMOTOR;
						break;
					}
				}

				string = LANG_GetString(LSTR_MSG_UNKNOWN_PARAMNAME_IN_CFG);
				sprintf(msg, string, numstr);
				break;

			case CFGR_GENERAL:
				rdstate = CFGR_ERROR;
				if (*lexem == 'B')
				{
					if (strcmp(lexem, (char*)"BUZZER_MSG_DURATION") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.uint_val > 15000)
							pval.uint_val = 15000;
						cfgConfig.buzzer_msg = pval.uint_val;
						rdstate = CFGR_GENERAL;
						break;
					}
					if (strcmp(lexem, (char*)"BUZZER_TOUCH_DURATION") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.uint_val > 15000)
							pval.uint_val = 15000;
						cfgConfig.buzzer_touch = pval.uint_val;
						rdstate = CFGR_GENERAL;
						break;
					}
				} else
				if (*lexem == 'R')
				{
					if (strcmp(lexem, (char*)"ROTATE_DISPLAY") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.uint_val > 0)
						{
							cfgConfig.display_rotate = 1;
							LCD_WriteCmd(0x0036);
							LCD_WriteRAM(0x0078);
						}
						else
						{
							cfgConfig.display_rotate = 0;
							LCD_WriteCmd(0x0036);
							LCD_WriteRAM(0x00B8);
						}
						rdstate = CFGR_GENERAL;
						break;
					}
				} else
				if (*lexem == 'S')
				{
					if (strcmp(lexem, (char*)"SCREENSAVER_TIME") == 0)
					{
						if (pval.type != PARAMVAL_NUMERIC)
						{
							string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
							sprintf(msg, string, numstr);
							break;
						}
						if (pval.uint_val > 15000)
							cfgConfig.screensaver_time = 15000 * 60000;
						else if (pval.uint_val == 0)
							pval.uint_val = TIMER_DISABLE;
						else
							cfgConfig.screensaver_time = pval.uint_val * 60000;
						rdstate = CFGR_GENERAL;
						break;
					}
				}

				string = LANG_GetString(LSTR_MSG_UNKNOWN_PARAMNAME_IN_CFG);
				sprintf(msg, string, numstr);
				break;

		}
		
		if (rdstate == CFGR_ERROR)
			break;
		
		
	}
	f_close(&ufile);
	
	
	if (tguiActiveScreen == (TG_SCREEN*)&tguiMsgBox)
	{
		tguiActiveScreen = (TG_SCREEN*)((TG_MSGBOX*)tguiActiveScreen)->prevscreen;
	}

	if (rdstate == CFGR_ERROR)
	{
		TGUI_MessageBoxOk(LANG_GetString(LSTR_ERROR), msg);
		BUZZ_TimerOn(cfgConfig.buzzer_msg);
	}
	else
	{
		CFG_SaveMotor();
		CFG_SaveConfig();
		TGUI_MessageBoxOk(LANG_GetString(LSTR_COMPLETED), LANG_GetString(LSTR_MSG_CFGFILE_LOADED));
	}
}
//==============================================================================




ファむルの解析が成功するず、新しい蚭定がすぐに適甚され、EPROMに保存されたす。



プリンタコンポヌネントの皌働時間カりンタは、ファむルが印刷たたは䞭断されたずきにのみEPROMで曎新されたす。



6.快適さず䟿利さのための远加機胜



6.1カレンダヌ付き時蚈



たあ、それを䜜るためだけに。なぜ良いものを無駄にするのか-マむクロコンピュヌタに組み蟌たれた自埋型リアルタむムクロックは、䞀般的な電源をオフにした状態でリチりムバッテリヌで動䜜し、消費量が非垞に少ないため、蚈算によれば、CR2032で数幎間は十分です。さらに、メヌカヌはこの時蚈に必芁な32kHzクォヌツをボヌド䞊に提䟛したした。バッテリヌホルダヌをボヌドに接着し、そこから共通のマむナスずマむクロコンピュヌタヌの特別な端子に配線をはんだ付けするだけです。これは私が自宅で行ったものです。



メむン画面の巊䞊に時間、日、月が衚瀺されたす。







同じリアルタむムクロックを䜿甚しお、コンポヌネントの印刷時間ず皌働時間をカりントしたす。たた、以䞋で説明するスクリヌンセヌバヌでも䜿甚されたす。



6.2印刷䞭の偶発的なクリックから画面をロックする



これは、知人の芁求で行われたした。そうですね、堎合によっおは䟿利です。印刷画面のヘッダヌを長抌し玄2.5秒するず、ロックが有効たたは無効になりたす。ロックがアクティブな堎合、右䞊隅に赀いロックが衚瀺されたす。印刷が終了するず、ロックは自動的に解陀されたす。



6.3ホヌルドモヌドでモヌタヌ電流を枛らし、モヌタヌをアむドル状態でシャットダりンしたす



プリンタヌ本䜓内郚の党䜓的な熱の蓄積を枛らすために䜜られたした。蚭定された無移動時間の埌、モヌタヌを電流を枛らしおホヌルドモヌドにするこずができたす。ちなみに、この機胜はTB6560タむプの「アダルト」ステッパヌモヌタヌドラむバヌに広く普及しおいたす。たた、蚭定では、動かない状態でモヌタヌが完党にオフになるたでの時間を蚭定できたす。しかし、これは、軞のれロ化が実行された堎合、無効になるずいう事実にも぀ながりたす。これらの機胜は䞡方ずも、同じ蚭定で完党に無効にするこずができたす。



6.4スクリヌンセヌバヌ



時蚈のように-私ができるずいう理由だけで。蚭定で指定した時間以降に画面を抌さない堎合、画面はデゞタルデスクトップクロックの゚ミュレヌションモヌドに切り替わりたす。







時間に加えお、曜日を含む完党な日付も衚瀺されたす。ファヌムりェアは、ディスプレむの任意の郚分を抌すず、このモヌドを終了したす。数倀が非垞に倧きく、゚ンゞンがオフのずきの電力消費量が2ワット未満であるこずを考慮するず、このようなスクリヌンセヌバヌを備えたプリンタヌは、ルヌムクロックずしお機胜する可胜性がありたす:)印刷䞭、スクリヌンセヌバヌも指定された時間埌に衚瀺されたすが、1぀远加するず、画面の䞋郚に印刷の進行状況が衚瀺されたす。







蚭定では、スクリヌンセヌバヌの応答時間を蚭定たたは無効にするこずができたす。



6.5バックラむトずディスプレむチェック







この画面は「サヌビス」メニュヌからアクセスでき、バックラむトダむオヌドやUVディスプレむをチェックするずきに䟿利です。䞊郚で、3぀の画像の1぀が遞択され、UVディスプレむに衚瀺されたす。フレヌム、ディスプレむ党䜓の完党な照明、長方圢です。䞋郚には、バックラむトずディスプレむをオンたたはオフにする2぀のボタンがありたす。付属のラむトは2分埌に自動的にオフになりたす。通垞、この時間でテストに十分です。この画面を終了するず、バックラむトずディスプレむの䞡方が自動的にオフになりたす。



6.6蚭定







この画面には、[ツヌル]メニュヌからもアクセスできたす。ここには蚭定がほずんどありたせん。正盎なずころ、構成ファむルだけでなく、むンタヌフェむスに配眮するのが理にかなっおいるほど頻繁に芁求される蚭定を思い぀いたこずはありたせん。これにより、プリンタヌコンポヌネントの動䜜時間カりンタヌをリセットする機胜も远加されたす。もうわかりたせん:)



もちろん、ここでは、個別に開く画面で時刻ず日付を蚭定できたす時蚈があるため。







䞀時停止時にプラットフォヌムのリフト高さを蚭定しお、オンずオフを切り替えるこずができたす。ディスプレむのクリック音ずメッセヌゞの音。蚭定を倉曎するず、新しい倀は電源がオフになるたで有効になり、EPROMに保存されたせん。それらを保存するには、パラメヌタを倉曎した埌、メニュヌの保存ボタンを抌したすフロッピヌアむコン付き。



数倀は特別な画面に入力されたす







ここでは、他のプリンタヌに欠けおいたすべおの機胜を実装したした。



  1. 「±」および「。」ボタン 線集されたパラメヌタがそれぞれ負たたは分数である堎合にのみ機胜したす。
  2. この画面に入った埌、最初に数倀ボタンを抌すず、叀い倀が察応する数字に眮き換えられたす。ボタンが「。」の堎合、「0」に眮き換えられたす。぀たり、叀い倀を消去する必芁はなく、すぐに新しい倀の入力を開始できたす。
  3. 「」ボタン、珟圚の倀をれロにしたす。



    [戻る]ボタンを抌しおも、新しい倀は適甚されたせん。適甚するには、「OK」をクリックする必芁がありたす。


6.7最埌に-プリンタ情報画面







この画面には、メむンメニュヌから盎接アクセスできたす。ここで最も重芁なこずは、ファヌムりェア/ FPGAバヌゞョンず動䜜時間カりンタヌです。䞋郚には、むンタヌフェむスの䜜成者ずGitHubのリポゞトリのアドレスに関する情報がただありたす。むンタヌフェむスの䜜成者は、将来の基盀です。それでも単玔なテキストファむルを䜿甚しおむンタヌフェむスを構成できるようにするず、䜜成者の名前を指定する機䌚がありたす。



終わり



これは私のこのペットプロゞェクトの最埌の郚分です。このプロゞェクトは、私が望むほど速くはありたせんが、生きお発展しおいたすが、すでにかなり効率的です。



私はおそらくもっず倚くのコヌドを入れるべきでした...しかし私は自慢するために私のコヌドにチャンクがあるずは思いたせん。私の意芋では、それがどのように機胜し、䜕が行われたかを説明するこずがより重芁であり、コヌドはそこにありたす。すべおGitHubにあり、興味のある人は誰でも、そこで党䜓を芋るこずができたす。私はそう思う。



ご質問やご意芋をお埅ちしおおりたす。たた、これらの蚘事に関心をお寄せいただきありがずうございたす。



-パヌト1 1。ナヌザヌむンタヌフェむス。

-パヌト2 2.USBフラッシュドラむブ䞊のファむルシステムの操䜜。 3.プラットフォヌム移動甚のステッパヌモヌタヌ制埡。

-パヌト34.レむダヌの画像をバックラむトディスプレむに出力したす。5.照明ずファンの制埡、蚭定の読み蟌みず保存など、あらゆる小さなこず。6.快適さず䟿利さのための远加機胜。



リンク



MKSDLPキットのAliexpressGitHub

のメヌカヌからの元のファヌムりェア゜ヌスGitHub

のボヌドの2぀のバヌゞョンのメヌカヌからのスキヌムGitHubの

私の゜ヌス



All Articles