Baumkuchen’s Workshop

バイオリンと電子工作、DIY、ジョギングなど。

作りかけの自作CNCを完成させます#4-コントロールソフト2

コントローラソフトの続きです。いよいよ、実際にGrblのコードの移植を試みます。

baum-kuchen.hatenablog.com

一気に移植する手は、ビルドが通るまで大変になると思われるので、徐々に取り込む範囲(ソースコード、関数他)を増やしていく戦法を取ります。 

 

ヘッダー

最初に、Grblのヘッダーファイルを全てインクルードした状態でビルドを通します。この状態でビルドが通るまでやれば、関数を必要な範囲で徐々に追加していけると思います。

前回も書いたように、Atmega328依存部は、大体cpu_map_atmega328p.hに定義されているので、これをpic用に書き換えます。といっても、できるだけ元ソースはいじらない方針なので、#ifdefでpic用の定義cpu_map_pic18f14k50.hを読み込ませる形とします。

cpu_map.c

    #ifdef CPU_MAP_PIC18F14K50 // 
    #include "cpu_map/cpu_map_pic18f14k50.h
    #endif 

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

/* GRBL code ******************************************************************/ 
// Start Grbl main loop. Processes program inputs and executes them. protocol_main_loop();

このとき使用するundef.cとundef.hは次の通り。

undef.c

#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){}

未登録の関数に加え、グローバル変数も追加。

undef.h

#include <xc.h> /* XC8 General Include File */
#include "grbl.h" /////////////////////////////////////////////////////////////////////////////// //register substitute #define SREG STATUS /////////////////////////////////////////////////////////////////////////////// //undefined macro #define sei()
#define cli()

SREGは、nuts_bolts.hに定義されているマクロで使用されていますが、Atmega328のステータスレジスタなので、PICであればSTATUSレジスタに相当するので#defineで置き換えます。あとは、sei()、cli()は割り込み許可、不許可のマクロですが、当面使わないので#defineだけして、逃げます。

これで、一応ビルドは通ることができました。ただし、何にも動きませんが(実際には、serial_read()からGコードが来るのをひたすら待っている状態)。メモリ使用率も結構行ってます。

Memory Summary:
Program space used 19C8h ( 6600) of 4000h bytes ( 40.3%)
Data space used 269h ( 617) of 300h bytes ( 80.3%)

シリアル入出力部

次に、シリアル入出力関数を実装して、PCのターミナルから何かしら反応が返ってくるようにします。

シリアルエミュレータCDCのサンプルコードを一部流用し、PCからデバイスが入力する関数

   getsUSBUSART(uint8_t *buffer, uint8_t len)

と、デバイスからPCへ出力する関数

   putUSBUSART(uint8_t *data, uint8_t Length)

を使って、それぞれserial_read()、serial_write()を実装します。

serial_read()

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;
}

RS232_Out_Data:シリアルデータのバッファ

RS232_Out_Data_Rdy:バッファにデータがあるかどうか

なお、変数名が_Outとなっていますが、PCからCDCデバイスへの出力の意味で、Grblへの入力に相当します。

また、まだ実装はしていませんが、実施のGrblのシリアル受信割り込みでは特別なリアルタイムコマンド(?(ステータス)、!(一時停止)、~(再開)、@(ドア開)、crl-X(リセット))に対する処理がありますが、ここは未実装です。USBのCDCデバイス処理に手を入れたくないので、serial_read()内で判断できないかと思っています。

serial_write()

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();
}

USB_Out_Buffer:シリアルデータのバッファ

USBUSARTIsTxTrfReady()でUSB通信が可能か(送信可能か)を確認し、可能であれば、Host側にデータを送る(設定をする)。実際の転送は、CDCTxService()で処理されるらしく、これはメインで定期的にコールする必要があるようですが、今回はserial_write()の中で呼ぶことにしてます。

コマンド応答

USB-シリアルからのコマンドに反応するようにします。

protocol_main_loop()は、シリアルデータを受信して処理するだけ(今はダミー関数で何もしない)なので、report.cとprint.cを組み込むことで、多少、反応するようにしてみます。これができれば、コマンド解析の大きな流れが確認できて、あとは、各部のソースまたは関数を追加していけば全体を構築できます。

ビルドは、以下の定義を追加することで、割とすんなりできました。これは、AVR用のマクロで、ともにプログラムメモリへの割つけに関するものですが、詳しくはマニュアル*1を参照願います。

//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%)

動作確認

早速、コントローラに焼きこんで動作確認します。

コントローラのUSBポートとPCのUSBポートを接続し、TeraTermを立ち上げ。COM6を選んで接続まで完了。ちなみに、コントローラ内では実際のシリアル通信が行われているわけではないので、ボーレート等は何でもOKです。

f:id:Baum_kuchen:20210828172000p:plain

接続後、画面上は、何も出ていませんが、ここでキーボードのエンターを押したら次の様に反応がありました。ここまでは、成功です。

f:id:Baum_kuchen:20210828171258p:plain

 

Gコード解析部

ここから先は少し、苦労しそうです。

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()を呼び出しています。

f:id:Baum_kuchen:20210828204740p:plain

(図注:system_execute_line()以下は表示していません)

本丸のsystem_execute_line()、gc_execute_line()の前に、すでにファイルとしては組み込んでいるreport.c内の関数あたりから攻めようかと思います。ただし、ソースコードsystem.cを組み込んでしまうと、一気に範囲が広がりそうなので、思案が必要です。

今後について

ということで、次に登る山(Gコード解析部)が大きそうなので、今回は、ここで終了です。次で、なんとなくGrblの動きっぽくなれば良いかなと思います。

今回も最後まで御覧いただきありがとうございました。プログラムメモリ量で暗雲がたれこんでいますが、まだまだ続けますので、よろしかったらお付き合い願います。

 

*1:MPLAB® XC8 C Compiler User’s Guide for AVR® MCU