From 6c65df5c68880ec33885580a189d075b70d947b0 Mon Sep 17 00:00:00 2001 From: David Escalona Date: Fri, 15 Sep 2017 10:11:49 +0200 Subject: [PATCH] dey-examples: add libdigiapix SPI example https://jira.digi.com/browse/DEL-4865 Signed-off-by: David Escalona --- apix-spi-example/.gitignore | 6 + apix-spi-example/Makefile | 37 ++++ apix-spi-example/README.md | 120 +++++++++++ apix-spi-example/main.c | 415 ++++++++++++++++++++++++++++++++++++ 4 files changed, 578 insertions(+) create mode 100644 apix-spi-example/.gitignore create mode 100644 apix-spi-example/Makefile create mode 100644 apix-spi-example/README.md create mode 100644 apix-spi-example/main.c diff --git a/apix-spi-example/.gitignore b/apix-spi-example/.gitignore new file mode 100644 index 0000000..74a3271 --- /dev/null +++ b/apix-spi-example/.gitignore @@ -0,0 +1,6 @@ +apix-spi-example +*.o +.cproject +.project +.settings/ +*~ diff --git a/apix-spi-example/Makefile b/apix-spi-example/Makefile new file mode 100644 index 0000000..7fc790f --- /dev/null +++ b/apix-spi-example/Makefile @@ -0,0 +1,37 @@ +# +# 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. +# + +BINARY := apix-spi-example + +CFLAGS += -Wall -O2 + +CFLAGS += $(shell pkg-config --cflags libdigiapix) +LDLIBS += $(shell pkg-config --libs libdigiapix) + +.PHONY: all +all: $(BINARY) + +$(BINARY): main.o + $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ + +.PHONY: install +install: $(BINARY) + install -d $(DESTDIR)/usr/bin + install -m 0755 $^ $(DESTDIR)/usr/bin/ + +.PHONY: clean +clean: + -rm -f *.o $(BINARY) diff --git a/apix-spi-example/README.md b/apix-spi-example/README.md new file mode 100644 index 0000000..8941e67 --- /dev/null +++ b/apix-spi-example/README.md @@ -0,0 +1,120 @@ +Digi APIX SPI Example Application +================================== +The example application writes a page of an external EEPROM memory with random +data and then reads the data back to validate it. + +Most of EEPROM memories are compatible with the sample specifying the page size and +the number of address bytes. + +The SPI connections for the sample depends on the running platform: + - **CCIMX6 SBC**: SPI connector of the board. + - VCC: Pin 1 + - GND: Pin 8 + - SPI-1 CLK: Pin 2 + - SPI-1 MISO: Pin 3 + - SPI-1 MOSI: Pin 4 + - SPI-1 SS0: Pin 5 + + - **CCIMX6UL SBC Express**: Expansion connector of the board. + - VCC: Pin 17 + - GND: Pin 20 + - SPI-3 CLK: Pin 23 + - SPI-3 MISO: Pin 21 + - SPI-3 MOSI: Pin 19 + - SPI-3 SS0: Pin 24 + + - **CCIMX6UL SBC Pro**: SPI connector of the board. + - VCC: Pin 1 + - GND: Pin 8 + - SPI-1 CLK: Pin 2 + - SPI-1 MISO: Pin 3 + - SPI-1 MOSI: Pin 4 + - SPI-1 SS0: Pin 5 + +The following device tree modifications are required for the sample to work: + - **CCIMX6 SBC**: +``` +&ecspi1 { + status = "okay"; + + spidev@0 { + reg = <0>; + compatible = "spidev"; + spi-max-frequency = <1000000>; + status = "okay"; + }; +}; +``` + - **CCIMX6UL SBC Express**: +``` +&ecspi3 { + status = "okay"; + + spidev@0 { + reg = <0>; + compatible = "spidev"; + spi-max-frequency = <1000000>; + status = "okay"; + }; +}; +``` + - **CCIMX6UL SBC Pro**: +``` +&ecspi1 { + status = "okay"; + + spidev@0 { + reg = <0>; + compatible = "spidev"; + spi-max-frequency = <1000000>; + status = "okay"; + }; +}; +``` + +Running the application +----------------------- +Once the binary is in the target, launch the application: + +``` +root@ccimx6ulstarter:~# ./apix-spi-example +Example application using libdigiapix SPI support + +Usage: apix-spi-example + + SPI device index to use + SPI slave index to use + Number of EEPROM memory address bytes + EEPROM memory page size in bytes + EEPROM memory page index to use + +Aliases for SPI can be configured in the library config file +``` + +Compiling the application +------------------------- +This example 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-neon-dey-linux-gnueabi +$ make +``` + +More information about [Digi Embedded Yocto](https://github.com/digi-embedded/meta-digi). + +License +------- +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. diff --git a/apix-spi-example/main.c b/apix-spi-example/main.c new file mode 100644 index 0000000..d256acd --- /dev/null +++ b/apix-spi-example/main.c @@ -0,0 +1,415 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ARG_SPI_DEVICE 0 +#define ARG_SPI_SLAVE 1 + +#define BUFF_SIZE 256 + +#define WREN 0x06 +#define WRDI 0x04 +#define WRITE 0x02 +#define READ 0x03 +#define RDSR 0x05 + +#define CLK_MODE SPI_CLK_MODE_0 +#define CHIP_SELECT SPI_CS_ACTIVE_LOW +#define BIT_ORDER SPI_BO_MSB_FIRST +#define MAX_BUS_SPEED 1000000 /* 1MHz */ +#define BITS_PER_WORD SPI_BPW_8 + +#define OPERATION_BYTES 1 + +static spi_t *spi_dev; +static unsigned int page_size, address_bytes = 0; +static uint8_t *tx_buffer; +static uint8_t *rx_buffer; + +/* + * usage_and_exit() - Show usage information and exit with 'exitval' return + * value + */ +static void usage_and_exit(char *name, int exitval) +{ + fprintf(stdout, + "Example application using libdigiapix SPI support\n" + "\n" + "Usage: %s \n\n" + " SPI device index to use\n" + " SPI slave index to use\n" + " Number of EEPROM memory address bytes\n" + " EEPROM memory page size in bytes\n" + " EEPROM memory page index to use\n" + "\n" + "Aliases for SPI can be configured in the library config file\n" + "\n", name); + + exit(exitval); +} + +/* + * cleanup() - Frees all the allocated memory before exiting + */ +static void cleanup(void) +{ + /* Free spi */ + spi_free(spi_dev); + + /* Free buffers */ + free(tx_buffer); + free(rx_buffer); +} + +/* + * sigaction_handler() - Handler to execute after receiving a 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); +} + +/* + * enable_write() - Sets the SPI write enable bit. + * + * Return: EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +static int enable_write(void) +{ + uint8_t write_data[1] = {0}; + + printf("[INFO] Setting write enable bit...\n"); + write_data[0] = WREN; + + return spi_write(spi_dev, write_data, sizeof(write_data)); +} + +/* + * read_status_register() - Reads the SPI status register. + * + * @status: Variable to store the read status. + * + * Return: EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +static int read_status_register(uint8_t *status) +{ + uint8_t write_data[2] = {0}; + uint8_t read_data[2] = {0}; + + printf("[INFO] Reading status register...\n"); + write_data[0] = RDSR; + if (spi_transfer(spi_dev, write_data, read_data, 2) != EXIT_SUCCESS) { + return EXIT_FAILURE; + } + + printf("[INFO] SPI Status Register is 0x%02x\n", read_data[1]); + *status = read_data[1]; + + return EXIT_SUCCESS; +} + +/* + * write_page() - Writes an EEPROM page with the given data. + * + * @page_index: index of the EEPROM page to write. + * @data: the data to write. + * + * Return: EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +static int write_page(int page_index, uint8_t* data) +{ + uint8_t *write_data; + uint16_t page_address = page_size * page_index; + uint8_t status = 0; + int i, ret = 0; + + /* Create the page write buffer */ + write_data = (uint8_t *)calloc(page_size + OPERATION_BYTES + address_bytes, + sizeof(uint8_t)); + if (write_data == NULL) { + printf("Unable to allocate memory to write the page."); + return EXIT_FAILURE; + } + + printf("[INFO] Writing %d bytes to page %d at address 0x%x...\n", page_size, + page_index, page_address); + write_data[0] = WRITE; // Operation. + for (i = 0; i < address_bytes; i++) { + write_data[i + OPERATION_BYTES] = (page_address >> (8 * (address_bytes - + i -1))); + } + + /* Fill the data array. */ + for (i = 0; i < page_size; i++) { + write_data[(i + OPERATION_BYTES + address_bytes)] = data[i]; + } + + /* Perform the write operation. */ + if (spi_write(spi_dev, write_data, page_size + OPERATION_BYTES + + address_bytes) != EXIT_SUCCESS) { + free(write_data); + return EXIT_FAILURE; + } + + /* Wait for the operation to complete. */ + do { + /* Read the status register to check the WIP (write-in-progress) bit. */ + ret = read_status_register(&status); + if (ret != EXIT_SUCCESS) { + free(write_data); + return EXIT_FAILURE; + } + /* Check the WIP (write-in-progress) status bit. */ + if (status & 0x01) { + printf("[INFO] Write in progress...\n"); + usleep(1); + } else { + printf("[INFO] Write finished!\n"); + } + } while (status & 0x1); + + free(write_data); + + return EXIT_SUCCESS; +} + +/* + * read_page() - Reads an EEPROM page. + * + * @page_index: index of the EEPROM page to read. + * @data: buffer to store the read data in. + * + * Return: EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +static int read_page(int page_index, uint8_t* data) +{ + uint8_t *write_data; + uint8_t *read_data; + uint16_t page_address; + int i = 0; + + page_address = page_size * page_index; + + /* Create the buffers */ + write_data = (uint8_t *)calloc(page_size + OPERATION_BYTES + address_bytes, + sizeof(uint8_t)); + read_data = (uint8_t *)calloc(page_size + OPERATION_BYTES + address_bytes, + sizeof(uint8_t)); + if (write_data == NULL || read_data == NULL) { + printf("Unable to allocate memory to read the page."); + return EXIT_FAILURE; + } + + printf("[INFO] Reading page %d at address 0x%x...\n", page_index, + page_address); + write_data[0] = READ; // Operation. + for (i = 0; i < address_bytes; i++) { + write_data[i + OPERATION_BYTES] = (page_address >> (8 * (address_bytes - + i -1))); + } + + /* Perform the read operation with a transfer */ + if (spi_transfer(spi_dev, write_data, read_data, page_size + + OPERATION_BYTES + address_bytes) != EXIT_SUCCESS) { + free(write_data); + free(read_data); + return EXIT_FAILURE; + } + for (i = 0; i < page_size; i++) { + data[i] = read_data[(i + OPERATION_BYTES + address_bytes)]; + } + + free(write_data); + free(read_data); + + return EXIT_SUCCESS; +} + +/* + * parse_argument() - Parses the given string argument and returns the + * corresponding integer value + * + * @argv: Argument to parse in string format. + * @arg_type: Type of the argument to parse. + * + * Return: The parsed integer argument, -1 on error. + */ +static int parse_argument(char *argv, int arg_type) +{ + char *endptr; + long value; + + errno = 0; + value = strtol(argv, &endptr, 10); + + if ((errno == ERANGE && (value == LONG_MAX || value == LONG_MIN)) + || (errno != 0 && value == 0)) + return -1; + + if (endptr == argv) { + switch (arg_type) { + case ARG_SPI_DEVICE: + return spi_get_device(endptr); + case ARG_SPI_SLAVE: + return spi_get_slave(endptr); + default: + return -1; + } + } + + return value; +} + +int main(int argc, char *argv[]) +{ + int spi_device, spi_slave, page_index, i = 0; + spi_transfer_cfg_t transfer_mode = {0}; + char *name = basename(argv[0]); + + /* Check input parameters */ + if (argc != 6) + usage_and_exit(name, EXIT_FAILURE); + + /* Parse command line arguments */ + spi_device = parse_argument(argv[1], ARG_SPI_DEVICE); + spi_slave = parse_argument(argv[2], ARG_SPI_SLAVE); + address_bytes = atoi(argv[3]); + page_size = atoi(argv[4]); + page_index = atoi(argv[5]); + if (spi_device < 0 || spi_slave < 0) { + printf("Unable to parse SPI device/slave arguments\n"); + return EXIT_FAILURE; + } + if (address_bytes <= 0) { + printf("Address bytes must be greater than 0\n"); + return EXIT_FAILURE; + } + if (page_size <= 0) { + printf("Page size must be greater than 0\n"); + return EXIT_FAILURE; + } + if (page_index < 0) { + printf("Page index must be greater or equal than 0\n"); + return EXIT_FAILURE; + } + + /* Register signals and exit cleanup function */ + atexit(cleanup); + register_signals(); + + /* Request SPI */ + spi_dev = spi_request((unsigned)spi_device, (unsigned)spi_slave); + if (!spi_dev) { + printf("Failed to initialize SPI\n"); + return EXIT_FAILURE; + } + + /* Configure the transfer mode */ + transfer_mode.clk_mode = CLK_MODE; + transfer_mode.chip_select = CHIP_SELECT; + transfer_mode.bit_order = BIT_ORDER; + if (spi_set_transfer_mode(spi_dev, &transfer_mode) != EXIT_SUCCESS) { + printf("Failed to configure SPI transfer mode\n"); + return EXIT_FAILURE; + } + + /* Configure the bits-per-word */ + if (spi_set_bits_per_word(spi_dev, BITS_PER_WORD) != EXIT_SUCCESS) { + printf("Failed to configure SPI bits-per-word\n"); + return EXIT_FAILURE; + } + + /* Configure the max bus speed */ + if (spi_set_speed(spi_dev, MAX_BUS_SPEED) != EXIT_SUCCESS) { + printf("Failed to configure SPI max bus speed\n"); + return EXIT_FAILURE; + } + + /* Set the write-enable bit. */ + if (enable_write() != EXIT_SUCCESS) { + printf("Failed to set the write-enable bit\n"); + return EXIT_FAILURE; + } + + /* Initialize the write and read buffers */ + tx_buffer = (uint8_t *)calloc(page_size, sizeof(uint8_t)); + rx_buffer = (uint8_t *)calloc(page_size, sizeof(uint8_t)); + if (tx_buffer == NULL || rx_buffer == NULL) { + printf("Failed to initialize read/write buffers\n"); + return EXIT_FAILURE; + } + + /* Fill the data to write with random bytes. */ + srand(time(NULL)); + for (i = 0; i < (page_size); i++) { + tx_buffer[i] = rand() % 255; + } + + /* Write the page. */ + if (write_page(page_index, tx_buffer) != EXIT_SUCCESS) { + printf("Write page failed\n"); + return EXIT_FAILURE; + } + + /* Read the page. */ + if (read_page(page_index, rx_buffer) != EXIT_SUCCESS) { + printf("Read page failed\n"); + return EXIT_FAILURE; + } + + /* Validate the read data. */ + printf("[INFO] Validating read data...\n"); + for (i = 0; i < page_size; i++) { + printf(" Byte %d: Write 0x%02x - Read 0x%02x", i, + tx_buffer[i], rx_buffer[i]); + if (tx_buffer[i] == rx_buffer[i]) { + printf(" - Correct\n"); + } else { + printf(" - Incorrect\n"); + } + } + + /* 'atexit' executes the cleanup function */ + return EXIT_SUCCESS; +}