STM32 e Controle de LEDs: Comparando as Bibliotecas HAL e LL

A STMicroelectronics fornece, além da biblioteca HAL, a biblioteca LL (Low-Level Library), que é uma alternativa de nível mais baixo para acesso direto ao hardware. Neste artigo, vamos entender as principais diferenças entre a LL e a HAL e aplicar a LL em um exemplo simples para controle de sequências de LEDs.

Biblioteca LL (Low-Level Library) e Suas Diferenças em Relação à HAL

A biblioteca LL (Low-Level Library) opera em um nível mais próximo do hardware, oferecendo controle direto sobre os registradores dos periféricos do microcontrolador. A LL é recomendada para cenários de aplicação com requisitos de alto desempenho, como sistemas de controle em tempo real. Enquanto a biblioteca HAL é adequada para desenvolvimento rápido e design de protótipos.

Vantagens da Biblioteca LL

  • Desempenho: Por trabalhar diretamente com os registradores, os drivers LL oferecem maior velocidade de execução.
  • Eficiência de Memória: As funções da LL demandam um espaço de memória consideravelmente menor em comparação à HAL, sendo adequada para dispositivos com recursos limitados.
  • Controle Preciso: Os drivers fornecidos pelo LL correspondem diretamente às funcionalidades dos periféricos descritos no manual de referência. Eles não adicionam camadas de abstração, permitindo um controle mais detalhado e que desenvolvedores otimizem o comportamento de seus sistemas conforme as necessidades específicas.
  • Operações Atômicas: Cada operação altera diretamente o conteúdo dos registradores periféricos. Não utilizam variáveis internas ou memória adicional para armazenar estados, contadores ou ponteiros de dados.

Desvantagens da Biblioteca LL

  • Complexidade e Curva Aprendizado: O uso dos drivers da biblioteca LL é relativamente difícil e requer um certo entendimento de controle de hardware.
  • Não Portátil: A portabilidade das funções da biblioteca LL é baixa. Diferentes modelos de chips STM32 podem ter diferentes versões das funções da biblioteca LL, que precisam ser consultadas de acordo com o modelo do chip.
  • Sem Suporte a Alguns Periféricos: Não inclui suporte para periféricos mais complexos ou que demandam uma pilha de software robusta, como USB.

Comparação com a Biblioteca HAL

CaracterísticaLL (Low-Layer)HAL (Hardware Abstraction Layer
Nível de controleAcesso direto aos registradoresNível alto de abstração
Complexidade de usoMaior menor
PortabilidadeMenor Maior
Consumo MemóriaMenorMaior
DesempenhoAltoModerado
AplicaçãoAplicações de alto desempenho Prototipagem e desenvolvimento rápido

Controle de Sequência de LEDs com Botão usando HAL e LL

O projeto consiste em usar a Franzininho C0 para alternar a piscagem de LEDs entre diferentes padrões quando um botão for pressionado. O objetivo é observar a diferença de consumo de memória e complexidade de implementação entre HAL e LL. 

Pinout Franzininho C0

Bibliotecas HAL e LL

Será utilizado o LED1, LED2 e BOTÃO que estão conectados, respectivamente, aos pinos PB6, PB7 e PA8. 

Lógica esperada no Funcionamento Projeto

  • Quando o microcontrolador se inicializa, os LEDs começam desligados.
  • Ao pressionar o botão, o padrão de piscagem dos LEDs muda. Cada vez que o botão é pressionado, o padrão avança, retornando ao padrão inicial após o último.
    • LED 1 aceso.
    • LED 2 aceso.
    • Alternar LEDs piscando
    • Todos os LEDs acesos.

Configuração CubeMX para HAL 

  1. Crie um novo projeto para a placa “STM32C011F6P6”.
Bibliotecas HAL e LL
  1. Entre na página de configurações de relógio e ajuste HCLK para 48 MHz. Volte para a página de configuração dos pinos, selecione “Trace and Debug” e  habilite “Serial Wire”.
Bibliotecas HAL e LL
Bibliotecas HAL e LL
  1. Selecione os pinos “PB6” e “PB7” e ajuste ambos como “Gpio_Output”. Depois, renomeie a Label como LED1 e LED2.
Bibliotecas HAL e LL
Bibliotecas HAL e LL
  1. Depois, selecione o pino “PA8” como “Gpio_Input” e renomeie a Label para “Botao”.
  1. Por fim, clique na aba “Project” e escolha a opção “Generate Code”.

Código utilizando HAL

Ao gerar o código você terá a seguinte estrutura:

Observe a estrutura e o código criado no arquivo ‘main.c’. Note que todas as funções, como a inicialização da GPIO, utilizam os drivers da HAL. 

No arquivo ‘main.c‘, modifique a função int main com o seguinte código: 

int main(void)
{
 /* USER CODE BEGIN 1 */
 /* USER CODE END 1 */
 /* MCU Configuration--------------------------------------------------------*/
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();
 /* USER CODE BEGIN Init */
 /* USER CODE END Init */
 /* Configure the system clock */
 SystemClock_Config();
 /* USER CODE BEGIN SysInit */
 /* USER CODE END SysInit */
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 /* USER CODE BEGIN 2 */
 static uint8_t estado_controle = 0;          // Estado inicial
 static uint8_t botao_pressionado = 0;          // Estado inicial
 static uint32_t ultimo_tempo = 0;  // Para debounce
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
	// Lê o estado atual do botão
	if (HAL_GPIO_ReadPin(BOTAO_GPIO_Port, BOTAO_Pin) == GPIO_PIN_RESET) {
	   // Verifica se o botão estava previamente liberado
	   if (!botao_pressionado && (HAL_GetTick() - ultimo_tempo > 70)) {
		   // Avança o estado
	       estado_controle++;
	       if (estado_controle > 4) {
	    	   estado_controle = 1;
	       }
	       // Atualiza o tempo do último acionamento
	       ultimo_tempo = HAL_GetTick();
	       // Marca o botão como pressionado
	       botao_pressionado = 1;
	    }
	 } else {
                 // Reseta o estado do botão para "não pressionado" quando liberado
	     botao_pressionado = 0;
	 }
	// Controle dos LEDs com base no estado do controle
	switch (estado_controle) {
	    case 1:
	       // LED1 aceso, LED2 apagado
	        HAL_GPIO_WritePin(GPIOB, LED1_Pin, GPIO_PIN_SET);
	        HAL_GPIO_WritePin(GPIOB, LED2_Pin, GPIO_PIN_RESET);
	        break;
	    case 2:
	       // LED2 aceso, LED1 apagado
	        HAL_GPIO_WritePin(GPIOB, LED1_Pin, GPIO_PIN_RESET);
	        HAL_GPIO_WritePin(GPIOB, LED2_Pin, GPIO_PIN_SET);
	        break;
	    case 3:
	    	// Alternando LEDs
	        HAL_GPIO_TogglePin(GPIOB, LED1_Pin | LED2_Pin);
	        HAL_Delay(500); // Alternância a cada 500 ms
	        break;
	   case 4:
	         // Ambos LEDs acesos
	         HAL_GPIO_WritePin(GPIOB, LED1_Pin, GPIO_PIN_SET);
	         HAL_GPIO_WritePin(GPIOB, LED2_Pin, GPIO_PIN_SET);
	         break;
	   default:
	       break;
	 }
 }
 /* USER CODE END 3 */
}

Basicamente, este código inicializa o hardware (HAL), configura o clock do sistema e os periféricos GPIO. Em seguida, monitora o botão utilizando as funções HAL_GPIO_ReadPin e HAL_GetTick para implementar a lógica de debounce com um intervalo de 70 ms. Com base no estado atual da variável estado_controle, os LEDs são controlados através das funções HAL_GPIO_WritePin e HAL_GPIO_TogglePin, alternando entre diferentes padrões.

Build e Gravação

Após o build, abra a ferramenta Build Analyzer. 

