Как стать автором
Обновить

Идеальная среда разработки для PIC — личный опыт

Время на прочтение11 мин
Количество просмотров67K
В связи с нововведениями на сайте, решил наконец-то вылезти из подполья и написать что-нибудь полезное. Ну а поскольку я программирую разные микроконтроллеры (МК) и являюсь фанатом Eclipse, то решил про это и написать. Начну со своей истории знакомства с программированием PIC, а закончу советами тем, кто по долгу службы или в силу увлечения программирует на МК семейства PIC, хотя, впрочем, эти же советы сгодятся и для других архитектур МК.

В среду железячников я попал в 2006 году на 4-м курсе учёбы в университете, когда пошёл на производственную практику в научно-технический центр, где, собственно, и работаю по сей день. В то время в нашей компании мейнстримом было использование Keil uVision2 для МК на базе C51 и ARM. Однако мне подсовывали простые задачи под PIC, вроде контроля и управления одним сигналом (кнопка вкл-выкл), и моей первой средой разработки были блокноты — бумажный и компьютерный, плюс книжки бумажные по PIC. Выглядела моя среда разработки примерно так:



Для компиляции файлов мне выдали экзешник компилятора и bat-файл, который использовался мной совершенно бездумно — даже не знаю, что за компилятор там был. В общем, суровые были времена…

Ах, если бы мне кто-то тогда подсказал, что есть такое чудо, как notepad++!


Потом был MPASM, но он убогий и мне про него почти нечего вспоминать. По-моему, под него я также писал в блокноте программки.

MPLAB IDE


По мере совершенствования своих навыков я узнал, что вместо блокнота можно использовать наикрутейшую, как мне тогда казалось, MPLAB IDE:



В её состав входят:

  • CC18 и ещё какой-то компилятор, которые можно выбирать в настройках проекта;
  • хороший набор библиотечных функций;
  • подключаемые inc-файлы описания МК семейства PIC, заточенные под использование в ассемблере;
  • встроенный отладчик и программатор;
  • Но главное — поддержка языка Си — это был для меня глоток свежего воздуха!

Хотя, если присмотреться к этой среде разработки, её убогость и отсталость могут отпугнуть любого мало-мальски привыкшего к хорошим условиям программиста, но я тогда об этом не знал. Справку по встроенным библиотечным функциям надо открывать отдельно и искать, что, где и как называется. Для новичков — непосильная задача. Тем не менее, на тематических форумах люди до сих пор спрашивают, какой компилятор лучше использовать; кто-то так и продолжает использовать MPLAB IDE.

MikroC


Задачи для PIC мне подкидывали всё реже и реже, начали набирать обороты разработки с МК серии C51, ARM7 (не путать с ARMv7!), Cortex-M. Но иногда ко мне снова обращались за помощью в написании программ под PIC, а я в силу любопытства пробовал новые средства разработки.
К тому времени уже давно и активно программировал в Keil uVision3 — возвращаться к допотопному MBLAB IDE совершенно не хотелось. Так я познакомился с MikroC, который поставляется вместе с программаторами PICKit:



Набор плюшек почти такой же как в MBLAB IDE, но всё же побогаче:

  • свой собственный компилятор
  • встроенные библиотеки функций с удобным поиском и доступным описанием;
  • подключаемые h-файлы описания МК семейства PIC;
  • набор дополнительных внешних утилит
  • широкий спектр примеров с исходниками
  • встроенный отладчик и программатор;
  • встроенные вкладки открытых файлов;
  • навигация по функциям в файле

Честно, для маленьких простых проектов, которые и составляют основную нишу программ под PIC, этого вполне достаточно. Даже новички нормально разбираются с помощью справки и быстро делают рабочий код. Большинство наших разработчиков, имеющих дело с PIC, используют эту среду при разработке.

Так или иначе, сделав очередной проект в MikroC, я благополучно забыл про PIC'и и думал, что уже никогда к ним не вернусь.

Однако история любит повторяться!


Через 3 года, в 2013 году, появилась задача разработать ПО по готовой КД, в которой был заложен PIC18F4680. Честно, я даже не знал, что среди PIC'ов бывают такие монстры, всегда имел дело только с мелочью!

Задачи были нетривиальные — реализация загрузчика для внутрисхемного обновления ПО, работа в режиме жёсткого реального времени, работа с АЦП, внешними ЦАП, линиями управления, несколькими таймерами-компараторами.

