[Stm32] Am I the only one not using HAL? - Page 1
Forum section: electronics / general

author: kgavionics
author_rank: Regular Contributor
timestamp: June 24, 2022, 12:42:02 am
content: Hi everyoneI'm an avionics technician, so embedded systems are not my field of work, but it's a hobby that I started with 8 bits MCU's a few years ago, and now I made the jump to the STM32 world. The transition was smooth because I knew about MCU and C language, so it was a logical step for me to code using registers (Hard coding) and I'm quite happy, because within a few months a coded my own library for an LCD, a Graphic LCD (monochrome), a rotary encoder a keypad and so on using Keil uvision (no HAL). Lately, I wanted to make the integrated LCD on the discovery 429 work and didn't have the time to go all the datasheet, but I couldn't find any single library for the ILI9341 chipset not using HAL. So, my question, am I the only one not using HAL ? Do I have to write all the libraries by myself?

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 24, 2022, 12:48:05 am
content: No you're not. There is an ongoing debate on using HALs/vendor libraries or doing without them. This is a frequently debated question here.But certainly, if you're doing without the HAL, you'll have to write a lot yourself, and will find fewer resources online.IIRC, someone here, ataradov, has examples of doing just that for the STM32 on github. He will chime in if he reads this.

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: June 24, 2022, 01:32:45 am
content: You're not alone. I use STM32 parts quite often and have never used the HAL or LL drivers. I write all my own code from scratch.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: June 24, 2022, 04:09:43 am
content: There is libopenCM3 also used in the book "Beginning STM32, Developing with FreeRTOS, libompenCM3 and GCC" by Warren Gay.ISBN-13 (pbk): 978-1-4842-3623-9ISBN-13 (electronic): 978-1-4842-3624-6Unfortunately LibopenCM3 does not seem to get much development and not very many processors are supported.I have also seen a several attempts based around C++ templates. Including by David Ledger (I think that's his name, The David who was an intern on the EEVBlog) See:And "Jeenode" whichhttps://jeelabs.org/projects/jeeh/https://jeelabs.org/2018/getting-started-bp/XPCChttp://xpcc.io/getting-started/https://github.com/roboterclubaachen/xpcchttp://blog.salkinium.com/typesafe-register-access-in-c++/http://www.eld.leidenuniv.nl/~moene/Home/books/Kvasirhttps://github.com/kvasir-io/Kvasirbmptkhttps://www.voti.nl/bmptk/docs/index.html

author: pcprogrammer
author_rank: Super Contributor
timestamp: June 24, 2022, 04:37:30 am
content: You are not aloneI too use STM32 devices without HAL and LL. Sure you have to do a lot of the work yourself, but that is hobbyAlso prefer to use plain C without the standard libraries. This way you have all the control. But that is me, a work horse who started programming in assembler and does not need the fancy pansy stuff.Trying to search through code generated with the STM cube crap is a dragon and I found it is quicker to just read the programmers manual and figure it out on your own then dig through the pile of shit the code generator created.

author: Siwastaja
author_rank: Super Contributor
timestamp: June 24, 2022, 06:03:13 am
content: Indeed you are not alone.And to clarify again, I'm never againstusingHAL, or finding benefits of using it. What I do loathe is the certain atmosphere where you are not a "cool kid" if you don't use it, as if HAL was some miraculous silver bullet to professional and efficient projects.But I do see this is not a problem anymore. Regarding the social game, wenon-hallersbasically wonNowadays I need to actually do the opposite, so I recommend at least looking at the STM32 HAL because in some cases it could be the right choice: familiarity of others with it makes cooperation easier, plus in simple* projects it can save time, especially if the developers are not experienced with STM32 hardware.*) simple in terms of required peripheral flexibility and timing. Actual code logic can be complex, something a person experienced with desktop PC environment would write.Also note how stupid it is to name a specific product under the general term. Like if you start manufacturing chocolate, would you name your product Chocolate? How confused are people going to be: "get some chocolate" - wait what, do you mean in general, or that specific brand? Same with "use HAL". We all write at least some very rudimentary HAL, even if just a few #defines in a header file of our own. This is not to be confused with ST's brand name.

author: tellurium
author_rank: Frequent Contributor
timestamp: June 24, 2022, 08:57:26 am
content: Quote from: kgavionics on June 24, 2022, 12:42:02 amI found it educational not only use ST HAL, but CMSIS as well.Meaning, writing everything from scratch. For example, this is a GPIO API implementation for stm32f746: 50 lines of code, with no dependencies whatsoever, easy to understand, delivers a high-level, generic API.Code:#define BIT(x) (1UL << (x))#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK)#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))#define PINNO(pin) (pin & 255)#define PINBANK(pin) (pin >> 8)enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG };enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN };enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE };enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN };struct gpio {volatile uint32_t MODER, OTYPER, OSPEEDR, PUPDR, IDR, ODR, BSRR, LCKR, AFR;};#define GPIO(N) ((struct gpio *) (0x40020000 + 0x400 * (N)))static struct gpio *gpio_bank(uint16_t pin) {return GPIO(PINBANK(pin));}static inline int gpio_read(uint16_t pin) {return gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;}static inline void gpio_write(uint16_t pin, bool val) {struct gpio *gpio = gpio_bank(pin);gpio->BSRR |= BIT(PINNO(pin)) << (val ? 0 : 16);}static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type,uint8_t speed, uint8_t pull, uint8_t af) {struct gpio *gpio = gpio_bank(pin);uint8_t n = (uint8_t) (PINNO(pin));RCC->AHB1ENR |= BIT(PINBANK(pin)); // Enable GPIO clockSETBITS(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n);SETBITS(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2));SETBITS(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2));SETBITS(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),((uint32_t) af) << ((n & 7) * 4));SETBITS(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2));}static inline void gpio_input(uint16_t pin) {gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,GPIO_PULL_NONE, 0);}static inline void gpio_output(uint16_t pin) {gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,GPIO_PULL_NONE, 0);}This makes it easy to do things like Ethernet init:Code:// Initialise Ethernet. Enable MAC GPIO pins, see https://www.farnell.com/datasheets/2014265.pdf section 6.10uint16_t pins[] = {PIN('A', 1), PIN('A', 2), PIN('A', 7), PIN('B', 13), PIN('C', 1), PIN('C', 4), PIN('C', 5), PIN('G', 11), PIN('G', 13)};for (size_t i = 0; i < sizeof(pins) / sizeof(pins); i++) {gpio_init(pins, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_INSANE, GPIO_PULL_NONE, 11);}

author: DC1MC
author_rank: Super Contributor
timestamp: June 24, 2022, 10:17:51 am
content: No, is not uncommon, usually is a method used by "consultant types" or developers that want job security. It make sure that nobody could get support for your own version of spaghetti and wheel reinvention from anywhere and also on-boarding another developer is not cost effective, so they will turn back to you. Usually coupled with useless documentation and architecture. Because the companies that developed this chips surely have no idea how theri stuff works and need some rando to do it from scratch in an "optimized" mode.At lest in my current workplace, whoever comes with this crap gets a swift kick in the behind.Cheers,DC1MC

author: Siwastaja
author_rank: Super Contributor
timestamp: June 24, 2022, 10:41:26 am
content: Quote from: DC1MC on June 24, 2022, 10:17:51 amYeah, have seen this a few times, and when "your company" is unable to finish the product and your customers start to ask difficult questions, somehow I get involved* to fix it. Then your incompetent developers have to come to the Internet to spew some bullshit like this to get their frustration off their chests. But don't worry, we both got paid, and the customer finally got something delivered.*) through some very weird routes I can't disclose because the end customers are usually very large and have weird non-disclosure requirements.See? People like this still exist! Imagine comments like above by DC1MC were the default commonplace reply just a few years ago. What a blast from the past.

author: DC1MC
author_rank: Super Contributor
timestamp: June 24, 2022, 08:00:40 pm
content: Quote from: Siwastaja on June 24, 2022, 10:41:26 amUmmm hmmm, when the "incompetent developers" have reached the end of their rope and the customers are starting to ask really hard questions, who you gonna call call, call Siwastaja, he will solve all with all the pending issues with one hand tied behind using his proprietary "pfuscherei.lib" optimized uncommented assembly, what CMSIS, what HAL, these are just bloat, raw metal baby, with my own register names, this how real consultants are doing it, it solves everything, just look at my freelancer.com (formerly known as rent-a-coder.com) reviews, are totally stellar.Then all the "incompetent developers" are fired, the support contracts with the chip manufacturer are cancelled, the whole software development is kicked out and the company has but one super-star, or rock-star, consultant Siwastaja, that will solve anything with a bit of highly optimized code that no one was capable of thinking about. Of course, this code is unmaintainable, tied to a very specific chip type and even mask version, and of course the toolchain must not be updated ever, because the whole optimized mess will not compile anymore. But other large companies (undisclosed, under NDA, no details can be revealed) are using libpfuscherei, so is really a way better solution than what the manufacturer and standard bodies promotes, they have no clue, all incompetents.Well, joking (and personal attacks) aside, I don't know why you feel so personally attacked Siwastaja, different companies have different development strategies, I'm pretty sure that your skills are appreciated in some circles, no need to resort to calling names and stuff if some other don't like the "extreme customization", to call it gently, promoted by some consultants.Cheers,DC1MC

author: peter-h
author_rank: Super Contributor
timestamp: June 24, 2022, 08:47:32 pm
content: The HAL code is useful to get started. It is what is produced by the code generator (Cube MX). Then you can read it to see what it does and learn from it.It is rarely actually wrong but is usually bloated because of insufficient granularity; for example a function for SPI TX/RX will do loads of checks, most of which are skipped. At 168MHz the cost of this bloat is small but sometimes it will cause big problems because e.g. the peripherals run at a much slower clock than the CPU.Also HAL contains error trapping and timeouts for a lot of stuff which can never fail unless the silicon is defective, and the timouts don't work anyway if interrupts are not running at the time (systick not running).I have mostly avoided HAL for code I have written entirely myself, but I have the luxury of having a working board to which I am just adding stuff. I would not want to start from scratch by reading the 2000 page RM. I did read every word of the 300 page data sheet when doing the PCB.

author: nctnico
author_rank: Super Contributor
timestamp: June 24, 2022, 09:06:50 pm
content: From what I have seen the HAL for the STM32 parts isn't bad perse (it is also MISRA compliant) but I'd research how effective ST's HAL routines are for a certain problem and whether there are known issues. IMHO it is a good idea to start with using the HAL and go from there; avoid trying to write your own HAL if it doesn't work and there are no known issues in the particular feature you are trying to use. You are likely doing something wrong. RTFM.

author: kgavionics
author_rank: Regular Contributor
timestamp: June 24, 2022, 09:17:58 pm
content: Thank you guys for all your inputs. For me, I won't be using the STM32 HAL even if I have to write every single library by myself. As a matter of fact, I almost coded all the peripherals libraries for the black pill board. Now, my next challenge would be to make the touchscreen LCD on the discovery board working which should be doable, but these days I don't have enough spare time, but if someone have a link to an already coded library not using the garbage HAL please let me know!Best regardsKaiser

author: nctnico
author_rank: Super Contributor
timestamp: June 24, 2022, 09:32:00 pm
content: Well, you might run into problems when you want to switch to a different line of STM32 controllers; the peripherals are allover the place so you likely end up needing to port a whole bunch of drivers if you have to use a different microcontroller from ST. This is one of the reasons I don't use STM32 microcontrollers in my own designs but the LPC series from NXP. The latter uses the same peripherals across the entire line so switching between one controller and the other is simple to do.

author: thm_w
author_rank: Super Contributor
timestamp: June 24, 2022, 09:37:21 pm
content: Porting a HAL based ILI9341 library to non-HAL, for someone already able to write low level code, should not be a major undertaking. All SPI writes should be abstracted out and can be replaced with your own functions.Of course if its high performance DMA based, well, you'll have to learn about implementing that, which will take some time and effort. But if you don't have this time, why are you reinventing the wheel and writing all your own peripheral libraries.

author: cfbsoftware
author_rank: Regular Contributor
timestamp: June 24, 2022, 11:01:14 pm
content: Quote from: kgavionics on June 24, 2022, 12:42:02 amYou are not alone but don't be too concerned. Despite 10+ years of professional C programming experience I found I spent more time debugging C code than writing it. All my microcontroller work is programmed in Oberon-07 so I don't use HAL. Not only do I write the libraries but also the compiler (ported from Prof Wirth's DEC StrongARM version) and the Astrobe IDE.

author: kgavionics
author_rank: Regular Contributor
timestamp: June 24, 2022, 11:21:31 pm
content: Quote from: thm_w on June 24, 2022, 09:37:21 pmI'm not reinventing the wheel.I was looking for a library that doesn't use HAL.Is this not a legitimate request?

author: isometrik
author_rank: Regular Contributor
timestamp: June 24, 2022, 11:47:30 pm
content: I always found that wrapping my head around the HAL intricacies is more complex than writing my own "driver" functions.The only exceptions are probably related to more complex functions (USB stack, etc.).

author: langwadt
author_rank: Super Contributor
timestamp: June 25, 2022, 12:04:23 am
content: Quote from: Siwastaja on June 24, 2022, 06:03:13 amor the opposite, you are not a "cool kid" if you use HAL, you have rewrite all the code in VI using nothing but the manualit may only take half an hour to get the job done with cube and HAL, but using a couple of days doing it the hard way toughens you

author: julian1
author_rank: Frequent Contributor
timestamp: June 25, 2022, 12:04:40 am
content: I used libopencm3 to drive ILI9341 specifically via spi. The only issue was adding blocking functions to avoid twiddling the DC (data/command) pin, while the underlying spi hardware is still writing/reading. It was a page or two of code.Once the underlying communication with the device is established, you can use any TFT initialization/command code. If you search github you find everyone copies from each other, as it is all platform invariant.

author: langwadt
author_rank: Super Contributor
timestamp: June 25, 2022, 12:06:22 am
content: Quote from: kgavionics on June 24, 2022, 09:17:58 pmif you don't have enough spare time, why are you doing it the hard slow way?

author: kgavionics
author_rank: Regular Contributor
timestamp: June 25, 2022, 12:18:33 am
content: Quote from: langwadt on June 25, 2022, 12:06:22 amBecause, I hate the buggy HAL!

author: kgavionics
author_rank: Regular Contributor
timestamp: June 25, 2022, 12:26:37 am
content: Quote from: langwadt on June 25, 2022, 12:04:23 amNot necessarily my friend! Once you know what you're doing, coding using bare metal approach is simple and doesn't take days, like you said! On top of that, understanding HAL has some learning curve to it, so might as well use it to code efficient code without a lot of bloat generated by cube MX.

author: langwadt
author_rank: Super Contributor
timestamp: June 25, 2022, 12:34:02 am
content: Quote from: kgavionics on June 25, 2022, 12:18:33 amI'm sure HAL has bugs like all other code, care to share some of the numerous bugs you found?

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 25, 2022, 12:39:54 am
content: And, you can use HAL without ever using Cube MX.

author: langwadt
author_rank: Super Contributor
timestamp: June 25, 2022, 12:46:40 am
content: Quote from: SiliconWizard on June 25, 2022, 12:39:54 amor you can use cube once to generate a project makefile and initial setup of all the common peripherals and then never againmany ways to skin a cat

author: thm_w
author_rank: Super Contributor
timestamp: June 25, 2022, 01:00:48 am
content: Quote from: kgavionics on June 24, 2022, 11:21:31 pmYou are writing something which already exists, that is reinventing the wheel. Of course its 100% fine to do this, many justifications for it.But you already wrote peripheral drivers, so you have SPI driver thats working right? Can you replace the HAL calls with your own?Checking github I see lots, but I didn't look closely:https://github.com/Isyaam/ES-STM32-ILI9341/blob/master/Src/ILI9341.cpphttps://github.com/i998/ILI9341_spi_STM32https://github.com/MakMe1/ili9341_stm32f4https://github.com/zoosmand/STM32F429I-disk1__std_periph__template/blob/main/Core/Src/ili9341.c

author: kgavionics
author_rank: Regular Contributor
timestamp: June 25, 2022, 01:38:40 am
content: I know where this discussion is heading!! If you're happy with HAL that's fine, and I respect that, but I'm sorry HAL it's not my cup of tea, that's all.

author: kgavionics
author_rank: Regular Contributor
timestamp: June 25, 2022, 01:41:49 am
content: Quote from: thm_w on June 25, 2022, 01:00:48 amHonestly, I didn't think about replacing the HAL function with my own! I try to do that!Thanks for the heads-up man

author: Doctorandus_P
author_rank: Super Contributor
timestamp: June 25, 2022, 02:34:27 am
content: So you're going to re-invent a better wheelMaybe do something in between?Just create a separate "test" project with the HAL and all that shit and then only copy some small parts out of it into your own project. Maybe also clean it up a bit? Starting with "working" code is often easier then starting from scratch.

author: pcprogrammer
author_rank: Super Contributor
timestamp: June 25, 2022, 05:32:54 am
content: Quote from: peter-h on June 24, 2022, 08:47:32 pmThis is easier said then done. To get to the bottom of what the generated code does you have to search through multiple files and filter the actual code from the many not helpful lines of commentQuote from: peter-h on June 24, 2022, 08:47:32 pmI have not tried that many, but all the attempts I made did not work. These were all target to USB, but never the less.Quote from: peter-h on June 24, 2022, 08:47:32 pmNo need to read the whole manual if you are only using a couple of the peripherals.Quote from: isometrik on June 24, 2022, 11:47:30 pmMy thoughts exactly, but see above, the code generated for USB did not work for me so I dug through the crap and learned about USB and wrote my own bare metal code. Probably lacks some protection but it works.Quote from: nctnico on June 24, 2022, 09:32:00 pmThis is not a very big problem, since most of the peripherals are similar. One difference I know of is the GPIO. The F1 series needs a different setup then the F3 series. Not a big deal. Read the manual and adapt your code.Quote from: nctnico on June 24, 2022, 09:06:50 pmThe problem I see with this if that when you go this road you are either stuck with it or have to rewrite the code to get rid of it when you are finalizing your work. So for me it is better to just start without it. You only have to do it once for every unique device you use.And where is the difference in reading the HAL documentation or just the low level user documentation, you have to read and learn anyway. I prefer the latter, but that is me.One thing I would like to state is that some of the posters approach a lot of the questions from a professional standpoint, meaning a business eye on things, like "we have to use HAL because of our customers and compliance rules", instead of reading that the original poster is a hobbyist. There is a big difference between the two worlds. Time and moneyAlso that it often evolves into a pissing contest about what is the best solution. (See the thread about debugging) A very human trade to which I'm no stranger, but when getting older you learn to see things clearer. There is no best solution, just a solution that fits you best.I only saw one post that gave the original poster an answer to his real question and that is this one:https://www.eevblog.com/forum/microcontrollers/(stm32)-am-i-the-only-one-not-using-hal/msg4260583/#msg4260583thm_w came up with a bunch of github links to repositories for the display driver and an option on how to get rid of the possible HAL in them. Kudos to him

author: Siwastaja
author_rank: Super Contributor
timestamp: June 25, 2022, 06:25:57 am
content: Quote from: DC1MC on June 24, 2022, 08:00:40 pmAll you can see is solving complexity by adding more complexity, so you project this mindset to others.No, I solve problems by starting over from scratch. I totally loathe mysterious "libpfuscherei" solutions, and replacing ST's HAL with something even more complex and custom is not going to cut it. So I actually completely understand what you mean and totally agree with you, however surprising that might sound!But there's more to it than that. You present a mistaken strawman, probably based on some actual negative experiences, but that does not match with my experience. Maybe I have been lucky never to see the crap you describe.In the end, microcontrollers are fundamentally simple devices - even something like STM32H7 series - and the key to writing maintainableandpowerful code is to not think about one's job security, but instead focus on using the available resources and documentation to solvetheproblem. The idea is not to choose between building a cathedral or renting an existing one, but to question if cathedral is needed at all, and maybe a simple shack with 2"x4" wooden framework and galvanized steel surface will do.I find it funny however how emotionalyougot about this, your first reaction to the topic being revealing your fantasies about getting people who don't agree with you fired, making yourself proactively agitated. I merely responded in the same tone, which is what I always do.In the end though, what I said is exactly correct: if you routinelyfirepeople who prefer any other workflow than ST's HAL library, then it means you fire most of the competent programmers and are likely unable to finish any project that has requirements that can't be solved by ST's HAL - and that doesn't not require much. This will also lead to the pattern I have seen - anything non-trivial gets outsourced to some greybeard who is known to be able to do it, because everyone within the company is "too busy".But of course I know you were just joking. You are not seriously firing people for something like that. Right? ... right?

author: DC1MC
author_rank: Super Contributor
timestamp: June 25, 2022, 08:54:15 am
content: Quote from: Siwastaja on June 25, 2022, 06:25:57 amNo, of course not, firing someone over such an issue, THAT will be incompetence, the proper way is to not introduce this cancer in the organization and do a proper interview process, from:a) "Show us your code" tob) "Here is some existing source code, how do you think it can be improved ?"if the answers are:"a) Oh, my source code is totally secret and under NDA, I don't have anything that I can show and anyway, all my projects are different and problem specific, because all the problems are totally different, so is my code totally different because each time I start from scratch."and"b) Immediately throw this crap away, I will start and reimplement it from scratch to show the incompetents how is done..."then you're not hired, period, whatever colour of beard you have, gray, pink or blue.Is that simple.

author: wek
author_rank: Frequent Contributor
timestamp: June 25, 2022, 08:57:02 am
content: There are three ways how to use the ILI9341 (more precisely, how to dump image data at it): one is RGB, where ILI9341 is more-less transparent through to the bare LCD and you have to shovel RGB at it continuously. This is how it's intended to be used on the Disco429 board, as that is aimed at showcasing the LTDC peripheral in the STM32F429, which was the first one incorporating that peripheral.That means to get LTDC working which is not that complex and may be fun to get going, but also you need a framebuffer storage, which usually means also to get SDRAM going (again the 'F429 was the first to implement SDRAM controller within the FMC module), which is not that impossible again, but maybe less fun.You still may need to send some initialization data to the ILI9341 through the side-channel (SPI), I'm not sure, I did not use this mode.The second method is to use the internal framebuffer of ILI9341 and write to it through SPI. It's then ILI9341 which refreshes the LCD periodically, so you can write to it lazily only when you want and have time. On the Disco429 board this is viable, but maybe not that spectacular (i.e. don't expect play back video); however it frees up much of the mcu's pins for other useful purposes.The third method is to use internal framebuffer of ILI9341 and write to it in parallel; this is extremely unwieldy on the Disco429 so no point to dwell on it further.You may want to have a look atTilen Majerle's work(he started on those "libraries" before he became to be ST FAE). It does incorporate some sort of "librarysm", judge yourself.JW

author: nctnico
author_rank: Super Contributor
timestamp: June 25, 2022, 09:11:58 am
content: Quote from: DC1MC on June 25, 2022, 08:54:15 amThe 'not invented here syndrom' is very strong in some people!

author: AndyC_772
author_rank: Super Contributor
timestamp: June 25, 2022, 09:13:41 am
content: The problem with HAL is that, despite the name, it doesn't actually do any abstraction. Instead, it presents a way for the coder to specify exactly what values need to be written into what registers in the device, using macros and data structures which must be learnedin additionto fully understanding how the hardware works and how to configure it.Given that there isneveran alternative to actually reading and understanding the reference manual, there is no benefit to be had by including an additional layer of obfuscation. Once you know that register X must be written with flags A | B | C in order to put the hardware into the mode you need, just write the register. Your code is simpler, clearer, smaller, faster, less ambiguous, easier to debug.The analogy I've used before is that the HAL is like having a guide to "help" you navigate a foreign city. You might want to request, say, "buy a train ticket to <place>, returning tomorrow", or "order the chicken with noodles, but without the nuts", and hope that the guide understands your request and how to make it happen. Instead, though, the HAL replies "OK, sure, I can do that... you tell me exactly which words to say, and I'll say them for you".For the same reason, it's not even portable between devices, so that argument goes out the window too.

author: Siwastaja
author_rank: Super Contributor
timestamp: June 25, 2022, 09:31:21 am
content: Quote from: AndyC_772 on June 25, 2022, 09:13:41 amYou are mixing up things. What is called "STM32 HAL" actually does a lot of abstraction, and is relatively simple to use, and indeed (often, sometimes) saves the developer from having to know all the internals. Coupled with some cubemx-like autogeneration it can actually save time. The problem is just that this isn't fully scalable and I have had people come to me when they can't continue on that path. So while I don't like that workflow and am just fine without it, and know many who totally agree and are totally able to work with my workflow no problems whatsoever, it's still a valid argument pro STM32 HAL.What you seem to be describing is the earlier thing which name I already forgot, the one where you copy-paste 50 lines of code setting up almost-the-same-but-not-quite fields as in the peripheral registers in a struct, then call an init function with this. It always was a huge PITA. Yet just some 5 years ago youhadto use it or you were a NIHilist and not a cool kid. Later it was deprecated, good riddance, and everyone conveniently forgot their insistence of using that crap.NIH is obviously crappy attitude but if you are able to write the necessary 30 lines of code to use DMA to communicate through SPI and sync this with ADC, this is not NIH. This is common sense and ability to use available resources to your advantage. And I definitely do notice my tendency to start from scratch even when it is not necessary. This is why I don't force this attitude unto others and don't force myself into projects. I get called only when my workflow is an advantage and not hindrance. But I can understand the need of different workflows and attitudes, and I can see the power in different types of people working together and combining their different skillsets. Unlike some who are so full of themselves they just call other workflows cancerous and want their users fired.If someone is seeing challenges with STM32 peripherals and consultants are ruining their codebases by not using STM32HAL, they have very crappy consultants or something more serious going on. Real challenges are elsewhere.But one thing is sure: user manuals, C language and base GNU tools never get old or irrelevant. Everything else seems to be a moving target. It's not far-fetched to say that insistence of some trend tool of year such as CubeMX combined with trend library of year, say STM32 HAL, isexactlyNIH, too, because the alternative is NOT building a massive cathedral from scratch, but instead use simple, ages old, well tested patterns.But, STM32 HAL seems to now be stable enough and simple enough it's not too cancerous. Just be aware of people who strongly insist on any certain piece of technology and name-calls others NIHilists. Irrational fear of NIH, leading to forced reuse of unsuitable or overcomplicated code, is an equally big problem than actual NIH itself. Be careful.

author: kgavionics
author_rank: Regular Contributor
timestamp: June 25, 2022, 11:19:33 am
content: The argument that I have to write everything from the scratch (which I've heard more than once in this thread) is totally not true,because for a given series of Stm32, you have to write let's say a Gpio_int function only once and then it can be used in all the projects (copy/paste).The same holds true for every single peripheral for that particular MCU.I think programming in bare metal( no HAL) is more suited to people who have more solid background in electrical engineering, with some exceptions of course!

author: Siwastaja
author_rank: Super Contributor
timestamp: June 25, 2022, 11:57:00 am
content: Quote from: kgavionics on June 25, 2022, 11:19:33 amUsually, for sane developers at least, these "own" solutions are so obvious and simple that no one complains.In a recent co-operation project, everybody accepted myCode:IO_TO_GPO(GPIOA, 5);HI(GPIOA, 5);LO(GPIOA, 5);macros without any issue at all. I think you need quite mental gymnastics if you thinkCode:MX_GPIO_Init(); // Regenerate with pointy-clicky whenever you need changes.HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);is any more readable, writable, or modifiable. But it sure might beacceptable. Is whichever NIH? I don't think so. People use NIH as a random personal insult like "you are gay", without knowing what it actually means.It is also part of life to admit different coding styles, frameworks etc. exist and be able to dive into existing project. It's like tab vs. spaces; I have a definite opinion on that, but if someone writes spaces in the files, I try to adjust and also use spaces within that file.Key to co-operation is not to make everyone to think in exact same way, but instead make everyone contribute the part they are best at. Some are great at putting together a fairly complex MCU project with filesystems, SD cards, ethernet, USB and whatever by pointy-clicking in CubeMX, utilizing autogeneration and libraries to the fullest, and do that in matter of weeks. And then, I like to build timing critical motor control, switch mode converters, data acquisition and processing, and so on. Both are skills that are needed in real life. Co-operation can bring it all together.

author: mblinov
author_rank: Contributor
timestamp: June 25, 2022, 11:52:46 pm
content: HALs (and automatic peripheral code generation) can definitely be a major time saver in my experience, especially if you're frequently flipping between different MCU families. I found myself to have become rather spoiled by the STM32 code configurator - the other day I had to initialise a timer in an Arduino, and I found myself mentally groaning having to open the datasheet and start figuring out which configuration bits to set to what value. There is definitely something to be said about "just give me a free-running timer with this limit and that prescaler, click , click , done."I've mostly worked with the STM32 HAL, and its automatic peripheral code generator. Most of my opinions about HALs and autogenerators stem from using CubeMX. It seems well-engineered and well-organised enough, atleast for my purposes, and the generated code is readable and makes sense. I've yet to hit a bug in the generated code, though admittedly I haven't written a huge amount of STM32 code.What I think is most overlooked, is knowing exactly what code and files the autogenerator is responsible for, where it generates code, under what circumstances it generates code, and to give it "room to breathe" so to speak. It is also important to know how it integrates with your IDE. I am using Atollic with CubeMX, which used to be its own IDE before being bought out by ST and rebranded as the STM32CubeIDE. The most important workflow habit I've found, is that its best to let the autogenerator have free-realm in the files that it creates, and to physically isolate your own code from it - create a separate directory, say "App/", and place all your code in there. Don't fall for the "Put your code here" comments placed all over the autogenerated main.c file. I think this is very bad advice. The auto-generated main.c should have only TWO lines added: The first, declares your function "AppMain" as extern, and the second, which calls your AppMain() function. AppMain() is your "real" main function, and you keep its definition far, far away from the dirty hands of the code generator, under the "App/" directory. Doing this is where you need to know how the autogenerator integrated with your IDE. with Eclipse, you have to do a surprising amount of button-clicking just to have your code in your own directory.The other thing I found important was to understand the structure of the autogenerated code. Again, this in my experience is completely implicit, but it will be there. One of those autogenerated files will be the "top level" header file, and the rest will be MCU/configuration-specific. I've seen projects include auto-generated include files, not realising that they are actually including files that are specific to the MCU or peripheral that was selected in the code configurator. Had the user changed the MCU in the code configurator, and re-generated the project, the user's application code would no longer compile. Its really important to avoid the impulse of "Hey, I can see X defined in file 'XYZ.h', I'll just include it and job done". Have a look first if it has a parent include file. I'm pretty sure in STM32-land, the only auto-generated include file that is "portable" is main.h. It will include all the other peripheral header files as it needs to, depending on what peripherals you've enabled in the code configurator.Another sharp corner I've found in STM32, are the HAL peripheral handles. They will not be declared in the auto-generated top-level main.h file. Consequently I have to manually declare them as "extern" in my app code.Another point of frustration, is figuring out what given configuration option in the code configurator is responsible for what autogenerated code/file in the project directory. One technique I use is to generate lots of STM32 "test" projects, where I select a particular configuration in the code configurator, generate the project, then slightly change the configuration, and generate another, separate project in a different directory. Then I run 'grep' against the two project directories in order to figure out exactly what kind of code that particular change introduces. I think this is really a major shortcoming from the vendors. They need to treat their auto-generator tools with no less importance than the compiler or IDE, and document the hell out of them.Anyway, when you isolate code this way, I've found the claims of cross-family portability to become practical. You should be able to just copy your conveniently isolated "App/" folder from your STM32F1-based project directory to the STM32F4-based project directory, tell Eclipse to search your "App/" directory for source files, and you should be, at the very least, good to compile. Plus you don't get any confusion as to who owns what code. Re-running the code generator isn't gonna overwrite any of your application code. And providing you didn't accidently include any of the MCU-specific auto-generated headers (in STM32, you should only include the "main.h" autogenerated include file), combined with only using the peripheral handles as per the HAL, any potential differences in BSP code shouldn't have any impact in the application code.Ofcourse, in practice it often isn't that simple. But (knock wood) so far I've "gotten away" with it.

author: Siwastaja
author_rank: Super Contributor
timestamp: June 26, 2022, 06:25:29 am
content: Quote from: mblinov on June 25, 2022, 11:52:46 pmFamilies, maybe - say between STM32F7 and STM32F4 - but between manufacturers, no way. This is because there is no standardized code interface - and it's no surprise, there practically cannot be without sacrificing a lot of what makes microcontrollers microcontrollers. All attempts to do so have failed. Thus, every hardware abstraction interface is totally NIH.But, if your workflow is based on bare GNU tools and no manufacturer specific anything at all, then switching between different vendors is easy-peasy. Especially if everything's still ARM.I had to port some code from STM32 to nRF52, two very different products with completely different approaches to manufacturer SDKs, and if I had been relying on those, it would have been a complete new learning experience from scratch. This was emphasized by the fact how weirdly different nRF52 is. With STM32, you have fairly complex peripherals with some quirks, eating up some time if you do everything yourself, and at least somewhat usable HAL library which can at least get trivial cases right quite easily. With nRF52, the peripheral designers should be awarded for unusually clean and simple-to-use design, documentation and register interface, but their SDK guys made it all so difficult to understand and use that it turns the ease-to-use argument completely around, in favor of not using any of their library code.With the current chip shortage, being manufacturer agnostic has been immensely helpful. Sadly, manufacturer peripheral libraries, code generators and IDEs are manufacturer-specific. Even more sadly, they have tendency to have lifetime anywhere betweeen 2-5 years. So if you have to use 3 different manufacturers during 10 years, you likely learn and unlearn through 6 - 10 different "frameworks". And indeed, complaining about X has been a recurring theme on this forum. Next year, X is something else.Now STM32HAL and even CubeMX have been surprisingly long-lived so far, so maybe there is some hope of things getting at leaststable. But I wouldn't hold my breath. Reading manuals, writing C and compiling with GCC never gets old, that's guaranteed.

author: tellurium
author_rank: Frequent Contributor
timestamp: June 26, 2022, 07:52:29 am
content: Quote from: mblinov on June 25, 2022, 11:52:46 pmCan the same thing be implemented by the API function or macro?Like, the way it is done by e.g. libopencm3? See the API:Code:void rcc_set_pll_source(uint32_t pllsrc);void rcc_set_ppre2(uint32_t ppre2);void rcc_set_ppre1(uint32_t ppre1);Simple and clear. No need for point and clicks mess. No frustrations with missing code blocks, files. Friendly for team development, where noone can suddenly regenerate and commit a bunch of surprising shit in the repository. The source code is self-descriptive: "ah, here we set the PLL source to external oscillator", rather than implicitly hidden in the auto-generated blocks.I wish ST just hires libopencm3 guys and they do the right thing.The current CubeMX style "assumes" only one developer working on a firmware, and there is no such thing as version control exists.

author: Siwastaja
author_rank: Super Contributor
timestamp: June 26, 2022, 08:26:30 am
content: Quote from: tellurium on June 26, 2022, 07:52:29 amThis is simple and clear because it doesn't abstract anything. It's kind of sweet spot; it satisfies the programmer's desire to do simple bare metal with no unnecessary abstraction, but it also satisfies the superficial company policy of needing some layer. Luckily, the higher-up does not understand it is not abstracting anything, so you get a pass.But, what the heck is pllsrc? It's still part specific. What are ppre2 and ppre1? They are just divisors. So you need to know what the input clock frequency is, and you also need to know where in the clock tree the signal divided by PPRE2 goes, to which peripherals.Proper general abstraction of MCU peripherals is still a pipe dream. Real implementations that actually exist and are usable, are like what you describe. You can't avoid understanding the target chip and its peripherals.

author: tellurium
author_rank: Frequent Contributor
timestamp: June 26, 2022, 08:33:37 am
content: Quote from: Siwastaja on June 26, 2022, 08:26:30 amSure.But I was not talking about the abstraction - I was talking about getting rid of code generation step.

author: cfbsoftware
author_rank: Regular Contributor
timestamp: June 27, 2022, 11:35:30 am
content: Quote from: kgavionics on June 25, 2022, 11:19:33 amWhen you use copy/paste you end up with multiple copies of the same code. That becomes a maintenance problem whenever a bug fix or modification has to be made. I prefer to maintain a single copy of the source and link the compiled code into all the projects that reference it at build time.QuoteI'm one of those exceptions

author: tepalia02
author_rank: Regular Contributor
timestamp: June 28, 2022, 10:07:30 am
content: I think there is nothin wrong with your approach.

author: profdc9
author_rank: Frequent Contributor
timestamp: June 29, 2022, 08:43:49 pm
content: Not to dive into a religious discussion, but all I can say is what has worked for me.Probably many here like me have been concerned about the ongoing chip shortage, and certainly abstractions like HAL and CubeMX can mitigate migration of code to newer available chips as intended. I think a lot of the reasoning that goes into HAL is that resource and execution efficiency is less important than development agility and hardware flexbility. This could well be the case if one has a low volume product or a limited lifespan product for which cost and/or long term availability of parts are not going to be an issue.The problem I see with this approach is for large volume, cost-sensitive applications and where long-term availability is concerned, this is not necessarily an acceptable tradeoff. Having to use a more expensive microcontroller because HAL requires it for consumer electronics where profit margins may be relatively low. Furthermore, having to revise code on an existing microcontroller to modify or add features in the field also becomes harder if adding additional features using automatic code generation adds even more code bloat that might make the existing hardware inadequate for the upgrade. In these cases, rather than start out with an abstract library such as HAL or generated with CubeMX, one might start with bare hardware programming or a library with less overhead.I kind of think that a manufacturer should be providing copious code examples rather than a one-size-fits-all monolithic library. The library is intended to hide details from the programmer which I think ultimately the programmer will need to know. Code examples tend to provide understanding of the resources being used rather than try to hide them. I usually end up looking for such code examples to get an idea of the parts of my problem that I need to understand so that I can write my own code.

author: westfw
author_rank: Super Contributor
timestamp: June 30, 2022, 12:44:33 am
content: QuoteI sort-of see it as the reverse. Using a particular vendor's special tools, libraries, or OSes, makes it a lot harder to switch to a different vendor. They are motivated NOT to make their tools "abstract" enough that they'll work on some other chip, but "abstract enough" that you don't learn the "deep principles" that are portable. :-(I guess it's nice that TI has a free TI-RTOS that you can use, but actually USING it sounds like a good way to get locked in to a vendor that doesn't have a great record WRT chip longevity (Luminary (cough)) USB stacks are probably another good example - has anyone seen a USB stack in a vendor library that it "about the same" as some other vendor's stack? (Hmm: maybe Atmel and NXP both have LUFA?) And then, how much have you really learned about USB?

author: thm_w
author_rank: Super Contributor
timestamp: June 30, 2022, 01:30:35 am
content: Quote from: westfw on June 30, 2022, 12:44:33 amBut they didn't state "other vendors" they said "newer chips", which can be from the same vendor.And in the case of ST,someparts can be second sourced from Gigadevices/Mindmotion/HK/Megawin/CKS/Geehy

author: Kleinstein
author_rank: Super Contributor
timestamp: June 30, 2022, 05:07:31 am
content: Using HAL or similar abstaction layers gives some vendor lock-in, but also a simpler path to other (especially newer) chips from the same vendor. Even if using direct hardware access it is usually much easier to port to a µC with the same pheriphery and thus the same vendor. Though using the same CPU the ARM based µCs can be quite different in the pheripery. How important the periphery deails are depends a lot on the applications: some only use the basics (and thus easy to port) and other care about the details or special features and are thus hard (possibly impossible) to port.

author: Siwastaja
author_rank: Super Contributor
timestamp: June 30, 2022, 05:49:20 am
content: Quote from: profdc9 on June 29, 2022, 08:43:49 pmCall factual posters religious. State thatyouarenotgoing to dive in. Then dive in 100% and post totally religious claimIt's exactly the opposite, as westfw says. Chip shortage has been a breeze for people like myself, no problem whatsoever. We can use any microcontroller!Yet, people who only can work with STM32HAL and CubeMX are in constant agonizing pain, obviously because it has been really tricky to get almost any STM32 part.Sorry, could not resist - this was just so hilarious. I would have assumed everybody understood what "vendor lock-in" means and why it is a bad thing (unless it can't be avoided), but clearly this isn't the case.Really, if you want an actual protip instead of bullshit, the answer is simple: write your own minimal and simple hardware abstraction (don't spend too much time on it, it doesn't need to be fancy), which can be ported quickly as needs arise.

author: westfw
author_rank: Super Contributor
timestamp: June 30, 2022, 07:49:10 am
content: I think a key thing is that YOU need to have an “abstraction” of the things you want your microcontrollers to do. Whether you implement those abstractions by using vendor tools or bare metal, they make it MUCH easier to drill down through the tool or chip documentation. “Here is what I think I want - how does this particular chip (or library) DO that” leaves you in a better position than just facing 1000 pages of documentation and trying to figure out how to get to a high level application.Hopefully, this “personal abstraction” is somewhere between Arduino’s triviality and the vendors’s horribly complete library. But perhaps it doesn’t need to be.And your opinions can quickly eliminate some choices. “I want a stream-oriented UART” can rule out all sorts of more “file oriented” abstractions, and “I want a zero-copy USB driver” eliminates lots of the choices (so maybe it’s not a great “want”?)

author: Kleinstein
author_rank: Super Contributor
timestamp: June 30, 2022, 08:32:59 am
content: The hardware abstraction libraries are supposed to make things easier. However the documaentaion for HAL is about as long that documentation for the direct hardware. If in doubt you still need to look at the HW documentation if the HAL docs are missing details or just refers to the hardware documentation.As long one is using the µC in the HAL is meant to use it, it is OK. This includes many simpler programs. However with more special functions and unusual use (e.g. use the UART to send a bit pattern instead of normal serial data) not directly supported by HAL things get nasty and one has to resort to direct register porgramming / maybe LL. So even more to read and learn and extra traps when the same HW is used both directly and via HAL. One of the more frustrating parts is when you hit a limitiation of HAL that is not well documented and after reading the chapter from HAL over an over again still have to resort to doing it by hand.HAL also adds quite some code length, which can become a problem with a smaller (e.g. <= 32 kB Flash) µC.

author: peter-h
author_rank: Super Contributor
timestamp: June 30, 2022, 09:27:36 am
content: Looking at the project I've been coding for the last 1-2 years, about the only HAL functions still there are the ones which set up the pin alternate function mapping. New stuff I always write directly by reference to the RM.The HAL functions are useful to see how something was done, and I often use them after stripping out ~90% of the code - because the writer tests loads of config register bits for optional features, which is stupid because you would never bloat your code with e.g. checking the SPI config for whether CRC is enabled when 20 lines further back youdisabledthe CRC featureEnjoy thishttps://www.eevblog.com/forum/microcontrollers/32f417-spi-running-at-one-third-the-speed-it-should/

author: Siwastaja
author_rank: Super Contributor
timestamp: June 30, 2022, 10:57:47 am
content: Quote from: peter-h on June 30, 2022, 09:27:36 amEven this is very simple, I came up with this in a few minutes and most of that work is silencing an old GCC version from giving unnecessary warning:Code:#define IO_TO_ALTFUNC(port, idx) do{ uint32_t _tmp_ = (port)->MODER; _tmp_ &= ~(1UL<<((idx)*2)); _tmp_ |= 1UL<<((idx)*2+1); (port)->MODER = _tmp_; }while(0)#define IO_SET_ALTFUNC(port, pin, af) do{ _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wshift-count-negative\"") _Pragma("GCC diagnostic ignored \"-Wshift-count-overflow\"") \if((pin)<8) {uint32_t _tmp_ = (port)->AFR; _tmp_ &= ~(0b1111UL<<((pin)*4)); _tmp_ |= af<<((pin)*4); (port)->AFR = _tmp_;} \else {uint32_t _tmp_ = (port)->AFR; _tmp_ &= ~(0b1111UL<<(((pin)-8)*4)); _tmp_ |= af<<(((pin)-8)*4); (port)->AFR = _tmp_;} _Pragma("GCC diagnostic pop") }while(0)#define IO_ALTFUNC(port, pin, af) do{ IO_TO_ALTFUNC((port),(pin)); IO_SET_ALTFUNC((port),(pin),(af));}while(0)Usage:Code:IO_ALTFUNC(GPIOB, 5, 1); // sets GPIOB.5 to AF1.That macro looks ugly but you only need to do it once.

author: nctnico
author_rank: Super Contributor
timestamp: June 30, 2022, 12:22:30 pm
content: Such a macro is way better off living live as a function. Additional bonus is that the code is there only once instead of being replicated over and over.

author: peter-h
author_rank: Super Contributor
timestamp: June 30, 2022, 01:33:04 pm
content: What CPU does the above work on?

author: Siwastaja
author_rank: Super Contributor
timestamp: June 30, 2022, 02:19:57 pm
content: Quote from: nctnico on June 30, 2022, 12:22:30 pmI'd agree, nowadays I recommend using functions. It was written as a macro to get it inlined, and I haven't bothered to change something that works.End result is the same. Even if it looks messy, the if-else will be compile-time constant and it reduces to a few simple register writes with literals. By making it static inline __attribute__((always_inline)), nothing changes really, but code will be more readable than a macro because we can get rid of (argument_parentheses) and weird do-while(0) wrapper.In any case, this is something to be inlined. If the compiler truly implements it as a function with external linkage, code size will grow and execution speed gets slower because now it suddenly becomes run-time calculation.By saying "code is replicated over and over", maybe you did not realize that this macro does nothing more than write a few literals from ROM (code) to registers. You can't make it significantly smaller; it's extremely small even if "replicated". If it is a true function, then it will be significantly larger, and at the call site, you still have to store the argument literals - and they will take roughly the same amount of code ROM from the literal pool.All this code does is make thecompilercalculate what bits to clear and set, from compile-time constants. This will be ANDing and ORing with literals, and thoseliteralsare what the compiler actually calculates, instead of doing it by hand, or by CPU run-time.Quote from: peter-h on June 30, 2022, 01:33:04 pmAll STM32's AFAIK. At least all I have used have the same MODER and AFR register naming and bit slicing conventions. But geez, this is all very simple stuff.

author: cv007
author_rank: Frequent Contributor
timestamp: July 01, 2022, 07:38:22 am
content: QuoteOr move over to C++, which has some nice advantages.Just trying to compare the macro posted to what a gpio c++ class does-https://godbolt.org/z/6s4n3vTxsThey end up producing the same code (the mode setting is done different, but same result). That will change if the gpio class takes on storage, which it doesn't in this simple example as was done in a way only to compare the macro. One difference in this particular case is by default the class constructor is going to enable the gpio clock in rcc, so no real need to think about whether the gpio clock is already enabled, although the macro could be changed to do that also. Not very efficient to keep enabling the same bit in rcc, but harmless and keeps one away from problems if this is otherwise required to be done separately.Change the class to a template version where no storage is needed because all the needed info about a pin will be contained in the template parameters, then you are truly going to always do just as well as any macro or static C code. The downside to that is templates are infectious and it gets difficult to pass around pins as each is a unique type (requiring more templates).You can use the normal/non-template version to always get to the macro equivalent (via optimization) if you do not let a gpio class instance (if even created) take on storage by letting it live beyond the current scope (which was done in the example). Much simpler to let your handful of pins use some bytes for each instance as it is an insignificant amount. Ease of use beats saving some bytes.Another example for initializing a group of pins, such as ethernet pins (I think I saw something earlier in the thread using macros/enums/code to do this)-https://godbolt.org/z/c1zjGc1M1C++ is just a better, more powerful macro system.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 01, 2022, 08:21:07 am
content: Quote from: cv007 on July 01, 2022, 07:38:22 am... but also demonstrates how much boilerplate (which is not very readable, IMHO, either) one needs to write for the "nice C++ style". Of course, your C++ class does a lot more than just this, so it's hard to compare.QuoteBetter I don't know, more powerful for sure.For readability and maintainability, best would be a simple C function:Code:static inline __attribute__((always_inline)) io_altfunc(GPIO_Typedef_whatever_it_was * port, int pin, uint32_t af){if(pin<8){uint32_t tmp = port->AFR;tmp &= ~(0b1111UL<<(pin*4));tmp |= af<<(pin*4);port->AFR = tmp;}else{uint32_t tmp = port->AFR;tmp &= ~(0b1111UL<<((pin-8)*4));tmp |= af<<((pin-8)*4);port->AFR = tmp;}uint32_t _tmp_ = port->MODER;tmp &= ~(1UL<<(idx*2));tmp |= 1UL<<(idx*2+1);port->MODER = tmp;}The same as the macro but without (), \ and do-while(0) stupidity, and with typing of the arguments.Compare this to the C++ class used to achieve the same and many would agree it is more obvious what happens here.C++ template system shines when you needtypegenericity, same code working for any arbitrary data type. In C, there is no other ways to macros to achieve that (see libraries like uthash, for example) - it works but is pretty crude. Here this is of course irrelevant.

author: voltsandjolts
author_rank: Unknown
timestamp: July 01, 2022, 08:30:53 am
content: Finding and hiring embedded programmers is troublesome, certainly in my locality.I see a mix of embedded skills, such as picbasic, mikropascal and C (preferred of course).During hiring processes I haven't seen anyone with embedded C++ skills, and that is what steers me away from it.Hiring a C++ programmer (and allowing C++ to be used) is a risk because replacing them would be difficult and leave some projects in limbo.

author: peter-h
author_rank: Super Contributor
timestamp: July 01, 2022, 10:47:53 am
content: A guy in a local company employing ~50 coders said C++ was the only way to have say 20 working on the same product and have any chance of it working eventually.There was no pretence of high productivity, however, apparently because few people are really good at it.The above was applied even with 8/16 bit micros like the H8. The result was really slow e.g. a Modbus data request for some floating point values would see a gap of 1-2 seconds before anything started coming back, but that was because a) floats were used without hardware fp b) double floats were used pointlessly but were the default type c) nobody was bothered so long as it worked. Today, say arm32, it would all be a lot faster, but they still use C++ now for the above reason.I've never written C++ and don't intend toI get the impression it is used for embedded inlargecompanies only. But if that was true, wouldn't ST produce C++ versions of all their Cube libs? Big companies are by far the biggest chip users.

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 01, 2022, 11:19:43 am
content: And why would you use C++ in embedded.You are working with limited program memory and limited data memory, why waste it on al the overhead it might bring.The same goes for using malloc for that matter. For most of the embedded code it is known how much memory is needed and it is carefully planned what goes where.For me plain C is much easier to read. No hidden functionality you have to dig for. But Java I find even worse. Whats up with the "class.sub_class.yet_another_class.function()" crap

author: wilhe_jo
author_rank: Regular Contributor
timestamp: July 01, 2022, 01:49:37 pm
content: Quote from: pcprogrammer on July 01, 2022, 11:19:43 amBecause embedded includes >1MB flash, and you would get way faster, small, easier to read and better code with C++.This horrific piece of bloatware ST calls a HAL is truely written by someone without experience.I mean, you have numerous structs that pollute your stack just to do some case-switching inside some crazy-bastard-#if-orgy-spagetti-code....All these "embedded-HAL" things are not really a HAL. They - at best - are just another layer of confusion.Even those Arduino Guys have understood that C++ makes for better looking code!But well, that's my view and you're welcome to use plain C (as I do as well).For me, the best combination is to have a very,very,very thin layer of C under a functional HAL made in C++.If done right, you get absolutely clean code without any overhead (in my experience you get faster and smaller binaries with c++ because of better high-level-optimization) that is a dream to port to other platforms.But I think at this point, it's important to quote Bjarne Stroustrup: "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off"73

author: Siwastaja
author_rank: Super Contributor
timestamp: July 01, 2022, 02:25:17 pm
content: Quote from: voltsandjolts on July 01, 2022, 08:30:53 amYes - a simple procedural C function is easy, because it is understandable and translatable to any other language. (Of course, with all the weird C quirks like integer promotions and whatnot, but most of these apply to C++ as well.) Such function is also compatible with both C and C++ (though not compatible with some C++programmers).C++ in embedded sadly screams the "rockstar" mentality. If used really really well, then others can of course accept to use and adapt to the project and even learn in process. But it's a risk.Reason why C++ usually isn't worth it in basic IO and peripheral things is because in C, the solution is nearly minimal already. If your interface to turn GPIOA pin 5 high is, for example, set_pin(GPIOA, 5, PIN_HIGH) or similar, and the implementation is 1 or 2 lines of code, there is not much you can do to make it any smaller or more readable, or any easier to use.Arduino usesalmostzero C++ features and their subset and coding style is very C-like. They make code look nicer by eliminating some boilerplate common in both C and C++ by a code preprocessing step.

author: emece67
author_rank: Frequent Contributor
timestamp: July 01, 2022, 03:26:37 pm
content: .

author: peter-h
author_rank: Super Contributor
timestamp: July 01, 2022, 03:35:07 pm
content: QuoteST HAL code does provide a practical benefit, even if you don't use it as-is: it gives you a starting point.For example I am trying to put together an AES256 encrypt/decrypt function which uses the 32F417 AES hardware, preferably with DMA. There is some code online but it is all very old (~10 years) and one can't be sure whether more recent code (from ST) has silently fixed some issues. So I found stm32f4xx_hal_cryp.c from 2021. I now need to spend a day or two wading through that huge mess and pull out just the stuff I needbut it is a lot less work than just reading the RM and spend a few days fixing problems.

author: cv007
author_rank: Frequent Contributor
timestamp: July 01, 2022, 03:40:46 pm
content: QuoteThe problem with macros or static functions, is you first have to come up with names that do not collide with anything else. The next problem if using macros is your collection of macros grows and grows, and it ends up being another language you have to take care of and document, and can be hard to see what is actually happening. Static functions in a header are better, but still left with the naming problem and there is little or no validation that your arguments make any sense (some arguments will be be validated by the way they are used in the function/macro, such as a struct/register pointer, but other normal values will not).I do think you end up with more readable code, easier to use, and same code generated with C++.C++Code:Gpio led{ A5, LOWISON }; //we now have a name we can use- led, do not need a define for the pin or manually specify port/pin each time we use the ledled.off().mode( OUTPUT );led.on(); //no need to know whether we need hi/low for on, we already decided when led was created//can also init at instance creationauto led = Gpio(A5,LOWISON).off().mode( OUTPUT ); //off will set pin high, so led will be off when output mode selectedauto sw = Gpio(C1, LOWISON).mode( INPUT ).pull( PULLUP );while(1){ led.on( sw.isOn() ); } //led state matches switch (on() is overloaded, can also take true/false argument)macros-Code:#define LED GPIOA,5 //will want something like this as we do not want to change all code if our led moves to another pinset_pin(LED, PIN_HIGH); //off before outputset_pin_mode(LED, OUTPUT);set_pin(LED, PIN_LOW); //we have to know whether low or high is on, or create more macros#define SW GPIOC,1set_pin_mode( SW, INPUT );set_pin_pull( SW, PULLUP );while(1){ set_pin( LED, get_pin(SW) ? PIN_HIGH : PIN_LOW ); } //need to figure out get_pin returns 0 when sw pressed, and led is low when onWith C++, I think the usage of the class functions 'attached' to the instance makes for nicer use since the first item is the instance (led) and what follows is an available function for the instance (ide's will help here).

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 01, 2022, 03:44:26 pm
content: Quote from: wilhe_jo on July 01, 2022, 01:49:37 pmSure some devices have big or external FLASH, but not all. And smaller, easier to read and better code with C++, well that is your opinion.For me certainly not true for Arduino. Sure it is nice for easy testing and if you are a noob, but for real serious stuff come onIn case of a bug in the Arduino backware you are f....d. Good luck finding it.Quote from: wilhe_jo on July 01, 2022, 01:49:37 pmNo argument there. That's what this thread is about. Not using that crap.Quote from: wilhe_jo on July 01, 2022, 01:49:37 pmEverybody should use what they like best. For me it is plain C on the bare bones of the target. No standard library, no nothing. Put in plenty of comments and you get the easiest to read code.Quote from: wilhe_jo on July 01, 2022, 01:49:37 pmWell there you go, If you know what you are doing, I rather shoot myself in the foot then blow my whole leg off

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 01, 2022, 03:57:36 pm
content: HAL is dangerous!

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 01, 2022, 05:36:08 pm
content: What is "dangerous" is to use something without understanding it.

author: langwadt
author_rank: Super Contributor
timestamp: July 01, 2022, 05:56:00 pm
content: Quote from: pcprogrammer on July 01, 2022, 11:19:43 amthat you don't understand something and think it is too complicated doesn't necessarily mean that it is bad/bloated/wasteful, it just means you don't understand it...

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 01, 2022, 06:09:36 pm
content: Quote from: langwadt on July 01, 2022, 05:56:00 pmWhat makes you think I don't understand C++. (Or Java for that matter)I have used it enough to know how it works and that I don't like using it on embedded systems.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 02, 2022, 07:28:05 am
content: Quote from: pcprogrammer on July 01, 2022, 06:09:36 pmThe confusion is here: people disagree about what C++ means.Those who use it in embedded use a minimalistic subset, removing all features that do not work well on embedded; for example, C++ exceptions.If you are familiar with C++ application development and have seen "full" C++ being used, it is no wonder using it in embedded seems crazy.In embedded, basically they use just the class system with constructors, not even destructors because usually you use C++ in embedded to just init with a tad less writing, but never want to deinit, so objects are either global, or in main() so that they never go out of scope because of the infinite loop.Once you want full control where you init and deinit things dynamically, as in many of my projects, you do it in C because you want to control exactly what deinits() and WHERE, and in which order, because of shared resources, nasty cross-dependencies, and timing constraints. For similar reason, linux kernel is in C, not C++. C++ allows some nice abstractions, but it's exactly those nice abstractions which also fall apart when the use case becomes slightly unexpected. A true C++ pro is always able to somehow manage everything in C++, but us mere mortals then become unable to read their code and participate, because the class interactions can become very complex.The purpose to use C++ is to change the simple and readable 8 LoC C code into 15% simpler and 20% more readable 6 LoC C++ code, as demonstrated above by cv007. For this to look convincing, you have to ignore the work that went into writing the class implementation, which is much longer than the C equivalent, and also has quite some boilerplate in it. But sure enough, you "only need to do it once", and sure enough, for simple static cases, it sure does optimize to the same machine code.Some embedded C++ examples indeed look very good, there clearly is potential. But I still think an embedded C++ developer as a risk factor. Best case, C++ is "only in the name", or opposite kind of best case, the nice C++ abstractions happen to work with the project very well. Worst case - you have expanding unmaintainable mess with one rockstar C++ developer who is driven over by a bus.I think Linus Torvalds' description of C++ applies to embedded as well:QuoteThis is the general problem with object-oriented languages used to their "fullest", with cathedral-like class hierarchy design, often designed with tools like UML.Set of simple procedural functions with less built-in interdependency, spread over a few modules, does not look nearly as fancy, but also carries less risk and offers more opportunities for modification and reuse, even if the project scope isn't exactly as planned.

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 02, 2022, 09:01:49 am
content: Quote from: Siwastaja on July 02, 2022, 07:28:05 amWhich also applies to the plain C case. I only figured out once how to initialize the STM32F103 clock system and reuse it for every project I do with a STM32F103.And you are right, my C++ experience comes from graphical application development.

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 02, 2022, 02:09:37 pm
content: I understand pretty well how stm32 works despite using HAL frequently.I just can avoid the hassle of searching every register bit every time I need a small change and lets me develop very fast.To date it works fine, for most things I did there was no need of extracting every performance drop, neither I'm getting paid for what I do, so it's my time and my decision, and I don't want to spend so much time with it.Using HAL doesn't make you an idiot, if tomorrow I need to manually setup a DMA, SPI, UART, etc , I pretty much know the registers, HAL is not so much high-level as you might think like ex. Arduino.

author: dietert1
author_rank: Super Contributor
timestamp: July 02, 2022, 02:32:45 pm
content: The STM supplied open source libraries have been useful for me. Using CubeMX and the HAL libraries is very convenient and worth learning. Anyway, sometimes i could not use it.For example the SPI driver has so many options that a basic transfer can be to slow for a certain slave, like the ADS1256. When that sigma-delta ADC runs at 30 kilosamples per second, the HAL library functions can not send a command within 30 usec and synchronous to the DRDY output as required by the ADC. Anyway it was easy to extract the basic SPI operation from the HAL implementation in order to solve the problem. One nice thing about the STM32 SPI HW component were the four byte FIFOs for transmit and receive, as all transfers of the ADS1256 fit into this size. This simplified everything.Regards, Dieter

author: Siwastaja
author_rank: Super Contributor
timestamp: July 02, 2022, 02:51:11 pm
content: SPI peripherals with FIFOs are indeed the best thing since sliced bread!Not only it increases performance, it makes writing code simpler, and code more readable. Of course the downside is, if you write code assuming you have those FIFOs, then you need to pretty much rewrite it if you port it to something with no FIFOs.Example:Read accelerometer 3 x 16-bit values:In an ISR: write 7 bytes to SPI data register: command and 6 dummy bytes. This is just 3 memory accesses: word, halfword and byte.Set a timer, or SPI interrupt, whichever is fine, to give you another interrupt once you have full 7 bytes in RX FIFO.In the second ISR: Read 7 bytes from the DR (again 3 memory accesses), do whatever you want with the data.With DMA, you would still use an interrupt to do something with the data, and you would still read it from the memory. In this case, just accessing the SPI through the FIFO is just as efffient.Without the FIFO, you have to interrupt every damn byte, or use DMA, and the DMA will still chuck quite some memory BW because every byte creates a DMA transfer. With FIFO, the DMA and interrupt based both are viable options, and having more options never hurts.

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 02, 2022, 06:28:54 pm
content: I like FIFOs for peripherals. They are pretty common on MCUs. I haven't used a MCU that didn't have SPI or UART FIFOs, of at least 4 words - sometimes deeper -, in a long time. Even the PIC MCUs have FIFOs. Except maybe the very, very old models?

author: nctnico
author_rank: Super Contributor
timestamp: July 02, 2022, 06:54:34 pm
content: Quote from: SiliconWizard on July 02, 2022, 06:28:54 pmYou might see FIFOs go away. Recently I used a relatively new microcontroller from NXP. The (newer) UART doesn't have a FIFO; you are supposed to use DMA.

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 02, 2022, 08:46:21 pm
content: Most basic mcus don't have fifo, at best dual -buffering/shadow registers allowing to write new data once it started the transfer...

author: newbrain
author_rank: Super Contributor
timestamp: July 02, 2022, 10:54:52 pm
content: Quote from: wilhe_jo on July 01, 2022, 01:49:37 pmYou probably never dug deep in NXP SDK, at least the one for iMX RT.It's handles structures pointed by handle structures inside some other handle - plus static stuff pointing to the handles...As a cherry on top, the writers of the code only have a superficial knowledge of the English grammar and orthography (I think they also write their DSs).ST HAL would then look as lightweight as a feather.

author: peter-h
author_rank: Super Contributor
timestamp: July 03, 2022, 09:59:49 am
content: Clearly the ST ARM32 approach is "no FIFOs; use DMA if you need speed" but it leads to much more complicated code. You can easily spend a whole day or two getting DMA to work. There are various timing quirks. Of course the experts here will disagreeand I too have no problems with it now because after I finally managed to make it work I just recycle the code. The code structure is also completely different however; a FIFO is very simple to use (it is transparent) and makes UART, SPI, etc servicing from ISRs easy and efficient.I have an off-topic question here: do the 32F4 DMA controllers run at the CPU clock speed, or at the PCLK1/2 speed? If the latter, loading the (many) DMA registers is going to be pretty slow.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 03, 2022, 12:59:07 pm
content: Quote from: peter-h on July 03, 2022, 09:59:49 amThis sounds weird. The very first time I used DMA on MCUever, and this was on STM32, I think it took .... maybe quarter of a day to read through the DMA section of the reference manual and just get it working. After all, there is not much to configure: which channel (which peripheral it maps to), memory address from/to, transfer size, number of transfers. Then ONE (1) trick: clear status registers before enabling. And then: enable it. And I'm not some super genius who always gets things done in minutes, but geez, it's still five lines of code.So I find this just... weird.If interested, check out nRF52. Their "simplicity of use" paradigm works with DMA, too: basically they just decided to drop non-DMA use of peripheralscompletely, and make DMA trivially simple to use. So instead of checking flags and reading out separate "data registers", or configuring separate DMA peripheral to map with other peripherals, their peripherals like UART or SPI just have pointer register where you want the data to be written to, or read from! I mean, is it legal to make things this obvious and simple?

author: peter-h
author_rank: Super Contributor
timestamp: July 03, 2022, 02:06:46 pm
content: I saidQuoteand I was rightQuoteYou are in the top 0.1%.QuoteThatcould take somebody many days to find out, many days wasted posting on forums where a hundred desperate people posted the same Q over years and never got a reply.That is where looking at somebody's code can save a lot of time, but a great deal of code online does not work and was never tested. Somebody just posted it to look smart...The business of coupling peripherals to DMA is full of tricks. Like timer -> DAC with the DAC fed from DMA. You have to enable this andthenenable that. I do have it running but it wasn't trivial. Actually I paid someone about £1000 to write the whole piece of code (but couldn't directly use what he wrote because he did it for a particular ST dev board... another story).

author: voltsandjolts
author_rank: Unknown
timestamp: July 03, 2022, 03:34:21 pm
content: Quote from: peter-h on July 03, 2022, 02:06:46 pmYes, he is very good isn't he, a credit to the forum in the way he shares know-how.(not sarcasm btw)

author: Siwastaja
author_rank: Super Contributor
timestamp: July 03, 2022, 03:39:30 pm
content: Quote from: peter-h on July 03, 2022, 02:06:46 pmThat should not happen because it is clearly spelled out in the reference manual. In fact, I have never tried what happens if I don't do it.It's literally the first thing in the step-by-step instructions:QuoteIt's a bit verbose and they explain every freaking bitfield within the registers in that step-by-step checklist, making it a full page long, but still, you just need at least half-decent attention span, not to be a genius.I purposely avoid posting a question on forums and instead just RTFM more carefully. Posting on forums about things thatareexplained in the manual is a problem for two reasons:1) you waste time waiting for answer, when you could just proceed by reading the manual,2) you decrease S/N ratio of the forums

author: peter-h
author_rank: Super Contributor
timestamp: July 03, 2022, 04:35:24 pm
content: A lot of truth in that - which is why when I get a solution Ipostthe details because it will help somebody one day.The product may be commercial but the company is my own and I don't care, because I am not revealing anything earth shattering.

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 03, 2022, 11:00:59 pm
content: Yeah so much DMA... The you read the errata and find only 2 dmas can run concurrently, more will lead to data corruption.Not sure if they fixed it...

author: cfbsoftware
author_rank: Regular Contributor
timestamp: July 03, 2022, 11:34:00 pm
content: Quote from: Siwastaja on July 03, 2022, 03:39:30 pmIn general the level of detail in the STM32 documentation is excellent. Unfortunately, however, verbose prose like this is not helpful when used to describe a sequence of events. I would find it infinitely more comprehensible if shown as a flow diagram or using some sort of pseudo-code. But then I'm a software engineer not a hardware engineer - does that make a difference?

author: peter-h
author_rank: Super Contributor
timestamp: July 04, 2022, 06:06:52 am
content: QuoteWhich chip is this on?That would be a huge issue.

author: jeremy0
author_rank: Contributor
timestamp: August 29, 2022, 02:24:10 pm
content: Quote from: peter-h on July 04, 2022, 06:06:52 amI'm not sure which device DavidAlfa was referring to, but thesilicon errata sheet for the SAMD21reads like a frickin Stephen King novel.QuoteorQuoteAnd then there are the undocumented shortcomings, such as the DMAC only supporting a subset of the documented functionality (e.g. no WRBADDR=BASEADDR, no BURSTLEN), andmysterious misbehaviour.