Essa ferramenta fornece uma visão do uso de memória do projeto. Com o nosso projeto, utilizando drivers da HAL, foi registrado um uso de 14,53% da memória flash.

Para realizar a gravação do código no microcontrolador, você pode utilizar um ST-LINK diretamente pela IDE, clicando em “Run” na barra de ferramentas. Alternativamente, é possível usar o STM32CubeProgrammer com um cabo USB. Para um guia detalhado sobre o uso do STM32CubeProgrammer, consulte o tutorial disponível em:

Configuração CubeMX para LL

Para utilizar os drivers LL (Low Layer) em vez dos HAL (Hardware Abstraction Layer), siga os passos abaixo:

  1. Abra o arquivo de configuração do projeto (.ioc) no STM32CubeMX.
  2. Todas as configurações previamente realizadas na configuração anterior serão mantidas.
  3. No menu, entre em Project Manager > Advanced Settings.
    1. Localize a seção de configuração dos drivers e altere o padrão de HAL para LL para os periféricos desejados.
  4. Após realizar essa alteração, gere novamente o código clicando em Generate Code. O código gerado passará a utilizar os drivers LL em vez dos HAL.
Bibliotecas HAL e LL

Código utilizando LL

A nova estrutura do projeto utilizará drivers LL. 

E o novo código que deve ser usado dentro do arquivo “main.c” em int main deverá seguir as funções da LL:

int main(void)
{
 /* USER CODE BEGIN 1 */
 /* USER CODE END 1 */
 /* MCU Configuration--------------------------------------------------------*/
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
 /* SysTick_IRQn interrupt configuration */
 NVIC_SetPriority(SysTick_IRQn, 3);
 /* USER CODE BEGIN Init */
 /* USER CODE END Init */
 /* Configure the system clock */
 SystemClock_Config();
 /* USER CODE BEGIN SysInit */
 /* USER CODE END SysInit */
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 /* USER CODE BEGIN 2 */
 static uint8_t estado_controle = 0;          // Estado inicial
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
	if (LL_GPIO_IsInputPinSet(BOTAO_GPIO_Port ,  BOTAO_Pin) == 0) {
	       LL_mDelay(100);  // Espera pelo tempo de debounce
	    // Verifica novamente se o botão ainda está pressionado após o delay
	    if (LL_GPIO_IsInputPinSet(BOTAO_GPIO_Port ,  BOTAO_Pin) == 0) {
	    	estado_controle++;
	        if (estado_controle > 4) {
	        	estado_controle = 1;
	        }
	    }
	}
	// Controle dos LEDs com base no estado do controle
	switch (estado_controle) {
		case 1:
		   // LED1 aceso, LED2 apagado
                             LL_GPIO_SetOutputPin(LED1_GPIO_Port, LED1_Pin);
		   LL_GPIO_ResetOutputPin(LED2_GPIO_Port, LED2_Pin);
	        break;
	    case 2:
	    	// LED2 aceso, LED1 apagado
	    	LL_GPIO_SetOutputPin(LED2_GPIO_Port, LED2_Pin);
	    	LL_GPIO_ResetOutputPin(LED1_GPIO_Port, LED1_Pin);
	        break;
	    case 3:
	    	// Alternando LEDs
	    	LL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
	    	LL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
	        LL_mDelay(500); // Alternância a cada 500 ms
	        break;
	   case 4:
	         // Ambos LEDs acesos
	          LL_GPIO_SetOutputPin(LED1_GPIO_Port, LED1_Pin);
	          LL_GPIO_SetOutputPin(LED2_GPIO_Port, LED2_Pin);
	         break;
	   default:
	       break;
	 }
 }
 /* USER CODE END 3 */
}

Assim como o código anterior, este também configura o relógio do sistema e inicializa os periféricos GPIO, mas utiliza a biblioteca LL (Low Layer). O programa realiza a leitura do botão e controla os LEDs de acordo com o estado de controle. Para a leitura de entradas, é usada a função LL_GPIO_IsInputPinSet.