Кстати, немного отвлекаясь от темы: только на этом проекте я в полной мере понял, что такое банки памяти в PIC, как они работают и какие ограничения накладывают на разработку ПО. К примеру, все банки у МК по 256 байт. И хоть убейся, но для PIC нельзя создать структуру, превыщающую по объёму эти 256 байт — ограничение всплыло наружу при реализации протокола обмена, ну да ладно, проехали…

К этому времени Keil uVision3 мне уже изрядно поднадоел, поскольку сложность проектов росла и мне не хватало имевшегося в Keil функционала. Где-то с 2011 года я освоил Eclipse, GCC, синтаксис makefile — и все свои проекты начал вести с использованием этих инструментов. К тому же, у меня уже был опыт применения связки Eclipse + SDCC для реализации проекта под C51 МК. После появления Keil uVision4 я его установил, протестировал пол-часика и снёс, ибо по удобству программирования он всё равно сильно отстаёт от Eclipse.

Eclipse + SDCC


В настоящее время Eclipse де-факто является стандартом в области разработки ПО для встраиваемых систем. Вот список IDE, основанных на Eclipse, от популярных брендов:

  • NXP LPCXpresso IDE
  • Freescale CodeWarrior
  • Xilinx Platform Studio
  • Texas Instruments CCS
  • Android Development Tools

Автоподстановка, всплывающие подсказки по автодополнению, макросы, затемнение неактивных участков кода, удобная навигация по коду и многое-многое другое, — я не буду всё перечислять, — многие разработчики встраиваемых систем совершенно не привыкли и не знают всех этих плюшек, значительно облегчающих жизнь:



Главной проблемой чистого Eclipse для разработки на C/C++ под МК является сложность вхождения в него железячных программистов, замена привычных инструментов, работающих после установки в 1-2 клика, на какие-то плагины, требующие настройки, или, что ещё хуже, на вручную написанные makefile — всё это требует значительных первоначальных усилий по чтению и изучению документации, поиску помощи и пособий для начинающих в интернете. Говорю как человек, имеющий опыт по переводу команды программистов-железячников на Eclipse.
Только для моей команды разработчиков
Коли прочитали эту статью — дайте знать, я хоть узнаю, как у нас читают профильные хабы на Хабрахабре

Однако, за месяц полностью освоив синтаксис и один раз написав качественный makefile, все остальные проекты создаются по накатанному шаблону и требуют лишь минимальной индивидуальной настройки.

Также пришлось сделать ряд дополнительных телодвижений по настройке проектов под PIC — по умолчанию Eclipse понимает синтаксис GCC. Различные макросы и директивы, встроенные в другие компиляторы (будь то СС18 или SDCC), приходится разделять на этапе компиляции и на этапе индексации проекта. Чтобы при навигации в коде редактор не выдавал ложных ошибок на неизвестные директивы, к исходникам проекта подключается файл eclipse-syntax.h:
eclipse-syntax.h
#ifndef ECLIPSE_SYNTAX_H_
#define ECLIPSE_SYNTAX_H_

// keyword SDCC defined when compiling with SDCC compiler
#ifndef SDCC

	#ifdef __SDCC_PIC18F4680
		#error "SDCC not found, project compile will be with errors!"
	#endif

	// file not parsed through makefile - just for proper eclipse syntax
	#ifndef __CC18__
		#error "__CC18__ not found, use `-D__CC18__` in makefile for proper CC18 compilation!"
		#define near
		#define far
		#define rom
		#define ram
		#define _asm
		#define _endasm
		#define Nop()
		#define ClrWdt()
		#define Sleep()
		#define Reset()
		#define clrwdt
		#define nop

		#define __code
		#define __data
		#define __xdata

		#define __sfr
		#define __sbit

		#define __naked
		#define __wparam

		#define __bit char
		#define __at(num)

	#else	//	__CC18__ defined - compile stage!
	#endif	// __CC18__

	#define __inline

	#define __asm
	#define __endasm

	#define __interrupt(x)
	#define INTERRUPT(x)

	#define USING(x)

	#define CRITICAL

	#define CRITICAL_START
	#define CRITICAL_END

	#define _REENTRANT

#else	// if SDCC defined

	#define INTERRUPT(x) __shadowregs __interrupt (x)

	//#define USING(x) __using (x)
	#define USING(x)

	#define CRITICAL __critical

	#define CRITICAL_START __critical {
	#define CRITICAL_END }
#endif	// SDCC defined

#endif /* ECLIPSE_SYNTAX_H_ */


Кроме того, в SDCC у меня не получилось слинковать большой проект в готовый бинарник — потребовалось также настроить GPUtils, в состав которого входят gpasm, gpdasm, gplink и скрипты .lkr карт памяти МК PIC. Правда, из-за одного найденного мной бага в SDCC на этапе отладки кода я в итоге вернулся на CC18 компилятор и линковщик. Тем не менее, SDCC и GPUtils были полностью настроены — для страждущих привожу часть makefile, касающуюся опций запускаемых компиляторов и линковщиков CC18, SDCC, GPUtils:
Кусочки makefile
###########################################################
# project-specific compile options
###########################################################
# Project definitions
CHIP = 18F4680
DEFINES := -DPIC$(CHIP)
#DEFINES += -D__SDCC_PIC$(CHIP)	# use SDCC compiler
DEFINES += -D__CC18__	# use MPLAB CC18 compiler
#DEFINES += -DOPTIMIZE_BITFIELD_POINTER_GET	# SDCC memory optimize for bitfield structures
###########################################################
#  common part for all sdcc-based projects
###########################################################
	SDCC_BASE = c:/DevTools/SDCC
	CC		= "$(SDCC_BASE)/bin/sdcc.exe"
	LD		= "$(SDCC_BASE)/bin/sdcc.exe"
	ELF2HEX	= "$(SDCC_BASE)/bin/packihx.exe"
	HEX2BIN = "$(SDCC_BASE)/bin/makebin.exe"
	
###########################################################
#  common part for all MPLAB MCC18-based projects
###########################################################
	MPLAB_BASE	= c:/DevTools/CC18
	CC_MPLAB	= "$(MPLAB_BASE)/bin/mcc18.exe"
	AS_MPLAB	= $(MPLAB_BASE)/mpasm/mpasmwin.exe
	LD_MPLAB	= $(MPLAB_BASE)/bin/mplink.exe

###########################################################
# GPUtils used with SDCC for linking project
###########################################################
	GPUTILS_BASE = c:/DevTools/GNUPICutils
	GPASM	= "$(GPUTILS_BASE)/bin/gpasm.exe"
	GPDASM	= "$(GPUTILS_BASE)/bin/gpdasm.exe"
	GPLINK	= "$(GPUTILS_BASE)/bin/gplink.exe"

###########################################################
# C preprocessor flags for MPLAB MCC18 compiler
###########################################################
#optimization parameters (default = full optimization)
OPT_ENABLE_ALL	:= -O+	# Enable all optimizations (default)
OPT_DEBUG		:=-Ou- -Ot- -Ob- -Op- -Or- -Od- -Opa-
OPT 	:=$(OPT_ENABLE_ALL)
#OPT 	:=$(OPT_DEBUG)

CFLAGS_MPLAB := -p $(CHIP)
CFLAGS_MPLAB += -I $(MPLAB_INC_DIR)
CFLAGS_MPLAB += -nw=2066	# suppress Warning [2066] type qualifier mismatch in assignment
CFLAGS_MPLAB += -ml	# Large memory model
CFLAGS_MPLAB += -ls # large stack (can span multiple banks)
#CFLAGS_MPLAB += -scs # Enable default static locals
#CFLAGS_MPLAB += -sco # Enable default overlay locals (statically allocate activation records). Ignored if set --extended
CFLAGS_MPLAB += --extended	# generate extended mode code

COMPILE_MPLAB_STRING=$(CC_MPLAB) $(CFLAGS_MPLAB) $< -fo=$@ $(DEFINES) $(OPT)

AFLAGS_MPLAB := /y
AFLAGS_MPLAB += /rDEC				# set default radix HEX/DEC/OCT
AFLAGS_MPLAB += /l-				# disable listing file
#AFLAGS_MPLAB += /l$(OBJDIR_MPLAB)	# enable listing file
AFLAGS_MPLAB += /o	# specify path for object files
#AFLAGS_MPLAB += /o$(OBJDIR_MPLAB)	# specify path for object files
#AFLAGS_MPLAB += /q					# enable quiet mode 
AFLAGS_MPLAB += /d__LARGE__			# define symbol
AFLAGS_MPLAB += /p$(CHIP)			# set processor type

#ASSEMBLE_MPLAB_STRING=$(AS_MPLAB) $(AFLAGS_MPLAB) %<

# used linker script
LDFLAGS_MPLAB := $(CHIP)_g.lkr
# objects to compile
LDFLAGS_MPLAB += $(OBJS_MPLAB)
LDFLAGS_MPLAB += $(MPLAB_LIBS)
# specify chip for proper linking
LDFLAGS_MPLAB += /p$(CHIP)
# verbose mode operation
#LDFLAGS_MPLAB += /v
# generate report file for stack analysis
LDFLAGS_MPLAB += /g
# generate .LST file and no .COD file
LDFLAGS_MPLAB += /i
# do not invoke MP2COD (no .COD or .LST file)
LDFLAGS_MPLAB += /w
# link MPLAB libs
LDFLAGS_MPLAB += /l $(MPLAB_LIB_DIR)
# generate MAP file
LDFLAGS_MPLAB += /m $(EXEDIR)/$(PROJECT_NAME)_mplab.map
# set output file
LDFLAGS_MPLAB += /o $(EXEDIR)/$(PROJECT_NAME)_mplab.hex

###########################################################
# C preprocessor flags for SDCC v.3.3.0 compiler
###########################################################
# ----- processor selection -----
 CFLAGS := -m$(ARCH)
 CFLAGS += -p$(CHIP)
 
# ----- preprocessor options -----
 CFLAGS += $(INCS)
 CFLAGS += $(DEFINES)
 
# ----- verbose & dependancy generate -----
# CFLAGS += -M # generate dependencies
# CFLAGS += -E #
# CFLAGS += -C # dont discard comments
 
 CFLAGS += -c # dont link file (i.e. have multiple source files)
  
 CFLAGS += $(DEBUG)

# ----- common settings -----
#CFLAGS += --nostdinc # This will prevent the compiler from passing on the
					  # default include path to the preprocessor.
#CFLAGS += --nostdlib # This will prevent the compiler from passing on the
 					  # default library path to the linker.

#CFLAGS += --less-pedantic # Disable some of the more pedantic warnings.

 CFLAGS += --stack-auto # All functions in the source file will be compiled as reentrant.
						# It automatically implies --int-long-reent and --float-reent.
CFLAGS += --int-long-reent # Integer (16 bit) and long (32 bit) libraries have been compiled as reentrant.
CFLAGS += --float-reent # Floating point library is compiled as reentrant.

#CFLAGS += --no-peep
#CFLAGS += --funsigned-char # The default signedness for every type will be unsigned.
#CFLAGS += --cyclomatic # This option will cause the compiler to generate an information
 						# message for each function in the source file. The message contains
 						# the number of edges and nodes the compiler detected in the
 						# control flow graph of the function, and most importantly
						# the cyclomatic complexity.

# ----- optimization options -----
#CFLAGS += --nogcse # Will not do global subexpression elimination, this option may be used
 					# when the compiler creates undesirably large stack/data spaces to store
 					# compiler temporaries.
#CFLAGS += --noinvariant # Will not do loop invariant optimizations.
#CFLAGS += --noinduction # Will not do loop induction optimizations.
#CFLAGS += --nojtbound # Will not generate boundary condition check when switch statements
 						# are implemented using jumptables.
#CFLAGS += --noloopreverse # Will not do loop reversal optimization.
#CFLAGS += --nolabelopt # Will not optimize labels (makes the dumpfiles more readable).
 CFLAGS += --nooverlay # The compiler will not overlay parameters and local variables of any function.
 CFLAGS += --peep-asm # Pass the inline assembler code through the peep hole optimizer.
 #CFLAGS += --opt-code-speed # Optimize for code speed rather than size
 #CFLAGS += --opt-code-size # Optimize for code size rather than speed
 CFLAGS += --fomit-frame-pointer # Frame pointer will be omitted when the function uses
								 # no local variables.
 CFLAGS += --use-non-free #  Search / include non-free licensed libraries and header files

# ----- special options for pic16 port of SDCC -----
 CFLAGS += --pstack-model=large	# use stack model 'small' (default) or 'large'
# don't use extended instruction set - SDCCman, $4.6.20.1 Known Bugs
#CFLAGS += -y --extended 		# enable Extended Instruction Set/Literal Offset Addressing mode
#CFLAGS += --pno-banksel        # do not generate BANKSEL assembler directives
 CFLAGS += --obanksel=2         # set banksel optimization level (default=0 no)
 CFLAGS += --denable-peeps      # explicit enable of peepholes
 CFLAGS += --no-optimize-goto   # do NOT use (conditional) BRA instead of GOTO
 CFLAGS += --optimize-cmp       # try to optimize some compares
 CFLAGS += --optimize-df        # thoroughly analyze data flow (memory and time intensive!)
#CFLAGS += --preplace-udata-with=udata_shr # Place udata variables at another section: udata_acs, udata_ovr, udata_shr
#CFLAGS += --ivt-loc=           # Set address of interrupt vector table.
#CFLAGS += --nodefaultlibs      # do not link default libraries when linking
#CFLAGS += --use-crt=           # use <crt-o> run-time initialization module
#CFLAGS += --no-crt             # do not link any default run-time initialization module
#CFLAGS += --mplab-comp         # enable compatibility mode for MPLAB utilities (MPASM/MPLINK)
#CFLAGS += --asm=               # Use alternative assembler
#CFLAGS += --link=              # Use alternative linker
 CFLAGS += --debug-xtra         # show more debug info in assembly output
 CFLAGS += --debug-ralloc       # dump register allocator debug file *.d
 CFLAGS += --pcode-verbose      # dump pcode related info
 CFLAGS += --calltree           # dump call tree in .calltree file
#CFLAGS += --gstack             # trace stack pointer push/pop to overflow

###########################################################
# linker flags
###########################################################
#gputils (GNU PIC Utils) used to link objects and libs.
 GPLINK_FLAGS	= -c -m -w -r -I $(LIBDIR) -s $(GPUTILS_BASE)/lkr/$(CHIP)_g.lkr
 
#SDCC linker not used
 #LDFLAGS := -m$(ARCH)
 #LDFLAGS += $(DEBUG)
 #LDFLAGS += --profile

 #LDFLAGS += --code-size $(FLASH_SIZE) # Code Segment size
#LDFLAGS += --code-loc $(FLASH_LOC) # The start location of the code location, default value is 0

 #LDFLAGS += --iram-size $(IRAM_SIZE) # Internal Ram size

#LDFLAGS += --xram-loc $(XRAM_LOC) # The start location of the external ram, default value is 0
#LDFLAGS += --xram-size $(XRAM_SIZE) # External Ram size

#LDFLAGS += --stack-loc $(STACK_LOC) # By default the stack is placed after the data segment.
 									 # Using this option the stack can be placed anywhere in the
 									 # internal memory space of the 8051.

##############################################################################
# MPLAB CC18 compiler - linker 
$(HEX_MPLAB): $(OBJS_MPLAB) Makefile
	@echo "--- CC18 Linking objects to $(HEX_MPLAB) ..."
	@$(LD_MPLAB) $(LDFLAGS_MPLAB)

##############################################################################
# SDCC compiler - linker 
$(HEX): $(OBJS) Makefile
	@echo "--- SDCC Linking objects to $(HEX) ..."
	$(GPLINK) $(GPLINK_FLAGS) -o $(HEX) $(OBJS) $(LIBS)
	$(GPDASM) -p$(CHIP) $(HEX) > $(DASM)



Эпилог


Как видно, в итоге я пришёл к использованию связки Eclipse с внешними компиляторами. Изучение опций компиляции — дело нужное и не столь сложное, чтобы просто так от него отказываться — любой программист сможет их изучить и применить при необходимости. Думаю, в итоге у меня получилась идеальная связка, доступная на сегодняшний день для создания проектов под PIC.

Собирая воедино все средства разработки, вот список компиляторов, которыми я пользуюсь в связке с Eclipse и получаю от этого истинное удовольствие при программировании:
  • CC18 для PIC
  • SDCC для C51
  • gnu-arm-embedded для ARM7 и Cortex-M
  • MinGW для x86

Очевидно, при необходимости список может легко дополняться.

Надеюсь, прочитав мою историю, кто-то решится наконец для себя сойти со старых IDE и освоить новые.

Дерзайте!
Теги:
Хабы:
Всего голосов 26: ↑24 и ↓2+22
Комментарии9

Публикации

Истории

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань