dey-examples/apix-spi-example/main.c

435 lines
11 KiB
C

/*
* 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 <errno.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <libdigiapix/spi.h>
#define DEFAULT_SPI_ALIAS "DEFAULT_SPI"
#define DEFAULT_SPI_ADDRESS_SIZE 1
#define DEFAULT_SPI_PAGE_SIZE 16
#define DEFAULT_SPI_PAGE_INDEX 0
#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
*
* @name: Application name.
* @exitval: The exit code.
*/
static void usage_and_exit(char *name, int exitval)
{
fprintf(stdout,
"Example application using libdigiapix SPI support\n"
"\n"
"Usage: %s <spi-dev> <spi-ss> <address-size> <page-size> <page-index>\n\n"
"<spi-dev> SPI device index to use or alias\n"
"<spi-ss> SPI slave index to use or alias\n"
"<address-size> Number of EEPROM memory address bytes\n"
"<page-size> EEPROM memory page size in bytes\n"
"<page-index> 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 */
ldx_spi_free(spi_dev);
/* Free buffers */
free(tx_buffer);
free(rx_buffer);
}
/*
* 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);
}
/*
* 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 ldx_spi_get_device(endptr);
case ARG_SPI_SLAVE:
return ldx_spi_get_slave(endptr);
default:
return -1;
}
}
return value;
}
/*
* 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 ldx_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 (ldx_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 (ldx_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 (ldx_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;
}
int main(int argc, char *argv[])
{
int spi_device = 0, spi_slave = 0, page_index = 0, i = 0;
spi_transfer_cfg_t transfer_mode = {0};
char *name = basename(argv[0]);
/* Check input parameters */
if (argc == 1) {
/* Use default values */
spi_device = ldx_spi_get_device(DEFAULT_SPI_ALIAS);
spi_slave = ldx_spi_get_slave(DEFAULT_SPI_ALIAS);
address_bytes = DEFAULT_SPI_ADDRESS_SIZE;
page_size = DEFAULT_SPI_PAGE_SIZE;
page_index = DEFAULT_SPI_PAGE_INDEX;
} else if (argc == 6) {
/* 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]);
} else {
usage_and_exit(name, EXIT_FAILURE);
}
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 = ldx_spi_request((unsigned int)spi_device, (unsigned int)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 (ldx_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 (ldx_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 (ldx_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;
}