2018年5月31日木曜日

CubeMXで新規プロジェクト まとめ

 前に書いたやつのまとめ。

 最低限、コンパイラが吐くエラーを自力で修正できる必要がある。あとMakefileをかなりいじるので、そのあたりの知識も。
 それとsyscalls.cというファイルが必要になる。どっかから拾ってきてください。

 Win10のWSLで実行することを前提にしています。まぁmakeとか一通り動いてarm-none-eabi-gccとかも一通り入ってればだいたい問題ない。

1 プロジェクトを作る

File > New Project (Ctrl-N)でNew Projectを表示する。
 使用するMCUを選択する。左側のMCU Filtersでコアやシリーズ、パッケージを選択したり、製品名を入力する。
 今回はSTBee F4miniを使用するので、STM32F405RGを選択する。
 MCUs List n itemsでアイテムをダブルクリックして終了。


 Project > Settings...でProject Settings画面を開く。
 Project Nameでプロジェクト名を入力する。
 Toolchain / IDEをMakefileにする。

 Code GeneratorタブでGenerate peripheral initialization as a pair of '.c/.h' files per peripheral、Set all free pins as analog (to optimize the power consumption)、Enable Full Assertの3個にチェックを入れる。

 OKでProject Settingsダイアログを閉じる。

2 初期設定

GPIOの設定を行う。

 左のツリーからRCCのHigh Speed Clock (HSE)とLow Speed Clocl (LSE)をCrystal/Ceramic Resonatorにする。

 左のツリーからUSB_OGT_FSのModeをDevice_Onlyにする。
 USB_DEVICEのClass For FS IPをCommunication Device Class (Virtual Port Com)にする。

 右のパッケージのイラストからPA0-WKUPをクリック、GPIO_Inputを選択。右クリックでEnter User Labelをクリックし、USER_SWと入力。同じようにPD2をGPIO_Outputにし、USER_LEDと入力。

3 クロックの設定

Clock Configurationタブを開く。
 Clock configuration Do you want to run automatic clock issues solver? というダイアログが出てくるので、Noで閉じる。
 左側のInput frequencyをボードの水晶の値である12にする。
 PLL Source MuxでHSEを選択、System Clock MuxでPLLCLKを選択する。
 右側のHCLK to AHB bus, core, memory and DMA (MHz)に168と入力し、Enterで反映する。少し待たされた後、赤枠のエラーが消えたりして設定が反映される。

4 その他の設定

Configurationタブを開く。
 左側のFREERTOSのEnableをチェックする。


 右側のFREERTOSアイコンをクリックし、FREERTOS Configurationダイアログを開く。
 Task and Queuesのタブを開く(デフォルトでこのタブのはず)。

 TasksのAddでNew Taskダイアログを開く。
 とりあえずLEDを点滅させるタスクをつくる。Task NameにLED_blink、PriorityにosPriorityLow、Stack Sizeに128、Enter FunctionにLED_blink_func、Code Generation OptionにAs externalを設定し、OKでダイアログを閉じる。
 コンソールのタスクも作っておく。Task Nameにconsole、PriorityにosPriorityBelowNormal、Stack Sizeに1024、Entry Functionにconsole_func、Code Generation OptionにAs externalを設定する。

 QueuesのAddでNew Queueダイアログを開く。
 Queue Nameにstdout_queue、Queue Sizeに256、Item Sizeにuint8_tとし、OKで閉じる。
 再びAddでダイアログを開き、stdin_queue、128、uint8_tとして追加。

 Config parametersタブでUSE_IDLE_HOOKとUSE_TRACE_FACILITYとUSE_STATS_FORMATTING_FUNCTIONSをEnableにする。

 OKでFREERTOS Configurationダイアログを閉じる。

5 コード生成

Project > Generate Code (Ctrl-Shift-G)をクリック。
 Warning: Code Generationと警告が出てくるが、Yesで続行する。

 しばらく待つとThe Code is successfulyとダイアログが出るので、Open Folderで出力フォルダを開くか、Closeでダイアログを閉じる。

6 コードの修正と追加

Makefileを開く。

 C_SOURCES =  \の下に Src/syscalls.c \ と Src/CCM.c \ を追加。

 # ASM sourcesの上に以下のコードを追加する。
"
CPP_SOURCES = \
Src/LED_blink.cpp \
Src/Console.cpp \
"

 CC = とかAS = とかの$(BINPATH)/$(PREFIX)を$(BINPATH)$(PREFIX)にする(スラッシュを消す)。
 CCの下にCPP = $(BINPATH)$(PREFIX)g++を追加する。

 LDFLAGS = にある-specs=nano.specsを削除する。

 CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" を CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" に変更。

 # list of objectsの OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o))) を OBJECTS = $(addprefix $(BUILD_DIR)/,$(C_SOURCES:.c=.o)) に変更。

 # list of ASM program objectsの上に以下のコードを追加。
"
OBJECTS += $(addprefix $(BUILD_DIR)/,$(CPP_SOURCES:.cpp=.o))
vpath %.cpp $(sort $(dir $(CPP_SOURCES)))
"

 vpath %.s $(sort $(dir $(ASM_SOURCES))) の下に以下のコードを追加。
"
DEPS = $(OBJECTS:%.o=%.d)
-include $(DEPS)
"

 OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o))) を OBJECTS += $(addprefix $(BUILD_DIR)/,$(ASM_SOURCES:.s=.o)) に変更。

 $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) の下に mkdir -p $(dir $@); を追加。

 $(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ を $(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(<:.c=.lst) $< -o $@ に変更。

 $(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)の上に以下のコードを追加。
"
$(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR)
mkdir -p $(dir $@);
$(CPP) -std=c++11 -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(<:.cpp=.lst) $< -o $@
"

 $(CC) $(OBJECTS) $(LDFLAGS) -o $@を$(CPP) $(OBJECTS) $(LDFLAGS) -o $@に変更する。


 STM32F405RGTx_FLASH.ldを開く。
 _Min_Stack_Size=の下に_heap_end = ORIGIN(RAM)+LENGTH(RAM)-4;を追加する。
 *(.ccmram*)の下に*CCM.oを追加する


 Inc/FreeRTOSConfig.hのUSER CODE END Definesの上に #define configAPPLICATION_ALLOCATED_HEAP 1 を追加する。


 SrcフォルダにCCM.cを追加。以下の行を追加。
"
#include <FreeRTOS.h>

#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
uint8_t ucHeap[configTOTAL_HEAP_SIZE];
#endif
"


 SrcフォルダにLED_blink.cppを追加。以下の行を追加。
"
#include <cmsis_os.h>
#include <main.h>
#include <stm32f4xx_hal.h>

extern "C" void LED_blink_func(void const *argument)
{
    while (1)
    {
        HAL_GPIO_TogglePin(USER_LED_GPIO_Port, USER_LED_Pin);
        osDelay(HAL_GPIO_ReadPin(USER_SW_GPIO_Port, USER_SW_Pin) == GPIO_PIN_SET
                    ? 100
                    : 500);
    }
}
"


 SrcフォルダにConsole.cppを追加。以下のコードを追加。
"
#include <cmsis_os.h>
#include <stm32f4xx_hal.h>
#include <string.h>

extern "C" void console_func(void const *argument)
{
    while (1)
    {
        char str[100];
        scanf("%99[^\n]%*[^\n]", str);
        getchar();

        {
            char *const p(strchr(str, '\r'));

            if (p)
            {
                *p = '\0';
            }
        }

        if (!strcmp(str, "tasklist"))
        {
            char buff[10 * 45];
            osThreadList((uint8_t *)buff);
            printf(
                "Name            State   Priorty Stack   Num\n"
                "*******************************************\n"
                "%s"
                "*******************************************\n",
                buff);
        }
    }
}
"



 Srcにどこかから持ってきたsyscalls.cをコピーして入れておく。

 _read_rの中を以下のようにする。
"
uint8_t *p = (uint8_t *)ptr;
extern osMessageQId stdin_queueHandle;

while (len--)
{
    uint8_t data;
    xQueueReceive(stdin_queueHandle, &data, portMAX_DELAY);
    *p++ = data;

    if (data == '\n')
    {
        break;
    }
}

_ssize_t i = (uint32_t)p - (uint32_t)ptr;

return (i);
"

 _write_rの中を以下のようにする。
"
uint8_t *p = (uint8_t *)ptr;
int i = 0;
extern osMessageQId stdout_queueHandle;

if (__get_IPSR() == 0)
{
    for (i = 0; i < len; i++)
    {
        xQueueSend(stdout_queueHandle, p++, portMAX_DELAY);
    }
}
else
{
    for (i = 0; i < len; i++)
    {
        xQueueSendFromISR(stdout_queueHandle, p++, 0);
    }
}

return (i);
"


 Src/freertos.cを開く。
 #include <usbd_cdc_if.h> を追加する。

 vApplicationIdleHookに HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); を追加する。

 StartDefaultTaskの無限ループの前に osDelay(500); を追加する。
 無限ループの中を以下のように書き換える。
"
static uint8_t buffer[32];
uint16_t count = 0;
BaseType_t dequeue_state = pdPASS;

while (dequeue_state == pdPASS && count < sizeof(buffer))
{
    dequeue_state = xQueueReceive(stdout_queueHandle, &buffer[count], 1);

    if (dequeue_state)
    {
        count++;
    }
}

if (count > 0)
{
    CDC_Transmit_FS(buffer, count);
}
osDelay(2);
"



 Src/usbd_cdc_if.cを開く。
 #include <cmsis_os.h> を追加。

 CDC_Control_FS関数のスイッチのケースCDC_GET_LINE_CODINGに以下のコードを追加。
"
*((uint32_t*)&pbuf[0]) = 115200;
pbuf[4] = 1;
pbuf[5] = 0;
pbuf[6] = 8;
"

 CDC_Receive_FS関数のreturnの前に以下のコードを追加。
"
extern osMessageQId stdin_queueHandle;
uint32_t i = 0;
for (i = 0; i < *Len; i++)
{
    xQueueSendFromISR(stdin_queueHandle, &Buf[i], 0);
}
"


 あとはmakeすればbuildフォルダにelfとかhexとかbinとかが作られてるので、任意のツールでマイコンに書き込めばOK。

 注意点として、このmakefileはヘッダの変更を認識しない。ヘッダファイルを書き換えた場合は、責任を持ってそれに依存するすべてのソースファイルをタッチするか、あるいはmake cleanでオブジェクトファイルを削除して再びビルドすること。

 Cubeが吐いたmakefileにはC_SOURCESに絶対パスを指定するファイルが含まれる場合がある。その場合は最初のスラッシュを削除し、相対パスで認識されるようにすること。

 未確認だが、割り込みの中でHALのタイムアウトを指定する関数を呼ぶ場合、うまく動かないと思う(タイムアウトしないはず)。対策はいろいろあるが、そもそも割り込みの中で何ミリ秒もポーリングするような処理はするな、ということで。
 ほぼ同じところが原因で、FreeRTOSのコンテキストスイッチを1kHz以外に設定するといろいろ不具合を起こすはず。


* WSLでGCCが古い問題
 Win10 WSLのUbuntuのaptで入れたarm-none-eabi-gccはバージョン4.9.3で更新が止まってる。新しいGCCを使いたい場合は、GNU Arm Embedded Toolchain | Downloads – Arm DeveloperからWindows ZIPをダウンロードしてきてスペースや全角文字を含まないディレクトリに解凍しておく。binフォルダにarm-none-eabi-gcc.exe等一式が入っているので、MakefileのBINPATHにこのフォルダのパスを入れておく。MakefileはWSLで実行するので、ディレクトリ名はC:\等ではなく/mnt/c/のようになる点に注意。そしてCC = $(BINPATH)$(PREFIX)gcc.exeのように拡張子を追加する。
 あとはmakeすれば新しいgccでビルドできる。

 C:\Devz\ARM\launchpad\bin\arm-none-eabi-gcc.exe のようなディレクトリ構成の場合、 BINPATHは /mnt/c/Devz/ARM/launchpad/bin/ のようになるはず。


更新:2018/06/15
 [update]依存関係を正常に処理できるように
 [add]BINPATHの例を追加

0 件のコメント:

コメントを投稿