Разработка под stm32 в Linux (GCC + CMake + Eclipse + openocd)

Ко мне наконец-то пришли посылки c отладочной платой и JTAG-адаптером из Китая, и я решил продолжить свои изыскания.
В результате получилась рабочая и вполне удобная среда для разработки под армы в Linux.
UPD: Теперь всё лежит на github'е — https://github.com/ObKo/stm32-cmake

Что требуется:

  • Более-менее современный дистрибутив Linux
  • CMake не ниже 2.6 (лучше 2.8)
  • Тулчейн: CodeSourcery G++ lite
  • STM32 Standart Peripherals Library v3.5.0
  • Eclipse + CDT + GDB Hardware Debugging — см. здесь
  • Open On-Chip Debugger (OpenOCD) — деббагер — лучше собрать из репозитория git, т.к. у версии 0.4.0 есть проблемы с флешем у stm'ок)
  • JTAG адаптер, поддерживаемый OpenOCD (почти любой, у меня J-LINK)
  • Файл тулчейна для cmake и заготовка проекта.

Структура проекта

  • stm32.cmake — файл тулчейна для cmake, немного описывался в прошлой статье, общий для всех проектов.
  • CMakeLists.txt — файл проекта cmake
  • stm32_flash.ld.in — скрипт линкера
  • stm32f10x_conf.h — заголовочный файл — конфигурация — в нем мы должны расскоментировать нужные нам инклуды.
  • stm32f10x_it — cтандартные обработчики прерываний
Файл тулчейна

# Путь к тулчейну
SET(TOOLCHAIN_DIR /opt/arm-toolchain)
# Путь к библиотеке стандартной переферии.
SET(STM32_StdPeriphLib_DIR /opt/STM32F10x_StdPeriph_Lib_V3.5.0)

# Пути, где лежат заголовки
SET(STM32_StdPeriphLib_INCLUDE_DIRS 
    ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/inc/
    ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/
    ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/CoreSupport/
)

# Другие полезные пути
SET(TOOLCHAIN_BIN_DIR ${TOOLCHAIN_DIR}/bin)
SET(TOOLCHAIN_LIBC_DIR ${TOOLCHAIN_DIR}/arm-none-eabi/libc)
SET(TOOLCHAIN_INC_DIR ${TOOLCHAIN_LIBC_DIR}/include)
SET(TOOLCHAIN_LIB_DIR ${TOOLCHAIN_LIBC_DIR}/usr/lib)

SET(CMAKE_SYSTEM_NAME Linux CACHE INTERNAL "system name")
SET(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor")

# Компиляторы
SET(CMAKE_C_COMPILER ${TOOLCHAIN_BIN_DIR}/arm-none-eabi-gcc CACHE INTERNAL "")
SET(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN_DIR}/arm-none-eabi-g++ CACHE INTERNAL "")
SET(CMAKE_ASM_COMPILER ${TOOLCHAIN_BIN_DIR}/arm-none-eabi-as CACHE INTERNAL "")

# objcopy и objdump для создания хексов и бинариков
SET(CMAKE_OBJCOPY ${TOOLCHAIN_BIN_DIR}/arm-none-eabi-objcopy CACHE INTERNAL "")
SET(CMAKE_OBJDUMP ${TOOLCHAIN_BIN_DIR}/arm-none-eabi-objdump CACHE INTERNAL "")

# Флаги компиляторов, тут можно подкрутить
SET(CMAKE_C_FLAGS "-isystem ${TOOLCHAIN_INC_DIR} -mthumb -mcpu=cortex-m3 -fno-builtin -Wall -std=gnu99" CACHE INTERNAL "c compiler flags")
SET(CMAKE_CXX_FLAGS "-isystem ${TOOLCHAIN_INC_DIR} -mthumb -mcpu=cortex-m3 -fno-builtin -Wall" CACHE INTERNAL "cxx compiler flags")

SET(CMAKE_EXE_LINKER_FLAGS "-nostartfiles -Wl,-Map -Wl,main.map -mthumb -mcpu=cortex-m3" CACHE INTERNAL "exe link flags")
SET(CMAKE_MODULE_LINKER_FLAGS "-L${TOOLCHAIN_LIB_DIR}" CACHE INTERNAL "module link flags")
SET(CMAKE_SHARED_LINKER_FLAGS "-L${TOOLCHAIN_LIB_DIR}" CACHE INTERNAL "shared lnk flags")

SET(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_LIBC_DIR} CACHE INTERNAL "cross root directory")
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH CACHE INTERNAL "")
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "")
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "")

# Стартовые файлы - в них происходит низкоуровневая (начальная) инициализация чипа
SET(STM32_STARTUP_CL ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7/startup_stm32f10x_cl.s)
SET(STM32_STARTUP_HD ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7/startup_stm32f10x_hd.s)
SET(STM32_STARTUP_HD_VL ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7/startup_stm32f10x_hd_vl.s)
SET(STM32_STARTUP_LD ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7/startup_stm32f10x_ld.s)
SET(STM32_STARTUP_LD_VL ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7/startup_stm32f10x_ld_vl.s)
SET(STM32_STARTUP_MD ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7/startup_stm32f10x_md.s)
SET(STM32_STARTUP_MD_VL ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7/startup_stm32f10x_md_vl.s)
SET(STM32_STARTUP_XL ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7/startup_stm32f10x_xl.s)

# Исходник CMSIS
SET(STM32_SYSTEM_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c)

# Модули библиотеки стандартной переферии 
SET(STM32_MISC_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/misc.c)
SET(STM32_ADC_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_adc.c)
SET(STM32_BKP_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_bkp.c)
SET(STM32_CAN_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_can.c)
SET(STM32_CEC_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_cec.c)
SET(STM32_CRC_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_crc.c)
SET(STM32_DAC_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_dac.c)
SET(STM32_DBGMCU_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_dbgmcu.c)
SET(STM32_DMA_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_dma.c)
SET(STM32_EXTI_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_exti.c)
SET(STM32_FLASH_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_flash.c)
SET(STM32_FSMC_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_fsmc.c)
SET(STM32_GPIO_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c)
SET(STM32_I2C_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_i2c.c)
SET(STM32_IWDG_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_iwdg.c)
SET(STM32_PWR_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_pwr.c)
SET(STM32_RCC_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c)
SET(STM32_RTC_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rtc.c)
SET(STM32_SDIO_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_sdio.c)
SET(STM32_SPI_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_spi.c)
SET(STM32_TIM_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_tim.c)
SET(STM32_USART_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_usart.c)
SET(STM32_WWDG_SOURCE ${STM32_StdPeriphLib_DIR}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_wwdg.c)

Файл проекта cmake

# Имя проекта, без пробелов.
PROJECT(stm32template)

# Нужен cmake >=2.6
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
# Включаем ассемблер
ENABLE_LANGUAGE(ASM)

# Размера флеша 
SET(STM32_FLASH_SIZE "512K")
# Размер рамы
SET(STM32_RAM_SIZE "64K")
# Адрес стека - конец оперативки => 0x20000000 + размер оперативки
SET(STM32_STACK_ADDRESS "0x20010000")

# Адреса RAM и Flash
SET(STM32_FLASH_ORIGIN "0x08000000")
SET(STM32_RAM_ORIGIN "0x20000000")

# Дерективы препроцессора, нужные для библиотеки STM.
ADD_DEFINITIONS( 
# Тут нужно подставить свой чип (MD, MD_VL и т.д) 
  -DSTM32F10X_HD
  -DUSE_STDPERIPH_DRIVER
)

# Стартовый файл
# Тут нужно подставить свой чип (MD, MD_VL и т.д) 
SET(STARTUP_SOURCE ${STM32_STARTUP_HD})

# Какие модули собирать
SET(MOD_SOURCES
#  ${STM32_MISC_SOURCE}
#  ${STM32_ADC_SOURCE}
#  ${STM32_BKP_SOURCE}
#  ${STM32_CAN_SOURCE}
#  ${STM32_CEC_SOURCE}
#  ${STM32_CRC_SOURCE}
#  ${STM32_DAC_SOURCE}
#  ${STM32_DBGMCU_SOURCE}
#  ${STM32_DMA_SOURCE}
#  ${STM32_EXTI_SOURCE}
#  ${STM32_FLASH_SOURCE}
#  ${STM32_FSMC_SOURCE}
  ${STM32_GPIO_SOURCE}
#  ${STM32_I2C_SOURCE}
#  ${STM32_IWDG_SOURCE}
#  ${STM32_PWR_SOURCE}
  ${STM32_RCC_SOURCE}
#  ${STM32_RTC_SOURCE}
#  ${STM32_SDIO_SOURCE}
#  ${STM32_SPI_SOURCE}
#  ${STM32_TIM_SOURCE}
#  ${STM32_USART_SOURCE}
#  ${STM32_WWDG_SOURCE}
)

# Исходники проекты (наши исходники)
SET(PROJECT_SOURCES
  main.c
)

# Исходники стандартых обработчиков прерываний и CMSIS
SET(SERVICE_SOURCES
  stm32f10x_it.c
  ${STM32_SYSTEM_SOURCE}
)

# Флаги компилятора для разных типов сборки.
SET(COMPILE_DEFINITIONS_DEBUG -O0 -g3 -DDEBUG)
SET(COMPILE_DEFINITIONS_RELEASE -Os)

# Конфигурируем файл - скрипт компилятора: заменяем переменные в файле на размеры и адреса 
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/stm32_flash.ld.in ${CMAKE_CURRENT_BINARY_DIR}/stm32_flash.ld)
SET(CMAKE_EXE_LINKER_FLAGS "-T${CMAKE_CURRENT_BINARY_DIR}/stm32_flash.ld ${CMAKE_EXE_LINKER_FLAGS}")

# Добавляем пути поиска заголовочных файлов
INCLUDE_DIRECTORIES(
  ${CMAKE_CURRENT_SOURCE_DIR}/.
  ${STM32_StdPeriphLib_INCLUDE_DIRS}
)

# Собираем исходники пректа, модули, и т.д. в elf
ADD_EXECUTABLE(${CMAKE_PROJECT_NAME}.elf ${PROJECT_SOURCES} ${MOD_SOURCES} ${STARTUP_SOURCE} ${SERVICE_SOURCES})

# Конвертируем elf в hex и bin
ADD_CUSTOM_COMMAND(TARGET ${CMAKE_PROJECT_NAME}.elf POST_BUILD COMMAND ${CMAKE_OBJCOPY} ARGS -Oihex ${CMAKE_PROJECT_NAME}.elf ${CMAKE_PROJECT_NAME}.hex)
ADD_CUSTOM_COMMAND(TARGET ${CMAKE_PROJECT_NAME}.elf POST_BUILD COMMAND ${CMAKE_OBJCOPY} ARGS -Obinary ${CMAKE_PROJECT_NAME}.elf ${CMAKE_PROJECT_NAME}.bin)

Как завести?

cmake
В директории проекта запускаем в консоли:

$ cmake -G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=<путь к файлу тулчейна> -DCMAKE_BUILD_TYPE=Debug ./

Получаем проект для эклипса.
OpenOCD
Нужно найти скрипт для своего JTAG-адаптера в /usr/share/openocd/scripts/interface/
Запускаем openocd (пример для JLINK):

$ sudo openocd -f interface/jlink.cfg -f board/stm3210c_eval.cfg

Теперь можно подключится к openocd c помощью телнета:

$ telnet localhost 4444

На 3333 порту будет находится интерфейс к gdb.
Eclipse
File -> Import

General -> Existing project into workspace

Выбираем путь к нашему проекту

Далее настраиваем для проекта отладку, как написано здесь, только порт отладчика 3333.

В приложенном архиве — файл тулчейна и заготовка проекта.
  • +2
  • 02 июля 2011, 20:30
  • Kosyak
  • 1
Файлы в топике: stm32_cmake_template.tar.gz

Комментарии (18)

RSS свернуть / развернуть
А зачем CMake? Неужели нельзя создать проект средствами эклипса?
0
  • avatar
  • Vga
  • 02 июля 2011, 22:05
CDT использует обычные Makefile. Ну а с cmake'ом я привык работать, и знаю его лучше чем make.
0
А почему бы не поставить вот этот плагин, и не заморачиватся с мэйкфайлами.
0
И что, она их даже генерить не умеет?
0
Статью бы эту перебросить в блог STM32
0
Боже мой — как сложно. А взять IAR + J-Link (20$ DIY) и по SWD иметь всё удовольствие минут за 5? хотя под linux тоже же самое за 15 минут. Хотя лампочками моргать можно просто выключателем в комнате.
0
  • avatar
  • x893
  • 22 августа 2011, 01:01
А что, у Вас IAR безглючно работает под Wine?
0
Студии XeoArt надо изучить вопрос о возможности использовании IE9. Особенно когда добавляешь коментарии — под ним не добавляется ничего — под FF4 работает.
0
  • avatar
  • x893
  • 22 августа 2011, 01:03
Я сделал что-то похожее только с AVR микроконтроллером. robot-develop.org/archives/2952 — тут результат моей работы.
0
Я тут проще поинтересуюсь по вашей статейке,
нельзя ли через cmake все таки автоматизировать создание hex'а?
Еще бы конечно avrdude как то тоже привязать но это уже не принципиально.

Я кстати из Win7 + WinAvr + cmake + Code::Blocks собирать собирает =) Но в практике еще не юзаю по выше озвученной причине.
0
Да можно… я это в последствии автоматизировал… это будет выглядеть так

# create avr hex
MACRO(AVR_CREATE_HEX name)

# example of a command to the shell
# avr-objcopy-O ihex-R.eeprom beacon beacon.hex
ADD_CUSTOM_COMMAND(TARGET ${name} POST_BUILD COMMAND avr-objcopy ARGS -O ihex -R.eeprom ${name} ${HEX_PATH}/${name}.hex)

ENDMACRO(AVR_CREATE_HEX)

только в WIN я это не тестировал — но один парень говорит что у него работает
0
О, оперативненько =)
Сейчас заметил что эти макро уже есть.
Но вот как пользоваться что-то не догоняю.
.hex автоматом не создается.
может как то make AVR_CREATE_HEX надо набрать?
0
AVR_CREATE_HEX(${EXECUTABLE_NAME}) — где входная переменная это имя исполняемого файла или библиотеки…
0
те ты сразу создаешь либу или исполняемый файл

ADD_LIBRARIES()
ADD_EXECUTABLE()

а потом то имя которое в них передавал пихаешь в этот макрос — а он запустит нужную прогу и сгенерит тебе HEX но учти что там у меня используется переменная ${HEX_PATH} — это папка куда валятся хексы ее нужно определить в главном cmake уровнем выше
0
Итак:
MACRO(AVR_CREATE_HEX name)
ADD_CUSTOM_COMMAND(TARGET ${name} POST_BUILD COMMAND avr-objcopy ARGS -O ihex -R.eeprom ${name} ${HEX_PATH}/${name}.hex)
ENDMACRO(AVR_CREATE_HEX)
Расположил в navigation_crosscompile.cmake т.к. он ругался что не знает макро AVR_CREATE_HEX когда лежал в корневом CMakeLists.txt

В корневой CMakeLists.txt добавил
SET(HEX_PATH ${NAVIGATION_BASE_DIRECTORY}/master)

В master директории в CMakeLists.txt добавил AVR_CREATE_HEX(master) в конец.
Hex'а не создается =(
чяднт?
0
Блин я его не там искал =)
Он оказывается в директории с исходником генерится, а не с проектом =/
Спасибо за помощь =)
0
Мне например не нравиться eclipse я использую QtCreator или студию от Microsoft.
0
Поимел проблемы со сборкой из консоли, ассемблер ругался на неправильные ключи компиляции.

Решил заменой:
# Дерективы препроцессора, нужные для библиотеки STM.
ADD_DEFINITIONS( 
# Тут нужно подставить свой чип (MD, MD_VL и т.д) 
  -DSTM32F10X_HD
  -DUSE_STDPERIPH_DRIVER
)

На:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSTM32F10X_HD -DUSE_STDPERIPH_DRIVER")
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.