Cortex Microcontroller Software Interface Standard ( CMSIS ) содержит в своем составе программный интерфейс к периферийным модулям ядра и периферии различных микроконтроллеров Cortex-M3(M0).
Из обязательной части стандарт рекомендует использовать доступ к регистрам с помощью указателей на структуры в языке С ( например, доступ к регистру GPIOB_BRR осуществляется как GPIOB->BRR ). Для этого достаточно подключить к проекту заголовочный файл для конкретного микроконтроллера, который содержит включение заголовочного файла ядра core_cm3.h и заголовочного файла system_xxxx.h дополнительных функций CMSIS для конкретной модели xxxx микроконтроллера. Эти файлы должны быть включены в проект , если Вы собираетесь хоть как-то использовать CMSIS.
Необязательная часть стандарта CMSIS содержит различные функции инициализации ядра и периферии. Для использования этих функций необходимо откомпилировать исходные файлы core_cm3.c и system_xxxx.c, где xxxx – общее название микроконтроллера( семейства ). В каталоге CMSIS есть примеры простых проектов для различных инструментальных средств, в том числе и Sourcery G++ Lite, что делает возможным использование компилятора C из коллекции GCC.
Проект предназначен для работы в интегрированной среде разработки Keil uVision 4 с использованием внешнего компилятора GCC. Попытка переделать проект в Eclipse не увенчалась успехом, поскольку часть необходимого для загрузки программы кода добавляется к проекту средой разработки . Пришлось написать стартовый код , скрипт компоновки и Makefile самостоятельно.
Поскольку для операционных систем windows — семейства инструментальных средств предостаточно, то я буду делать уклон на использование Eclipse + CMSIS в Linux ( Fedora Electronic Lab ).
Итак , скачиваем проект ( загрузить ) и копируем его в свое рабочее пространство. Далее делаем импорт проекта в Eclipse( File → Import → General → File System ).
Проект полностью написан на языке С , содержит стартовый файл ( тоже на С ). Также , как и все проекты для ARM под GCC этот имеет скрипт компоновщика и Makefile. Теперь рассмотрим более детально каждый из перечисленных файлов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
/* startup_STM32F10x.c */ extern void SystemInit( void ); extern const void __cs3_stack; extern const void _fdata; extern const void _data; extern const void _edata; extern const void _bss; extern const void _ebss; /* Эти строки объявляют внешние переменные, в которых хранятся адреса начала и конца различных областей памяти. Конкретные адреса присваиваются переменным в скрипте компоновки. */ void __attribute__(( weak )) Reset_Handler( void ); void __attribute__(( weak )) NMI_Handler( void ); void __attribute__(( weak )) HardFault_Handler( void ); void __attribute__(( weak )) MemManage_Handler( void ); void __attribute__(( weak )) BusFault_Handler( void ); void __attribute__(( weak )) UsageFault_Handler( void ); void __attribute__(( weak )) SVC_Handler( void ); void __attribute__(( weak )) DebugMon_Handler( void ); void __attribute__(( weak )) PendSV_Handler( void ); void __attribute__(( weak )) SysTick_Handler( void ); /* Объявления обработчиков прерываний ядра Cortex-M3. Атрибут «weak» используется для того, чтобы подставлять обработчики, заданные по-умолчанию, если пользователь не прописывает свой обработчик вектора прерывания. */ __attribute__ ( ( section(".cs3.interrupt_vector" ) ) ) void (* const int_Vectors[] )( void ) = { &__cs3_stack, Reset_Handler, NMI_Handler, HardFault_Handler, BusFault_Handler, UsageFault_Handler, 0, 0, 0, 0, SVC_Handler, DebugMon_Handler, 0, PendSV_Handler, SysTick_Handler }; /* Прописывает таблицу векторов прерываний в секции с названием ".cs3.interrupt_vector", которая потом будет размещена в секции кода .text, начиная с нулевого адреса. У микроконтроллеров с ядром Cortex-M3 по нулевому адресу нужно прописывать адрес вершины стека,который загружается в регистр SP( stack pointer ), обычно это последний адрес в ОЗУ. Следующий адрес (0x0 + 4) содержит адрес обработчика вектора сброса. Далее со смещением в 4 байта располагаются все остальные векторы прерываний. Таблица векторов конкретных моделей микроконтроллеров, кроме прерываний ядра, может иметь до 240 обработчиков прерываний различных периферийных устройств. */ void Default_Handler( void ); #pragma weak NMI_Handler = Default_Handler #pragma weak HardFault_Handler = Default_Handler #pragma weak MemManage_Handler = Default_Handler #pragma weak BusFault_Handler = Default_Handler #pragma weak UsageFault_Handler = Default_Handler #pragma weak SVC_Handler = Default_Handler #pragma weak DebugMon_Handler = Default_Handler #pragma weak PendSV_Handler = Default_Handler #pragma weak SysTick_Handler = Default_Handler /* Директива #pragma устанавливает обработчики по-умолчанию для неиспользуемых векторов прерываний, в данном случае это обработчик Default_Handler, который содержит бесконечный цикл. */ void Default_Handler( void ) { while( 1 ); }/* Default_Handler */ extern int main( void ); /* Далее расположен обработчик вектора сброса. В нем выполняется копирование констант из памяти программ в память данных , производится инициализация нулевыми значениями секции .bss , в которой расположены статические переменные языка С. */ void Reset_Handler( void ) { unsigned int * src, *dst; src = ( unsigned int * )&_fdata; for( dst = ( unsigned int *)&_data; dst < (unsigned int *)&_edata; dst++ ) { *dst = *src; } for( dst = ( unsigned int *)&_bss; dst < (unsigned int *)&_ebss; dst++ ) { *dst = 0; } SystemInit(); main(); while( 1 ); }/* Reset_Handler */ /* Функция SystemInit является расширением CMSIS и производит начальную инициализацию микроконтроллера. Если Вы не планируете использовать расширенные функции CMSIS – просто закомментируйте эту строчку. Напоследок выполняется вызов главной функции в языке программирования С под названием main. После main расположен бесконечный цикл на тот случай , если функция вернет код ошибки( int main( void ) ). */ |
Скрипт компоновщика STM32F103.ls написан для микроконтроллера STM32F103RBT6, который содержит 128 кБайт памяти программ и 20 кБайт памяти данных. Если объем памяти Вашего микроконтроллера STM32F10x отличается от заданного — подкорректируйте файл скрипта компоновки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/* STM32F103.ls */ OUTPUT_FORMAT("elf32-littlearm","elf32-bigarm","elf32-littlearm") ENTRY( Reset_Handler ) MEMORY { flash(rx) : ORIGIN = 0x00000000, LENGTH = 128k sram(rwx) : ORIGIN = 0x20000000, LENGTH = 20k } SECTIONS { . = 0x0; .text : { KEEP( *(.cs3.interrupt_vector)); *(.text .text.* ); *(.rodata .rodata.* ); _fdata = .; } > flash .=0x20000000; .data : { _data = .; *(.data .data.* ); _edata = .; } > sram .bss : { _bss = .; *(.bss .bss.* ); _ebss = .; } > sram __cs3_stack = 0x20000000 + ( 20*1024 ); } |
В теле MEMORY именуются доступные области памяти, устанавливаются права доступа ( rwx ), описываются начальные адреса и размеры областей.
В теле SECTIONS описываются доступные секции, основные из которых .text (память программ), .data ( память данных ) и .bss( секция переменных с нулевым начальным значением ).
Для сегмента кода устанавливается начальный адрес 0x00000000, туда ( в .text ) впихивается секция векторов прерываний .cs3.interrupt_vector. Звездочка в начале означает, что если Вы пожелаете разнести таблицу векторов по разным файлам, то все эти области будут сложены в одну. Также секции .text и .rodata из всех исходных файлов будут скомпонованы в одну секцию .text и .rodata соответственно.
Переменная __cs3_stack будет содержать последний адрес в области памяти данных.
Итак, постепенно мы переходим к последнему файлу из рассматриваемого списка — Makefile.
Макеfile для проекта с использованием CMSIS ничем не отличается от мейкфайла для любого другого проекта, за исключением ключа компиляции -DSTM32F10X_MD, который аналогичен директиве #define STM32F10X_MD и определяет имя STM32F10X_MD, необходимое для подстановки в файлах CMSIS строк, соответствующих классификации MEDIUM STM32.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
.PHONY: all clean program CROSS_COMPILE = arm-none-eabi- CHIP = cortex-m3 OPT_LEVEL = 0 DEBUG_FORMAT = dwarf-2 OUT_DIR = ./out OBJ_DIR = ./obj PROJECT_NAME = CMSIS_example OBJECTS = $(OBJ_DIR)/startup_STM32F10x.o $(OBJ_DIR)/main.o $(OBJ_DIR)/core_cm3.o $(OBJ_DIR)/system_stm32f10x.o ELF_FILE = $(OUT_DIR)/$(PROJECT_NAME).out BIN_FILE = $(OUT_DIR)/$(PROJECT_NAME).bin LST_FILE = $(OUT_DIR)/$(PROJECT_NAME).lst MAP_FILE = $(OUT_DIR)/$(PROJECT_NAME).map LD_SCRIPT = STM32F103.ls DEFS = -DSTM32F10X_MD CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)gcc AS = $(CROSS_COMPILE)gcc OC = $(CROSS_COMPILE)objcopy OD = $(CROSS_COMPILE)objdump PROG = openocd CCFLAGS = -Wall -ffunction-sections -fdata-sections -g$(DEBUG_FORMAT) -mcpu=$(CHIP) -c -mthumb -o$(OPT_LEVEL) $(DEFS) LDFLAGS = -Wl,--gc-sections -Wl,-Map,$(MAP_FILE) -Wl,-T$(LD_SCRIPT) OCFLAGS = -O binary ODFLAGS = -S -D PROG_CL = -d0 PROG_CL += -f interface/ftdi_jtag.cfg -f target/stm32.cfg PROG_CL += -c init -c targets PROG_CL += -c "halt" PROG_CL += -c "flash probe 0" PROG_CL += -c "flash write_image erase $(ELF_FILE) 0x08000000 elf" PROG_CL += -c "reset run" PROG_CL += -c shutdown all : $(BIN_FILE) $(LST_FILE) @echo "*** building is completed!!! ***" $(BIN_FILE) : $(ELF_FILE) $(OC) $(OCFLAGS) $(ELF_FILE) $(BIN_FILE) $(LST_FILE) : $(ELF_FILE) $(OD) $(ODFLAGS) $(ELF_FILE) > $(LST_FILE) $(ELF_FILE) : $(OBJECTS) $(LD_SCRIPT) $(LD) $(LDFLAGS) -o $(ELF_FILE) $(OBJECTS) $(OBJ_DIR)/main.o : main.c $(CC) $(CCFLAGS) main.c -o $(OBJ_DIR)/main.o $(OBJ_DIR)/startup_STM32F10x.o : startup_STM32F10x.c $(CC) $(CCFLAGS) startup_STM32F10x.c -o $(OBJ_DIR)/startup_STM32F10x.o $(OBJ_DIR)/core_cm3.o : core_cm3.c $(CC) $(CCFLAGS) core_cm3.c -o $(OBJ_DIR)/core_cm3.o $(OBJ_DIR)/system_stm32f10x.o : system_stm32f10x.c $(CC) $(CCFLAGS) system_stm32f10x.c -o $(OBJ_DIR)/system_stm32f10x.o clean: rm -f $(OBJ_DIR)/*.o $(OUT_DIR)/*.out $(OUT_DIR)/*.lst $(OUT_DIR)/*.map $(OUT_DIR)/*.bin @echo "*** cleaning is completed!!! ***" program : $(ELF_FILE) $(PROG) $(PROG_CL) @echo "*** programming is completed!!! ***" |
В качестве программатора используется openocd. Для того, чтобы прошить программу в память микроконтроллера, необходимо всего лишь запустить цель «Make program» из окна «Make targets» в Eclipse.
Наконец-то я убрал из Makefile абсолютный путь к каталогу bin пакета Sourcery G++ Lite в Fedora Electronic Lab. Для этого пришлось вручную добавить строчку PATH=$PATH:/usr/local/CodeSourcery/Sourcery_G++_Lite/bin/ в файл /etc/profile.
Вторая проблема, с которой я столкнулся , работая в Fedora Linux, — это отказ openocd распознавать адаптер FTDI JTAG. Проблема решается запуском openocd с правами пользователя root. Чтобы избежать всяческих неприятных моментов, связанных с ограничениями прав доступа, я запускаю Eclipse из-под root-а.
Кто работал в Linux, знает , что графический вход в систему для суперпользователя root заблокирован в целях безопасности. Найденные в сети интернет способы разблокироть графический вход под root-ом в Fedora Electronic Lab не приведут к желаемому результату, поэтому единственным вариантом остается запускать Eclipse из терминала с правами root-а.
1 2 3 |
$su $ #/usr/bin/eclipse |
Рабочее пространство располагается в каталоге /root/workspace.
Для копирования файлов я использую менеджер файлов, подобный Norton(Volkov) Commander-у, который называется Midnight Commander( mc ), для его установки необходимо выполнить следующую команду в терминале при наличии соединения с сетью интернет.
1 |
#yum install mc |
Запуск менеджера файлов осуществляется командой mc.
1 |
#mc |
Теперь хочу сказать пару слов о самой программе. Программа предельно проста, она осуществляет поочередную установку и сброс PC12 микроконтроллера STM32F103RBT6 для управления светодиодом, который расположен на этой линии порта C в отладочной плате STM32F103-HB. Для создания временной задержки используется цикл for, значение параметра задержки функции Delay приблизительно соответсвует значению в миллисекундах.
Viewed 21751 times by 5595 viewers
Comments
Здравствуйте!
У меня вопрос по работе с Code Sourcery. Заголовочные файлы можно инклюдить в кавычках и в угловых скобках. Я так понимаю, если написать имя в угловых скобках, то code sourcery возьмет заголовочный файл из той папки, куда codesourcery установлен. Но один заголовочный файл может лежать в разных подпапках, например J:\ARM\Sourcery 2010.09-51\arm-none-eabi\include\stdint.h и J:\ARM\Sourcery 2010.09-51\lib\gcc\arm-none-eabi\4.5.1\include\stdint.h. Где прописано какой именно файл используется?
И с подключением библиотек тоже не могу разобраться. Пути к библиотекам нужно всегда конкретно указывать линкеру директивой -L? Зачем тогда директива -nostdlib?
Некоторые библиотеки ( например stdlib ) являются стандартными для языка С, поэтому никаких дополнительных движений для их подключения совершать не нужно. Ключ «-nostdlib» указывает GCC не подключать автоматически библиотеку stdlib. Ключем -L Вы можете указать компилятору пути к своим или «левым» библиотекам. Конкретная же библиотека подключается с помощью ключа -lname, название библиотеки в этом примере должно быть libname.a или libname.so
Comments are closed.