MPLAB: PIC24F (XC16) でmallocを使う

MCC(Microchip Code Configuration)を使ってUSBホスト機能を実装しようとしていきなりハマった件。

以下のようにUSBHostTasks()というAPIが用意されている。メインループ内で適切なタイミングでコールすると、接続されたUSBデバイスとの接続処理などをよろしくやってくれる。(データのやり取りはまた別)

// main.c
int main(void)
{
    SYSTEM_Initialize();
    
    USBHostInit(0);
    
    while (1)
    {
        USBHostTasks();
        :
    }
    return -1;
 }

ほかのサンプルソースなどを見てもおおかた上のようなコードになっているので、どんなものかコールしてみたところ、エラーが発生してしまって正しく動作しない。

デバッグ実行して問題個所を確認すると、以下のコードで問題が発生していた。pEndpoint0に対して、USB_MALLOCでメモリを動的に確保してポインタに設定している。

// usb_host.c
bool USBHostInit(  unsigned long flags  )
{
    // Allocate space for Endpoint 0.  We will initialize it in the state machine,
    // so we can reinitialize when another device connects.  If the Endpoint 0
    // node already exists, free all other allocated memory.
    if (usbDeviceInfo.pEndpoint0 == NULL)
    {
        if ((usbDeviceInfo.pEndpoint0 = (USB_ENDPOINT_INFO*)USB_MALLOC( sizeof(USB_ENDPOINT_INFO) )) == NULL)
        {
            printf("[USBHostInit]: Cannot allocate for endpoint 0.\r\n");
            return false;
        }

USB_MALLOCは単純にmallocをマクロ化しているだけである。XC8コンパイラでは、動的にメモリを確保することはできないが、XC16からできるらしい。知りませんでした。ただ私の環境だと、malloc()がNULLを返してしまってエラーになっている模様。もちろんデータメモリも十分に空きがある。(USB_ENDPOINT_INFOは34バイトぐらい)

#ifndef USB_MALLOC
    #define USB_MALLOC(size) malloc(size)
#endif

ヒープサイズを指定する

通常C言語のmallocやC++などのnew演算子で確保されるデータは、ヒープ領域に確保される。通常のPCで動作するプログラム作成では、このサイズを気にする必要はほとんどない。そういえば、PICはどうなんだ?と調べたところ、XC16コンパイラの設定やドキュメントにありました。

MPLAB® XC16 C コンパイラ ユーザガイド

ここの「10.13 動的メモリ割り当て」に記述があった。規定値はゼロで、mallocなど動的に確保する場合は、指定しろと。

ちなみにMLAのホストサンプルプログラムのコンパイラ設定にもちゃっかりヒープサイズが指定されてました。(2000バイトほど)

xc16heap03

真似して指定したところ、malloc()でちゃんと領域確保できるようになった。

「ちゃんと教えてくれないと分からん!」と思ったけど、そういえばワーニングが表示されてました。。

xc16heap04

そういうことだったのね。。