Май 172012
 

Начал собирать небольшую плату для реализации одного проекта. Пока набор функций и пользовательский интерфейс полностью не продуман, поэтому делаю на готовой макетной плате, запаял туда STM32F103RCT6. Микроконтроллер выбран с большим запасом, у этого чипа 256 КБайт Flash памяти, 48 КБайт памяти SRAM, а также полно всяческой периферии (серия STM32L152 по набору внутренних модулей и тактовой частоте этому “камушку”  уступает, зато в ней есть встроенный контроллер ЖКИ и, конечно, главный ее плюс — малое энергопотребление). Далее будет описан процесс подключения встроенного ST-LINK к внешнему микроконтроллеру. Заодно рассмотрим процедуру создания проекта в Keil. Это очень мощная среда разработки, и обойти ее вниманием было бы незаслуженно.

Навесных компонентов у микроконтроллера пока по минимуму: блокировочные конденсаторы у выводов питания, RC-цепочка с кнопкой на выводе RESET, и светодиод с резистором на одном выводе микроконтроллера. Еще на плате установлен линейный стабилизатор на 3,3В.

Макетка

Ниже на рисунке выделены выводы питания на кристалле, ко всем им подведено питание и “земля”, а также подключены блокировочные конденсаторы по 0,1 – 0,2 мкФ.

Выводы питания

Поначалу ST-LINK никак не хотел распознавать внешний микроконтроллер, программа ST-LINK Utility при попытке соединения выдавала сообщение, что не видит подключенного кристалла. Причиной оказались выводы VDDA, VSSA, не подключенные к питанию, поскольку задача была только проверить работоспособность схемы, эти выводы я не стал сразу соединять с линиями питания. Как оказалось зря, питание VDDA предназначено не только для АЦП и ЦАП, но и для работы схемы сброса микроконтроллера и PLL:

VDDA domain

Для работы ST-LINK с внешним устройством надо выполнить следующие действия:

  • На плате STM32L-DISCOVERY убрать 2 перемычки с выводов CN3. Этими перемычками линии SWDIO и SWCLK встроенного программатора ST-LINK соединяются с нужными выводами микроконтроллера STM32L152, размещенного на плате. Соответственно, если мы хотим программировать и отлаживать другой чип, встроенный кристалл надо отцепить от линий SWD.
  • Подсоединить внешний чип к выводам разъема CN2.

Разъемы

Расположение выводов разъема CN2 и порядок подключения их к внешнему устройству (для STM32F103 в корпусе LQFP64):

  1. VDD с внешней платы – здесь не задействован.
  2. SWCLK (тактовая частота SWD) – вывод 49 МК (PA14).
  3. GND
  4. SWDIO (ввод/вывод данных) – вывод 46 МК (PA13).
  5. NRST – сброс (в схеме не задействован).
  6. SWO (зарезервирован) – не задействован.

Как видим, использованы всего 2 вывода SWD – для тактирования SWCLK и для обмена данными SWDIO. Ну и, конечно же, вывод GND у обоих плат должен быть общим. Для питания +5В также взял с Discovery, поскольку на моей плате установлен линейный преобразователь на +3,3В (не самый лучший вариант, в дальнейшем питание платы будет внешним, питание от USB использовал только для проверки работы схемы). Еще есть вывод NRST, здесь он тоже не задействован. Но у ST-LINK есть функция установления соединения во время аппаратного сброса. Это на тот случай, когда нужные выводы микроконтроллера в программе конфигурируются так, что программирование через  SWD становится невозможным. В этом случае соединение устанавливается одновременно с принудительным внешним аппаратным сбросом МК, чтобы не успела запуститься на выполнение программа, содержащаяся во флэш-памяти.

После правильной распайки и подключения, заработало все сражу же, сначала попробовал через ST-Link Utility, никаких проблем не возникло. Чтение и стирание памяти прошло без проблем, теперь предстояло проверить работу отладчика. Для этой цели решил создать простейший проект в среде Keil – помигать светодиодом.

