STM32 в Linux

В данном посте я опишу процесс развертывания минимальной сборки для того чтобы собрать проект и прошить микроконтроллер с помощью программатора st-link, на примере отладочной платы STM32L-Discovery.

Самое главное для любой разработки под другие архитектуры, это toolchain. Мной был выбран Sourcery G++ Lite. Он доступен с официального сайта. На момент написания поста, была доступна последняя версия 2011.03-42.

Качается Toolchain в виде архива. Поэтому разархивированием его запихнем /opt. На моем примере у меня получилось так: /opt/codesourcery/arm-2011.03/. А внутри уже файлы из архива.

Далее нам потребуется отладчик и собственно тот инструмент с помощью которого мы сможем прошить микроконтроллер: OpenOCD. Этот инструмент так же можно скачать бесплатно с sourceforge.net.

Процесс установки описан в архиве с OpenOCD, но для ленивых приведу пример процесса установки:

tar -zxvf openocd-x.x.x.tar.gz  
cd openocd-x.x.x.tar.gz  
./configure --prefix=/usr --enable-jlink --enable-amtjtagaccel --enable-ft2 --enable-stlink
make  
sudo checkinstall make install  

Следующим шагом для нас будет скачать библиотеки для необходимого нам контроллера. Их можно скачать на сайте st.com. В нашем случае это STM32L1xx standard peripherals library. Этот архив содержит множество различных библиотек и много других важных для сборки инструментов.

Распакуем содержимое архива, и покидаем в папку с проектами папки Utilities и Libraries. Первая папка содержит различные библиотеки связанные с отладочной платой, а именно STM32L-Discovery. А вторая, все остальное необходимое. Теперь важно выбрать линковщик и sturtup файл для нашей сборки. Линковщик можно найти в директории Projects, в папке с Templates. Для своего проекта, я выбрал линковщик для TrueSTUDIO, поэтому вытащим STM32_flash.ld соответствующего микроконтроллера в предварительно созданную папку Linker в папке с проектами.

Когда все приготовления закончены, нам нужно как то собрать проект. Для этого создадим папку проекта, если конечно у вас ее еще нет. Примеры проектов можно взять все из того же архива от st.com. И так, когда проект был выбран и помещен в папку с проектами, создадим файлик, который поможет нам в сборке прошивки для микроконтроллера: Makefile.

Я не буду объяснять все тонкости его создания, т.к. об этом можно найти множество информации в интернете, поэтому приведу свой вариант боевого, так сказать, файлика Makefile:

####################################################
#
# On command line:
#
# make all = Create project
#
# make clean = Clean project files.
#
# To rebuild project do "make clean" and "make all".
#
#################################################
#
# Start of default section
#
#TOOLCHAIN = /opt/gcc-arm-none-eabi/bin
TOOLCHAIN = /opt/codesourcery/arm-2011.03/bin  
TRGT = $(TOOLCHAIN)/arm-none-eabi-  
CC   = $(TRGT)gcc  
LD   = $(TRGT)gcc  
CP   = $(TRGT)objcopy  
AS   = $(TRGT)as#gcc -x assembler-with-cpp  
AR   = $(TRGT)ar  
GDB  = $(TRGT)gdb  
HEX  = $(CP) -O ihex  
BIN  = $(CP) -O binary -S  
MCU  = cortex-m3

# List all default C defines here, like -D_DEBUG=1
DDEFS = -DSTM32L1XX_MD -DUSE_STDPERIPH_DRIVER  
# List all default ASM defines here, like -D_DEBUG=1
DADEFS =

# List all default directories to look for include files here
DINCDIR =

# List the default directory to look for the libraries here
DLIBDIR =

# List all default libraries here
DLIBS =

#
# End of default section
#################################################

#################################################
# Start of user section
#

#
# Define project name and Ram/Flash mode here
PROJECT        = IOToggle  
RUN_FROM_FLASH = 1

# List all user C define here, like -D_DEBUG=1
UDEFS =

# Define ASM defines here
UADEFS =

# List C source files here
LIBSDIR    = ../Libraries  
CORELIBDIR = $(LIBSDIR)/CMSIS/Include  
DEVDIR  = $(LIBSDIR)/CMSIS/Device/ST/STM32L1xx  
DEVINCDIR = $(DEVDIR)/Include  
STMSPDDIR    = $(LIBSDIR)/STM32L1xx_StdPeriph_Driver  
STMSPSRCDDIR = $(STMSPDDIR)/src  
STMSPINCDDIR = $(STMSPDDIR)/inc  
#STMUSBDIR = $(LIBSDIR)/STM32_USB-FS-Device_Driver
#STMUSBSRCDIR = $(STMUSBDIR)/src
#STMUSBINCDIR = $(STMUSBDIR)/inc
DISCOVERY    = ../Utilities/STM32_EVAL/STM32L152D_EVAL

LINKER = ../Linker  
LDFILE = STM32_flash.ld  
SRC = ./main.c  
SRC += ./system_stm32l1xx.c  
SRC += ./stm32l1xx_it.c  
#SRC += $(DEVDIR)\Source\Templates\system_stm32f0xx.c
#SRC += startup_stm32f0xx.S
#SRC += $(DISCOVERY)\stm32f0_discover

#y.c
## used parts of the STM-Library
SRC += $(STMSPSRCDDIR)/misc.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_exti.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_pwr.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_adc.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_flash.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_rcc.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_aes.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_flash_ramfunc.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_rtc.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_aes_util.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_fsmc.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_sdio.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_comp.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_spi.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_crc.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_i2c.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_syscfg.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_dac.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_iwdg.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_tim.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_dbgmcu.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_lcd.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_gpio.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_usart.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_dma.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_opamp.c  
SRC += $(STMSPSRCDDIR)/stm32l1xx_wwdg.c

## used parts of the USB Device Driver STM-Library
#SRC += $(STMUSBDIR)/usb_core.c  
#SRC += $(STMUSBDIR)/usb_init.c  
#SRC += $(STMUSBDIR)/usb_int.c  
#SRC += $(STMUSBDIR)/usb_mem.c  
#SRC += $(STMUSBDIR)/usb_regs.c  
#SRC += $(STMUSBDIR)/usb_sil.c

# List ASM source files here
ASRCDIE = $(DEVDIR)/Source/Templates/TrueSTUDIO  
ASRC = $(ASRCDIE)/startup_stm32l1xx_md.s  
#ASRC = $(ASRCDIE)/startup_stm32l1xx_hd.s
#ASRC += $(ASRCDIE)/startup_stm32l1xx_mdp.s

# List all user directories here
UINCDIR = $(DEVDIR)/Include \  
          $(CORELIBDIR)     \
          $(STMSPINCDDIR)     \
          $(DISCOVERY)        \
      $(DEVINCDIR)     \
      $(STMSPINCDDIR)  \
       $(STMSPSRCDDIR)  \
          ./
#         $(STMUSBINCDIR)     \
          ./inc     
# List the user directory to look for the libraries here
ULIBDIR =

# List all user libraries here
ULIBS =

# Define optimisation level here
OPT = -Os

#
# End of user defines
#################################################
#
# Define linker script file here
#
ifeq ($(RUN_FROM_FLASH), 0)  
LDSCRIPT = $(LINKER)/$(LDFILE)  
FULL_PRJ = $(PROJECT)_ram  
else  
LDSCRIPT = ../Linker/STM32_flash.ld  
FULL_PRJ = $(PROJECT)_rom  
endif

INCDIR  = $(patsubst %,-I%,$(DINCDIR) $(UINCDIR))  
LIBDIR  = $(patsubst %,-L%,$(DLIBDIR) $(ULIBDIR))

ifeq ($(RUN_FROM_FLASH), 0)  
DEFS    = $(DDEFS) $(UDEFS) -DRUN_FROM_FLASH=0 -DVECT_TAB_SRAM  
else  
DEFS    = $(DDEFS) $(UDEFS) -DRUN_FROM_FLASH=1  
endif

ADEFS   = $(DADEFS) $(UADEFS)  
OBJS  = $(ASRC:.s=.o) $(SRC:.c=.o)  
LIBS    = $(DLIBS) $(ULIBS)  
MCFLAGS = -mcpu=$(MCU)

ASFLAGS = $(MCFLAGS) -g -gdwarf-2 -mthumb -Wa -amhls=$(<:.s=.lst) $(ADEFS)

CPFLAGS = $(MCFLAGS) $(OPT) -gdwarf-2 -mthumb   -fomit-frame-pointer -Wall -Wstrict-prototypes -fverbose-asm -Wa,-ahlms=$(<:.c=.lst) $(DEFS)  
LDFLAGS = $(MCFLAGS) -mthumb -nostartfiles -T$(LDSCRIPT) -Wl,-Map=$(FULL_PRJ).map,--cref,--no-warn-mismatch $(LIBDIR)

# Generate dependency information
CPFLAGS += -MD -MP -MF .dep\$(@F).d

#
# makefile rules
#

all: $(OBJS) $(FULL_PRJ).elf  $(FULL_PRJ).hex $(FULL_PRJ).bin  
ifeq ($(RUN_FROM_FLASH), 0)  
    $(TRGT)size $(PROJECT)_ram.elf
else  
    $(TRGT)size $(PROJECT)_rom.elf
endif

 %o: %c
    $(CC) -c $(CPFLAGS) -I . $(INCDIR) $< -o $@

%o: %s
    $(AS) -c $(ASFLAGS) $< -o $@

%elf: $(OBJS)
    $(CC) $(OBJS) $(LDFLAGS) $(LIBS) -o $@

%hex: %elf
    $(HEX) $< $@

%bin: %elf
    $(BIN)  $< $@

clean:  
    rm -f $(OBJS)
    rm -f $(FULL_PRJ).elf
    rm -f $(FULL_PRJ).map
    rm -f $(FULL_PRJ).hex
    rm -f $(FULL_PRJ).bin
#    del $(SRC:.c=.c.bak)
    rm -f $(SRC:.c=.lst)
#   del $(ASRC:.s=.s.bak)
    rm -f $(ASRC:.s=.lst)
    rm -fr  .dep /S /Q


# 
# Include the dependency files, should be the last of the makefile
#
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)
# *** EOF ***

Спешу предупредить, что если вы выбрали другой файл линковщика, а т.е. не TrueSTUDIO, то вам следует выбрать соответствующий sturtup файл в переменных ASRCDIE и ASRC. Так же далеко не обязательно собирать все файлы в разделе "used parts of the STM-Library", поэтому можно закомментировать лишние библиотеки, так или иначе, в процессе сборки, по вышедшей ошибке, вы сможете увидеть, какой файл закомментировали зря.

И так. У нас есть все необходимое, чтобы собрать прошивку, поэтому не будем медлить и напишем заветное make all, запуская тем самым инструкцию all в Makefile и компилируя все необходимые файлы для прошивки. Если вы все сделали правильно, то в финале вы получите .elf, .hex, .bin и .map файлы. Их то мы и будем записывать на микроконтроллер.

Немного об отладочной плате. Будучи в линуксе, вы не имете прав на общение с отладочной платой не выполнив команду от root. Поэтому, для упращения процесса работы, нужно создать конфигурационный файлик в /etc/udev/rules.d/. Для рассматриваемой отладочной платы был создан файл /etc/udev/rules.d/90-stm32ldiscovery.rules. С таким содержимым:

ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="0666"

Для обладателей других плат, нужно изменить idVendor и idProduct. Их вы можете просмотреть в выводе команды dmesg после подключения отладочной платы:

[ 4898.094103] usb 2-1.8: USB disconnect, device number 3
[ 4898.932324] usb 2-1.8: new full-speed USB device number 4 using ehci-pci
[ 4899.026137] usb 2-1.8: New USB device found, idVendor=0483, idProduct=3748
[ 4899.026142] usb 2-1.8: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 4899.026145] usb 2-1.8: Product: STM32 STLink
[ 4899.026148] usb 2-1.8: Manufacturer: STMicroelectronics

В строке New USB device found, idVendor=0483, idProduct=3748 вы можете увидеть необходимые данные.

Возвратимся к нашей прошивке. Не выходя из директории с проектом, это нужно будет для того чтобы подцепить файлы прошивки, выполним команду

openocd -f /usr/share/openocd/scripts/board/stm32ldiscovery.cfg

Эта команда запустит сервер, подключаясь к которому, в последующем мы сможем прошивать и отлаживать плату.

Откроем другой терминал и выполним команду

telnet localhost 4444

Если openocd был запущен, то вы получите доступ к отладочной плате. Через этот терминал можно производить управление платой. Выполним reset halt и проверим состояние платы с помощью команды poll. Вы можете заметить, что работа программы на плате была остановлена и состояние платы перешло в halted. Полное описание отладчика и команд можно посмотреть в руководстве пользователя OpenOCD. А теперь запишем прошивку:

> flash probe 0
flash size = 128kbytes  
flash size = 128kbytes  
flash 'stm32lx' found at 0x08000000  
> flash write_image erase Имя_файла_прошивки.bin 0x08000000
auto erase enabled  
target state: halted  
target halted due to breakpoint, current mode: Thread  
xPSR: 0x61000000 pc: 0x20000012 msp: 0x20000800  
wrote 4096 bytes from file demo.bin in 0.325034s (12.306 KiB/s)  
> reset
target state: halted  
target halted due to breakpoint, current mode: Thread  
xPSR: 0x01000000 pc: 0x08000010 msp: 0x20000800  
> exit
Connection closed by foreign host.  

Если вы все сделали правильно, то прошивка будет на плате, и вы сможете проверить ее работу, с помощью отладчика. В качестве отладчика можно использовать все тот же Sourcery G++ Lite. Он находится в той же директории, что и другие файлы toolchein-а.

arm-none-eabi-gdb Имя_файла_прошивки.elf

Выполнив эту команду вы попадете в отладчик, наберите target remote :3333, чтобы получить доступ к плате. Работа с отладчиком довольно таки проста, работа с ним описана все в том же руководстве пользователя OpenOCD.

На данном этапе можно запустить программу командой cont и остановить сочетанием клавиш Ctrl+C. После остановки можно посмотреть содержимое какой либо переменной на этом моменте выполнения программы командой print Имя_переменной. Для выхода из отладчика наберите quit.

И так. если вы смогли выполнить все указанные пункты, то можете считать, что справились со всеми настройками по сборке и отладке микроконтроллеров STM32 на Linux.