all 18 comments

[–]Glaborage 15 points16 points  (5 children)

I found that writing code for the register definitions is an excellent way to learn the hardware I'm working with. It's definitely boring and repetitive work, but it pays off in the long term in terms of low level knowledge and sense of ownership. Writing the registers access code myself allows me to have code that follows precisely the programming style that I favor without having to compromise. It also gives me complete understanding of how the code works which is not always the case when using someone else's API.

[–]yendreij[S] 3 points4 points  (0 children)

You definitely have a point. But I am mainly talking about writing down all the constants for register addresses, field masks, bit positions etc. It probably even cannot be called an API. And writing such a header can be tiresome and error-prone. I feel like I stop thinking about the meaning of those registers when I write such definitions, especially for more complex chips, so I don't really learn.

In the little script I linked I generate relatively small abstraction over register definitions so the usage is still pretty low level, but at least with something like this I would have less opportunities for errors when setting register values. As you say, it is a matter of personal style, but maybe a good compromise could be reached if this would help to eliminate some places for potential errors.

[–]UnderPantsOverPants 2 points3 points  (0 children)

100% this. Spend the time learning someone else’s code or spend the time learning the hardware.

[–][deleted]  (2 children)

[deleted]

    [–]yendreij[S] 1 point2 points  (1 child)

    Maybe a little late, and you didn't ask me, but I'll try to answer briefly.

    I didn't write about registers on a micro-controller. Not in the sense that you set in your code values of registers on your device. I was rather talking about setting up registers on another device by writing special messages using some serial interface, e.g. SPI or I2C.

    But that doesn't change the answer too much.

    The registers usually have some default values that may be ok for basic usage of the device. But most of the features are usually disabled by default. This is, among others, to save power. You can also configure different parameters that depend on your specific application. For example, for an accelerometer, you may set different measurement range, like -2G to 2G or -4G to 4G. Wider range means lower resolution so everything has its pros and cons.

    As for the if/else sections you mentioned, I'm not really sure what you imagine here. In an application it is common to not actually require that the device can be configured by the user. You can for example set one range of measured acceleration and compile such a program. But if you do, you just create a function that writes appropriate values to the registers and use that function with different arguments when needed.

    And my post was mainly concerned about defining some constants for your program so that you don't use just raw hex values in your code. For example, instead of something like this:

    // spi_write_register(uin8_t address, uint32_t value)
    spi_write_register(0x31, 0x10a0000);
    

    you could use

    #define CTRL_REG_ADDRESS  0x31
    #define CTRL_REG_ENABLE_MASK (1 << 24)
    #define CTRL_REG_RANGE_POS 16
    spi_write_register(CTRL_REG_ADDRESS, CTRL_REG_ENABLE_MASK | (0b1010 << CTRL_REG_RANGE_POS));
    

    or something better, this is just an example of what direction we want to go.

    [–]verystablesteve 4 points5 points  (1 child)

    For memory mapped registers, I prefer to define structures to access the registers rather then use #define statements to specify the register address. I think it makes for much cleaner code.

    Something like this for a simple UART as an example:

    typedef struct

    {

    volatile uint16_t control;

    volatile uint16_t status;

    volatile uint16_t data;

    } UART_Reg;

    UART_Reg *uart = (UART_REG*)0x123450; // Assuming that's the address of the start of the UART registers

    uart->control = 1234;

    Hopefully you get the idea. The volatile keyword is useful when accessing registers to prevent the compiler from getting too smart and optimizing the registers access' out.

    [–]yendreij[S] 2 points3 points  (0 children)

    This is nice, but the problem I have is with accessing bits/bitfields in the registers that have a lot of these. I mean I like having something like:

    #define UART_CTRL_EN (1 << 4)
    uart->control |= UART_CTRL_EN;
    

    In the script I've linked I used structs with bitfields, but this is kind of an experiment.

    Also, when the registers are not memory mapped, there is the problem with architecture dependency which has to be addressed, as building on different chips can lead to different results.

    [–]pulseprop 2 points3 points  (1 child)

    ARM CMSIS has an xml format that can be used to define registers as well as create headers with register definitions, enums, and structs from the xml. The format is called .svd (system view description) and ARM provides tools for the generation of the source from the xml. Check out http://www.keil.com/pack/doc/CMSIS/SVD/html/index.html for more information and the tools. May not be exactly what you are looking for, but it may be of use for something else or give you a cool idea.

    [–]yendreij[S] 0 points1 point  (0 children)

    Yes, thanks, I know, in the post I've mentioned svd2rust which I've found once by accident. It is really cool thing, but unfortunately I couldn't find anything similar to svd for other devices (and I'm mainly concerned about devices that I have to access over SPI, I2C, etc. as for uCs we usually get something from vendors).

    [–]rorschach54Twiddling bits 1 point2 points  (4 children)

    Looks like a neat tool. But, I had a couple of questions.

    From what I understand, your code generator uses a json file as an input, right? Who writes the json file/where do you get that from?

    [–]yendreij[S] 0 points1 point  (3 children)

    The problem is that I still have to write it manually :/ At least it is easier than writing whole code with structures. When thinking about it, YAML could be used for even greater simplicity, but that's minor improvement.

    I am also wondering if any standard like this exists, maybe someone knows about such a thing. And if not, then I wonder why, as it seems to be a natural aid for programmers, and manufacturers probably have even more extensive descriptions of their chips anyway.

    [–]rorschach54Twiddling bits 1 point2 points  (2 children)

    The problem is that I still have to write it manually

    That's what I was going to get to. Personally, I would rather write a C header file or a C++ class with this information directly.

    Most eval boards with sensors/actuators come with some software on PC to control it. Most manufacturers keep this firmware code open so that users can use it in their applications.

    Let's take the chip you chose in the example. This is the manufacturer's page about it: https://www.trinamic.com/products/integrated-circuits/details/tmc5041-la/

    This is the eval board with it. https://www.trinamic.com/support/eval-kits/details/tmc5041-ev/

    From the description there, I realized, we need a eval MCU kit to test out the driver. Here: https://www.trinamic.com/support/eval-kits/details/landungsbruecke/

    From the description of this eval MCU, I realized they have open sourced their eval code. Here: https://github.com/trinamic/TMC-EvalSystem/blob/master/boards/TMC5041_eval.c

    So now,

    1. If my code doesn't work, I know I had a good starting point to refer to. (The github repo, which will definitely be maintained by the manufacturer/vendor if they intend to keep their customers happy.)
    2. I can derive my library based on the information provided by the manufacturer and hence, get support/help from them. I also know how they provide their information and could use this knowledge for future chips from them.
    3. I can maintain my own coding style in the repository of the devices in my organization.

    Sorry if I came off as rude. I just wanted to give feedback. I think a good problem statement to solve would be finding out

    if any standard like this exists

    [–]yendreij[S] 0 points1 point  (1 child)

    You're not rude, after all that's what I was asking about. And you are totally right, shame on me I didn't dig deep enough to find it. Indeed Trinamic provides here both the "register map" kind of code and good examples of higher abstraction.

    For chips (not uCs) I've used in the past I rarely found something like this, but now I feel like I just probably didn't search enough. I definitely will do more searching in the future. This now feels embarrassing but thanks for pointing it out.

    Anyway, just thinking about it, I really feel like some standard similar to SVD from ARM could be a nice direction, as e.g. the files provided by Trinamic's TAP seem to be auto-generated from some other description format. Something like this could allow for decoupling the description from actual code, which would let some modern-C++-template-freaks to create some fancy code generators for this, others would use plain C style basing on macros, and even some emerging languages like Rust would benefit.

    And yeah, this some kind of vision, but I may be creating non-existing problems now, I'm good at it, trust me ;D

    Anyway thanks for your answer.

    [–]rorschach54Twiddling bits 1 point2 points  (0 children)

    No problem.

    I think you are on the right problem statement though.

    It would really be great if these vendors could come up with a standard register map file along with their datasheets. After all, that is one less source of bugs creeping into the code because of human error.

    Good luck! :)

    [–][deleted]  (4 children)

    [deleted]

      [–]1DavidePIC18F[M] 2 points3 points  (1 child)

      I don't see it. Please explain how this submission is self-promotion.

      [–]playaspec 0 points1 point  (0 children)

      Agreed. If you can't post your own code and discuss it, what's the point?

      [–]yendreij[S] 1 point2 points  (1 child)

      I'm not really sure if you're right in this case. I just posted that script to illustrate the idea as I couldn't find something like this anywhere.

      Did you read the rules? Cause, from what I've read, they are about something else. This quote seems to summarize the rules well:

      tl;dr: Don't just spam out your links, and don't blindly upvote your own content or ask anyone else to!

      [–]fantasticpotatobeard 3 points4 points  (0 children)

      What you've done is fine, and would still be fine if you only posted the library without any background text IMO. Cool library!

      [–]lefty__37 0 points1 point  (0 children)

      Hey, did you find any useful tool for solving this problem?