最低限、コンパイラが吐くエラーを自力で修正できる必要がある。あと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);
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);
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。
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 件のコメント:
コメントを投稿