Electronics > Microcontrollers

Precision of operations using #define

<< < (3/3)

Siwastaja:

--- Quote from: Silenos on June 13, 2021, 10:31:46 am ---Concurring with constexpr functions for c++.
As for C for mcu target or sth I personally refrained from struggling with preprocessor to do any advanced crap, found it not really worth the time. I generate initialization lines in included external textfiles with external tools, as those are used for generation od the const data anyway.

--- End quote ---

Yes, your method has some advantages:
* You increase visibility into the system and remove uncertainty because the exact literal value is available without tediously looking at assembly listing
* You can create a reproducible record of stages that lead into the calculation of the final value. You can do this manually, documented using comments (which, on the other hand, is prone to typing errors, but on the other hand, is error-checkable thanks to visibility), or automated using simple tools - matlab script, python script, a small C program.

One typical example of this mindset difference is; how do you deal with UART baud rate registers? The appealing way is to initialize it like USART->BRR = F_CPU/BAUDRATE, which results in some baud rate you exactly won't know and only trust it "likely works" given some implicit assumptions like F_CPU being many orders of magnitude bigger than BAUDRATE; but the code "looks better" and is more easily modified for different baud rates.

The second way is to manually calculate F_CPU/BAUDRATE, then round it up or down, document the resulting actual baudrate and the error% to the desired baudrate as a comment. The most complex, third option is to write either compile-time tool or runtime configuration function that takes desired baudrate, finds the closest one, makes sure error is below some maximum allowable error threshold, and reports error if it isn't. The first and last options are most error prone due to being simplistic and complex, respectively.

Silenos:

--- Quote from: Siwastaja on June 13, 2021, 11:27:14 am ---Yes, your method has some advantages:
* You increase visibility into the system and remove uncertainty because the exact literal value is available without tediously looking at assembly listing
* You can create a reproducible record of stages that lead into the calculation of the final value. You can do this manually, documented using comments (which, on the other hand, is prone to typing errors, but on the other hand, is error-checkable thanks to visibility), or automated using simple tools - matlab script, python script, a small C program.

One typical example of this mindset difference is; how do you deal with UART baud rate registers? The appealing way is to initialize it like USART->BRR = F_CPU/BAUDRATE, which results in some baud rate you exactly won't know and only trust it "likely works" given some implicit assumptions like F_CPU being many orders of magnitude bigger than BAUDRATE; but the code "looks better" and is more easily modified for different baud rates.

The second way is to manually calculate F_CPU/BAUDRATE, then round it up or down, document the resulting actual baudrate and the error% to the desired baudrate as a comment. The most complex, third option is to write either compile-time tool or runtime configuration function that takes desired baudrate, finds the closest one, makes sure error is below some maximum allowable error threshold, and reports error if it isn't. The first and last options are most error prone due to being simplistic and complex, respectively.

--- End quote ---
Well, that "method" I run for math, graphic, databases, or things exactly like @ricko_uk's - whatever what is detached from mcu config, assuming his target is mcu. To have the control over the math precision, in his case. Yes, that's most likely my educated duck from forcing and assuring C compiler to actually always calculate the load whatever it is. That is why I consider the c++ constexpr actually really nice feature.

Baudrate case? I don't know, any automated calculation tool would converge to entity resembling Cube etc, because there would always be someone to think some additional check to consider up. And the boss would get mad if he found out.
I actually looked up what I did with last uart:

--- Code: ---    //USART2->BRR = 555U; /* 64mhz/115200 = 555,555 */
    USART2->BRR = 69U; /* 64mhz/921600 = 555,555*/
--- End code ---
Perfection.  :)

Siwastaja:

--- Quote from: Silenos on June 13, 2021, 01:17:53 pm ---I actually looked up what I did with last uart:

--- Code: ---    //USART2->BRR = 555U; /* 64mhz/115200 = 555,555 */
    USART2->BRR = 69U; /* 64mhz/921600 = 555,555*/
--- End code ---
Perfection.  :)

--- End quote ---

Resembles what I'm usually doing, including forgetting to update the comment :).

cv007:
>how do you deal with UART baud rate registers?

If using c++, and the cpu speed is known at compile time, then use the compiler to do the work-
https://godbolt.org/z/bEPKKP7W8

If the cpu speed/baud rate combo will not work as you want, you let the compiler tell you. Compiler does not like == you get no compiled code. The only downside is some template syntax which is easy enough. Not really something you necessarily want scattered all over your code, only because you now have to figure out what type of function you are dealing with- template syntax or normal, but it is useful for things like the example given.

edit- since I have that code example, I'll show a use for the typed enums in addition to their normal c++ usefulness- in this case it is a template function that takes any number of arguments where all of the arguments have to be any of the 3 enum types (any order). The values provided in the function argument are computed at compile time, and end up as a single value to be written to a single register in this case.
https://godbolt.org/z/Yv4Tcvfq7

Lots of ways to do similar, but in many cases you will end up with several writes to the register (although not that important). This just happens to be another option where you can provide arguments in any order and via templates and typed enums you can get the values computed at compile time and when done you can write the value/values to the register/registers (that value passed around can be a struct also, containing a number of values). The typed enum makes it happen since they are unique types and provides a way for template argument deduction to end up in the correct function template.

SiliconWizard:
If you're using integer calculations, be they compile-time or run-time, as long as you follow a few basic rules (promotion and literal suffixes), results will be exact. No need to overthink it, especially for things as simple as baud rate calculation. If you are afraid of rounding errors, just use integers only.

Only in rare cases where you'd need very large integers for representing fractional frequencies, for instance, and which could lead to overflow when multiplied, should you be careful. In any case, a quick analysis of the calculation should tell you what's OK and what is not.

Navigation

[0] Message Index

[*] Previous page

There was an error while thanking
Thanking...
Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod