STM32F103 + HAL: Использование STM32CubeMX и библиотеки HAL в среде Linux/SW4STM32

разделы: STM32 , среда разработки , дата: 20 октября 2016г.

HAL - это дальнейшее развитие библиотеки SPL, выпущенной фирмой "ST Microelectronics", ориентированный на то, что бы дать разработчику единый инструмент для работы со всеми чипами STM32. Этим единым инструментом стала кросс-платформенная утилита с графическим интерфейсом STM32CubeMX, а сам фреймворк HAL стал называться STM32Cube. И если утилита STM32CubeMX действительно одна для всех микроконтроллеров STM32, то фреймворк STM32Cube/HAL для каждой линейки чипов свой. Т.е. все так же как и в SPL.

STM32CubeMX - позволяет сгенерироваль проект на основе CMSIS+HAL под различные IDE, что помогает избежать многих граблей, на начальном этапе освоения STM32.

Нас будет интересовать линейка STM32F1xx, документация по HAL для этой линейке доступна на официальном сайте Description of STM32F1xx HAL drivers. User Manual UM1850

Установка связки STM32CubeMX + IDE в системах Windows, тривиальна и многократно описана в сети, а вот в Linux могут возникнуть сложности.

Для Eclipse существует плагин STM32Cube, который позволяет запустить STM32CubeMX в окошке Eclipse. Есть в сети пошаговая инструкция как этим плагином пользоваться: Установка и настройка Eclipse, STM32CubeMX под Windows, и я даже где-то видел видео, где все работает. Но в моем случае, проект который в итоге генерировался, Eclipse-ом почему-то не желался приниматься:

И хотя возможно, что можно было бы доконфигурировать проект Eclipce'а, лучше будет воспользоваться готовым IDE "SystemWorkbench for STM32"(он же SW4STM32) компании AC6 на базе того же Eclipse:

SW4STM32 полностью бесплатный, доступный для Linux, Windows и MacOS, не содержит никаких ограничений на размер генерируемого кода. Скачать его можно c официального сайта компании AC6 http://www.ac6-tools.com/downloads/SW4STM32/

Установка STM32CubeMX под Linux тоже не доставляет труда. Процесс можно посмотреть на youtube:

Попробуем написать Blink с помощью этих двух инструментов. Целевым микроконтроллером будет STM32F103CBT6, светодиод будет на PB1, тактирование от внешнего кварца на 8 MHz. Версия java:

$ java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

Запускаем STM32CubeMX и создаем новый проект:

Через выпадающие списки выбираем целевой микроконтроллер:

Во вкладке "Pinout", в пункте SYS включаем отладку по SerialWire, а в через регистры контроля и тактирования RCC устанавливаем внешний кварц - HSE. На ножке PB1 выставляем режим GPIO_Output:

Во вкладке "Clock Configuration" подключаем кварц и устанавливаем нужные делители и множитель PLLMul

Во вкладке "Configuration" устанавливаем режим GPIO для PB1:

Последний этап, через меню->Genrate Code запускаем экспорт проекта. Здесь в качестве целевой IDE указываем SW4STM32, рабочий каталог должен совпадать с рабочим каталогом SW4STM32:

При этом если STM32CubeMX был запущен впервые на данном компьютере, то перед сохранением проекта, будет закачана библиотека HAL для данной линейки микроконтроллеров. Библиотеку можно будет найти в ~/STM32Cube. Так же как SPL она содержит проекты с примерами для работы с той или иной периферией:

Осталось запустить SW4STM32, и через меню->File->Import импортировать сгенерированный проект:

После чего можно попытаться собрать проект:

Если проект собрался успешно, то во вкладке Console будет соответствующий отчет:

Осталось написать саму программу. В while-цикле функции main() следует вставить код:

      HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
      HAL_Delay(500);
      HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
      HAL_Delay(500);

Осталось скомпилировать проект и прошить чип:

darkstar:~/workspace/myCube3/Debug: st-flash write ./myCube3.bin 0x08000000
2016-10-18T16:03:55 INFO /home/flanker/mydev/tools/stlink/src/common.c: Loading device parameters....
2016-10-18T16:03:55 INFO /home/flanker/mydev/tools/stlink/src/common.c: Device connected is: F1 Medium-density device, id 0x20036410
2016-10-18T16:03:55 INFO /home/flanker/mydev/tools/stlink/src/common.c: SRAM size: 0x5000 bytes (20 KiB), Flash: 0x20000 bytes (128 KiB) in pages of 1024 bytes
2016-10-18T16:03:55 INFO /home/flanker/mydev/tools/stlink/src/common.c: Attempting to write 3684 (0xe64) bytes to stm32 address: 134217728 (0x8000000)
Flash page at addr: 0x08000c00 erased
2016-10-18T16:03:56 INFO /home/flanker/mydev/tools/stlink/src/common.c: Finished erasing 4 pages of 1024 (0x400) bytes
2016-10-18T16:03:56 INFO /home/flanker/mydev/tools/stlink/src/common.c: Starting Flash write for VL/F0/F3 core id
2016-10-18T16:03:56 INFO /home/flanker/mydev/tools/stlink/src/flash_loader.c: Successfully loaded flash loader in sram
  3/3 pages written
2016-10-18T16:03:56 INFO /home/flanker/mydev/tools/stlink/src/common.c: Starting verification of write complete
2016-10-18T16:03:56 INFO /home/flanker/mydev/tools/stlink/src/common.c: Flash written and verified! jolly good!

Работать должно без вопросов.

Попробуем исследовать работу программы с помощью отладчика:

$ arm-none-eabi-gdb ./myCube3.elf
Reading symbols from ./myCube3.elf...done.
(gdb) target remote localhost:4242
Remote debugging using localhost:4242
Reset_Handler () at ../Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/gcc/startup_stm32f103xb.s:82
82        movs r1, #0
(gdb) load
Loading section .isr_vector, size 0x10c lma 0x8000000
Loading section .text, size 0xd20 lma 0x800010c
Loading section .rodata, size 0x2c lma 0x8000e2c
Loading section .init_array, size 0x4 lma 0x8000e58
Loading section .fini_array, size 0x4 lma 0x8000e5c
Loading section .data, size 0x4 lma 0x8000e60
Start address 0x80001b0, load size 3684
Transfer rate: 2 KB/sec, 614 bytes/write.
(gdb) break main
Breakpoint 1 at 0x8000c90: file ../Src/main.c, line 72.
(gdb) continue
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, main () at ../Src/main.c:72
72        HAL_Init();
(gdb) 

Поставим точку останова на главный цикл программы:

(gdb) n
75        SystemClock_Config();
(gdb) n
78        MX_GPIO_Init();
(gdb)
88                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
(gdb) list
83
84        /* Infinite loop */
85        /* USER CODE BEGIN WHILE */
86        while (1)
87        {
88                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
89                HAL_Delay(500);
90                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
91                HAL_Delay(500);
92
(gdb) break main.c:86
Breakpoint 2 at 0x8000cec: file ../Src/main.c, line 86.
(gdb) continue
Continuing.

Breakpoint 2, main () at ../Src/main.c:88
88                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
(gdb) 

Попробуем ткнуть наугад:

(gdb) p SystemCoreClock
$1 = 72000000

Здесь вроде все нормально, как и должно быть.

(gdb) n
89                HAL_Delay(500);

Здесь задержки формируются с помощью функции HAL_Delay. Функция использует внутренний таймер ARM - SysTick. Почитать о нем можно здесь: ARM. Учебный Курс. SysTick — Системный таймер. Это обычный 24-битный счетчик, который ничего не умеет, этакий аналог TIMER0 в AVR и TIM4 в STM4. SysTick предназначен специально для формирования задержек или как таймер планировщика в операционной системе.

Заглянем внутрь функции:

(gdb) s
HAL_Delay (Delay=Delay@entry=500) at ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c:325
325     {
(gdb) n
327       tickstart = HAL_GetTick();
(gdb) list 
322       * @retval None
323       */
324     __weak void HAL_Delay(__IO uint32_t Delay)
325     {
326       uint32_t tickstart = 0;
327       tickstart = HAL_GetTick();
328       while((HAL_GetTick() - tickstart) < Delay)
329       {
330       }
331     }
(gdb) s
HAL_GetTick () at ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c:310
310       return uwTick;
(gdb) list 
305       *       implementations in user file.
306       * @retval tick value
307       */
308     __weak uint32_t HAL_GetTick(void)
309     {
310       return uwTick;
311     }
312
313     /**
314       * @brief This function provides accurate delay (in milliseconds) based 

так это работает:

(gdb) continue
Continuing.

Breakpoint 2, main () at ../Src/main.c:88
88                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
(gdb) p HAL_GetTick()
$2 = 2000
(gdb) c
Continuing.

Breakpoint 2, main () at ../Src/main.c:88
88                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
(gdb) p HAL_GetTick()
$3 = 3000
(gdb) c
Continuing.

Breakpoint 2, main () at ../Src/main.c:88
88                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
(gdb) p HAL_GetTick()
$4 = 4000

Теперь посмотрим, как происходит переключение светодиода:

(gdb) s
HAL_GPIO_WritePin (GPIOx=GPIOx@entry=0x40010c00, GPIO_Pin=GPIO_Pin@entry=2, PinState=PinState@entry=GPIO_PIN_SET)
    at ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c:487
487       if(PinState != GPIO_PIN_RESET)
(gdb) disass
Dump of assembler code for function HAL_GPIO_WritePin:
=> 0x08000554 <+0>:     cbnz    r2, 0x8000558 
   0x08000556 <+2>:     lsls    r1, r1, #16
   0x08000558 <+4>:     str     r1, [r0, #16]
   0x0800055a <+6>:     bx      lr
End of assembler dump.
(gdb) info registers r0 r1 r2
r0             0x40010c00       1073810432
r1             0x2      2
r2             0x1      1
(gdb) list
482     {
483       /* Check the parameters */
484       assert_param(IS_GPIO_PIN(GPIO_Pin));
485       assert_param(IS_GPIO_PIN_ACTION(PinState));
486
487       if(PinState != GPIO_PIN_RESET)
488       {
489         GPIOx->BSRR = GPIO_Pin;
490       }
491       else

Здесь нас будет интересовать число 0x40010c00 в r0. Можно предположить, что это адрес порта. Изучение аппнота AN3422 Application note Migration of microcontroller applications from STM32F1 to STM32L1 serie подтверждает догадку:

Структура GPIO описана в заголовочном файле stm32f103xb.h

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

Про GPIO можно почитать здесь: Практический курс STM32. Урок 1 - GPIO. Порты ввода-вывода

(gdb) x/8xw 0x40010c00
0x40010c00:     0x44484424      0x44444444      0x0000fcdd      0x00000010
0x40010c10:     0x00000000      0x00000000      0x00000000      0x00000000

Регистр GPIOB->BSRR в пятом столбце первой строчки, в котором записано значение 0х10. Посмотрим как он меняется:

(gdb) si
0x08000558      493         GPIOx->BSRR = (uint32_t)GPIO_Pin << 16;
(gdb) si
0x0800055a      493         GPIOx->BSRR = (uint32_t)GPIO_Pin << 16;
(gdb) x/8xw 0x40010c00
0x40010c00:     0x44484424      0x44444444      0x0000fcdf      0x00000012
0x40010c10:     0x00000000      0x00000000      0x00000000      0x00000000

Как видно значение регистров ODR и BSRR изменилось. Идем дальше:

(gdb) n
main () at ../Src/main.c:89
89                HAL_Delay(500);
(gdb) n
90                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
(gdb) s
HAL_GPIO_WritePin (GPIOx=GPIOx@entry=0x40010c00, GPIO_Pin=GPIO_Pin@entry=2, PinState=PinState@entry=GPIO_PIN_RESET)
    at ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c:487
487       if(PinState != GPIO_PIN_RESET)
(gdb) si
493         GPIOx->BSRR = (uint32_t)GPIO_Pin << 16;
(gdb) disass
Dump of assembler code for function HAL_GPIO_WritePin:
   0x08000554 <+0>:     cbnz    r2, 0x8000558 
=> 0x08000556 <+2>:     lsls    r1, r1, #16
   0x08000558 <+4>:     str     r1, [r0, #16]
   0x0800055a <+6>:     bx      lr
End of assembler dump.
(gdb) x/8xw 0x40010c00
0x40010c00:     0x44484424      0x44444444      0x0000fcdf      0x00000012
0x40010c10:     0x00000000      0x00000000      0x00000000      0x00000000
(gdb) si
0x08000558      493         GPIOx->BSRR = (uint32_t)GPIO_Pin << 16;
(gdb) si
0x0800055a      493         GPIOx->BSRR = (uint32_t)GPIO_Pin << 16;
(gdb) x/8xw 0x40010c00
0x40010c00:     0x44484424      0x44444444      0x0000fcdd      0x00000010
0x40010c10:     0x00000000      0x00000000      0x00000000      0x00000000

Значение регистров снова сбросилось. Думаю, что здесь все понятно.

поделиться: