From b541f277024b26091613cc649dfa69ff285c8c13 Mon Sep 17 00:00:00 2001 From: fgilmar Date: Tue, 13 Nov 2018 17:46:07 +0100 Subject: [PATCH] can: add two examples for the can interface Added two examples about how to configure and set a CAN interface to establish a communication receiving and sending frames. Signed-off-by: fgilmar --- apix-can-examples/Makefile | 44 ++++ apix-can-examples/README.md | 134 ++++++++++++ apix-can-examples/can-recv-example.c | 300 +++++++++++++++++++++++++++ apix-can-examples/can-send-example.c | 299 ++++++++++++++++++++++++++ 4 files changed, 777 insertions(+) create mode 100644 apix-can-examples/Makefile create mode 100644 apix-can-examples/README.md create mode 100644 apix-can-examples/can-recv-example.c create mode 100644 apix-can-examples/can-send-example.c diff --git a/apix-can-examples/Makefile b/apix-can-examples/Makefile new file mode 100644 index 0000000..677aa10 --- /dev/null +++ b/apix-can-examples/Makefile @@ -0,0 +1,44 @@ +# +# Copyright 2017, Digi International Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# +all: BINARYTX BINARYRX + +BINARYTX := apix-can-send-example +BINARYRX := apix-can-recv-example + +BINARIES := $(BINARYTX) $(BINARYRX) + +CFLAGS += -Wall -O0 + +CFLAGS += $(shell pkg-config --cflags libdigiapix) +LDLIBS += $(shell pkg-config --libs libdigiapix) + +BINARYTX: can-send-example.o + $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $(BINARYTX) + +BINARYRX: can-recv-example.o + $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $(BINARYRX) + +.PHONY: install +install: $(BINARIES) + install -d $(DESTDIR)/usr/bin + install -m 0755 $^ $(DESTDIR)/usr/bin/ + +.PHONY: all +all: $(BINARYTX) $(BINARYRX) + +.PHONY: clean +clean: + -rm -f *.o $(BINARYTX) $(BINARYRX) diff --git a/apix-can-examples/README.md b/apix-can-examples/README.md new file mode 100644 index 0000000..1898518 --- /dev/null +++ b/apix-can-examples/README.md @@ -0,0 +1,134 @@ +Digi APIX CAN Example Applications +=================================== + +Example applications to access and manage CAN interface using the Digi APIX library. + +These applications enable one CAN interface of the board and start a tranmission or +reception using the selected baudrate. + +In the reception example the application will wait for frames and print them in the +console. You can configure filters to listen to some kind of frames only. + +In the transmission example the application will generate and send frames configure the DLC, +the ID and print in the console. + +The CAN interface for this example depends on the running platform: + + ConnectCore 6 SBC: CAN connector of the board (J27). + CAN1_L: Pin 1 + CAN1_H: Pin 2 + GND: Pin 3 + CAN2_L: Pin 4 * + CAN2_H: Pin 5 * + GND: Pin 6 + + ConnectCore 6 Plus SBC: CAN connector of the board (J27). + CAN1_L: Pin 1 + CAN1_H: Pin 2 + GND: Pin 3 + CAN2_L: Pin 4 * + CAN2_H: Pin 5 * + GND: Pin 6 + + ConnectCore 6UL SBC Express: Raspberry PI connector (J8). + (An external CAN transceiver is needed). + RX: Pin 26 + TX: Pin 22 + + ConnectCore 6UL SBC Pro: CAN connector of the board (J27). + CAN1_L: Pin 1 + CAN1_H: Pin 2 + GND: Pin 3 + CAN2_L: Pin 4 * + CAN2_H: Pin 5 * + GND: Pin 6 + + ConnectCore CC8X SBC Pro: Expansion connector (J27). + CAN1_L: D 5 + CAN1_H: D 6 + GND: Pin 3 + CAN2_L: C 5 + CAN2_H: C 6 + + +*Note: These pins are not enabled by default, you need to create a custom device tree + +Running the apix-can-recv-example application +----------------------- +Once the binary is in the target, launch the application: + +``` +# ./apix-can-recv-example + Example application using libdigiapix CAN support" + Usage: %s -i -b [options]" + -i Name of the CAN interface" + -b Bitrate to use (Hz)" + -f Comma-separated filter list in the format" + id:mask (id and mask values in hex)" + -p Print message info" + -c Print message counter" + -h Help" + Examples:\n" + %s -i can0 -b 500000 -f 023:fff,006:00f + %s -i can1 -b 100000 + +``` +If no arguments are provided, the example will use the default values: + - Specific application default values are defined in the main file. + + The application will wait for frames. If you provide some filters it will only + wait for the chosen ones. + + Running the apix-can-send-example application +----------------------- +Once the binary is in the target, launch the application: + +``` +# ./apix-can-send-example +Example application using libdigiapix CAN support" + "Usage: %s -i -b [options]" + "-i Name of the CAN interface" + "-b Bitrate to use (Hz)" + "-n Number of messages to send (default 1)" + "-t Inter frame delay in ms (default 100)" + "-I Message id in hex (default 123)" + "-l Payload length (default 8)" + "-r Generate a random ID (will ignore the -I parameter)" + "-p Generate a random payload (will ignore the -l parameter)" + "-e Use extended id" + "-R Set RTR" +``` + +You need to provide at least the can-iface and the bitrate. +For the other values if arguments are not provided, the example will use the default values: + - Specific application default values are defined in the main file. + +The application will send frames periodically with the selected time rate. + +Compiling the application +------------------------- +These demos can be compiled using a Digi Embedded Yocto based toolchain. Make +sure to source the corresponding toolchain of the platform you are using, e.g: + +``` +$> . /environment-setup-cortexa7hf-vfp-neon-dey-linux-gnueabi +$> make +``` + +More information about [Digi Embedded Yocto](https://github.com/digi-embedded/meta-digi). + +License +------- +Copyright 2018, Digi International Inc. + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/apix-can-examples/can-recv-example.c b/apix-can-examples/can-recv-example.c new file mode 100644 index 0000000..68bcd7b --- /dev/null +++ b/apix-can-examples/can-recv-example.c @@ -0,0 +1,300 @@ +/* + * Copyright 2018, Digi International Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_RECEPTION_BUFFER 512*1024 + +static can_if_t *can_if; +static struct can_filter *cfilter; +static struct can_filter *canfilter; +static bool running = true; +static bool prn_msg_info = false; +static bool prn_msg_count = false; + +/* + * usage_and_exit() - Show usage information and exit with 'exitval' return + * value + * + * @name: Application name. + * @exitval: The exit code. + */ +static void usage_and_exit(char *name, int exitval) +{ + printf( + "Example application using libdigiapix CAN support\n" + "\n" + "Usage: %s -i -b [options]\n\n" + "-i Name of the CAN interface\n" + "-b Bitrate to use (Hz)\n" + "-f Comma-separated filter list in the format\n" + " id:mask (id and mask values in hex)\n" + "-p Print message info\n" + "-c Print message counter\n" + "-h Help\n" + "\n" + "Examples:\n" + "%s -i can0 -b 500000 -f 023:fff,006:00f\n" + "%s -i can1 -b 100000\n" + "\n", name, name, name); + + exit(exitval); +} + +/* + * cleanup() - Frees all the allocated memory before exiting + */ +static void cleanup(void) +{ + if (canfilter) + free(canfilter); + + if (can_if) { + ldx_can_stop(can_if); + ldx_can_free(can_if); + running = false; + } +} + +/* + * sigaction_handler() - Handler to execute after receiving a signal + * + * @signum: Received signal. + */ +static void sigaction_handler(int signum) +{ + /* 'atexit' executes the cleanup function */ + exit(EXIT_FAILURE); +} + +/* + * register_signals() - Registers program signals + */ +static void register_signals(void) +{ + struct sigaction action; + + action.sa_handler = sigaction_handler; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + + sigaction(SIGHUP, &action, NULL); + sigaction(SIGINT, &action, NULL); + sigaction(SIGTERM, &action, NULL); +} + +static void can_rx_callback(struct canfd_frame *frame, struct timeval *tv) +{ + static uint32_t nframe = 1; + + if (prn_msg_count) { + printf("CAN frame %u\n", nframe); + } else { + printf("CAN frame\n"); + } + + if (prn_msg_info) { + printf( + " - Time: %ld.%06ld\n" + " - Type: %s\n" + " - ID: %x\n" + " - Data length: %u\n" + " - Data: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n" + "\n", + tv->tv_sec, tv->tv_usec, ldx_can_is_extid_frame(frame) ? + "Extended ID" : "Standard ID", ldx_can_get_id(frame), frame->len, + frame->data[0], frame->data[1], frame->data[2], frame->data[3], + frame->data[4], frame->data[5], frame->data[6], frame->data[7]); + } + + nframe++; +} + +static int strchartimes(const char *str, int c) +{ + int count = 0; + char *character; + + for (character = (char *)str; + character && *character != '\0'; + character++) { + if (*character == c) + count++; + } + + return count; +} + +/* + * parse_filters() - + * + * @str: String that contains all the filters + * @cfilter: Struct that contains the filters + * @nfilters: Number of valid filters found on str. + * + * Return: 0 on success, error code otherwise. + */ +static int parse_filters(char *str, struct can_filter **cfilter, int *nfilters) +{ + int ret; + int posible_filters; + int good_filters = 0; + char *p = str; + + /* First compute the number of filters */ + posible_filters = strchartimes(str, ','); + posible_filters++; + + /* Allocate the memory for all the possible filters */ + canfilter = malloc(sizeof(struct can_filter) * posible_filters); + if (!canfilter) { + printf("Unable to allocate memory for filters\n"); + return -ENOMEM; + } + + /* Parse the information for each filter */ + while (posible_filters-- && p) { + ret = sscanf(p, "%x:%x", + &canfilter[good_filters].can_id, + &canfilter[good_filters].can_mask); + if (ret == 2) + good_filters++; + + p = strchr(p, ','); + if (p) + p++; + } + + *nfilters = good_filters; + *cfilter = canfilter; + + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + char *name = basename(argv[0]); + char *iface; + can_if_cfg_t ifcfg; + int nfilters = 0; + int opt; + int ret; + struct can_filter deffilter; + + deffilter.can_id = 0; + deffilter.can_mask = 0; + + if (argc <= 3) { + usage_and_exit(name, EXIT_FAILURE); + } + + ldx_can_set_defconfig(&ifcfg); + + while ((opt = getopt(argc, argv, "i:b:f:pc")) > 0) { + switch (opt) { + case 'i': + iface = optarg; + break; + + case 'b': + ifcfg.bitrate = strtoul(optarg, NULL, 10); + break; + + case 'f': + ret = parse_filters(optarg, &cfilter, &nfilters); + if (ret) { + printf("Unable to parse filter information\n\n"); + usage_and_exit(name, EXIT_FAILURE); + } + break; + + case 'p': + prn_msg_info = true; + break; + + case 'c': + prn_msg_count = true; + break; + + case 'h': + usage_and_exit(name, EXIT_SUCCESS); + break; + + default: + usage_and_exit(name, EXIT_FAILURE); + } + } + + printf("Requesting CAN interface %s... ", iface); + + can_if = ldx_can_request_by_name(iface); + if (!can_if) { + printf("ERROR\n"); + return EXIT_FAILURE; + } + printf("OK\n"); + + /* Register signals and exit cleanup function */ + atexit(cleanup); + register_signals(); + + /* Increase the buffer reception size */ + ifcfg.rx_buf_len = MAX_RECEPTION_BUFFER; + + printf("Initializing CAN interface... "); + ret = ldx_can_init(can_if, &ifcfg); + if (ret) { + printf("ERROR\n"); + goto error; + } + printf("OK\n"); + + /* + * Configure a callback to process the defined filters, otherwise, + * use the default filter. + */ + if (nfilters == 0) { + nfilters = 1; + cfilter = &deffilter; + } + ret = ldx_can_register_rx_handler(can_if, can_rx_callback, + cfilter, nfilters); + + if (ret < 0) { + printf("Failed to register rx msg handler\n"); + goto error; + } + + while (running) { + sleep(5); + printf("Waiting for CAN frames...\n"); + } + +error: + + printf("\n\nCan receive frame application has finished\n"); + + return ret; +} diff --git a/apix-can-examples/can-send-example.c b/apix-can-examples/can-send-example.c new file mode 100644 index 0000000..383e0db --- /dev/null +++ b/apix-can-examples/can-send-example.c @@ -0,0 +1,299 @@ +/* + * Copyright 2018, Digi International Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TX_RETRIES 10 + +#define RANDOM_ID_BIT 0 +#define EXT_ID_BIT 1 +#define RTR_BIT 2 +#define RANDOM_DLC_BIT 3 + +#define RANDOM_ID_MASK 0x01 +#define EXT_ID_MASK 0x02 +#define RTR_BIT_MASK 0x04 +#define RANDOM_DLC_MASK 0x08 + +#define RANDOM_ID(random_id) (random_id << RANDOM_ID_BIT) +#define EXT_ID(extended_id) (extended_id << EXT_ID_BIT) +#define RTR(rtr) (rtr << RTR_BIT) +#define RANDOM_DLC(dlc) (dlc << RANDOM_DLC_BIT) + +static can_if_t *can_if; +static bool running = true; + +/* + * usage_and_exit() - Show usage information and exit with 'exitval' return + * value + * + * @name: Application name. + * @exitval: The exit code. + */ +static void usage_and_exit(char *name, int exitval) +{ + printf( + "Example application using libdigiapix CAN support\n" + "\n" + "Usage: %s -i -b [options]\n\n" + "-i Name of the CAN interface\n" + "-b Bitrate to use (Hz)\n" + "-n Number of messages to send (default 1)\n" + "-t Inter frame delay in ms (default 100)\n" + "-I Message id in hex (default 123)\n" + "-l Payload length (default 8)\n" + "-r Generate a random ID (will ignore the -I parameter)\n" + "-p Generate a random payload (will ignore the -l parameter)\n" + "-e Use extended id\n" + "-R Set RTR\n" + "\n" + "Examples:\n" + "%s -i can0 -b 500000 -n 100 -R\n" + "%s -i can1 -b 100000\n" + "\n", name, name, name); + + exit(exitval); +} + +/* + * ms_sleep() - Wait ms miliseconds + * + * ms: Number of ms to wait. + */ +void ms_sleep(uint32_t ms) +{ + struct timespec ts; + + if (ms < 1000) { + ts.tv_sec = 0; + ts.tv_nsec = ms * 1000000; + } else { + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + } + + nanosleep(&ts, NULL); +} + +/* + * cleanup() - Frees all the allocated memory before exiting + */ +static void cleanup(void) +{ + if (can_if) { + ldx_can_stop(can_if); + ldx_can_free(can_if); + running = false; + } +} + +/* + * sigaction_handler() - Handler to execute after receiving a signal + * + * @signum: Received signal. + */ +static void sigaction_handler(int signum) +{ + /* 'atexit' executes the cleanup function */ + exit(EXIT_FAILURE); +} + +/* + * register_signals() - Registers program signals + */ +static void register_signals(void) +{ + struct sigaction action; + + action.sa_handler = sigaction_handler; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + + sigaction(SIGHUP, &action, NULL); + sigaction(SIGINT, &action, NULL); + sigaction(SIGTERM, &action, NULL); +} + +void update_msg(struct canfd_frame *frame, uint32_t id, uint8_t dlc, uint8_t flags) +{ + uint8_t index; + + if (flags & RANDOM_ID_MASK) + id = rand() % 2047 + 1; + + if (flags & EXT_ID_MASK) { + frame->can_id = id & CAN_EFF_MASK; + frame->can_id |= CAN_EFF_FLAG; + } else { + frame->can_id = id & CAN_SFF_MASK; + } + + if (flags & RTR_BIT_MASK) + frame->can_id |= CAN_RTR_FLAG; + + if (flags & RANDOM_DLC_MASK) + dlc = rand() % 8 + 1; + + frame->len = dlc; + + /* Currently we just do incremental updates on the payload */ + for (index = 0; index < dlc; index++) { + frame->data[index] = frame->data[index] + 1; + if (frame->data[index]) + break; + } + +} + +int main(int argc, char **argv) +{ + char *name = basename(argv[0]); + char *iface; + can_if_cfg_t ifcfg; + int opt; + int ret; + uint32_t ms_delay = 1; + uint32_t num_msgs = 1; + uint32_t msg_id = 0x123; + uint8_t msg_len = 8; + uint8_t flags = 0; + struct canfd_frame frame; + + srand (time(NULL)); + + if (argc <= 3) { + usage_and_exit(name, EXIT_FAILURE); + } + + ldx_can_set_defconfig(&ifcfg); + + while ((opt = getopt(argc, argv, "i:b:n:t:I:l:erRp")) > 0) { + switch (opt) { + case 'i': + iface = optarg; + break; + + case 'b': + ifcfg.bitrate = strtoul(optarg, NULL, 10); + break; + + case 'n': + num_msgs = strtoul(optarg, NULL, 10); + break; + + case 't': + ms_delay = strtoul(optarg, NULL, 10); + break; + + case 'I': + msg_id = strtoul(optarg, NULL, 16); + break; + + case 'l': + msg_len = strtoul(optarg, NULL, 10); + break; + + case 'e': + flags |= EXT_ID_MASK; + break; + + case 'r': + flags |= RANDOM_ID_MASK; + break; + + case 'R': + flags |= RTR_BIT_MASK; + break; + + case 'p': + flags |= RANDOM_DLC_MASK; + break; + + default: + usage_and_exit(name, EXIT_FAILURE); + } + } + + printf("Requesting CAN interface %s... ", iface); + + can_if = ldx_can_request_by_name(iface); + + if (!can_if) { + printf("ERROR\n"); + return EXIT_FAILURE; + } + printf("OK\n"); + + /* Register signals and exit cleanup function */ + atexit(cleanup); + register_signals(); + + printf("Initializing CAN interface... "); + ret = ldx_can_init(can_if, &ifcfg); + if (ret) { + printf("ERROR\n"); + goto error; + } + printf("OK\n"); + + memset(&frame, 0, sizeof(frame)); + + while (running && num_msgs) { + int retries = TX_RETRIES; + /* If we need to create more configuration bits, we have this variable flags*/ + update_msg(&frame, msg_id, msg_len, flags); + + while (retries--) { + ret = ldx_can_tx_frame(can_if, &frame); + if (!ret) { + break; + } else if (ret == -CAN_ERROR_TX_RETRY_LATER) { + ms_sleep(1); + } else { + printf("Failed to send CAN frame (%d)\n", ret); + goto error; + } + } + + if (!retries) { + printf("Failed to send CAN frame after %d tries\n", ret); + goto error; + } + + num_msgs--; + + if (ms_delay) + ms_sleep(ms_delay); + } + + ms_sleep(1000); + +error: + + printf("\n\nCan send frame application has finished\n"); + + return ret; +}