Skip to main content
Explorer
March 6, 2020
Question

Writing own drivers vs HAL for professional project

  • March 6, 2020
  • 9 replies
  • 7669 views

Hi,

firstly i apologise if this is in the wrong section as I'm asking more for technical advice than an answer for a specific technical question.

First a bit of background:

I'm a PhD student that is the sole engineer on a three year project that includes hardware design and embedded firmware programming. For the embedded side I've elected to use the STM32H7 line for its fast clock speeds and DSP capabilities as I am going to be doing DSP related work further down the line. i have spent the past two months working with STM32F4 and getting to learn the fundamentals as it is a simpler device than the H7. My previous experience in embedded systems was 8 bit PIC as an undergraduate.

Issue:

The first month spent with the STM32F4 I played around with STM32CUBEMX for the clocks and UART and HAL for the ADC peripheral and got some fairly complicated system (read in to the ADC, do some stuff, output results over GPIO and over USART) up and running. Because I also want to learn the hardware itself, in the second month I spent writing my own drivers from scratch. I got GPIO with interrupts, and SPI up and running. Then I elected to move to the H7 as I want to do some fairly intensive DSP work later on, and in two months have only written GPIO drivers based on the previous work I did for the STM32F4. It is taking me ages to understand the peripherals present in the H7 as they seem to be orders of magnitude more complicated than the F4 (the SPI is a good example of this). If I want to go down the writing my own drivers route, I also have to write drivers for: The clock sources, the USART, the DMA, the ADC, timers and I may want to interface with a TFT display at some point (maybe TouchGFX?). My concern is that I won't have enough time before I get to the DSP part of the project if I write my own drivers as there is just too much work to do (for someone who has to understand how each of the peripherals work first). My other concern however is that if I use HAL to get the job done quicker, I'm not learning enough about the system. I would like to have deep knowledge of the STM32 when this project is done, but my fear is that HAL will abstract way too much. Worse case scenario for me is after the project attempting to get a job that requires register level knowledge and I only have HAL knowledge.

I'd also like to note that I have no professionals here that I can go to for advice. If I get stuck on something, the project halts until I can find the solution.

Can anyone give me any advice on how to make a decision with the project? Do i proceed to write my own drivers and get a better understanding of the chip and more performance out of the chip but at running the risk of running out of time or do I just proceed with HAL and get everything up and running nice and quickly and not worry about the details?

Any help or advice would be appreciated, I'm at the point in the project now where I need to make this decision before I proceed with continuing to write the drivers.

    This topic has been closed for replies.

    9 replies

    Explorer
    March 6, 2020

    I reckon the F7 device would be an alternative, and reasonable compromise in performance vs. complexity.

    I suggest to have closer look at Cube and H7 related issues here before getting started.

    > I would like to have deep knowledge of the STM32 when this project is done, but my fear is that HAL will abstract way too much. Worse case scenario for me is after the project attempting to get a job that requires ARM knowledge and I only have HAL knowledge.

    I think most seasoned engineers would agree to your worries.

    Work environments, architectures, MCU vendors and toolchains change quite often. No employer in my career ever cared about which IDEs I worked with.

    CDyer.1Author
    Explorer
    March 6, 2020

    Hi,

    thanks for the response. I've actually struggled to determine what the differences are between the F7 and the H7 other than the H7 is newer and practically double the clock speeds. I'll have a look at some of the F7 nucleo board datasheets.

    Yeah that's my concern. I like ST but what if the potential future employer is using completely different drivers or worse a completely different chip such as TI or NXP? I want to get as much out of this project personally as I can and HAL wouldn't help with that.

    Graduate II
    March 6, 2020

    Personally I find MCU and peripheral knowledge is pretty transportable, but then I was doing ARM stuff in the mid 80's and did PASCAL, C and Assembler as an undergrad. Have a pragmatic understanding of the plumbing and function you'll be fine.

    Workplaces tend to have MCU and Tool preferences, there may be historic/investment reasons for this, With foundational knowledge you can step into any role and interview well, chips/tools are very transitory.

    Find a level that is comfortable, I can do register level coding, but it is a slog, I'm not a fan of HAL, much preferred the older SPL, but they provide for a quick means to pull everything together. You can then discard or refactor portions that you don't like or are just broken, but also tune things based on your own critical paths. ie mash out a working proof-of-concept, then focus time where it makes a difference. Initialization/configuration code is usually run once, its usually not worth spending hours on optimizing, let the compiler/linker optimize and remove dead code. Focus on user interface, it saves a lot of friction from end users/support.

    It is easy to get lost in the minutia, focus on the details, and know where to find the specifics when you need them.

    The H7 is much more complex than the F7 due to the busing for multi-core, and the dual porting of memory/peripherals and local memories. Check the bus matrix diagrams, be sure to balance where you use/hold resources. Have the CM4 deal with more mundane stuff.

    Visitor II
    March 6, 2020

    I pretty much second what clive1 said, but note that the HAL is not complete nor efficient considering DSP-use.

    The HW can usually do much more than what's supported by HAL, and HAL tends to be slow, because it's quite generic.

    I'd vote for doing it yourself. Or maybe with some help from CMSIS.

    CDyer.1Author
    Explorer
    March 6, 2020

    I've not actually looked too deeply into CMSIS but I was under the impression that it was purely for the ARM stuff, not the vendor specific stuff such as peripheral access, control etc. Although I know I'll need it at some point for the DSP implementation.

    Super User
    March 6, 2020

    > get a job that requires ARM knowledge and I only have HAL knowledge.

    ARM is the MCU, not the peripherals you mentioned above. They will vary alot from vendor to vedor at register level.

    If you abandon existing abstraction levels you have to read the data sheets, reference manuals errata sheets *very* carefully. And, you will have bugs in your code, so you re-read again and spend a lot of time with it. And documentaion? And re-usability? If I was your supervisor, I would not let you do this without a good reason. After you finish, the next person has to live with your project. Yes HAL will hurt you with annoyances and bugs, but nobody stops you digging deeper on demand. So I would go with a top-down approach and keep your target (PhD) in sight.

    And, you will learn alot during that time. You will speak fluently I2C / SPI / ... and your favorite tools / IDE. I have hired ~100 engineers during my career and it rarely happend that the applicants uses exactly the same tools as we did. But, if you can demonstrate your knowledge in a nearby field, wouldn't that be great?

    CDyer.1Author
    Explorer
    March 6, 2020

    I know the ARM is the CPU and the STM32 is the MCU, containing the ARM core + STs peripherals. I appreciate that outside the ARM core that different vendors will vary the peripherals and how they are configured but won't using HAL prevent learning a more intimate knowledge of the peripherals, the basics of which can then be transferred to any system? An example being SPI, learning how to configure it correctly in HAL isn't exactly giving you the best understanding of how the MCU configures it. It would be extremely difficult to then go to NXPs LPC series and do the same thing , especially as they don't have a HAL equivalent (unless I'm mistaken), you'd have to do it via low level. I have to admit that using HAL I got the fundamentals of the firmware done in about a month whuch was great whereas writing my own drivers, I've barely gotten past toggling a LED with interrupts. A lot of people are saying that the HAL isn't suitable for DSP or for time critical or safety critical applications, what are your thoughts on that? Just to clarify, this project should result in a final commercial product.

    Visitor II
    March 6, 2020

    Assuming this is not a commercial product being developed you are better off using the HAL and focusing on the core application code. You have a fixed deadline in a PhD program that can't be extended, so there's little margin for delays writing and debugging your own drivers. Yes, some risks are higher in that you have to trust the HAL will work reliably for what you want to do, but all projects have some degree of risk. Being non-commercial you can tolerate a less robust design. Like the Curate's Egg, some parts of the HAL are palatable....

    On the other hand, if this IS a commercial product you have to consider long term reliability and maintainability. That's why I never use the HAL, LL or Cube software. If a critical design goal is reliability, especially if there's a safety factor involved, then spend the time to develop your own code library. I require tight control over code quality and functionality for what I work on (medical equipment) so I have an extensive and well-tested library of both code and hardware for STM32 across several families (no, don't ask, all proprietary and very expensive). That library represents quite a few years of work, more than would be justified in your project.

    Jack Peacock

    CDyer.1Author
    Explorer
    March 6, 2020

    Unfortunately this is a commercial product (the PhD aspect has kind of been bolted on) and the final product is expected to constantly operate in an industrial environment for years. With the product it's replacing, some of the installations have been operating constantly for 20 years! While the process isn't quite as critical as your medical equipment example, the last thing I want is for some unforeseen bug in the HAL to bring the system crashing down. Not that my own code would be bug free, but it would certainly be easier to fix any issues that may arise. With your final point, that's my biggest concern. To write a fully featured in-house library would be a project in of itself and realistically I have only a number of months to get to a base line where I can start the DSP work. As I'm looking to implement a handful of peripherals and largely operate them in their simplest modes, I think it may be worth spending some time just writing basic drivers for what i need rather than writing something fully featured.

    Visitor II
    March 6, 2020

    You might like the middle ground and use the LL libraries. They abstract away a little bit, not enough that your stuff comes out of the oven broken, but a little bit more than direct register access. Perhaps it's the compromise that'll satisfy?

    DSP wise your efforts are going to be in CMSIS and possibly even optimizing beyond the compiler say for SIMD. Heaven help you if you have to use the gcc assembler's funky mnemonics (or did they adopt ARM mnemonics? That would be cool). Depending on your timeline and budget it may be worth setting up a dev environment you like with visual studio and clang, or even paying for IAR.

    Visitor II
    March 6, 2020

    To make a proper decision, one should make a list of the pros and cons of each option. You have got some experience yourself, there are a couple of bullet points from me.

    For HAL

    • Comes with a boatload of examples for most peripherals.
    • Works reasonably well in lots of use cases.
    • Offers somewhat more portability between series as the register interface on supported platforms.

    Against HAL

    • Does not even try to cover the hardware capabilities. Lacks the interface for some very basic use cases.
    • Poor documentation. One-liner explanations of functionality that would sometimes require pages to properly explain. Invents its own terminology which is never defined properly. Limitations are not apparent.
    • Poor diagnose and error recovery capabilities. A function returns HAL_ERROR, then... what?
    • As a consequence of the points above, development time is unpredictable. Something might work at the first try, or require a lot more time to troubleshoot than writing a driver from scratch.
    • Another consequence is that there is little agreement on what the proper usage would be. Developers must make lots of assumptions themselves. It can lead to endless arguments and conflicts in a team of developers.
    • Demands lots of resources. Functions are too general, requiring lots of runtime decisions to make. Most apparent in time-critical code, in interrupt handler and elsewhere. Not really suited for real-time signal processing.
    • Poor code quality. Lots of improper volatile usage, and other assorted bugs.
    • Poor code readability. Definitions in the device headers are redefined without apparent reason, even basic C constructs are hidden behind defines.
    • The interface is not stable, sometimes there are incompatible changes between versions.
    • There are no guarantees that HAL will be maintained in the future. As far as we know it, ST might just abandon it next week, as they abandoned SPL.

    For the register interface

    • Covers all capabilities of the hardware.
    • Extensively documented in the reference manual and the datasheet, down to the signaling level. Makes the required development effort more predictable.
    • Enables optimal use of resources.
    • The CMSIS naming convention enables writing readable code, and can be easily matched with the reference manual.
    • The interface is stable, it won't change in the lifetime of the product. (There are extremely rare exceptions, like the infamous rev. Y of the STM32H7 series).
    • Peripherals are compatible within one series, and more often than not across some series too.

    Against the register interface.

    • Peripherals differ between low-end and high-end series, sometimes they are incompatible.
    • Actual code examples are missing for most series. There are however step-by-step instructions in the reference manual, ready to be implemented in code with little effort.
    • Interconnections and dependencies between peripherals are poorly documented. There is some improvement in the later series though.

    There is a third option for the sake of completeness, the LL interface, but it just combines the disadvantages of HAL and the registers, so not really worth considering.

    You can consider a mixed approach, prototyping using HAL, and using the register interface only when you hit an obstacle, then gradually converting your work to use less and less of HAL. There is no rule saying that you can only use one of them in a project, but of course once you touch a peripheral register, some assumptions in HAL might become invalid, so e.g. initializing UART with HAL, then doing the data transfer through the registers is fine, but it probably won't work the other way round.

    CDyer.1Author
    Explorer
    March 6, 2020

    An interesting read and plenty of points to consider, thanks. For the third option, I've yet to do anything with the LL interface but I was considering if this was the best middle ground to take as it isn't as bare metal as directly programming the registers but it isn't as abstract as HAL either. Perhaps combing LL with CMSIS? Would you be willing to explain why this may be a bad idea?

    Visitor II
    March 6, 2020

    But the LL interface is as bare metal as programming the registers directly, most LL functions being one-liner inline functions reading or writing a single register, or a bitfield in it.

    Consider the workflow.

    Using the LL functions you follow the instructions in the reference manual, but then instead of accessing the register directly, look up the LL function that does the same. Searching the headers, you find a #define creating a LL_whatever alias for it. Then search for that alias to finally find the function using it.

    Then your code has to be modified, either by someone else, or by you a year later, when you have long forgotten what each line does. The documentation of the function is a Captain Obvious one-liner, so wind it back, possibly through a chain of #defines, to the CMSIS definition which can be looked up in the reference manual.

    On the other hand, if you read 'to enable this feature on the ABC peripheral, set the GHI bits in the DEF register' in the reference manual, you can blindly write

    ABC1->DEF |= ABC_DEF_GHI;

    and the fellow who has to fix something there can go straight to the ABC chapter and look up the DEF register there.

    Visitor II
    March 6, 2020

    Start with HAL and when needed write some parts yourself, atleast you get mostly working starting point, which you can use as basis for your own code.

    Ie. Let HAL do the periephal init and then write the code that takes care of for example spi send and receive yourself, if you want you can then replace the init codes later on with your own implementation when you know rest of the periephal driver works, which simplifies things alot.

    Visitor II
    March 6, 2020

    Do start with HAL as the added value/know how is first on implementing your DSP stuff.

    If you have debug issues, you can compare with similar HAL examples running on a standard board.

    To me the debug time is the most important to optimize.

    Today, I used LL for USART because found out HAL was a bit too clunky for the protocol I was using (Bluetooth Monitor Android App through HC-06)

    For an end product, you are responsible for it, so you will have to progressively master the HAL and ruggetize it.

    Maybe at some point of time it will become possible to make pull request of the GIT HAL (and check how long it takes to be digested).

    I fully agree on other comments about optimizing later.  and focus on your added value "ie mash out a working proof-of-concept, then focus time where it makes a difference". Every time you try to implement a functionality, think of yourself going back to the project a year after finishing it. This will help coding style beyond "standards". Also true, the less indirection (or clue hunting) to read what code does is important. There is a delicate compromize between modularity and optimisation.

    Super User
    March 6, 2020

    Stop talking, get to work. Need SPI? Talking to an external chip? Bitbang it first. LA is your friend. Then read the SPI chapter and write the simplest polled SPI implementation. Usually the extra features compared to the simpler older version of the peripheral can be ignored. Then proceed to DMA. KISS. Write dozens of simple experiments. Play. Take notes. Ask here if in doubts.

    Premature abstraction, modularisation and systemization is way worse than premature optimization.

    Tuesday, report with what you're done.

    JW

    Visitor II
    March 6, 2022

    Herr come the WCET topic that only power savy minded people battle with first at more advanced level. The other escape path is to cranck up the core frequency to avoid the issue which may pop once overnight to 1 percent of the devices....