コントローラソフトの続きです。いよいよ、実際にGrblのコードの移植を試みます。
一気に移植する手は、ビルドが通るまで大変になると思われるので、徐々に取り込む範囲(ソースコード、関数他)を増やしていく戦法を取ります。
最初に、Grblのヘッダーファイルを全てインクルードした状態でビルドを通します。この状態でビルドが通るまでやれば、関数を必要な範囲で徐々に追加していけると思います。 前回も書いたように、Atmega328依存部は、大体cpu_map_atmega328p.hに定義されているので、これをpic用に書き換えます。といっても、できるだけ元ソースはいじらない方針なので、#ifdefでpic用の定義cpu_map_pic18f14k50.hを読み込ませる形とします。 cpu_map.c cpu_map_pic18f14k50.hの中は、こに時点では修正しなくてもビルドは通りました。 あとは部分的に関数を追加していきますが、ソースコードファイル単位で追加していきたいので、一時的に未定義となると関数がある場合は、undef.cと言うファイルを作って関数定義だけ追加し、ビルドは通します。なお、ソースコード単位で追加すると、途中、使われ無い関数も出てくるが、敢えて削除せずそのまま(出来るだけgrblのソースコードには修正を加えない方針)としました。 また、IO定義もすべて、cpu_map_xx.hに定義されていないで、直接コード内でAtmega328のレジスタ類が呼ばれているところがあるので、そこについては、undef.hというファイルを作って、一旦、逃げることにします。 前回も書いたがgrblの構造として、シリアルとタイマー割り込みとメインのGコード解析部からなるので、まずメインから攻めます。main()関数の中は、一連の初期化のあと、protocol_main_loop()関数が呼ばれるだけですので、まず、この関数を取り込むため、ソースコードとして、plotoco.cをPICプロジェクトに追加してビルドします。 main.c このとき使用するundef.cとundef.hは次の通り。 undef.c 未登録の関数に加え、グローバル変数も追加。 undef.h SREGは、nuts_bolts.hに定義されているマクロで使用されていますが、Atmega328のステータスレジスタなので、PICであればSTATUSレジスタに相当するので#defineで置き換えます。あとは、sei()、cli()は割り込み許可、不許可のマクロですが、当面使わないので#defineだけして、逃げます。 これで、一応ビルドは通ることができました。ただし、何にも動きませんが(実際には、serial_read()からGコードが来るのをひたすら待っている状態)。メモリ使用率も結構行ってます。 次に、シリアル入出力関数を実装して、PCのターミナルから何かしら反応が返ってくるようにします。 シリアルエミュレータCDCのサンプルコードを一部流用し、PCからデバイスが入力する関数 getsUSBUSART(uint8_t *buffer, uint8_t len) と、デバイスからPCへ出力する関数 putUSBUSART(uint8_t *data, uint8_t Length) を使って、それぞれserial_read()、serial_write()を実装します。 serial_read() RS232_Out_Data:シリアルデータのバッファ RS232_Out_Data_Rdy:バッファにデータがあるかどうか なお、変数名が_Outとなっていますが、PCからCDCデバイスへの出力の意味で、Grblへの入力に相当します。 また、まだ実装はしていませんが、実施のGrblのシリアル受信割り込みでは特別なリアルタイムコマンド(?(ステータス)、!(一時停止)、~(再開)、@(ドア開)、crl-X(リセット))に対する処理がありますが、ここは未実装です。USBのCDCデバイス処理に手を入れたくないので、serial_read()内で判断できないかと思っています。 serial_write() USB_Out_Buffer:シリアルデータのバッファ USBUSARTIsTxTrfReady()でUSB通信が可能か(送信可能か)を確認し、可能であれば、Host側にデータを送る(設定をする)。実際の転送は、CDCTxService()で処理されるらしく、これはメインで定期的にコールする必要があるようですが、今回はserial_write()の中で呼ぶことにしてます。 USB-シリアルからのコマンドに反応するようにします。 protocol_main_loop()は、シリアルデータを受信して処理するだけ(今はダミー関数で何もしない)なので、report.cとprint.cを組み込むことで、多少、反応するようにしてみます。これができれば、コマンド解析の大きな流れが確認できて、あとは、各部のソースまたは関数を追加していけば全体を構築できます。 ビルドは、以下の定義を追加することで、割とすんなりできました。これは、AVR用のマクロで、ともにプログラムメモリへの割つけに関するものですが、詳しくはマニュアル*1を参照願います。 ただし、ビルドは通っても、メモリもほぼ余裕がありません。 早速、コントローラに焼きこんで動作確認します。 コントローラのUSBポートとPCのUSBポートを接続し、TeraTermを立ち上げ。COM6を選んで接続まで完了。ちなみに、コントローラ内では実際のシリアル通信が行われているわけではないので、ボーレート等は何でもOKです。 接続後、画面上は、何も出ていませんが、ここでキーボードのエンターを押したら次の様に反応がありました。ここまでは、成功です。 ここから先は少し、苦労しそうです。 protocol_main_loop()で、シリアル入力1ライン分を切り出し、protocol_execute_line()で処理します。ここまでは実装済みですが、そこから、コマンドを解析するのが、system_execute_line()で、report.c、setting.c内の関数群及び、gcode.c内のgc_execute_line()関数となります。このほか、protocol_execute_line()からは、リアルタイムコマンドに対応するprotocol_execute_realtime()を呼び出しています。 (図注:system_execute_line()以下は表示していません) 本丸のsystem_execute_line()、gc_execute_line()の前に、すでにファイルとしては組み込んでいるreport.c内の関数あたりから攻めようかと思います。ただし、ソースコードsystem.cを組み込んでしまうと、一気に範囲が広がりそうなので、思案が必要です。 ということで、次に登る山(Gコード解析部)が大きそうなので、今回は、ここで終了です。次で、なんとなくGrblの動きっぽくなれば良いかなと思います。 今回も最後まで御覧いただきありがとうございました。プログラムメモリ量で暗雲がたれこんでいますが、まだまだ続けますので、よろしかったらお付き合い願います。 続きです。 ヘッダー
#ifdef CPU_MAP_PIC18F14K50 //
#include "cpu_map/cpu_map_pic18f14k50.h
#endif
メインループ
/* GRBL code ******************************************************************/
// Start Grbl main loop. Processes program inputs and executes them.
protocol_main_loop();
#include "grbl.h"
///////////////////////////////////////////////////////////////////////////////
//global variables
parser_state_t gc_state;
system_t sys;
settings_t settings;
///////////////////////////////////////////////////////////////////////////////
//unincluded functions
void report_status_message(uint8_t status_code){}
void report_realtime_status(){}
void report_init_message(){}
void report_alarm_message(int8_t alarm_code){}
void report_feedback_message(uint8_t message_code){}
uint8_t serial_read(){return 0;}
void serial_write(uint8_t data){}#include <xc.h> /* XC8 General Include File */
#include "grbl.h"
///////////////////////////////////////////////////////////////////////////////
//register substitute
#define SREG STATUS
///////////////////////////////////////////////////////////////////////////////
//undefined macro
#define sei()
#define cli()
Memory Summary:
Program space used 19C8h ( 6600) of 4000h bytes ( 40.3%)
Data space used 269h ( 617) of 300h bytes ( 80.3%)
シリアル入出力部
uint8_t serial_read() {
uint8_t c;
if (RS232_Out_Data_Rdy == 0) // only check for new USB buffer if the old RS232 buffer is
{ // empty. This will cause additional USB packets to be NAK'd
while( !(LastRS232Out = getsUSBUSART(RS232_Out_Data,64)) ); //until the buffer is free.
if(LastRS232Out > 0)
{
RS232_Out_Data_Rdy = 1; // signal buffer full
RS232cp = 0; // Reset the current position
}
}
if (RS232_Out_Data_Rdy)
{
//check if realtime command char exists
c = RS232_Out_Data[RS232cp++];
if(RS232cp >= LastRS232Out) {
RS232_Out_Data_Rdy = 0;
}
}
LATCbits.LATC4 = 0; //for debug
return c;
}
void serial_write(uint8_t data) {
//serial_write()
//We need to buffer it up for eventual transmission to the USB host.
if(NextUSBOut < (CDC_DATA_OUT_EP_SIZE - 1))
{
USB_Out_Buffer[NextUSBOut] = data;
++NextUSBOut;
USB_Out_Buffer[NextUSBOut] = 0;
}
//If any bytes are waiting, and the endpoint is available, prepare to
//send the USB packet to the host.
if((USBUSARTIsTxTrfReady()) && (NextUSBOut > 0))
{
putUSBUSART(&USB_Out_Buffer[0], NextUSBOut);
NextUSBOut = 0;
}
//actual tx service
CDCTxService();
}
コマンド応答
//def for report.c
# define PSTR(s) ((const char *)(s))
//def for print.c
#define pgm_read_byte_near(a) (*a)
Memory Summary:
Program space used 35C6h ( 13766) of 4000h bytes ( 84.0%)
Data space used 2BAh ( 698) of 300h bytes ( 90.9%)
動作確認
Gコード解析部
今後について