STM32 and FreeRTOS. 4. Step towards the HAL

  • Tutorial
HAL 9000: I'm completely operational, and all my circuits are functioning perfectly.
or this should be the first article, but for some reason I always write this kind of thing towards the end

Previously it was about streams , about semaphores and queues

One of the main obstacles to switching to STM32 is the abundance of texts, instructions and manuals that describe how to work with the controller. The culprit of this abundance was STMicroelectronics itself, which at first systematically confused its users, and then offered the wrong exit options.

The problem lies in the variety of controllers produced, which for some reason required different initialization procedures even for the same periphery. And the code running on one controller refused to work on another. As a result, collections of shamanic recipes walk around the network, understanding which requires a lot of time and burning datasheets.

But not so long ago, ST realized what pit she fell into and began to get out of it intensely, attracting new forces. And precisely because of this, now the start time has been reduced to awkwardly small values. What does it look like in practice? Welcome to cat.

First, I’ll give you advice on how to quickly determine what you should not read about STM32.

Firstly, the year of publication is earlier than 2011-2012. Just believe that “what is then” and “what is now” are two big differences. SPL and HAL in the STM world are not just another abbreviation.

Secondly, if you encounter such code (this is just a piece of ADC initialization, if anyone is interested).

RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; 
ADC1->SMPR2 |= ADC_SMPR2_SMP0 | ADC_SMPR2_SMP1 
        | ADC_SMPR2_SMP2 | ADC_SMPR2_SMP3 | ADC_SMPR2_SMP4 
        | ADC_SMPR2_SMP5 | ADC_SMPR2_SMP6 | ADC_SMPR2_SMP7;     
ADC1->SQR1 |= ADC_SQR1_L_2 | ADC_SQR1_L_1 | ADC_SQR1_L_0;
ADC1->SQR2 |= ADC_SQR2_SQ8_2 | ADC_SQR2_SQ8_1 | ADC_SQR2_SQ8_0 
        | ADC_SQR2_SQ7_2 | ADC_SQR2_SQ7_1;           

As a result, after watching such ... magnificence, the majority with the words “oh well, I don’t care, I’ll somehow live with pinMode, how to make such a fuss” threw the board, bought just in case, into the far corner of the desk drawer. I will not hide, I myself was the same and only a great need made me study what is hidden behind the abbreviations CMSIS and SPL. Repeating my path now makes absolutely no sense (and I tried to forget most of what I read), well, except if you have a very big desire to get to the giblets. Therefore, here I am telling for those who need to go.

So what is the problem? The main and only problem is the initialization of the periphery. Those shamanistic dances with beats did not arise from scratch. And 99% of the problems “it doesn’t work for me” are precisely in the incorrect initialization. Wrong frequencies, an attempt to use resources allocated for another, and so on and so forth. What especially aggravates the problem is that there are no error codes or there are no executions. Everything is like a sapper: did something wrong - everything is dead.

Do you think these are fairy tales? Here's a picture of the STMF3 timing scheme


Counting places where you can make a mistake, I propose to do it yourself


As a result, ST rolled out the first SPL (Standard Peripheral Library), which at least improved the situation, but not much. And then the next version came up, which they called HAL. And to make life easier for developers, the STM32CubeMX utility was updated, which allowed literally a couple of mouse clicks to generate an initialization code for the whole variety of boards, processors and peripherals. And next to it I put the so-called firmware, into which I stuffed a bunch of examples of work specifically for this processor.

And the fact that by pressing two buttons you can get 100% (I have never met the opposite), the working code absolutely crushes the thoughts "to optimize and optimize" ...

The program itself is built on the usual principle of "choose with your mouse what you need and press the button." I chose one timing scheme - the program immediately showed the frequencies and highlighted in red those places that cannot work with such frequencies. We tried to use the functionality that is impossible under the given conditions (for example, the leg is already occupied with something else) - the error will again be highlighted.

Here in the figure on the left I showed a list of functions that can be hung on a single leg. After counting, usually AVRschiki (where 4 functions per leg is the maximum) chuckle for a long time and count something in the mind. And when I tell them that there can be more than 160 such legs, they generally precipitate.

But most of all I am pleased with the fact that now thanks to HAL it is not necessary to rewrite the code for working with peripherals. If written somewhere

НAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET); 

Then I know that on all the processors currently available to me, this code will translate the PA2 leg to “zero”. And even the written function of taking the average value of the desired ADC channel will also not resist when switching from L1 to F4

int GetADCValue(uint32_t Channel,uint32_t Count)
{
	int val = 0;
	ADC_ChannelConfTypeDef   sConfig;
	sConfig.Channel=Channel;
	sConfig.Rank=1;
	sConfig.SamplingTime=10;
	HAL_ADC_ConfigChannel(&hadc,&sConfig);
	for(int i = 0; i < Count; i++)
	{
		HAL_ADC_Start(&hadc);
		HAL_ADC_PollForConversion(&hadc,1);
		val += HAL_ADC_GetValue(&hadc);
	}
return val / Count;
}

And now a little magic laid down according to the canons of STM32

What do you think, how much will I have to make the effort to get a com-port in usb from STM32F3 that will simply return what was sent to it? I calculated here - 12 mouse clicks (turn on USB, select CDC, generate) and 6 lines of code in the CDC_Receive_FS function (usbd_cdc_if.c file)

for (int i = 0; i < *Len; i++)
        UserTxBufferFS[i] = UserRxBufferFS[i];
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, &UserTxBufferFS[0], *Len);
USBD_CDC_TransmitPacket(&hUsbDeviceFS);
 USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &UserRxBufferFS[0]);
 USBD_CDC_ReceivePacket(&hUsbDeviceFS); 

And in about the same way and with the same complexity, a “sound card”, “flash drive” or HID is implemented. Yes, they will give only the initialization code, you will need to write the logic yourself, but this is also an awesome help.

I won’t even upload the project, because it’s worth dragging what I hope you already have on your computer, but I will show the result.



To the question "where to get an example?" the answer is simple - in the “firmware” downloaded during the generation, there are a bunch of examples. And I recommend pumping up (in the menu) “firmware” for other processor models - very often the examples do not overlap and the same “ADC via DMA” is only in one place, although it works great there and there.

In a separate paragraph, I note that although the STM32Cube helps the programmer in a quick start, it still requires an understanding of what is being configured and why it is being configured in this way. For example, by default interrupts and DMAs for USART are disabled, so some functions simply will not work. In general, carefully look at the tabs, since the program allows you to regenerate the project, saving what has already been written.

Somewhere here, programmers usually go “eat” to generate “something like that”, change descriptors, buffer sizes and change the displayed name in the task manager to “Super Cool Device”. Well, I'll leave you with this.

Finish and benefit.

Also popular now: