cancel
Showing results for 
Search instead for 
Did you mean: 

HAL Library seems to not follow classic programming guidelines

Werner Dähn
Associate II
Posted on June 09, 2018 at 15:42

I understand that programming enterprise software on a 8 core CPU with 4GHz and a simple STM32 MCU is different, nevertheless I would have added a few things to the HAL layer from the beginning:

  1. All HAL Init calls check if there is a collision with other resources. I configure PA1 as GPIO, then I use UART1 which needs PA1, hence the UART config should return an error. Granted, CubeMX is supposed to help you with that in a graphical manner but it does not in case you assign pins dynamically. And even if, what's wrong with validating that in a debug compile firmware a second time? Wouldn't that help beginners significantly?
  2. Programming a MCU is very hardware related. You need to open that gate to provide power to... by setting a register, switch the line to alternate pin by another register etc. From a HAL library I would expect to abstract exactly that. I want to enable UART1 on pin PA6, therefore the__HAL_RCC_GPIOA_CLK_ENABLE is called implicitly by the HAL_GPIO_Init function, the remap is called and the corresponding AF clock enabled and.... Things like that. Why should I remember every dependency if the HAL can do that for me?Another example: How many lines of code do you need to get an quadruple encoder (up/down counter) to work? Logically one call saying which pins to use and maybe choose the timer number. The counter specific X2 falling/X2 rising/X4 setting is needed also. Everything else can be derived from that information. Today you need 20-30 lines of code and if just one is wrong, you will not find out easily.
  3. The implementation is often too basic. Most obvious in the UART part of HAL. You can do char polling, add a interrupt callback or DMA. Fine. But useless as in most cases you would use a ring buffer. No rocket science but since when is that requested and yet still not available out of the box? Another missing receiver method is when timing plays a role like with serial packets. Every 20ms I get a packet that starts with 0x01 and ends with 0xEF and lasts for up to 5ms.

Wouldn't such things help greatly to get started? And to debug? And make the user experience better? And lower frustrations? And cause proper error messages rather than simply not working?

Or am I missing something.

Note: this post was migrated and contained many threaded conversations, some content may be missing.
60 REPLIES 60
Posted on June 15, 2018 at 09:10

Old guys on a trip down the memory lane ...

I know PL/M and ALGOL merely from books, never had access to machine running such heavy stuff at that times. But I barely escaped FORTRAN at my unversity, and got 'teached' the hot new C. Not really learned anything I didn't already know, though.

Basically I agree with Vlad - code generators, even for hardware-level MCU code - will be the future. But ST's CubeMX will not. The quality of every release shows there are too many unilluminated hands involved. Which makes me believe the code generator is not based on a formal/abstract description of the MCU and peripherals (kind of RTL or GIMPLE), but rather manned by a hand-crafted repository.

Never heard of BLISS before either.

Posted on June 15, 2018 at 10:10

Actually, I'm not interested in hiding the hardware details at all - I want to understand them better. I just don't want to wade through them all the time. The existing tools and libraries are much less helpful than they could be for many mundane tasks which are essentially boiler plate for most of my projects, and which frequently baffle newcomers to the platform, leading to many questions in this forum. The generated code should be sufficiently readable that it will educate the interested.

I really like the idea of something like CubeMX, but its implementation is simply dreadful. Why can it only generate C code in terms of HAL (also dreadful)? Why not C++ using CMSIS, or <YourFavouritLibrary>, or even Rust? Configuring the hardware is completely orthogonal to the generated implementation, so why not exploit this? One approach to this might be to have several alternative suites/libraries of driver plugins, with each library resting on some common base code or an application framework. One library might generate everything in terms of CMSIS, another SPL, and so on. No one is forced down any particular path, and the platform is open to new developments. I wanted this tool to appeal to experienced no-libraries types as well as to newbies and Arduino coders. The point is to create a productivity tool which genuinely enhances productivity, and without generating horrible code should not be used in production.

I'm not too worried about a proliferation of driver APIs. I've used enough large widget libraries over the years of which I needed only a tiny fraction. Who cares that there are ten thousand driver classes, so long as there is one which implements that buffered DMA UART you need for your project? This seems preferable to every developer having to independently re-invent every wheel. And if the library doesn't contain just the widget you need, write your own based on what there is, and optionally add it to the library. 

I don't know about this being my calling. I'm just an old fart with some ideas that may or may not be worth pursuing.

Al   

Posted on June 15, 2018 at 10:24

I'm not familiar with Cypress PSOC Creator, but that does look very interesting. Does the diagram approach expose *all* the capabilities of the hardware?

Ahmad M.Nejad
Associate III
Posted on June 15, 2018 at 13:09

The basic idea is: Machines are better than humans in repetitive works.

You may refer to the references every day and read the paragraphs several times. If you convert them in tabular forms, database engines can read this tables in short time.

I had successful experiences with using Access instead of Excel in petrochemical-plant design. Saving time was amazing. An hour instead of a month!

ST Company can employ special engineers to carry out this process. The results may be astounding, but I'm sure ST will win the MCU market.

Posted on June 15, 2018 at 11:43

Basically I agree with Vlad - code generators, even for hardware-level MCU code - will be the future.

Then the Future is Here.

http://www.st.com/content/st_com/en/about/media-center/press-item.html/n4052.html

 

Honestly, guys, please, take your great ideas and go coding, 'cos I don't want to pay for them.

JW

Posted on June 15, 2018 at 13:17

Not sure I understand how that works, but I like the idea of static checking.

I've been working on using C++ templates to represent the hardware more directly in a type safe manner, and to capture maps of the metadata such as GPIO alternate functions, and DMA stream/channels. Every peripheral is represented by a distinct type so, for example, I can use a type function to map the each peripheral to the correct clock bit in RCC. Similarly for mapping IRQ numbers, alternate functions, and whatnot. The goal is a replacement for CMSIS which is easily understandable in terms of the reference manual, and which gives compilation errors when mistakes are made. At least, it would be nice to have static assertions which check that PinX is correct for TimerY ChannelZ, and that sort of thing.

Probably sounds hideous. But everything is resolved at compile time, and the optimiser outputs very efficient code. Simple example. This is working code:

// Very simple driver template

template <typename GPIO, typename GPIO::Pin PIN>

class DigitalOutput

{

public:

    DigitalOutput()

    {   

        // Peripheral base address used to determine RCC AHB index.     

        RCC::AHBENR.EN[RCCClock<GPIO::ADDR>::index] = true;

        GPIO::MODER.MODE[PIN]     = GPIO::Mode::Output;

        GPIO::OSPEEDR.OSPEED[PIN] = GPIO::OSpeed::High;

        GPIO::OTYPER.OTYPE[PIN]   = GPIO::OType::PushPull;

        GPIO::PUPDR.PUPD[PIN]     = GPIO::PuPd::PullUp;

        reset();

    }

    void set()    { GPIO::ODR.OD[PIN] = true; }

    void reset()  { GPIO::ODR.OD[PIN] = false; }

    void toggle() { bool val = GPIO::ODR.OD[PIN]; GPIO::ODR.OD[PIN] = !val; }

};

// Driver instances

DigitalOutput<GPIOD, GPIOD::Pin::Pin14> led1;

DigitalOutput<GPIOC, GPIOC::Pin::Pin12> led2;

int main()

{

    led1.set();

    ...

}

It's a work in progress: promising but very early stages. One of the issues is how to extend support to multiple processors without compromising the specificity of the supported features. My preference is for the library to be generated: choose your processor; generate the library with some tool; use the library... To port the program to another processor: regenerate the library; recompile your code; maybe there are errors from using features which don't exist on the new target.

I realise not everyone loves C++, of course.

Al
Posted on June 15, 2018 at 13:58

Who cares that there are ten thousand driver classes, so long as there is one which implements that buffered DMA UART you need for your project? This seems preferable to every developer having to independently re-invent every wheel.

This your quote is your reply to your first sentence:

Actually, I'm not interested in hiding the hardware details at all - I want to understand them better. I just don't want to wade through them all the time.

To allow the developer to focus on what he needs at the moment means necessarily to hide all the zillions of the MCU internal details from distracting the developer's attention and allow his mind focus on his GOAL.  It is amazing that all those (like myself in the past and yourself at present) who have years of experience dealing with MCU chip' underwear think that this should be goal/means/necessity of the application developer.........

Posted on June 15, 2018 at 14:07

The basic idea is: Machines are better than humans in repetitive works.

Very true Ahmad - and this is another strong reason why HAL-like libraries (if/when well made) should intelligently deal with all the dependencies of the MCU internal registers while providing application-related function, the developer instead should focus on his application.

Posted on June 15, 2018 at 14:21

'...

the developer instead should focus on his application...'

agreed. that's why layering is the single most important invention in software engineering, and why I very much liked the GUI-based programming approach implemented by cypress.

unfortunately ST isn't there yet. Think about how ST's hardware portfolio could be elevated by an equally competent software offering. an opportunity missed so far and for so long.

Posted on June 15, 2018 at 14:48

I want both. I want high level abstractions which are easy to use for boiler plate and to get beginners off the ground. I write such code with application-facing APIs which completely encapsulates the hardware details, the SPL calls, and the CMSIS code.

But I also want useful low level abstractions which directly reflect the nitty gritty details of the hardware, so that I can implement or modify new high level objects more easily. CMSIS sort of does this, but I think it could be better. SPL is a sort of halfway house; HAL is I don't know what; and I haven't a clue about LL.

I already have solutions for making my own drivers self-contained, reusable and agnostic about the particular hardware they are using. It just seems to me that ST should be providing solutions so that everyone benefits from them. CubeMX falls very wide of the mark. I don't know the history of it at all, but it looks like a model that works for tiddly 8-bit devices, but hasn't scaled at all well.

I suspect I have exhausted my 2p on this thread.