Keil, как и IAR, делает программные продукты для профессиональных разработчиков. Список поддерживаемых микроконтроллеров ARM у них очень большой. Также поддерживаются все основные аппаратные средства отладки для этих семейств. Плюс к тому большое количество примеров работы с различной внутренней периферией микроконтроллеров и оценочными платами сделаны именно под эти две среды разработки, а также у обоих производителей имеется мощная справочная система для этих продуктов. Если их сравнивать, то у обеих IDE есть свои достоинства и недостатки. Далее, описывая процесс создания проекта в Keil, рассмотрим некоторые из них, те плюсы и минусы, которые на самом виду.

Полнофункциональные версии продуктов этих компаний стоят недешево, но есть бесплатные версии с ограничениями. У Keil бесплатная версия ограничивает максимальный размер выходного файла величиной 32 КБайт. Для изучения более чем достаточно.

Для загрузки бесплатной версии необходимо зайти на Keil.com, и загрузить установочный файл MDK-ARM. Перед началом загрузки предложат заполнить анкету. После загрузки и установки можно начать создавать новый проект.

Итак, заходим в меню Project и выбираем пункт New uVision Project. Откроется окно сохранения проекта, обязательно создаем сначала каталог для нового проекта, даем название проекту и сохраняем. Каталог лучше создавать отдельный под каждый проект, файлов там будет много. Я незатейливо назвал все “led” – светодиод. Вот как получилось

Каталог проектов

Как видим, в общую папку для проектов добавлен каталог Libraries из стандартной библиотеки STM32F10x_StdPeriph_Lib. Библиотеку можно загрузить с сайта производителя STMicroelectronics. Далее появляется новое окно, где выбирается производитель и нужный кристалл.

 

Keil_Выбор МК

В моем случае это STM32F103RC. Справа находится краткое описание характеристик выбранного микроконтроллера (а у IAR такого нет). Жмем ОК, появляется такое окно

Keil_файл startup

Тут нам предлагают создать файл начальной инициализации и сразу же подключить его к проекту. Соглашаемся. Далее создадим файл основной программы. Заходим в меню File и выбираем пункт New. В окне редактора кода откроется файл Text1. Сохраним его File –> Save As под именем main.c, пусть пока будет пустой. А теперь обратимся к окну Project в левой части и займемся его содержимым. Создадим там новые каталоги и добавим туда необходимые библиотечные файлы. Идем снова в меню Project и выбираем там Manage –> Component, Environments, Books.

Project_Manage

Для начала переименуем группу Source Group 1 в Startup (двойной клик по названию группы). Затем создадим еще две новые группы: System Files и Source Code (вверху, над названиями групп, кнопка с квадратиком – New(Insert) называется). Все названия взяты из справки Keil, где описано создание приложения, можно называть группы как вам больше нравится. А теперь необходимо добавить к проекту нужные файлы, выделяем группу и жмем кнопку Add Files. Всего добавляем три файла:

  • В группу Source code файл main.c
  • В группу System Files файлы core_cm3.c (LibrariesCMSISCM3CoreSupport) и system_stm32f10x.c (LibrariesCMSISCM3DeviceSupportSTSTM32F10x)

Получим вот такой список подключенных файлов в окне Project

Список файлов

Все эти действия можно сделать другим способом, правой кнопкой мыши вызывая контекстное меню в окне Project у  Target 1 и названий групп, через его элементы Add Group и Add Files to Group.

Теперь, когда минимально необходимое количество файлов подключено к проекту, можно перейти к его конфигурации. В меню Project выбираем Options for Target… (или то же самое, вызвав контекстное меню правой кнопкой мыши у Target 1 в окне Project). Откроется окно настроек проекта.

Keil_Options_Target

На вкладке Target можно ничего не менять. Обратите внимание, что начальные адреса и размеры регионов памяти уже заданы. Размеры зависят от выбранного чипа, и изменяются автоматически (у IAR они прописываются вручную в настройках линкера). Можно перейти на вкладку Device, если надо поменять микроконтроллер или вновь посмотреть на его характеристики.

На вкладке Output необходимо поставить галочку в пункте Create HEX File.

На вкладке C/C++ можно задать уровень оптимизации при компиляции проекта, а также необходимо прописать пути к подключенным файлам. Жмем кнопочку справа от поля ввода Include Paths. Далее в открывшемся окне создаем новый путь (жмем New (Insert)). Появляется новое поле ввода, справа от него кнопка выбора каталога. Отыскиваем поочередно все подключенные файлы, каждый новый путь к каталогу должен быть в отдельной строке. Вот что у меня получилось.

Пути к файлам

А у IAR с этим все печально, эти пути надо прописывать ручками, при этом вычисляя количество переходов на верхний уровень .. от папки с проектом и задавая их вот такими конструкциями $PROJECT_DIR$… Keil же, как видите, всю эту работу берет на себя, предоставляя пользователю нормальный интерфейс выбора каталога с возможностью работать лишь мышкой.

Вкладка Debug. Здесь выбираем способ отладки: в симуляторе или в железе. У меня это ST-Link Debugger. Также ставим галочку у Run to main().

Keil_Options_Debug

Жмем кнопку настроек дебаггера Settings. Здесь переключаем отладку с JTAG на SW.

Настройка дебаггера

(Последняя версия Кейла на данный момент – это MDK-ARM 4.50. Раньше это окошко с настройками было меньше, там был только выбор между JTAG и SWD).

Вкладка Utilities.  Здесь в качестве драйвера для программирования флэш выбираем ST-Link Debugger. На этом настройки закончены, можно сохранять, жмем ОК.

Теперь можно приступить к написанию программы. Светодиод я подвесил к выводу PB0 порта B. Вот такой текст у меня получился, пояснения будут ниже.

#include "stm32f10x.h" 

uint32_t i; //Объявляем переменную для цикла задержки 

int main()
 {
  //Конфигурация порта В
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //Тактирование порта В
  GPIOB->CRL |= GPIO_CRL_MODE0_0; //Вывод PB0 порта В настроен как выход
  GPIOB->CRL &= ~GPIO_CRL_CNF0; //Режим Push-Pull для вывода PB0 порта В
  while(1)
  { 
   GPIOB->BSRR |= GPIO_BSRR_BS0; //Устанавливаем в "1" PB0, зажгли светодиод
   for(i=0; i<0x0003FFFF; i++); //Задержка
   GPIOB->BSRR |= GPIO_BSRR_BR0; //Сбрасываем в "0" PB0, светодиод погасили
   for(i=0; i<0x0003FFFF; i++); //Задержка
  } 
 }

Вначале, как обычно, подключается файл stm32f10x.h. В нем заданы все структуры внутренней периферии микроконтроллера, а также сделаны готовые битовые маски с символическими именами для записи в регистры нужных значений. Там все довольно понятно прописано и снабжено комментариями. Для операций с отдельными битами регистров здесь есть практически все, что нужно. Есть и другой вариант – использование библиотек, в которых имеются готовые функции инициализации периферии, с их помощью можно также создавать нужную конфигурацию. Далее объявлена переменная i, она нужна для цикла формирования временной задержки. Ну а саму процедуру настройки порта я поместил в основной цикл программы.

Теперь надо внести некоторые изменения в файл stm32f10x.h. Сначала находим его в библиотечных папках по адресу LibrariesCMSISCM3DeviceSupportSTSTM32F10x. Кликнем по нему правой кнопкой мыши и зайдем в Свойства. Тут снимаем галочку у атрибута “Только чтение”. Теперь его можно модифицировать. Найдем в начале файла такие строки:

Файл stm32f10x

Здесь надо раскомментировать одну строку с нужным семейством микроконтроллера. У меня микроконтроллер принадлежит к семейству “High density”, соответственно теперь #define задает нужное семейство. Сохраняем файл с внесенными изменениями. К какому именно семейству принадлежит микроконтроллер можно узнать из datasheet на него или на сайте STMicroelectronics, набрав название в поиске.

В отличие от серии STM32L, здесь процедура настройки выводов порта получилась короче. Рассмотрим детальней. Можно задать следующие режимы работы выводов порта:

  • Высокоомный (плавающий) вход. Этот режим задан по умолчанию после сброса МК
  • Вход с “подтяжкой” либо к плюсу питания либо к GND
  • Аналоговый режим (вывод используется АЦП)
  • Выход с открытым стоком
  • Выход Push-Pull
  • Настройка порта в режим альтернативных функций. Выход может быть как с открытым стоком так и Push-Pull.