Ao contrário da HAL, que utiliza uma única função para alterar o nível lógico de um pino, na LL são usadas funções específicas: LL_GPIO_SetOutputPin para definir um nível lógico alto e LL_GPIO_ResetOutputPin para um nível lógico baixo.

Outro ponto importante é que, na HAL, existe a função HAL_GetTick, que retorna o tempo em milissegundos desde a inicialização do microcontrolador. Nos drivers LL, essa função não está disponível e precisaria ser implementada de outra forma. Como este é um tutorial introdutório, foi utilizado um LL_mDelay como forma de contornar a ausência dessa função para implementar o debounce do botão. No entanto, destaco que essa abordagem não é a solução ideal para aplicações reais.

Build e Gravação

Realize o mesmo processo anterior para o build. Ao usar a biblioteca LL teremos uma redução na porcentagem do uso da memória flash. De 14,53% passou para 8.91%, uma diminuição de quase 40%. 

Funcionamento final

Em ambos os códigos, o funcionamento deve ser similar ao demonstrado no vídeo abaixo.

Conclusão

Ao longo deste artigo, conhecemos as principais diferenças entre as bibliotecas HAL (Hardware Abstraction Layer) e LL (Low-Level Library) da STMicroelectronics e demonstramos a implementação de um projeto prático de controle de LEDs usando ambas as bibliotecas. Analisamos o impacto no consumo de memória e a complexidade de implementação.

A biblioteca HAL, com seu nível mais alto de abstração, permite um desenvolvimento mais rápido e fácil, adequada para prototipagem e projetos onde o tempo de desenvolvimento é importante. Por outro lado, a biblioteca LL oferece um controle mais detalhado sobre o hardware, proporcionando um desempenho superior e um uso reduzido de memória.

A partir da implementação do exemplo de controle de LEDs, foi possível observar uma diminuição significativa no uso de memória flash ao usar a biblioteca LL. Enquanto a solução baseada em HAL registrou um uso de 14,53% da memória, a abordagem com LL reduziu esse consumo para 8,91%, representando uma economia de quase 40% no uso de memória. Essa redução pode ser útil para dispositivos com recursos limitados.

A compreensão de ambas as bibliotecas é importante, pois permite que o projetista escolha a melhor a depender do projeto. 

Desafio

Busque nas referências da LL (low-layer drivers) e tente reproduzir os seguintes exemplos utilizando os drivers da LL. 

  1. Interrupções Externas e USART: https://embarcados.com.br/aprendendo-a-trabalhar-com-interrupcoes-externas-e-usart-na-franzininho-c0/ 
  2. ADC: https://embarcados.com.br/trabalhando-com-adc-no-franzininho-c0-no-stm32cubeide/
  3. I2C: https://embarcados.com.br/configuracao-da-interface-i2c-na-franzininho-c0-para-utilizacao-de-display-oled/ 

Referências 

Description of STM32C0 HAL and LL (low-layer drivers): https://www.st.com/resource/en/user_manual/um3029-description-of-stm32c0-hal-and-lowlayer-drivers-stmicroelectronics.pdf   

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
3 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Benedito Garcia Flores Junior
Benedito Garcia Flores Junior
02/01/2025 11:38

Bom dia e um feliz anos novo
esta tendo uma falha na compilação usando RTOS, na parta threadx, no include tx_api.h
na seguinte macro #define tx_thread_create(t,n,e,i,s,l,p,r,c,a) _txe_thread_create((t),(n),(e),(i),(s),(l),(p),(r),(c),(a),(sizeof(TX_THREAD)))
falha: incompatible type for argument 3 of ‘_txe_thread_create’
parece que esta tudo certo, mas não consigo encontrar a solução
Obrigado

Benedito Garcia Flores Junior
Benedito Garcia Flores Junior
02/01/2025 11:32

boa tarde e um feliz ano novo

Home » Hardware » Microcontroladores » STM32 e Controle de LEDs: Comparando as Bibliotecas HAL e LL

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: