Używaj HAL’a z głową…

Co to jest HAL? W ogólności biblioteka funkcji do obsługi zasobów mikrokontrolera. Zebrała się grupa mądrych, doświadczonych ludzi, pomyślała i napisała. Podoba mi się to rozwiązanie. Czyste, łatwe i przejrzyste. Jednak jak każde narzędzie musi być używane  z głową. Ot pozorna funkcja do ustawiania stanu wyprowadzenia HAL_GPIO_WritePin(). Robi rzecz niby trywialną, ale jak jest złożona. To jest jej rozkład na poszczególne instrukcje (procesora, po kompilacji):

420:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c **** void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
 421:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c **** {
 842              		.loc 1 421 0
 843              		.cfi_startproc
 844              		@ args = 0, pretend = 0, frame = 8
 845              		@ frame_needed = 1, uses_anonymous_args = 0
 846              		@ link register save eliminated.
 847 0000 80B4     		push	{r7}
 848              		.cfi_def_cfa_offset 4
 849              		.cfi_offset 7, -4
 850 0002 83B0     		sub	sp, sp, #12
 851              		.cfi_def_cfa_offset 16
 852 0004 00AF     		add	r7, sp, #0
 853              		.cfi_def_cfa_register 7
 854 0006 7860     		str	r0, [r7, #4]
 855 0008 0B46     		mov	r3, r1
 856 000a 7B80     		strh	r3, [r7, #2]	@ movhi
 857 000c 1346     		mov	r3, r2
 858 000e 7B70     		strb	r3, [r7, #1]
 422:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****   /* Check the parameters */
 423:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****   assert_param(IS_GPIO_PIN(GPIO_Pin));
 424:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****   assert_param(IS_GPIO_PIN_ACTION(PinState));
 425:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c **** 
 426:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****   if(PinState != GPIO_PIN_RESET)
 859              		.loc 1 426 0
 860 0010 7B78     		ldrb	r3, [r7, #1]	@ zero_extendqisi2
 861 0012 002B     		cmp	r3, #0
 862 0014 03D0     		beq	.L55
 427:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****   {
 428:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****     GPIOx->BSRR = (uint32_t)GPIO_Pin;
 863              		.loc 1 428 0
 864 0016 7A88     		ldrh	r2, [r7, #2]
 865 0018 7B68     		ldr	r3, [r7, #4]
 866 001a 9A61     		str	r2, [r3, #24]
 429:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****   }
 430:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****   else
 431:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****   {
 432:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****     GPIOx->BRR = (uint32_t)GPIO_Pin;
 433:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c ****   }
 434:../Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_gpio.c **** }
 867              		.loc 1 434 0
 868 001c 02E0     		b	.L57

To na oko kilkanaście instrukcji, z czego min 10 z nich jest wykonywane zawsze. Dlatego warto jest poznać procesor bliżej, instukcje assembera i warto przeglądać to, co generuje kompilator. Wiem, używając biblioteki możemy szybko osiągnąć efekt. Jednak HAL, jak każde narzędzie nie użyte z głową, wcale nie musi poprowadzić nas właściwą drogą. Załóżmy, że mamy przerwanie timera, które zgłaszane jest co 1ms. W przerwaniu naszym zadaniem jest ustawić 3 różne wyprowadzenia procesora w określone stany. Sprawa prosta. W procedurze przerwania umieszczamy trzy wywołania 3x funkcję HAL_GPIO_WritePin() i sprawa jest załatwiona. Dokładamy kodu, do naszej aplikacji, dokładamy o okazuje się, że że coś się nie wyrabia. Mikrokontroler 72MHz… No skoro się nie wyrabia, bierzemy szybszy i sprawa załatwiona. A jednak moża było podejść inaczej i zastanowić się co tak naprawdę dzieje się w przerwaniu. 3 wywołania funkcji kosztują nasz 30 instrukcji procesora, w ciągu sekundy 30 tyś instrukcji procesora idących w przysłowiowy gwizdek. Gdyby zagłębić się w działanie procesora, to stosunkowo łatwo się dowiedzieć, że żeby zmienić stan wyprowadzenia trzeba wpisać jedynkę na odpowiednie miejsce w rejestrze BSRR lub BRR odpowiedniego bloku GPIO. Zatem zamiast wołania funkcji HAL można napisać GPIOC->BSRR=GPIO_PIN_5 lub GPIOC->BRR=GPIO_PIN_5. Kompilator kompiluje ten kod do trzech instrukcji a nie kilkunastu. Dzięki temu procesor nie musi skakać do funkcji i nie wykonuje dodatkowych 30 tyś instrukcji w ciągu 1s. 30 tyś instrukcji przy zegarze 72MHz, to nie jest to jakiś dramatyczny zysk na przestrzeni 1s, ale wszystko zależy od aplikacji. Tak więc warto poza znajomością bibliotek znać procesor i kompilator na jego najniższym poziomie i korzystać z narzędzi w sposób rozsądny.