Hacker News new | past | comments | ask | show | jobs | submit login
Quadcopter Programming Part 2: Using the CMSIS Library and First Takeoff (timakro.de)
71 points by timakro 6 days ago | hide | past | web | favorite | 18 comments

The article's Delay function unfortunately has a rollover bug. If the counter rolls over in the middle of a delay, the delay will return early. It's a nefarious bug because it only occurs every 50 days, and only if the program is delaying. Also, while it's probably ok in this usage since it's a 32-bit cpu, it would be better to use atomic access intrinsics or a critical section and memory barriers rather than volatile to make the counter variable "ISR safe".

Embedded code is a lot of fun, but if you want to safely work on the "really fun" stuff it's important to maintain a high level of quality and correctness for fundamental but lame utility functions like this. Otherwise you could literally build a ticking time bomb!

Not saying this isn't a real bug (it definitely is!), but most quadcopters aren't powered on for 50 minutes at a time, let alone 50 days. Unlikely to make anything actually fall out of the sky.

This is a good point, although the author notes in the article that the battery will only last a few minutes you never know if this code might get copied into a different project!

What I would do in this particular case is make a note of the limitations in the function block comment. There are always trade-offs in embedded code so I think it is fair to say that yes this code does not handle rollover, FYI.

>Also, while it's probably ok in this usage since it's a 32-bit cpu, it would be better to use atomic access intrinsics or a critical section and memory barriers rather than volatile to make the counter variable "ISR safe".

The dislike for volatile coming from people in higher systems is pretty tiring and unwarranted.

Volatile is a perfectly fine tool on low level mcus.

No, it is not, it will just work most of the time in low level MCUs because compilers support this abuse for lack of clearly better options. C and C++ have no support whatsoever for dealing with interrupts: volatile does not introduce any memory fences, atomics are for sharing data between threads which you do not have in many embedded systems, and sig_atomic_t is only defined for situations where you have a posix-like runtime environment. So sharing data between an ISR and the main program is undefined in C, and the compilers are free to compile „volatile“ the way most people expect, but the language does not guarantee that it works.

It can be argued that the only kind of variable that can be shared safely between an ISR and the rest of the code is a volatile sig_atomic_t, but that is an argument from analogy and not from first principles, as the C abstract machine has no concept of ISR. The same goes for the argument that you should use atomic intrinsics, plausible, but still just an analogy.

(Just in case anyone was wondering if C as a language is close to the metal: no, it is not.)

I found quadcopters to be a great way to get into embedded development. There are a couple posts on my blog [0] [1] related to getting Rust running on a quadcopter controller, although I never actually got my code controlling the drone in flight. The lesson I learned was that if I was going to do it again I'd definitely choose a board with an exposed debugger port, as I spent a lot of time repurposing one of the motor outputs into a serial transmit port just to do some "println debugging".

Not sure if OP is here, but I'd be interested in hearing about which debug options are easily exposed on that board. Can you use the port you are flashing the drone with as a standard serial port after your code is loaded? Looking forward to the future post about SWD that is introduced at the end of this post as well.

[0]: https://www.joshmcguigan.com/blog/betafpv-drone-flight-contr...

[1]: https://www.joshmcguigan.com/blog/betafpv-drone-flight-contr...

SWD is purposefully exposed on that board and instead of flashing via the bootloader I got flashing with gdb via SWD with the `load` command working. I just use a male header with some jumper wires for the connection. It seems stable enough. Some pictures of the debug port here:



What does this line of code mean?

    *(volatile uint32_t *)0x40021018 = 0x00000004;
Why is a pointer being created and dereferenced at the same time?

It's how you access memory mapped registers in C. It's writing the value 0x04 at that address using a 32-bit memory write. There is a peripheral register at that location.

You could split up the definition of the pointer and the dereferencing+assignment:

  volatile uint32_t* p = (volatile uint32_t *)0x40021018;
  *p = 0x00000004;

Thats memory mapped IO. Registers, and control registers are mapped onto certain regions of memory on ARM Coretx-M processors.

CMSIS is the most bizarre API i've ever seen. This kind of mixing ALLCAPS_WithUnderscores_AndCamelCase is like criminal. Also _Min_Heap_Size. And that is the reason i would never use it and i've always looked for alternatives. Now i've ended up using Ivory/Tower eDSL framework where just about everything is equally complicated - as building a spaceshuttle.

On the bright side, the core CMSIS headers should work with all ARM Cortex-M cores, so you don't get locked into one vendor's HAL to do things like setup interrupts or interact with registers that are common to ARM cores. It also has something approaching first-party support with a permissive license.

I think that the ALLCAPS parts come from acronyms in most cases. Like, NVIC = Nested Vector Interrupt Controller. The naming can get a little bit confusing, but it seems fairly consistent.

I thought, the Peripheral library is deprecated for STM32. Is there any reason to use it besides the STM32 CUBE framework?. Because, the later seems to support more STM32 product lines.

Stm32 cube mx is the newest version of the framework, but the old peripheral driver library is a bit simpler and easier to use! It is more low level and a light layer over the hardware.

The new cube library can be more complex.

The new cube libraries do include two abstraction layers, though. I agree that the "HAL" libraries can be very confusing because they try to do everything for you with endlessly nesting functions and macros, but the "LL" (Low-Level) ones are closer to the old peripheral driver libraries.

Just ordered one myself after reading your post, look forward to working on this platform!

Great! Be sure to check out the first part if you have trouble getting started. Best of luck :)

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact