|
NNP STM Generic Remote Module git-main
|
The root directory is the base of the project. Many important files relating to project configuration are located here. The .ioc file is used to generate configuration code in STM32CubeMX, the .launch files configure debugging parameters, and the .project file defines the project configuration in STM32CubeIDE.
The Src (or source) directory contains most of the operational code of the CanFest implementation. Specific details of each of the files can be found under Topics->CANFestival.
This directory also contains main.c, which contains the main initialization code of the module, configuring system clocks, peripherals used for communication with the CAN network, and peripherals used for internal state management.
The main file in this directory that will be modified for user implemtation is nmtSlave.c, which defines functionality on reception of a specific NMT command.
The Inc (or include) directory contains the header files for files within the Core/Src directory.
The main file in this directory that will be modified for user implementation is def.h, which defines NMT commands the module is able to receive.
The App (or application) directory contains the user application files. This is where your custom implementation of the module will live.
There are a few files present in the directory that are required for operation of the generic module, but are not part of the core CanFest implentation, and thus, are included here.
These files are acceltemp.c/h and eedata.c/h, which are the parts of the program which manage communication with the accelerometer, thermometer, diagnostics measurements, and emulated EEPROM. These files must not be changed unless you know what you're doing.
ObjDict.c/h are also in this directory, which defines the Object Dictionary from which most of the data communication with FESCAN will take place.
There are a few peripherals that are restricted from use in a custom module implementation due to use in the core module functonality. These peripherals are ADC1*, CAN1, TIM2, I2C2, and RCC.
| Peripheral | Reason |
|---|---|
| ADC1* | ADC1 is used to measure internal voltages for diagnostics reasons. ADC1 is able to be used if the diagnostics channels are initialized again when additional ADC channels need to be used (as is done in SWARM). If possible, an external ADC is recommended. |
| CAN1 | The CAN interface is used for communication with the broader network. |
| TIM2 | The Timer2 peripheral is used as the timebase for internally timing the CanFest layer. |
| I2C2 | The I2C2 interface is used to communicate with the accelerometer and thermometer on pins PB13 and PB14. |
| RCC | Changing the system clocks requires retiming the TIM2, CAN, I2C, and CanFest, and is not recommended. |
There are several notable differences between a project generated by STM32CubeIDE and the Generic Remote Module, all relating to the memory mapping of the module.
The following changes are made to the system_stm32l4xx.c file:
#define USER_VECT_TAB_ADDRESS - is defined#define VECT_TAB_OFFSET 0x00005000U - changed from 0x00000000UThese changes enable the implementation of the custom CAN bootloader.
This is the linker script that defines the addressing of FLASH, RAM, and other important addresses of the microcontroller.
The only change is made to the Memories definiton section.
The flash origin has a 0x5000 offset due to the bootloader, and the length is shortened, due to the bootloader, module parameter storage, and reserved EEPROM space.
For a 256K module:
The different sections of flash are shown below
| Bootloader | App Space | EEPROM | Parameter Space | |
|---|---|---|---|---|
| Start Address | 0x0000 | 0x5000 | 0x3E000 | 0x3F800 |
| End Address | 0x4FFF | 0x3DFFF | 0x3F7FF | 0x3FFFF |
| Size | 0x5000 | 0x39000 | 0x1800 | 0x800 |
| # of flash pages | 10 | 114 | 3 | 1 |
| Purpose | Module bootloader (first entry point) | Application space (leftover space from allocation of other sections) | Space for emulated EEPROM | Parameter space (Node number, app checksum, stim limits, etc.) |
[0x04, 0xD2, 0x03] at addresses 0x4FFD 0x4FFE and 0x4FFF respectively. [0x3F800 - 0x3F805] are used for the:0x08000000 to 0x00000000 by default, each of the addresses should add 0x08000000 to write to the absolute flash address 0x00004FFF becomes 0x08004FFFThe Object Dictionary is the primary index for reading and writing data via FESCAN/CanFest. The Object Dictionary conatins module configuration data, state information, sensor information, etc; and is where any data you would like to retrieve over FESCAN will live.
There are several entries in the Object Dictionary which are consistent across all FESCAN/CanFest devices. A non-exhaustive list of entries and their description are included below:
| Entry Index | Entry Description |
|---|---|
| 0x1000 - 0x2000 | CanFest Communication Parameters |
| 0x2003 | Temperature |
| 0x2010 | Module Status |
| 0x2011 | Accelerometer Data |
| 0x2500 | CAN Statuses |
| 0x2900 | Restore List |
| 0x3000 | Diagnostics |
Adding entries to the object dictionary is fairly simple. As an example, we will walk through adding two new entries to the Object Dictionary, SimpleVariable, and SimpleArray.
At the top of ObjDict.c (located in Core/App/), there is a list of mapped variables. To ensure compatibility with the network, mapped entries should use the built in variable types (UNS8, UNS16, UNS32, INTEGER8, INTEGER16, etc...).
To begin, we will add SimpleVariable (a UNS8) and SimpleArray (a UNS8[16]) to the list. Variables that are included in the Object Dictionary must have a default value assigned to them. This value can be updated within the application at any point.
In order for other parts of the program to be able to access these new variables we created, we need to add them to ObjDict.h as an extern variable.
With the new entries in the Object Dictionary created, we can now add them into the Object Dictionary itself. Scrolling through the Object Dictionary, you will see the repetition of a specific structure. The one for Accelerometer data is included below:
Indexes and their metadata in the Object Dictionary have a distinct naming structure:
ObjDict_IndexXYZW[] & ObjDict_highestSubIndex_objXYZW,
where XYZW is the index address of the mapped variables.
We will create a new entry in the Object Dictionary following the same structure. In this case we will include both variables we created in the same index, but they can easily be split up into different indices if desired.
For no particular reason, we will create the new entry at index 0xBEEF.
ObjDict_highestSubIndex_objBEEF defines how many subindices are present in the given index, and is equal to the number of variables you would like to include.Lets break down the different parts in a subindex:
sizeof(array), but the pointer to the object will be a reference to the 0th index of the array.Now that the Object Dictionary structure is defined, we can register the Object Dictionary entry in the indextable.
Near the end of ObjDict.c, there will be a large array named ObjDict_objdict, this is the indextable of our Object Dictionary.
To register our new index in the Object Dictonary, we can simply add our new entry to the end of the list.
Additionally, the index must be added to the search function ObjDict_scanIndexOD().
ObjDict_scanIndexOD() aligns with the index in ObjDict_objdict[].Callbacks can be added to specific subindices to call a function when modified in the Object Dictionary via FESCAN. This is useful if a specific variable is used for asynchronous code execution, or as an initialization parameter.
Adding a callback is fairly straightforward, and only requires slight modifications to our existing structures. We will continue from our SimpleVariable example above.
The first modification is at our 0xBEEF index:
We create a new array ObjDict_IndexBEEF_callbacks[] of type ODCallback_t, that contains as many NULL elements as we have in our subindex table (including subIndex count).
In ObjDict_scanIndexOD we add the set of callbacks to the end of the case statement, so instead of
like we had before, the table entry will look like this:
Now that our index in the Object Dictionary has slots for callback functions assigned, we can assign a callback function to it.
All callback functions for the object dictionary must have the following structure:
In our case, we will add the following function to app.c:
and the following line to app.h:
In initAppTask() we call the following function to register a change in SimpleVariable to our newly created function.
Indices in the Object Dictionary can be restored if they are included in the RestoreList entry in the ObjectDictionary (0x2900). A given state of the Object Dictionary can be saved via the SaveValues() function, or over the CAN network via the NMT_Do_Save_Cmd NMT command.
We will add our example index 0xBEEF to the restore list.
ObjDict.h lines up with the number of indices in RestoreList, otherwise, you will get a compilation errorApplication functionality is added via source/header files within the Core/app directory. Instead of a main.c, the module application code should live in app.c.
Initialization code takes place in initAppTask(), and the continuously running loop takes place in updateAppTask().
There are also several useful callbacks present in app.c that are linked to FESCAN/CanFest functionality.
State management callbacks are also available for user application modification.
An example of implemented application code is locally present in acceltemp.c, with a full application example in SWARM.
Implantables-GenericModule-Application.ioc is the STM32CubeMX configuration file that enables creation of initialization code for pin function and system clocks through a GUI.
As long as the existing set up peripherals and pin assignments are not modified, the ioc file can be used to add additional peripherals and GPIO to the project.
The repository is set up with the Doxygen toolchain in mind. The repository also contains custom GitHub Actions and Workflows that will automatically publish Doxygen documentation to GitHub Pages on each commit to the main branch. The Doxygen configuration also adds a revision history feature, which allows users to view a snapshot of the documentation as it was when a release was published on GitHub.
The working directory of Doxygen is the root of the repository, not the /manual folder.