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