Code Organization
In general, we try to encapsulate components of the firmware as independent modules. We have a common library that consists of a number of peripheral drivers and frameworks that are shared across the car.
All modules and drivers should have a simple, common API which can easily be ported across multiple platforms. Our drivers are designed to abstract most of the heavy lifting away from the user, allowing the development of systems without needing to worry about the inner workings of each component.
Version Control
We use git for our version control. There are a number of resources available for git, such as this simple guide.
Our firmware is stored on GitHub as an open-source project. To get involved, please speak to a software lead to be added to our GitHub organization.
In general, we follow the basic git flow. The master branch should contain stable, vetted code. Development branches should be created on a feature-by-feature basis, and a pull request to master should be created when the feature is complete. Please squash your branch before merging to master.
Coding Style
At Midnight Sun, we use a variant of the Google C++ Style Guide. In general:
Good | Not Good | Notes | |
---|---|---|---|
Include guards |
|
| Although Note that include guards are used to prevent double inclusion of headers. |
Conditionals and loops |
OR
|
OR
OR
| Braces are always required, even for single-line statements. They should be on the same line as the conditional. Adequate spacing should be provided between statements. In general, please be consistent. |
Indentation | 2 space indents | Anything else | Only use spaces. Do not use tabs. Use Unix-style line endings. |
Variable names | uint8_t descriptive_name = 0; | int a = 0; | Prefer descriptive, reasonable length variable names. Note that function and variable names follow the underscore_lowercase naming convention. |
Function names (public) | void module_func(void); | void func(void); | Prefix public functions with the module's name. |
Function names (private) | static void prv_func(void); | static void func(void); | Prefix private functions with prv_ to denote that it is not visible. |
Macros |
| #define macro(x) x*2 | Avoid macros as functions, but they should be ALL_CAPS and their parameters should be enclosed in brackets if used. |
Enums |
|
| Typedef enums because we generally use them to group bitmasks, and it would get messy quickly. Declare them in ALL_CAPS prefixed with the enum name. |
Datatypes | uint8_t, uint16_t, int16_t, etc. | char, int, short, etc. | Use explicit standard datatypes. |
Structs |
|
| Use UpperCamelCase for struct definitions. Typedef'd structs make it easier to think about them as objects, and provide some abstraction. The original struct should still be named to allow for forward declarations. |
Line lengths | Around 100 characters | Above 100 characters | Try to keep line lengths reasonable. Around 100 characters is a good length. |
Bitwise operations | REG |= BIT1 | BIT2; | REG |= BIT1 + BIT2; | Make use of bitwise operations whenever working with bitmasks. Use macro or enums to define bitmasks. Try to keep the order of operations from MSB to LSB. |