1
0
Fork 0

Update documentation, add CO_application.h, other updates

- add Doxfile
- add install to makefile
- fix in cocomm
This commit is contained in:
Janez 2021-05-17 13:58:16 +02:00
parent 87cfbac52b
commit 21e11a3bf1
13 changed files with 2785 additions and 65 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/html
/canopend
/cocomm/cocomm
*.o

@ -1 +1 @@
Subproject commit 76b43c88ef6d5490cb2f1518e10646e8dcb45c76
Subproject commit 1775b96450b2d3d79d8fdfad3a8347cd2ea3701e

108
CO_application.h Normal file
View file

@ -0,0 +1,108 @@
/**
* Application interface for CANopenNode.
*
* @file CO_application.h
* @ingroup CO_applicationLinux
* @author Janez Paternoster
* @copyright 2021 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_APPLICATION_H
#define CO_APPLICATION_H
#include "CANopen.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_applicationLinux Application interface to CANopenNode
* Application interface, similar to Arduino, extended to CANopen and
* additional, realtime thread.
*
* @ingroup CO_socketCAN
* @{
*/
/**
* Function is called once on the program startup, after Object dictionary
* initialization and before CANopen initialization.
*
* @param [in,out] bitRate Stored CAN bit rate, can be overridden.
* @param [in,out] nodeId Stored CANopen NodeId, can be overridden.
* @param [out] errInfo Variable may indicate error information - index of
* erroneous OD entry.
*
* @return @ref CO_ReturnError_t CO_ERROR_NO in case of success.
*/
CO_ReturnError_t app_programStart(uint16_t *bitRate,
uint8_t *nodeId,
uint32_t *errInfo);
/**
* Function is called after CANopen communication reset.
*
* @param co CANopen object.
*/
void app_communicationReset(CO_t *co);
/**
* Function is called just before program ends.
*/
void app_programEnd();
/**
* Function is called cyclically from main().
*
* Place for the slower code (all must be non-blocking).
*
* @warning
* Mind race conditions between this functions and app_programRt(), which
* run from the realtime thread. If accessing Object dictionary variable which
* is also mappable to PDO, it is necessary to use CO_LOCK_OD() and
* CO_UNLOCK_OD() macros from @ref CO_critical_sections.
*
* @param co CANopen object.
* @param timer1usDiff Time difference since last call in microseconds
*/
void app_programAsync(CO_t *co, uint32_t timer1usDiff);
/**
* Function is called cyclically from realtime thread at constant intervals.
*
* Code inside this function must be executed fast. Take care on race conditions
* with app_programAsync.
*
* @param co CANopen object.
* @param timer1usDiff Time difference since last call in microseconds
*/
void app_programRt(CO_t *co, uint32_t timer1usDiff);
/** @} */ /* CO_applicationLinux */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CO_APPLICATION_H */

View file

@ -163,10 +163,10 @@ extern "C" {
/**
* @defgroup CO_socketCAN_driver_target CO_driver_target.h
* Linux socketCAN specific @ref CO_driver definitions for CANopenNode.
*
* @ingroup CO_socketCAN
* @{
*
* Linux socketCAN specific @ref CO_driver definitions for CANopenNode.
*/
/**

View file

@ -41,10 +41,9 @@ extern "C" {
/**
* @defgroup CO_socketCAN socketCAN
* @{
*
* Linux specific interface to CANopenNode.
*
* @{
* Linux includes CAN interface inside its kernel, so called SocketCAN. It
* operates as a network device. For more information on Linux SocketCAN see
* https://www.kernel.org/doc/html/latest/networking/can.html
@ -65,11 +64,10 @@ extern "C" {
/**
* @defgroup CO_epoll_interface Epoll interface
* @ingroup CO_socketCAN
* @{
*
* Linux epoll interface to CANopenNode.
*
* @ingroup CO_socketCAN
* @{
* The Linux epoll API performs a monitoring multiple file descriptors to see
* if I/O is possible on any of them.
*

View file

@ -46,10 +46,10 @@ extern "C" {
/**
* @defgroup CO_socketCAN_ERROR CAN errors & Log
* CANopen Errors and System message log
*
* @ingroup CO_socketCAN
* @{
*
* CANopen Errors and System message log
*/
/**

View file

@ -43,7 +43,7 @@
#include "CO_epoll_interface.h"
#include "CO_storageLinux.h"
/* Call external application functions. */
/* Include optional external application functions */
#ifdef CO_USE_APPLICATION
#include "CO_application.h"
#endif
@ -117,10 +117,7 @@ typedef struct {
uint8_t pendingNodeId;
} mainlineStorage_t;
mainlineStorage_t mlStorage = {
.pendingBitRate = 0,
.pendingNodeId = CO_LSS_NODE_ID_ASSIGNMENT
};
mainlineStorage_t mlStorage = {0};
#if (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE
static CO_time_t CO_time; /* Object for current time */
@ -463,11 +460,31 @@ int main (int argc, char *argv[]) {
exit(EXIT_FAILURE);
}
#endif
#ifdef CO_USE_APPLICATION
/* Execute optional external application code */
uint32_t errInfo_app_programStart = 0;
err = app_programStart(&mlStorage.pendingBitRate,
&mlStorage.pendingNodeId,
&errInfo_app_programStart);
if (err != CO_ERROR_NO) {
if (err == CO_ERROR_OD_PARAMETERS) {
log_printf(LOG_CRIT, DBG_OD_ENTRY, errInfo_app_programStart);
}
else {
log_printf(LOG_CRIT, DBG_CAN_OPEN, "app_programStart()", err);
}
exit(EXIT_FAILURE);
}
#endif
/* Overwrite stored node-id, if specified by program arguments */
/* Overwrite node-id, if specified by program arguments */
if (nodeIdFromArgs > 0) {
mlStorage.pendingNodeId = (uint8_t)nodeIdFromArgs;
}
/* verify stored values */
if (mlStorage.pendingNodeId < 1 || mlStorage.pendingNodeId > 127) {
mlStorage.pendingNodeId = CO_LSS_NODE_ID_ASSIGNMENT;
}
/* Catch signals SIGINT and SIGTERM */
if(signal(SIGINT, sigHandler) == SIG_ERR) {
@ -613,6 +630,12 @@ int main (int argc, char *argv[]) {
CO_EMC_HARDWARE, storageInitError);
}
#endif
#ifdef CO_USE_APPLICATION
if (errInfo_app_programStart != 0) {
CO_errorReport(CO->em, CO_EM_INCONSISTENT_OBJECT_DICT,
CO_EMC_DATA_SET, errInfo_app_programStart);
}
#endif
#if (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE
/* Initialize time */
@ -647,33 +670,13 @@ int main (int argc, char *argv[]) {
continue;
}
}
#endif
#ifdef CO_USE_APPLICATION
/* Execute optional additional application code */
errInfo = 0;
err = app_programStart(!CO->nodeIdUnconfigured, &errInfo);
if(err != CO_ERROR_NO) {
if (err == CO_ERROR_OD_PARAMETERS) {
log_printf(LOG_CRIT, DBG_OD_ENTRY, errInfo);
}
else {
log_printf(LOG_CRIT, DBG_CAN_OPEN, "app_programStart()", err);
}
programExit = EXIT_FAILURE;
CO_endProgram = 1;
continue;
}
if(errInfo != 0 && !CO->nodeIdUnconfigured) {
CO_errorReport(CO->em, CO_EM_INCONSISTENT_OBJECT_DICT,
CO_EMC_DATA_SET, errInfo);
}
#endif
} /* if(firstRun) */
#ifdef CO_USE_APPLICATION
/* Execute optional additional application code */
app_communicationReset(!CO->nodeIdUnconfigured);
/* Execute optional external application code */
app_communicationReset(CO);
#endif
errInfo = 0;
@ -716,7 +719,8 @@ int main (int argc, char *argv[]) {
CO_epoll_processLast(&epMain);
#ifdef CO_USE_APPLICATION
app_programAsync(!CO->nodeIdUnconfigured, epMain.timeDifference_us);
/* Execute optional external application code */
app_programAsync(CO, epMain.timeDifference_us);
#endif
#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE
@ -753,7 +757,7 @@ int main (int argc, char *argv[]) {
}
#endif
#ifdef CO_USE_APPLICATION
/* Execute optional additional application code */
/* Execute optional external application code */
app_programEnd();
#endif
@ -808,8 +812,8 @@ static void* rt_thread(void* arg) {
#endif
#ifdef CO_USE_APPLICATION
/* Execute optional additional application code */
app_programRt(!CO->nodeIdUnconfigured, epRT.timeDifference_us);
/* Execute optional external application code */
app_programRt(CO, epRT.timeDifference_us);
#endif
}

View file

@ -36,10 +36,12 @@ extern "C" {
/**
* @defgroup CO_storageLinux Data storage with Linux
* Data initialize, store and restore functions with Linux.
*
* @ingroup CO_socketCAN
* @{
*
* Data initialize, store and restore functions with Linux, see @ref CO_storage
* See also @ref CO_storage.
*/

2585
Doxyfile Normal file

File diff suppressed because it is too large Load diff

View file

@ -69,6 +69,9 @@ all: clean $(LINK_TARGET)
clean:
rm -f $(OBJS) $(LINK_TARGET)
install:
cp $(LINK_TARGET) /usr/bin/$(LINK_TARGET)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@

View file

@ -1,4 +1,4 @@
CANopenLinux
CANopenLinux {#readmeCANopenLinux}
============
CANopenLinux is a CANopen stack running on Linux devices.
@ -7,6 +7,8 @@ It is based on [CANopenNode](https://github.com/CANopenNode/CANopenNode), which
CANopen is the internationally standardized (EN 50325-4) ([CiA301](http://can-cia.org/standardization/technical-documents)) CAN-based higher-layer protocol for embedded control system. For more information on CANopen see http://www.can-cia.org/.
CANopenLinux homepage is https://github.com/CANopenNode/CANopenLinux
Getting or updating the project
-------------------------------
@ -28,7 +30,7 @@ Usage
-----
Support for CAN interface is part of the Linux kernel, so called [SocketCAN](https://en.wikipedia.org/wiki/SocketCAN). CANopenNode runs on top of SocketCAN, so it should be able to run on any Linux machine, it depends on configuration of the kernel. Examples below was tested on Debian based machines, including Ubuntu and Raspberry PI. It is possible to run tests described below without real CAN interface, because Linux kernel already contains virtual CAN interface.
Windows or Mac users, who don't have Linux installed, can use [VirtualBox](https://www.virtualbox.org/) and install [Ubuntu](https://ubuntu.com/download/desktop) or similar.
Windows or Mac users, who don't have Linux installed, can use [VirtualBox](https://www.virtualbox.org/) and install [Ubuntu](https://ubuntu.com/download/desktop) or similar. To get confortable you may like to enroll [Introduction to Linux](https://training.linuxfoundation.org/training/introduction-to-linux/).
### CAN interfaces
@ -67,22 +69,26 @@ In own terminal run candump to display all CAN messages with timestamp:
### Running CANopenLinux device
Compile:
cd CANopenNode
cd CANopenLinux
make
Install (copy `canopend` application to the `/usr/bin/` directory):
sudo make install
Display options:
./canopend --help
canopend --help
Run on can0 device with CANopen NodeID = 4:
./canopend can0 -i 4
canopend can0 -i 4
If NodeID is not specified, then CANopen LSS protocol may be used. Program can be finished by pressing Ctrl+c or with CANopen reset node command.
After connecting the CANopen Linux device into the CAN(open) network, bootup message is visible. By default device uses Object Dictionary from `CANopenNode/example`, which contains only communication parameters. With the external CANopen tool all parameters can be accessed and CANopen Linux device can be configured (For example write heartbeat producer time in object 0x1017,0).
When CANopen Linux device is first connected to the CANopen network it shows bootup message and emergency message, because are missing storage files. To avoid emergency message it is necessary to trigger saveAll command (write correct code into parameter 0x1010,1 with SDO command) and restart the program.
When CANopen Linux device is first connected to the CANopen network it shows bootup message and emergency message, because of missing storage files. To avoid emergency message it is necessary to trigger saveAll command (write correct code into parameter 0x1010,1 with SDO command) and restart the program.
Note also, if there are multiple instances of canopend running from the same directory, storage path should be specified for each.
@ -92,17 +98,17 @@ CANopenNode includes CANopen ASCII command interface (gateway) specified by stan
To use ASCII command interface on canopend directly just run it with `-c "stdio"` and type the commands followed by enter in it.
./canopend can0 -i 1 -c "stdio"
canopend can0 -i 1 -c "stdio"
help
1 write 0x1010 1 vs save
1 reset node
To create CANopen Linux commander device on local socket run:
./canopend can0 -i 1 -c "local-/tmp/CO_command_socket"
canopend can0 -i 1 -c "local-/tmp/CO_command_socket"
#### cocomm
CANopenLinux/cocomm directory contains a small command line program, which establishes socket connection with `canopend` (CANopen Linux commander device). It sends standardized CANopen commands (CiA309-3) to gateway and prints the responses to stdout and stderr. See [cocomm/cocomm.md](cocomm/cocomm.md) for usage.
CANopenLinux/cocomm directory contains a small command line program, which establishes socket connection with `canopend` (CANopen Linux commander device). It sends standardized CANopen commands (CiA309-3) to gateway and prints the responses to stdout and stderr. See [cocomm/README.md](cocomm/README.md) for usage.
Creating new project
@ -111,10 +117,16 @@ Creating new project
New project can be started in new directory simply by adding customized makefile, custom Object Dictionary OD.h/c files and custom application source files in Arduino style, which are called from CO_main_basic.c file.
See [CANopenSocket](https://github.com/CANopenNode/CANopenSocket) for demo.
### Single or multi threaded application
By default canopend runs in single thread (CO_SINGLE_THREAD option in Makefile). Different events, such as can reception or timer expiration trigger looping through the stack (all code is non-blocking). It requires less system resources.
In multi threaded operation a real-time thread is established besides mainline thread. RT thread runs each millisecond and processes PDOs and optional application code with peripheral read/write, control program or similar. With this configuration race conditions must be taken into account, for example application code running from mainline thread must use CO_(UN)LOCK_OD macros when accessing OD variables.
See also [CANopenDemo](https://github.com/CANopenNode/CANopenDemo) for examples.
### Create new project with [KDevelop](https://www.kdevelop.org/)
### Create new project with KDevelop
- https://www.kdevelop.org/
- `sudo apt install kdevelop breeze`
- Run KDevelop, select: Project -> open project
- Navigate to project directory and click open.
@ -127,7 +139,7 @@ See [CANopenSocket](https://github.com/CANopenNode/CANopenSocket) for demo.
- `<path_to_project_files>`
- Language support, Defines, add:
- `CO_DRIVER_CUSTOM`
- Run -> Setup launches -> basicDevice:
- Run -> Setup launches -> `our_program`:
- Add Executable file and name it.
- Executable file: `<select_executable>`
- Arguments: `can0 -i 4`

View file

@ -6,15 +6,17 @@ Client socket interface to CANopenNode ASCII command interface
Compile and install
-------------------
cd cocomm
make
sudo make install
This will compile the `cocomm` utility and copy it to the /usr/bin/ directory.
This will compile the `cocomm` utility and copy it to the `/usr/bin/` directory.
Example usage
-------------
cocomm --help
cocomm "help"
cocomm "help datatype"
@ -43,7 +45,7 @@ Then make `cocomm` use that file:
Program writes data to stdout and messages in green or red color to stderr.
For more examples see [CANopenSocket](https://github.com/CANopenNode/CANopenSocket).
For more examples see [CANopenDemo](https://github.com/CANopenNode/CANopenDemo).
Background about communication paths, when using cocomm

View file

@ -1,14 +1,13 @@
/*
* Client socket command interface for CANopenSocket.
* Client socket interface to CANopenNode ASCII command interface.
*
* @file cocomm.c
* @author Janez Paternoster
* @copyright 2020 Janez Paternoster
*
* This file is part of CANopenSocket, a Linux implementation of CANopen
* stack with master functionality. Project home page is
* <https://github.com/CANopenNode/CANopenSocket>. CANopenSocket is based
* on CANopenNode: <https://github.com/CANopenNode/CANopenNode>.
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -62,7 +61,7 @@ fprintf(errStream,
"Options:\n"
" -f <input file> Path to the input file.\n"
" -s <socket path> Path to the unix socket. If not specified, path is obtained\n"
" from environmental variable, configured with:\n"
" from environmental variable, configured with:\n"
" 'export cocomm_socket=<socket path>'. If latter is not\n"
" specified, default value is used: '/tmp/CO_command_socket'.\n"
" -t <host> Connect via tcp to remote <host>. Set also with\n"
@ -85,7 +84,7 @@ fprintf(errStream,
"\n"
"For help on command strings type '%s \"help\"'.\n"
"\n"
"See also: https://github.com/CANopenNode/CANopenSocket\n"
"See also: https://github.com/CANopenNode/CANopenLinux\n"
"\n", progName, progName);
}
@ -244,7 +243,9 @@ int main (int argc, char *argv[]) {
}
}
if ((env = getenv("cocomm_candump")) != NULL) {
candump = env;
if (strlen(env) > 0) {
candump = env;
}
}
if ((env = getenv("cocomm_candump_count")) != NULL) {
candumpCount = atol(env);
@ -392,6 +393,10 @@ int main (int argc, char *argv[]) {
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.can_family = AF_CAN;
sockAddr.can_ifindex = if_nametoindex(candump);
if (sockAddr.can_ifindex == 0) {
perror(candump);
exit(EXIT_FAILURE);
}
int ret = bind(fd_candump, (struct sockaddr*)&sockAddr, sizeof(sockAddr));
if (ret < 0) {
fprintf(stderr, "CAN Socket binding failed \"%s\": ", candump);