Если не затрагивать регистры настройки альтернативных функций (лучше пока не будем), то регистров настройки режимов работы порта совсем немного. Точнее всего два – GPIOx_CRL и GPIOx_CRH. А если быть еще точнее, то оба эти регистра выполняют совершенно одинаковые функции, просто настройка всех 16 выводов порта в один регистр не поместилась, поэтому используется два регистра, один для младших 8 разрядов порта, второй для старших, соответственно. А поскольку в данном случае используется всего один вывод PB0, вся настройка производится всего через один регистр GPIOx_CRL. А для настройки режимов тут имеются только биты CNFy[1:0] и MODEy[1:0] для каждого вывода порта. Режим входа или выхода, а также максимальная скорость работы линии порта задаются битами MODEy[1:0]:

  • 00 – Вход (по умолчанию после сброса МК будет этот режим)

Все остальные значения, отличные от 00, конфигурируют линию порта как выход, но с различными максимальными скоростями работы:

  • 01 – Выход, 10 МГц
  • 10 – Выход, 2 МГц
  • 11 – Выход, 50 МГц

А вот режимы, задаваемые битами CNFy[1:0], зависят от того, как сконфигурирован вывод порта битами MODEy[1:0], как вход или как выход. Для входа в CNFy[1:0] задаются следующие режимы:

  • 00 – Аналоговый режим
  • 01 – Высокоомный (плавающий) вход (задан по умолчанию)
  • 10 – Вход с подтяжкой Pull-Up / Pull-Down
  • 11 – Значение зарезервировано

Если же вывод порта настроен как выход, тогда значения бит CNFy[1:0] задают другие режимы работы:

  • 00 – Выход Push-Pull
  • 01 – Выход с открытым стоком
  • 10 – Альтернативная функция, Push-Pull
  • 11 – Альтернативная функция, открытый сток
 //Конфигурация порта В 
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //Тактирование порта В 
GPIOB->CRL |= GPIO_CRL_MODE0_0; //Вывод PB0 порта В настроен как выход
GPIOB->CRL &= ~GPIO_CRL_CNF0; //Режим Push-Pull для вывода PB0 порта В

Итак, для работы порта сначала подаем на него тактирование (порты ввода-вывода здесь тактируются от шины APB2). Во второй строке записывается значение 01 в биты MODE0[1:0], что задает следующий режим работы вывода PB0 порта – выход с максимальной скоростью 10 МГц. Напоминаю, что все битовые маски заданы в файле stm32f10x.h, просто подбираем готовую. Вот такая запись там, к примеру, задает использованную здесь маску GPIO_CRL_MODE0_0:

#define GPIO_CRL_MODE0_0 ((uint32_t)0x00000001) /*!< Bit 0 */

В следующей строке обнуляем биты CNF0[1:0] – задаем режим Push-Pull для вывода PB0. По умолчанию там находится значение 01, в таком случае получили бы выход порта с открытым стоком. Вот и вся настройка порта. Значительно проще, чем в STM32L, там все это настраивается через несколько регистров. Какие еще регистры порта имеются в данном МК (STM32F103RC):

  • GPIOx_IDR – регистр входных данных. Используется для работы в режиме порта “вход”
  • GPIOx_ODR – регистр выходных данных. В него записываются данные, которые хотим увидеть на выходе порта. Для обоих регистров, входного и выходного, рабочими являются только 16 младших разрядов, старшие зарезервированы
  • GPIOx_BSRR – регистр побитовой установки или сброса выходов порта. Поскольку в выходной регистр GPIOx_ODR запись значений может производиться только через операции “чтение – модификация – запись” в эту процедуру может неожиданно вклиниться прерывание. Поэтому, чтобы по-быстрому поменять значения на выводах порта, предусмотрен специальный регистр GPIOx_BSRR, который позволяет управлять отдельными битами выходного регистра GPIOx_ODR. При этом операция установки / сброса бит является “атомарной”, то есть выполняется не по принципу “чтение – модификация – запись”, а намного быстрее, и возникшее прерывание уже ничего не успеет нарушить. Если попытаться объяснить “на пальцах”, то реализуется это через специальные области доступа к битам в памяти. Записанное туда значение автоматически модифицирует выходной регистр данных ODR. Младшие разряды GPIOx_BSRR отвечают за установку бит в выходном регистре, старшие за сброс. То есть, записывая “1” в разряд 0 (“set bit 0”) регистра BSRR, мы сразу же увидим ее на выходе PB0. И наоборот, “1” в разряде 16 (“reset bit 0”) BSRR установит “0” на выходе PB0.
  • GPIOx_BRR – наполовину то же самое, что и предыдущий регистр, но отвечает только за сброс бит в выходном регистре ODR. Используются только младшие 16 разрядов, старшие зарезервированы.
  • GPIOx_LCKR – регистр блокировки конфигурации. Используется чтобы защитить настройки портов от случайного изменения.

Теперь вернемся к программе, далее следует бесконечный цикл зажигания / гашения светодиода

 while(1)
 {
  GPIOB->BSRR |= GPIO_BSRR_BS0; //Устанавливаем в "1" PB0, зажгли светодиод 
  for(i=0; i<0x0003FFFF; i++); //Задержка
  GPIOB->BSRR |= GPIO_BSRR_BR0; //Сбрасываем в "0" PB0, светодиод погасили
  for(i=0; i<0x0003FFFF; i++); //Задержка
 }

Как видим, управление состоянием вывода PB0 осуществляется как раз через регистр побитовой установки / сброса BSRR. Между каждым состоянием введена задержка с помощью цикла for(). Теперь осталось нажав F7 (Build), запустить сборку проекта. Затем подключив устройство к компьютеру, можно запускать отладку, нажав кнопку Кнопка Debug , или через меню Debug.

Отладка в Keil

Общий вид

Далее можно выполнять пошаговую отладку Пошаговая отладка  или сразу запустить программу на выполнение Run .

Другие статьи:

  8 Responses to “STM32-DISCOVERY. Встроенный ST-LINK + STM32F103 + Keil.”

  1. PANYTA

    Здравствуйте.
    А почему здесь (в Keil) не «проходит» конструкция for(uint32_t i=0; i<0x0001FFFF; i++); ?
    Выдаёт ошибку main.c(105): error: #254: type name is not allowed
    В IAR это работает.

    • Не могу сразу ответить, с Keil я практически не работаю.

    • Потому что в кейле не поддерживается по умолчанию 99-ый стандарт языка С, а объявлять переменные в самом цикле for можно только начиная с этой версии стандарта.
      Что бы включить поддержку стандарта в Keil, зайдите Project Options, C/C++: и в строку Misc Controls допишите —c99. Будет компилироваться.
      Но лично я так стараюсь не делать, поскольку как показывает личный опыт, даже в 2013 году есть еще компиляторы, которые не дружат с 99 стандартом, и соответственно код становиться печально привязанным к компилятору. Так что мне кажется проще написать
      uint32_t i;
      for(i=0; i<0x0001FFFF; i++);
      Автору спасибо за статью! Кстати для тех у кого нету дискавери, могу посоветовать самодельный ST-LINK-V2 http://tablock.org.ua/post/107/programmator-otladchik-st-link-v2-svoimi-rukami-dlya-stm8-i-stm32

  2. Я что-то запутался, почему при тех-же настройках порта «В» светодиод не мигает выводе 3 и 4?

  3. Кажется понял. Может ли это быть из-за того что PB3 это TDO/SWO а PB4 это TRST? Я говорю о STM32F103VET6, хотя STM32f100RBT6b история точно такая же и там эти порты имеют такое же назначение.

    • Да, скорее всего так. Я сейчас расположение пинов не смотрел на эти контроллеры, но приходилось однажды перенастраивать выводы, отключать от интерфейса SWD или JTAG, по дефолту они настроены на подключение программатора, а другие функции могут не работать.