Revision d796dcdf README.txt
| README.txt | ||
|---|---|---|
| 69 | 69 |
4.1 Adding a New Module |
| 70 | 70 |
4.2 Handling a Custom I/O Event in the Main Thread |
| 71 | 71 |
4.3 Implementing a New Low-Level Driver |
| 72 |
4.4 Writing a Unit Test
|
|
| 72 |
4.4 Writing a Test |
|
| 73 | 73 |
|
| 74 | 74 |
================================================================================ |
| 75 | 75 |
|
| ... | ... | |
| 101 | 101 |
directory. Note that you will have to adapt the makefiles and scripts, and |
| 102 | 102 |
probably the operating system as well. |
| 103 | 103 |
AMiRo-BLT furthermore has its own required and recommended software tools as |
| 104 |
described in its README.txt file. Follow th instructions to initialize the |
|
| 104 |
described in its README.txt file. Follow the instructions to initialize the
|
|
| 105 | 105 |
development environment manually or use the ./setup.sh script. |
| 106 | 106 |
|
| 107 | 107 |
|
| ... | ... | |
| 112 | 112 |
copy of it as well. For the sake of compatibility, it is included in AMiRo-OS as |
| 113 | 113 |
a Git submodule. It is highly recommended to use the ./setup.sh script for |
| 114 | 114 |
initialization. Moreover, you have to apply the patches to ChibiOS in order to |
| 115 |
make AMiRo-OS work properly. It is recommended to use the .setup.sh script for |
|
| 115 |
make AMiRo-OS work properly. It is recommended to use the ./setup.sh script for
|
|
| 116 | 116 |
this purpose. |
| 117 | 117 |
If you would like to use a different kernel, you can add a subfolder in the |
| 118 | 118 |
./kernel/ directory and adapt the scripts and operating system source code. |
| ... | ... | |
| 164 | 164 |
AMiRo-BLT, you can replace the port value by '/dev/ttyUSB0'. |
| 165 | 165 |
Advanced users can use several connections to multiple modules simultaneously. |
| 166 | 166 |
Each additional programmer will be available as '/dev/ttyAMiRo<N>' (and |
| 167 |
'/dev/USB<N>' respectively) with <N> being an integer number starting from zero. |
|
| 168 |
Please note: Those interfaces are ordered by the time when they have been |
|
| 169 |
detected by the operating system. |
|
| 167 |
'/dev/ttyUSB<N>' respectively) with <N> being an integer number starting from |
|
| 168 |
zero. Please note: Those interfaces are ordered by the time when they have been |
|
| 169 |
detected by the operating system, so detaching a cable and plugging it in again |
|
| 170 |
may result in a different port name. |
|
| 170 | 171 |
|
| 171 | 172 |
|
| 172 | 173 |
2.2 - QtCreator IDE |
| 173 | 174 |
------------------- |
| 174 | 175 |
|
| 175 |
In order to setup QtCreator projects for the three AMiRo base modules, you can
|
|
| 176 |
use the provided ./setup.sh script. Further instructions for a more advanced
|
|
| 176 |
In order to setup QtCreator projects any supported module, you can use the
|
|
| 177 |
provided ./setup.sh script. Further instructions for a more advanced |
|
| 177 | 178 |
configuration of the IDE are provided in the ./tools/qtcreator/README.txt file. |
| 178 | 179 |
|
| 179 | 180 |
|
| ... | ... | |
| 193 | 194 |
Each time you modify any part of AMiRo-OS, you need to recompile the whole |
| 194 | 195 |
project for the according AMiRo module. Therefore you can use the ./Makefile by |
| 195 | 196 |
simply executing 'make' and follow the instructions. Alternatively, you can |
| 196 |
either use the makefiles provided per module in ./os/modules/<ModuleToCompile>
|
|
| 197 |
either use the makefiles provided per module in ./os/modules/<module_to_compile>
|
|
| 197 | 198 |
or - if you want to compile all modules at once - the makefile in the |
| 198 | 199 |
./os/modules folder. After the build process has finished successfully, you |
| 199 |
always have to flash the generated program to the robot. Therefore you need an
|
|
| 200 |
always have to flash the generated program to the module. Therefore you need an
|
|
| 200 | 201 |
appropriate tool, such as stm32flash (if you don't use a bootloader) or |
| 201 | 202 |
SerialBoot (highly recommended; provided by AMiRo-BLT). Similarly to the |
| 202 | 203 |
compilation procedure as described above, you can flash either each module |
| ... | ... | |
| 229 | 230 |
should be as unambiguous as possible (e.g. containing name and version number). |
| 230 | 231 |
All files, which directly depent on the hardware, and thus are not portable, |
| 231 | 232 |
belong here. Conversely, any code that can be reused on diferent hardware must |
| 232 |
not be put in the module folder.
|
|
| 233 |
not be put in this module folder.
|
|
| 233 | 234 |
|
| 234 | 235 |
In a second step you have to initialize all requried files (see below) in the |
| 235 |
newlly created module directory. It is recommended to use another module as
|
|
| 236 |
newly created module directory. It is recommended to use another module as |
|
| 236 | 237 |
template for your configuration: |
| 237 | 238 |
- alldconf.h |
| 238 | 239 |
Configuration header for the AMiRo-LLD project, which is part of AMiRo-OS. |
| 240 |
There are probably only very few configurations done here, since most setting |
|
| 241 |
depend on the content of aosconf.h and are handled modue unspecifically in in |
|
| 242 |
modules/aos_alldconf.h |
|
| 239 | 243 |
- aosconf.h |
| 240 | 244 |
Configuration header for the AMiRo-OS project. |
| 241 | 245 |
- board.h & board.c |
| ... | ... | |
| 243 | 247 |
well as initialization functions. |
| 244 | 248 |
- chconf.h |
| 245 | 249 |
Configuration header for the ChibiOS/RT system kernel. There are probably only |
| 246 |
very few configurations one here, since most settings depend on the content of
|
|
| 247 |
aosconf.h and are handled module unspecific in modules/aos_chconf.h
|
|
| 250 |
very few configurations done here, since most settings depend on the content
|
|
| 251 |
of aosconf.h and are handled module unspecifically in modules/aos_chconf.h
|
|
| 248 | 252 |
- halconf.h |
| 249 | 253 |
Configuration header for ChibiOS/HAL (hardware abstraction layer). |
| 250 | 254 |
- Makefile |
| 251 | 255 |
The GNU make script to build and flash AMiRo-OS for the module. |
| 252 | 256 |
- mcuconf.h |
| 253 |
Configuration file for ChibiOS/JAL to initialize the microcontroller (MCU). It
|
|
| 257 |
Configuration file for ChibiOS/HAL to initialize the microcontroller (MCU). It
|
|
| 254 | 258 |
is recommended to check the kernel/ChibiOS/demos/ directory for an example |
| 255 | 259 |
using the according MCU and copy the mcuconf.h from there. Depending on your |
| 256 | 260 |
hardware you may have to modify it nevertheless, though. |
| ... | ... | |
| 271 | 275 |
settings are used in other modules. |
| 272 | 276 |
|
| 273 | 277 |
Finally, you need to build and flash the project. The compiler might even help |
| 274 |
you getting everything set up correctly. Take time to understand compilation
|
|
| 275 |
errors and warning and get rid of all of those (warnings should not be ignored
|
|
| 276 |
since they are hints that something might be amiss and the program will not act
|
|
| 277 |
as intended). |
|
| 278 |
you getting everything set up correctly. Take the time needed to understand
|
|
| 279 |
compilation errors and warnings and get rid of all of those (warnings should not
|
|
| 280 |
be ignored since they are hints that something might be amiss and the program
|
|
| 281 |
will not act as intended).
|
|
| 278 | 282 |
|
| 279 | 283 |
Summing up, you have to |
| 280 | 284 |
1) create a module directory. |
| ... | ... | |
| 288 | 292 |
|
| 289 | 293 |
In order to handle custom I/O events in the main thread, AMiRo-OS offers several |
| 290 | 294 |
hooks to be used. First of all, you need to configure and enable the interrupt |
| 291 |
in the according GPIO. This can be done by implementing the
|
|
| 295 |
for the according GPIO. This can be done by implementing the
|
|
| 292 | 296 |
MODULE_INIT_INTERRUPTS() hook in the module.h file. For information how to use |
| 293 | 297 |
this hook, please have a look at existing modules. In the end, the interrupt |
| 294 | 298 |
callback functions has to emit an I/O event with the according bit in the flags |
| 295 |
mask set (like the _intCallback() function in aos_system.c). As result, whenever
|
|
| 296 |
a rising or falling edge (depends on configuration) is detected on that GPIO,
|
|
| 297 |
the interrupt service routine is executed and hence an I/O event is fired, which
|
|
| 298 |
can be catched by any thread in the system. |
|
| 299 |
mask set (like the _gpioCallback() function in aos_system.c). As result,
|
|
| 300 |
whenever a rising or falling edge (depends on configuration) is detected on that
|
|
| 301 |
particular GPIO, the interrupt service routine is executed and hence an I/O
|
|
| 302 |
event is fired, which can be catched by any thread in the system.
|
|
| 299 | 303 |
|
| 300 | 304 |
Next, you have to configure the main thread to whitelist the event flag (all I/O |
| 301 | 305 |
events are blacklisted by default). While system relevant events like power down |
| 302 |
are whitelisted by the OS, any custom events need to be added exl´plicitely. |
|
| 303 |
This is done via the optional AMIROOS_CFG_MAIN_LOOP_GPIOEVENT_FLAGSMASK macro, |
|
| 304 |
which should be defined in the module.h file. Example: |
|
| 306 |
are imlicitely whitelisted by the OS, any custom events need to be added |
|
| 307 |
exlplicitely. This is done via the optional |
|
| 308 |
AMIROOS_CFG_MAIN_LOOP_GPIOEVENT_FLAGSMASK macro, which should be defined in the |
|
| 309 |
module.h file. Example: |
|
| 305 | 310 |
|
| 306 | 311 |
#define AMIROOS_CFG_MAIN_LOOP_GPIOEVENT_FLAGSMASK \ |
| 307 | 312 |
(AOS_GPIOEVENT_FLAG(padX) | AOS_GPIOEVENT_FLAG(padY) | AOS_GPIOEVENT_FLAG(padZ)) |
| ... | ... | |
| 309 | 314 |
When AMIROOS_CFG_MAIN_LOOP_GPIOEVENT_FLAGSMASK has been defined correctly, the |
| 310 | 315 |
main thread will be notified by the according events and execute its event |
| 311 | 316 |
handling routine. Hence you have to implement another macro in module.h to |
| 312 |
handle the custom event(s) appropriately: MODULE_MAIN_LOOP_GPIOEVENT(eventflags). |
|
| 313 |
As you can see, the variable 'eventflags' is propagated to the hook. This |
|
| 314 |
variable is a mask, that allows to identify the GPIO pad(s), which caused the |
|
| 315 |
event, by the bits set. Following the example above, you can check which GPIOs |
|
| 316 |
have caused events by using if-clauses in the implementation of the hook: |
|
| 317 |
handle the custom event(s) appropriately: |
|
| 318 |
MODULE_MAIN_LOOP_GPIOEVENT(eventflags). As you can see, the variable |
|
| 319 |
'eventflags' is propagated to the hook. This variable is a mask, that allows to |
|
| 320 |
identify the GPIO pad(s), which caused the event, by the individually set bits. |
|
| 321 |
Following the example above, you can check which GPIOs have caused events by |
|
| 322 |
using if-clauses in the implementation of the hook: |
|
| 317 | 323 |
|
| 318 | 324 |
#define MODULE_MAIN_LOOP_GPIOEVENT(eventflags) { \
|
| 319 | 325 |
if (eventflags & AOS_GPIOEVENT_FLAG(padX)) { \
|
| ... | ... | |
| 337 | 343 |
In the AMiRo-OS framework, low-level drivers are located in the additional Git |
| 338 | 344 |
project AMiRo-LLD, which is included in AMiRo-OS as Git submodule at |
| 339 | 345 |
periphery-lld/AMiRo-LLD/ and acts similar to a static library. When adding a new |
| 340 |
low-level driver to the framework, you have to implement it, providing a |
|
| 341 |
(single) header file in periphery-lld/AMiRo-LLD/include/ and the required C |
|
| 342 |
sources in periphery-lld/AMiRo-LLD/source/. By convention, all filenames use the |
|
| 343 |
prefix 'alld_' to avoid ambiguities. Furthermore, files should be named by the |
|
| 344 |
exact designation of the hardware (e.g. 'alld_vcnl4020' instead of |
|
| 345 |
'alld_proximitysensor'). Since AMiRo-LLD is intended to be usable with other |
|
| 346 |
operating systems than AMiRo-OS, it provides an interface for accessing |
|
| 347 |
communication interfaces and basic functionalities of the operating system. On |
|
| 348 |
the one hand, several types are defined in periphery-lld/AMiRo-LLD/periphALtypes.h. |
|
| 349 |
The interface functions, on the other hand, are defined by AMiRo-LLD (cf. |
|
| 350 |
periphery-lld/AMiRo-LLD/templates/periphAL.h), but implemented by the operating |
|
| 351 |
system (cf. periphery-lld/periphAL.h). For the implementation of the driver, you |
|
| 352 |
must only use those types and functions to interact with the operating system. |
|
| 353 |
If you need further functionality, which is not provided by the interface yet, |
|
| 354 |
you are encouraged to extend periphAL. |
|
| 355 |
|
|
| 356 |
Furthermore, all files must define a guard, so that the whole driver is |
|
| 357 |
disabled, when the guard is not set explicitely. These guard again are named |
|
| 358 |
following a convention, but instead of explaning it here, just have a look at |
|
| 359 |
one of the existing drivers and look for lines like |
|
| 360 |
|
|
| 361 |
#if defined(AMIROLLD_CFG_USE_VCNL4020) || defined(__DOXYGEN__) |
|
| 362 |
|
|
| 363 |
With these guards in place, the driver will be omitted by default and needs to |
|
| 364 |
be enabled explicitely. In order to do so, you need to add an according #define |
|
| 365 |
in the alldconf.h file of any module, which shall use the new driver. |
|
| 366 |
|
|
| 367 |
Now the new driver is available and enabled, but not actually used yet. |
|
| 368 |
Therefore you have to add according memory structures to the module.h and |
|
| 346 |
low-level driver to the framework, you have to implement it, following the |
|
| 347 |
instructions given in periphery-lld/AMiRo-LLD/README.txt |
|
| 348 |
|
|
| 349 |
Now the new driver is available and can be enbled by simply including the |
|
| 350 |
driver's makefile script in the module makefile. In order to make actuale use of |
|
| 351 |
the driver you have to add according memory structures to the module.h and |
|
| 369 | 352 |
module.c files - just have a look at existing modules how this is done. In some |
| 370 | 353 |
cases you will have to configure additional interrupts and/or alter the |
| 371 | 354 |
configuration of a communication interface (e.g. I²C). Once again, you should |
| 372 | 355 |
take a look at existing modules and search the module.h for the hooks |
| 373 |
MODULE_INIT_INTERRUPTS() and MODULE_INIT_PERIPHERY_COMM(). |
|
| 356 |
MODULE_INIT_INTERRUPTS(), MODULE_INIT_PERIPHERY_IF and |
|
| 357 |
MODULE_SHUTDOWN_PERIPHERY_IF(). |
|
| 374 | 358 |
|
| 375 |
Finally, you will probably want to validate your implementation via a unit test.
|
|
| 376 |
How this can be done is explained in detail in the next guide.
|
|
| 359 |
Finally, you will probably want to validate your implementation via a test. How
|
|
| 360 |
this can be done is explained in detail in the next guide. |
|
| 377 | 361 |
|
| 378 | 362 |
Summing up, you have to |
| 379 | 363 |
1) implement the driver in AMiRo-LLD using periphAL only. |
| 380 |
2) fence all code in all files by a guard. |
|
| 381 |
3) set the guard in alldconf.h to enable the driver. |
|
| 382 |
4) add the driver to a module. |
|
| 364 |
4) add the driver to a module (Makefile, module.h and module.c). |
|
| 383 | 365 |
5) configure interrupts and interfaces as required. |
| 384 |
6) write a unit test.
|
|
| 366 |
6) write a test. |
|
| 385 | 367 |
|
| 386 | 368 |
|
| 387 |
4.4 Writing a Unit Test
|
|
| 369 |
4.4 Writing a Test |
|
| 388 | 370 |
------------------------ |
| 389 | 371 |
|
| 390 |
AMiRo-OS provides a unit test framework for conventient testing and the ability |
|
| 391 |
to opt-out all unit tests via the aosconf.h configuration file. There is also a |
|
| 392 |
dedicated folder, where all unit test code belongs to. In case you want to |
|
| 393 |
implement a unit test for a newly developed low-level driver, you should use the |
|
| 394 |
folders unittests/periphery-lld/inc and unittests/periphery-lld/src |
|
| 395 |
respectively. As with the low-level drivers, unit test files should use a prefix |
|
| 396 |
in their name, namely 'ut_' and all code should be fenced via guards that |
|
| 397 |
disable it by default (have a look at existing unit tests). Before you implement |
|
| 398 |
a vast test, however, it is highly recommended to start with some sceleton code |
|
| 399 |
(just copy an existing unit test, scoop out the test function, and rename |
|
| 400 |
according variables etc.) and make it compile and run. |
|
| 401 |
|
|
| 402 |
After you have initialized the unit test sceleton, you have to add the according |
|
| 403 |
aos_unittest_t (cf. core/inc/aos_unittest.h) object to the module.h and module.c |
|
| 404 |
files. These objects again require an shell command, so the unit test can be run |
|
| 405 |
via the AMiRo-OS shell. As with existing unit tests, this shell command callback |
|
| 406 |
function as well as any further required data should be implemented directly in |
|
| 407 |
module.c, so it not accessable from any other context. In most cases this |
|
| 408 |
callback function is trivial, anyway. |
|
| 409 |
|
|
| 410 |
In order to make the shell command, which executes the unit test, available in |
|
| 411 |
shell so a user can run it, it has to be associated with the shell. AMiRo-OS |
|
| 412 |
provides the hook MODULE_INIT_TESTS() for this purpose, which has to be |
|
| 413 |
implemented in the module.h file. Once again I recommend to have a look at an |
|
| 414 |
existing module, how to use this hook. |
|
| 415 |
|
|
| 416 |
Since the execution pipeline is set up now, you can fille your unit test with |
|
| 417 |
life. Remember that the test is executed by the shell thread, so you can access |
|
| 418 |
any functionality of the system, but might encounter race conditions, depending |
|
| 419 |
on what other applications run concurrently. |
|
| 372 |
AMiRo-OS provides a test framework for conventient testing and the ability to |
|
| 373 |
opt-out all tests via the aosconf.h configuration file. There is also a |
|
| 374 |
dedicated folder, where all test code belongs to. In case you want to implement |
|
| 375 |
a test for a newly developed low-level driver, you should have a look at the |
|
| 376 |
folder test/periphery-lld/. As with the low-level drivers, tests are placed in |
|
| 377 |
individual subfolders (e.g. test/periphery-lld/DEVICE1234_v1) and all should use |
|
| 378 |
the prefix 'aos_test_' in their name. Moreover, all code must be fenced by |
|
| 379 |
guards that disable it completely if the AMIROOS_CFG_TESTS_ENABLE flag is set to |
|
| 380 |
false in the aosconf.h configuration file. |
|
| 381 |
|
|
| 382 |
Now you will need to add the test to a specific module. Therefore, you should |
|
| 383 |
create a new test/ directory in the module folder, if such does not exist yet. |
|
| 384 |
In this directory, you create another subfolder, e.g. DEVICE1234/ and three |
|
| 385 |
additional files in there: |
|
| 386 |
- module_test_DEVICE1234.mk |
|
| 387 |
- module_test_DEVICE1234.h |
|
| 388 |
- module_test_DEVICE1234.c |
|
| 389 |
The makefile script is not required, but recommended to achieve maintainable |
|
| 390 |
code. This script file should add the folder to the MODULE_INC variable and all |
|
| 391 |
C source files to MODULE_CSRC. The .h and .c files furthermore define module |
|
| 392 |
specific data structures and a test function. |
|
| 393 |
|
|
| 394 |
In order to be able to call this function as a command via the AMiRo-OS shell, |
|
| 395 |
you need to add an according shell command to the module.h and module.c files. |
|
| 396 |
Whereas the command itself is typically very simple, just calling the callback |
|
| 397 |
function defined in the test/DEVICE1234/module_test_DEVICE1234.h/.c files, you |
|
| 398 |
have to add the command to a shell. In order to make the shell command, which |
|
| 399 |
executes the test, available in a shell so a user can run it, it has to be |
|
| 400 |
associated with the shell. AMiRo-OS provides the hook MODULE_INIT_TESTS() for |
|
| 401 |
this purpose, which has to be implemented in the module.h file. Once again I |
|
| 402 |
recommend to have a look at an existing module, how to use this hook. |
|
| 420 | 403 |
|
| 421 | 404 |
Summing up, you have to |
| 422 |
1) initialize a unit test sceleton in the unittests/ folder.
|
|
| 423 |
2) introduce an according object and configuration in module.h and module.c.
|
|
| 405 |
1) implement the test in the test/ folder.
|
|
| 406 |
2) implement a module specific wrapper in the module/test/ folder.
|
|
| 424 | 407 |
3) associate the shell command to a shell via the hook in module.h. |
| 425 |
4) implement the full unit test in the prevously created sceleton files. |
|
| 426 | 408 |
|
| 427 | 409 |
================================================================================ |
| 428 | 410 |
|
Also available in: Unified diff