225 lines
6.4 KiB
C
Executable File
225 lines
6.4 KiB
C
Executable File
/*
|
||
* wiringPiSPI.c:
|
||
* Simplified SPI access routines
|
||
* Copyright (c) 2012–2019 Gordon Henderson; 2019–2026 Contributors
|
||
***********************************************************************
|
||
* This file is part of wiringPi:
|
||
* https://github.com/WiringPi/WiringPi/
|
||
*
|
||
* wiringPi is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU Lesser General Public License as
|
||
* published by the Free Software Foundation, either version 3 of the
|
||
* License, or (at your option) any later version.
|
||
*
|
||
* wiringPi is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General Public
|
||
* License along with wiringPi.
|
||
* If not, see <http://www.gnu.org/licenses/>.
|
||
***********************************************************************
|
||
*/
|
||
|
||
|
||
#include <stdio.h>
|
||
#include <unistd.h>
|
||
#include <stdint.h>
|
||
#include <stdlib.h>
|
||
#include <fcntl.h>
|
||
#include <errno.h>
|
||
#include <string.h>
|
||
#include <sys/ioctl.h>
|
||
#include <asm/ioctl.h>
|
||
#include <linux/spi/spidev.h>
|
||
#include "wiringPi.h"
|
||
#include "wiringPiSPI.h"
|
||
|
||
|
||
// The SPI bus parameters
|
||
// Variables as they need to be passed as pointers later on
|
||
|
||
//static const char *spiDev0 = "/dev/spidev0.0" ;
|
||
//static const char *spiDev1 = "/dev/spidev0.1" ;
|
||
static const uint8_t spiBPW = 8 ;
|
||
static const uint16_t spiDelay = 0 ;
|
||
//https://datasheets.raspberrypi.com/cm4/cm4-datasheet.pdf SPI0-6, CS0-2
|
||
//cm5-datasheet.pdf SPI0-5, CS depend on SPInumber, can be up to 4
|
||
//spi0-4cs.dts extention for supporting CS0-4
|
||
const uint8_t WPI_MaxSPINumbers = 7 ;
|
||
const uint8_t WPI_MaxSPIChannels = 5 ;
|
||
|
||
|
||
static uint32_t spiSpeeds [7][3] =
|
||
{
|
||
{0, 0, 0},
|
||
{0, 0, 0},
|
||
{0, 0, 0},
|
||
{0, 0, 0},
|
||
{0, 0, 0},
|
||
{0, 0, 0},
|
||
{0, 0, 0},
|
||
};
|
||
|
||
static int spiFds [7][3] =
|
||
{
|
||
{-1, -1, -1},
|
||
{-1, -1, -1},
|
||
{-1, -1, -1},
|
||
{-1, -1, -1},
|
||
{-1, -1, -1},
|
||
{-1, -1, -1},
|
||
{-1, -1, -1},
|
||
};
|
||
|
||
|
||
int SPICheckLimits(const int number, const int channel) {
|
||
if (channel<0 || channel>=WPI_MaxSPIChannels) {
|
||
fprintf (stderr, "wiringPiSPI: Invalid SPI channel (%d, valid range 0-%d)", channel, WPI_MaxSPIChannels-1);
|
||
return -EINVAL;
|
||
}
|
||
if (number<0 || number>=WPI_MaxSPINumbers) {
|
||
fprintf (stderr, "wiringPiSPI: Invalid SPI number (%d, valid range 0-%d)", number, WPI_MaxSPINumbers-1);
|
||
return -EINVAL;
|
||
}
|
||
|
||
return 0; //sucess
|
||
}
|
||
|
||
|
||
#define RETURN_ON_LIMIT_FAIL int ret = SPICheckLimits(number, channel); if(ret!=0) { return ret; };
|
||
|
||
/*
|
||
* wiringPiSPIGetFd:
|
||
* Return the file-descriptor for the given channel
|
||
*********************************************************************************
|
||
*/
|
||
|
||
int wiringPiSPIxGetFd(const int number, int channel)
|
||
{
|
||
if (SPICheckLimits(number, channel)!=0) {
|
||
return -1;
|
||
}
|
||
return spiFds[number][channel];
|
||
}
|
||
|
||
int wiringPiSPIGetFd(int channel) {
|
||
return wiringPiSPIxGetFd(0, channel);
|
||
}
|
||
|
||
|
||
/*
|
||
* wiringPiSPIDataRW:
|
||
* Write and Read a block of data over the SPI bus.
|
||
* Note the data ia being read into the transmit buffer, so will
|
||
* overwrite it!
|
||
* This is also a full-duplex operation.
|
||
*********************************************************************************
|
||
*/
|
||
|
||
int wiringPiSPIxDataRW (const int number, const int channel, unsigned char *data, const int len)
|
||
{
|
||
|
||
RETURN_ON_LIMIT_FAIL
|
||
if (-1==spiFds[number][channel]) {
|
||
fprintf (stderr, "wiringPiSPI: Invalid SPI number/channel (need wiringPiSPIxSetupMode before read/write)");
|
||
return -EBADF;
|
||
}
|
||
|
||
struct spi_ioc_transfer spi ;
|
||
// Mentioned in spidev.h but not used in the original kernel documentation
|
||
// test program )-:
|
||
memset (&spi, 0, sizeof (spi)) ;
|
||
|
||
spi.tx_buf = (unsigned long)data ;
|
||
spi.rx_buf = (unsigned long)data ;
|
||
spi.len = len ;
|
||
spi.delay_usecs = spiDelay ;
|
||
spi.speed_hz = spiSpeeds [number][channel] ;
|
||
spi.bits_per_word = spiBPW ;
|
||
|
||
return ioctl (spiFds[number][channel], SPI_IOC_MESSAGE(1), &spi) ;
|
||
}
|
||
|
||
int wiringPiSPIDataRW (int channel, unsigned char *data, int len) {
|
||
return wiringPiSPIxDataRW(0, channel, data, len);
|
||
}
|
||
|
||
/*
|
||
* wiringPiSPISetupMode:
|
||
* Open the SPI device, and set it up, with the mode, etc.
|
||
*********************************************************************************
|
||
*/
|
||
|
||
|
||
int wiringPiSPIxSetupMode(const int number, const int channel, const int speed, const int mode)
|
||
{
|
||
int fd ;
|
||
char spiDev [32] ;
|
||
|
||
RETURN_ON_LIMIT_FAIL
|
||
if (mode<0 || mode>3) { // Mode is 0, 1, 2 or 3 original
|
||
fprintf (stderr, "wiringPiSPI: Invalid mode (%d, valid range 0-%d)", mode, 3);
|
||
return -EINVAL;
|
||
}
|
||
|
||
snprintf (spiDev, 31, "/dev/spidev%d.%d", number, channel) ;
|
||
if ((fd = open (spiDev, O_RDWR)) < 0) {
|
||
return wiringPiFailure (WPI_ALMOST, "Unable to open SPI device %s: %s\n", spiDev, strerror (errno)) ;
|
||
}
|
||
spiSpeeds [number][channel] = speed ;
|
||
spiFds [number][channel] = fd ;
|
||
|
||
// Set SPI parameters.
|
||
|
||
if (ioctl (fd, SPI_IOC_WR_MODE, &mode) < 0)
|
||
return wiringPiFailure (WPI_ALMOST, "SPI mode change failure: %s\n", strerror (errno)) ;
|
||
|
||
if (ioctl (fd, SPI_IOC_WR_BITS_PER_WORD, &spiBPW) < 0)
|
||
return wiringPiFailure (WPI_ALMOST, "SPI BPW change failure: %s\n", strerror (errno)) ;
|
||
|
||
if (ioctl (fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0)
|
||
return wiringPiFailure (WPI_ALMOST, "SPI speed change failure: %s\n", strerror (errno)) ;
|
||
|
||
return fd ;
|
||
}
|
||
|
||
|
||
int wiringPiSPISetupMode (int channel, int speed, int mode) {
|
||
return wiringPiSPIxSetupMode (0, channel, speed, mode);
|
||
}
|
||
|
||
|
||
/*
|
||
* wiringPiSPISetup:
|
||
* Open the SPI device, and set it up, etc. in the default MODE 0
|
||
*********************************************************************************
|
||
*/
|
||
|
||
int wiringPiSPIxSetup (const int number, const int channel, const int speed) {
|
||
return wiringPiSPIxSetupMode(number, channel, speed, 0) ;
|
||
}
|
||
|
||
|
||
int wiringPiSPISetup (int channel, int speed) {
|
||
return wiringPiSPIxSetupMode(0, channel, speed, 0) ;
|
||
}
|
||
|
||
|
||
int wiringPiSPIxClose (const int number, const int channel) {
|
||
|
||
RETURN_ON_LIMIT_FAIL
|
||
if (spiFds[number][channel]>0) {
|
||
ret = close(spiFds[number][channel]);
|
||
}
|
||
spiSpeeds [number][channel] = 0 ;
|
||
spiFds [number][channel] = -1 ;
|
||
return ret;
|
||
}
|
||
|
||
int wiringPiSPIClose (const int channel) {
|
||
return wiringPiSPIxClose (0, channel);
|
||
}
|
||
|