Визуальная электроника

Для одной из наших разработок понадобилось использовать программируемую интегральную схему, сокращенно ПЛИС. Если быть точнее FPGA (field-programmable gate array). Чтоб не решать задачу “в лоб”, было принято решение изготовить отладочные платы для экспериментов. На первой плате решено было применить микросхему FPGA Altera EP4CE10E22I7N. Чтобы она не чувствовала себя одиноко, было решено установить ARM микроконтроллер STM32F102C8T6, SRAM 512 Кбайт, держатель uSD карты, разъем USB, 12 битный R2R VGA цап, и 2 разъема PS/2. В первой стать я поделюсь следующим опытом:

  • Установка свободного компилятора GCC для архитектуры ARM
  • Установка и настройка среды разработки Eclipse.
  • Установка генератора стартового кода STM32CubeMx и создание проекта CDC USB.
  • Отладка проекта в Eclipse с помощью J-link.

Первым делом на нужно установить виртуальную машину Java, так как Eclipse написан именно на ней. Для этого переходим по ссылке: http://www.oracle.com/technetwork/java/javase/downloads/index.html. Скачиваем JDK файл. На время написания статьи актуальной версией была jdk-8u45-windows-x64.exe.

Скачиваем последнюю версию Eclipse IDE for C/C++ Developers: http://www.eclipse.org/downloads. У автора установлен eclipse-cpp-luna-SR2-win32-x86_64.

Скачанный архив распаковываем в удобное место, например “C:\eclipse”. Для того чтобы вывод сообщений об ошибках был на человеческом языке необходимо запустить regedit. В разделе HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage изменить параметр OEMCP = 1251.

При первом запуске Eclipse спросит расположение папки для хранения проектов. Выбираем “C:\arm_projects”, выбираем чекбокс запомнить каталог и жмем OK:

Если все прошло успешно, вы увидите окно запущенного Eclipse.

Теперь нам необходимо установить пакет программ для кросс компиляции программ для архитектуры ARM. После приобретения компанией Mentor производителя средств разработки CodeSourcery, самым разумным видится установка пакета расположенного по адресу: http://launchpad.net/gcc-arm-embedded. Скачиваем файл gcc-arm-none-eabi-4_9-2015q1-20150306-win32.zip. Далее создаем каталог “C:\ARMtoolchain\arm_gcc”. Копируем в него содержимое zip файла. Далее скачиваем Make for Windows по ссылке http://gnuwin32.sourceforge.net/packages/make.htm. Находим пакет с пометкой Complete package, except sources, скачиваем, запускаем. В качестве каталога для установки указываем “C:\ARMtoolchain”. Так же скачиваем CoreUtils for Windows по ссылке http://gnuwin32.sourceforge.net/packages/coreutils.htm, пакет должен называться Complete package, except sources. Устанавливаем в каталог “C:\ARMtoolchain”. Все, теперь у нас есть установленный Eclipse и GCC для архитектуры ARM!

Для упрощения разработки мы будем использовать достаточно продвинутую утилиту STM32CubeMX от STMicroelectronics. Переходим по адресу http://www.st.com/web/en/catalog/tools/PF259242 и выбираем STM32CubeF1. После установки запускаем STM32CubeMX.exe, выбираем Help->Install New libraries. Выбираем Firmware Package for Family STM32F1. После этой операции закрываем куб.

Теперь перейдем к установке плагинов Eclipse необходимых для работы и настройке.

Открываем Eclipse, выбираем пункт меню Help->Install New Software. В поле Work with вставляем http://gnuarmeclipse.sourceforge.net/updates, нажимаем Enter.

Выбираем все пакеты. Нажимаем Next 2 раза. Соглашаемся с условиями и нажимаем Finish. Появляется окно с предупреждениями- нажимаем OK. Перезагружаем Eclipse. В меню выбираем Windows ->Preferences, в открывшемся окне настроек C/C++ ->Build ->Environment, при помощи кнопки Add добавляем переменную PATH, имеющую значение “C:\ARMtoolchain\GnuWin32\bin; C:\ARMtoolchain\arm_gcc\bin” — в первой папке лежат утилиты “make.exe”, “rm.exe” и “echo.exe”, мы указываем где их искать. Во второй папке пути и до остальных утилит. Начальная настройка закончена, Eclipse готов к работе.

Приступим к созданию проекта USB последовательного порта в STM32CubeMX. На главной странице выбираем. New Project. В открывшемся окне выбираем вкладку MCU Selector. Настраиваем тип процессора Series=STM32F1, Lines=STM32F102, Package=LQFP48. В получившейся таблице выбираем контроллер STM32F102C8Tx.

Нажимаем OK.Первая вкладка называется Pinout, как не сложно догадаться она предназначена для конфигурации выводов контроллера. Настраиваем пункты USB, RCC, USB_DEVICE.Над выводами PB12-PB15 нажимаем левую кнопку мыши, в выпадающем списке выбираем GPIO_Output.

Выбираем следующую вкладку- Clock Configuration, и настраиваем тактирование согласно следующему рисунку:

В меню выбираем Project -> Settings ..., из списка Toolchain/IDE выбираем TrueSTUDIO, в верхнее окно вводим название проекта (у меня —“usb_vcp_test”), через кнопку Browse указываем на папку рабочего каталога Eclipse -“C:\arm_projects”.

 

Вкладку Code Generation оставляем без изменений:

 

Подтверждаем, что всё верно - OK.

В меню выбираем Project ->Generate Code, ждем окончания генерации кода и нажимаем Close. После этого STM32CubeMX можно закрыть.

В проводнике Windows открываем папку рабочего каталога Eclipse и, среди прочих, видим папку с тем именем проекта, что задавали в настройках Куба (у меня- “usb_vcp_test”). Открываем её, и в этой папке (в ”usb_vcp_test”) создаем папку “System”. Далее из папки “usb_vcp_test\TrueSTUDIO\usb_vcp_test” копируем в папку «System» файл: линкера “ “STM32F102CB_FLASH.ld”. Для других контроллеров- будет файл с наименованием Вашего контроллера и расширением ".ld". После этого папку “TrueSTUDIO” можно удалить.

Аналогично удалил в папке «usb_vcp_test» созданные Кубом файлы ".mxproject", “usb_vcp_test.ioc”. Будете Вы их удалять или нет — решать Вам.

Так-же в папку “System” копируем файл “syscalls.c”.

В папке “usb_vcp_test\Drivers\CMSIS\Device\ST\STM32F1xx\Source\Templates\gcc” меняем расширение у файла “startup_stm32f102x6.s” с ".s" на ".asm", так как используемый нами пакет утилит не понимает расширение ".s".

Теперь приступим к созданию проекта в Eclipse. Выбираем File->New->C Project. Имя проекта задаем “usb_vcp_test” остальное оставляем по умолчанию. 

Два раза жмем Next. На странице Gross GNU ARM Toolchain в окне Toolchain path указываем на “C:\ARMtoolchain\arm_gcc\bin” и жмем Finish:

 

Далее настроим наш проект. Выбираем пункт меню Project->Properties. Настраиваем как на рисунках:

 

Здесь необходимо выбрать ARM family=cortex-m3 и Instruction set=Thumb (-mthumb).

 

В этом пункте необходимо добавить Defined symbols: USE_HAL_DRIVER, ARM_MATCH_CM3, STM32F102x8.

 

На этом этапе добавляем пути для заголовочных файлов:

"${workspace_loc:/${ProjName}/Inc}"

"${workspace_loc:/${ProjName}/Drivers/CMSIS/Device/ST/STM32F1xx/Include}"

"${workspace_loc:/${ProjName}/Drivers/STM32F1xx_HAL_Driver/Inc}"

"${workspace_loc:/${ProjName}/Drivers/CMSIS/Include}"

"${workspace_loc:/${ProjName}/Middlewares/ST/STM32_USB_Device_Library/Core/Inc}"

"${workspace_loc:/${ProjName}/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc}" 

Осталось добавить скрипт линкера по адресу “G:\arm_projects\usb_vcp_test\System\ STM32F102CB_FLASH.ld”.

Открываем файл “Drivers\CMSIS\Device\ST\STM32F1xx\Include\stm32f1xx.h” и добавляем определение нашего процессора #define STM32F102x6 

C++ Code:
  1. /** @addtogroup Device_Included
  2.   * @{
  3.   */
  4. #define STM32F102x6
  5.  
  6. #if defined(STM32F100xB)
  7.   #include "stm32f100xb.h"
  8. #elif defined(STM32F100xE)
  9.   #include "stm32f100xe.h"
  10. #elif defined(STM32F101x6)
  11.   #include "stm32f101x6.h"
  12. #elif defined(STM32F101xB)
  13.   #include "stm32f101xb.h"
  14. #elif defined(STM32F101xE)
  15.   #include "stm32f101xe.h"
  16. #elif defined(STM32F101xG)
  17.   #include "stm32f101xg.h"
  18. #elif defined(STM32F102x6)
  19.   #include "stm32f102x6.h"
  20. #elif defined(STM32F102xB)
  21.   #include "stm32f102xb.h"
  22. #elif defined(STM32F103x6)
  23.   #include "stm32f103x6.h"
  24. #elif defined(STM32F103xB)
  25.   #include "stm32f103xb.h"
  26. #elif defined(STM32F103xE)
  27.   #include "stm32f103xe.h"
  28. #elif defined(STM32F103xG)
  29.   #include "stm32f103xg.h"
  30. #elif defined(STM32F105xC)
  31.   #include "stm32f105xc.h"
  32. #elif defined(STM32F107xC)
  33.   #include "stm32f107xc.h"
  34. #else
  35.  #error "Please select first the target STM32F1xx device used in your application (in stm32f1xx.h file)"
  36. #endif

Затем исключаем из проекта из проекта библиотеку “Drivers\CMSIS\DSP_Lib”. Для этого в Project Explorer на папке "DSP_Lib" нажимаем правую клавишу мыши, из контекстного меню выбираем Properties:

 

Точно такие же действия проделываем над еще двумя файлами- шаблонами: “Drivers\CMSIS\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_msp_template” и “Middlewares\ST\STM32_USB_Device_Library\Class\CDC\Src\usbd_cdc_if_template.c”.  После этих действий проект должен собраться:

22:48:07 **** Incremental Build of configuration Release for project usb_vcp_test ****

make all 

'Invoking: Cross ARM GNU Print Size'

arm-none-eabi-size --format=berkeley "usb_vcp_test.elf"

   text   data    bss    dec    hex filename

   14312   2464   4444  21220   52e4 usb_vcp_test.elf

'Finished building: usb_vcp_test.siz'

' '

 

22:48:11 Build Finished (took 3s.843ms)

 

Такие-же манипуляции производим для конфигурации Debug.

Теперь займемся созданием программы которая будет управлять нашей ПЛИС. Идея взята отсюда: http://www.mcu.by/tag/stm32f4/page/3.

Для начала в конец файла “usbd_cdc_if.c” добавляем следующие функции:

C++ Code:
  1. static struct
  2. {
  3.     uint8_t Buffer[CDC_DATA_HS_OUT_PACKET_SIZE];
  4.     int Position, Size;
  5.     char ReadDone;
  6. } RxBuffer;
  7.  
  8. char VCPInitialized;
  9.    
  10. static int8_t CDC_Init_FS(void)
  11. {
  12.   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, RxBuffer.Buffer);
  13.   VCPInitialized = 1;
  14.   return (USBD_OK);
  15. }
  16.  
  17.  
  18. static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
  19. {
  20.   RxBuffer.Position = 0;
  21.   RxBuffer.Size = *Len;
  22.   RxBuffer.ReadDone = 1;
  23.   return (USBD_OK);
  24. }
  25.   
  26. int VCP_read(void *pBuffer, int size)
  27. {
  28.     if (!RxBuffer.ReadDone)
  29.         return 0;
  30.  
  31.     int remaining = RxBuffer.Size - RxBuffer.Position;
  32.     int todo = MIN(remaining, size);
  33.     if (todo <= 0)
  34.         return 0;
  35.  
  36.     memcpy(pBuffer, RxBuffer.Buffer + RxBuffer.Position, todo);
  37.     RxBuffer.Position += todo;
  38.     if (RxBuffer.Position >= RxBuffer.Size)
  39.     {
  40.         RxBuffer.ReadDone = 0;
  41.         USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  42.     }
  43.  
  44.     return todo;
  45. }
  46.  
  47. int VCP_write(const void *pBuffer, int size)
  48. {
  49.     if (size > CDC_DATA_HS_OUT_PACKET_SIZE)
  50.     {
  51.         int offset;
  52.         for (offset = 0; offset < size; offset++)
  53.         {
  54.             int todo = MIN(CDC_DATA_HS_OUT_PACKET_SIZE,
  55.                            size - offset);
  56.             int done = VCP_write(((char *)pBuffer) + offset, todo);
  57.             if (done != todo)
  58.                 return offset + done;
  59.         }
  60.  
  61.         return size;
  62.     }
  63.  
  64.     USBD_CDC_HandleTypeDef *pCDC =
  65.             (USBD_CDC_HandleTypeDef *)hUsbDeviceFS.pClassData;
  66.     while(pCDC->TxState) { } //Wait for previous transfer
  67.  
  68.     USBD_CDC_SetTxBuffer(&hUsbDeviceFS, (uint8_t *)pBuffer, size);
  69.     if (USBD_CDC_TransmitPacket(&hUsbDeviceFS) != USBD_OK)
  70.         return 0;
  71.  
  72.     while(pCDC->TxState) { } //Wait until transfer is done
  73.     return size;
  74. }

При этом оригинальные функции комментируем:

C++ Code:
  1. /* Private functions ---------------------------------------------------------*/
  2. /**
  3.   * @brief  CDC_Init_FS
  4.   *         Initializes the CDC media low layer over the FS USB IP
  5.   * @param  None
  6.   * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  7.   */
  8. //static int8_t CDC_Init_FS(void)
  9. //{
  10. //  hUsbDevice_0 = &hUsbDeviceFS;
  11. //  /* USER CODE BEGIN 3 */
  12. //  /* Set Application Buffers */
  13. //  USBD_CDC_SetTxBuffer(hUsbDevice_0, UserTxBufferFS, 0);
  14. //  USBD_CDC_SetRxBuffer(hUsbDevice_0, UserRxBufferFS);
  15. //  return (USBD_OK);
  16. //  /* USER CODE END 3 */
  17. //}
  18.  
  19.  /**
  20.   * @brief  CDC_Receive_FS
  21.   *         Data received over USB OUT endpoint are sent over CDC interface
  22.   *         through this function.
  23.   *          
  24.   *         @note
  25.   *         This function will block any OUT packet reception on USB endpoint
  26.   *         untill exiting this function. If you exit this function before transfer
  27.   *         is complete on CDC interface (ie. using DMA controller) it will result
  28.   *         in receiving more data while previous ones are still not sent.
  29.   *                
  30.   * @param  Buf: Buffer of data to be received
  31.   * @param  Len: Number of data received (in bytes)
  32.   * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  33.   */
  34. //static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
  35. //{
  36. //  /* USER CODE BEGIN 6 */
  37. //  return (USBD_OK);
  38. //  /* USER CODE END 6 */
  39. //}

В конец файла “Src\usbd_cdc_if.h” добавляем описание новых функций и структуры:

C++ Code:
  1. int VCP_read(void *pBuffer, int size);
  2. int VCP_write(const void *pBuffer, int size);
  3. extern char VCPInitialized;

В файл "Src\main.c" добавляем:

C++ Code:
  1. /* USER CODE BEGIN Includes */
  2. #include "usbd_cdc_if.h"
  3. /* USER CODE END Includes */
C++ Code:
  1. /* USER CODE BEGIN PV */
  2. unsigned char rx_buff[64];
  3. unsigned char tx_buff[64];
  4. /* USER CODE END PV */
C++ Code:
  1. /* USER CODE BEGIN 3 */
  2. if (VCP_read(&rx_buff, 1) != 1)
  3. continue;
  4. VCP_write("\r\nYou typed ", 12);
  5. VCP_write(&rx_buff, 1);
  6. VCP_write("\r\n", 2);
  7. if(rx_buff[0]=='1'){HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, 0);} //MCU_DATA=0
  8. if(rx_buff[0]=='2'){HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, 1);} //MCU_DATA=1

Далее настроим отладку с помощью J-Link. Выбираем пункт меню Run-> External Tools ->External Tools Configuration. В поле Name пишем jlink-swd, поле Location указывает на путь к исполняемому файле дебаггера. У меня это “G:\Program Files (x86)\SEGGER\JLink_V490e\JLinkGDBServerCL.exe”. Working Directory устанавливаем в ${workspace_loc:/usb_vcp_test/Debug}. В поле Arguments добавляем -select USB -device STM32F102C8 -if SWD -speed auto.

 

На следующей вкладке добавляем нашу конфигурацию в меню External Tools.

Далее настроим параметры отладки. Выбераем пункт меню Run->Debug Configurations.

На пункте GDB_Hardware_Debugging нажимаем правую клавишу мыши и выбираем New.

 

В строку добавляем Project=usb_vcp_test, C/C++ Application=Debug/usb_vcp_test.elf Выбираем настройку Using Legacy GDB Hardware Debugging Launcher Select other. В открывшемся окне выбираем пункт Legacy GDB Hardware Debugging  Launcher.

 

На следующей вкладке настраиваем путь до программы отладчика. У меня это С:\ARMtoolchain\arm_gcc\bin\arm-none-eabi-gdb.exe”. Выбираем JTAG Devise=Generic TCP/IP, Host name or IP address=localhost, Port number=2331.

 

Изменяем название в поле Name на usb_vcp_test Debug. Далее настраиваем сценарий отладки. Добавляем следующие команды:

monitor speed auto

monitor endian little

monitor flash device = STM32F102C8

monitor flash download = 1

monitor flash breakpoints = 1

 

В окно Run commands добавляем:

monitor reset

На последней вкладке добавляем нашу конфигурацию в меню запуска.

 

На этом все! Осталось запустить наш проект. Для начала запускаем сервер отладки командой Run->External Tools->jlink-swd. Если все настроено правильно а отладчик подключен к плате, мы в консоли увидим следующее:

 

SEGGER J-Link GDB Server V4.90e Command Line Version

 

JLinkARM.dll V4.90e (DLL compiled Sep  8 2014 18:46:31)

 

-----GDB Server start settings-----

GDBInit file: none
GDB Server Listening port: 2331
SWO raw output listening port:  2332
Terminal I/O port:  2333

Accept remote connection:         localhost only

Generate logfile: off
Verify download: off
Init regs on start: on
Silent mode: off
Single run mode: off
Target connection timeout: 5 sec.

------J-Link related settings------

J-Link Host interface: USB
J-Link script: none
J-Link settings file: none

------Target related settings------

Target device:  STM32F102C8
Target interface: SWD
Target interface speed: 0kHz
Target endian: little

 

Connecting to J-Link...

J-Link is connected.

Firmware: J-Link Ultra Rev.1 compiled Dec  3 2013 14:27:53

Hardware: V1.00

S/N: 281100008

Feature(s): RDI, FlashBP, FlashDL, JFlash, GDB

Checking target voltage...

Target voltage: 3.33 V

Listening on TCP/IP port 2331

Connecting to target...Connected to target

Waiting for GDB connection...

 

Осталось запустить отладку, выбрав 1 usb_vcp_test Debug в меню с жуком Ж)

 

Получаем окно отладки:

Результат нашей работы на видео:

В следующей статье я расскажу о создании тестового проекта для FPGA Cyclone

Исходные файлы проекта: usb_vcp_test.zip

Файл драйвера Virtual Com Port:  stm_vcp_driver_1_4.zip

Добавить комментарий