一,SPI的定义
SPI是串行外设接口(Serial Peripheral Interface)的缩写。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,具有简单易用的特性,如今越来越多的芯片集成了这种通信协议。
二、SPI的通信原理
9260中的电路图中可以看到关于spi的两对八个引脚,分别是SPI0、SPI1。
1、9260的datasheet对SPI的描述
The SPI system consists
of two data lines and two control lines:
Master Out Slave In (MOSI): This data line supplies the output data from the master shifted into the input(s) of the slave(s).
Master In Slave Out (MISO): This data line supplies the output data from a slave to the input of the master. There may be no more than one slave transmitting
data during any particular transfer.
Serial Clock (SPCK): This control line is driven by the master and regulates the flow of the data bits. The
master may transmit data at a variety of baud rates; the SPCK line cycles once for each bit that is transmitted.
Slave Select (NSS): This control line allows slaves to be turned on and off by hardware.
spi支持外部通讯设备:包括ADC DAC LCD CAN 和一部分传感器等,提供主设备或者从设备的外部总线接口,支持快速发送。
2、管脚功能描述:
MOSI 主机发送从机接收线
MISO 主机接收从机发送线
SPCK 主机发出的同步时钟线
NSS 片选
3、功能描述
SPI 的工作模式分为主模式和从模式,由模式控制寄存器的MSTR位来控制,1-主模式,0-从模式
用于数据传输方式有两根线控制CS和CK,分别具有两种极性,就构成了四种组合。通过芯片选择寄存器中的CPOL来控制时钟极性
NCPHA控制时钟周期。
四、spi驱动程序
在设备注册函数中注册设备
static struct spi_board_info stamp9g20_spi_devices[] = {
{
.modalias = "spidev",
.chip_select = 0,
.max_speed_hz = 1 * 1000 * 1000,
.bus_num = 1,
.mode = SPI_MODE_1,
},
{
.modalias = "spidev",
.chip_select = 2,
.max_speed_hz = 1 * 1000 * 1000,
.bus_num = 1,
.mode = SPI_MODE_1,
},
};
at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
在devices.c中找到注册的相关函数:
static struct resource spi0_resources[] = {
[0] = {
.start
= AT91SAM9260_BASE_SPI0,
.end = AT91SAM9260_BASE_SPI0 + SZ_16K - 1,
.flags
= IORESOURCE_MEM,
},
[1] = {
.start
= AT91SAM9260_ID_SPI0,
.end = AT91SAM9260_ID_SPI0,
.flags
= IORESOURCE_IRQ,
},
};
static struct platform_device at91sam9260_spi0_device = {
.name = "atmel_spi",
.id = 0,
.dev = {
.dma_mask
= &spi_dmamask,
.coherent_dma_mask= DMA_BIT_MASK(32),
},
.resource
= spi0_resources,
.num_resources= ARRAY_SIZE(spi0_resources),
};
static const unsigned spi0_standard_cs[4] = { AT91_PIN_PA3, AT91_PIN_PC11, AT91_PIN_PC16, AT91_PIN_PC17 };
static struct resource spi1_resources[] = {
[0] = {
.start
= AT91SAM9260_BASE_SPI1,
.end = AT91SAM9260_BASE_SPI1 + SZ_16K - 1,
.flags
= IORESOURCE_MEM,
},
[1] = {
.start
= AT91SAM9260_ID_SPI1,
.end = AT91SAM9260_ID_SPI1,
.flags
= IORESOURCE_IRQ,
},
};
static struct platform_device at91sam9260_spi1_device = {
.name = "atmel_spi",
.id = 1,
.dev = {
.dma_mask
= &spi_dmamask,
.coherent_dma_mask= DMA_BIT_MASK(32),
},
.resource
= spi1_resources,
.num_resources= ARRAY_SIZE(spi1_resources),
};
static const unsigned spi1_standard_cs[4] = { AT91_PIN_PB3, AT91_PIN_PC5, AT91_PIN_PC4, AT91_PIN_PC3 };
void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices)
{
int i;
unsigned long cs_pin;
short enable_spi0 = 0;
short enable_spi1 = 0;
/* Choose SPI chip-selects */
for (i = 0; i < nr_devices; i++) {
if (devices[i].controller_data)
cs_pin = (unsigned long) devices[i].controller_data;
else if (devices[i].bus_num == 0)
cs_pin = spi0_standard_cs[devices[i].chip_select];
else
cs_pin = spi1_standard_cs[devices[i].chip_select];
if (devices[i].bus_num == 0)
enable_spi0 = 1;
else
enable_spi1 = 1;
/* enable chip-select pin */
at91_set_gpio_output(cs_pin, 1);
/* pass chip-select pin to driver */
devices[i].controller_data = (void *) cs_pin;
}
spi_register_board_info(devices, nr_devices);
/* Configure SPI bus(es) */
if (enable_spi0) {
at91_set_A_periph(AT91_PIN_PA0, 0);/* SPI0_MISO */
at91_set_A_periph(AT91_PIN_PA1, 0);/* SPI0_MOSI */
at91_set_A_periph(AT91_PIN_PA2, 0);/* SPI1_SPCK */
at91_clock_associate("spi0_clk", &at91sam9260_spi0_device.dev, "spi_clk");
platform_device_register(&at91sam9260_spi0_device);
}
if (enable_spi1) {
at91_set_A_periph(AT91_PIN_PB0, 0);/* SPI1_MISO */
at91_set_A_periph(AT91_PIN_PB1, 0);/* SPI1_MOSI */
at91_set_A_periph(AT91_PIN_PB2, 0);/* SPI1_SPCK */
at91_clock_associate("spi1_clk", &at91sam9260_spi1_device.dev, "spi_clk");
platform_device_register(&at91sam9260_spi1_device);
}
}
#else
void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) {}
#endif
其中比较重要的SPI信息的寄存器:
原型为:
struct spi_board_info {
/* the device name and module name are coupled, like platform_bus;
* "modalias" is normally the driver name.
*
* platform_data goes to spi_device.dev.platform_data,
* controller_data goes to spi_device.controller_data,
* irq is copied too
*/
char modalias[32];
const void
*platform_data;
void *controller_data;
int irq;
/* slower signaling on noisy or low voltage boards */
u32 max_speed_hz;
/* bus_num is board specific and matches the bus_num of some
* spi_master that will probably be registered later.
*
* chip_select reflects how this chip is wired to that master;
* it's less than num_chipselect.
*/
u16 bus_num;
u16 chip_select;
/* mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 is wrong.
*/
u8 mode;
/* ... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; but include stuff
* needed to behave without being bound to a driver:
* - quirks like clock rate mattering when not selected
*设备的芯片选择可能需要重新配置
*/
};
内核配置支持SPI
然后内核启动后在内核打印信息和文件系统中的sys/bus/platform/devices/中
在dev/文件中也找到了SPI 的句柄;
五、SPI应用层的编程:
/*
* SPI testing utility (using spidev driver)
*
* Copyright (c) 2007 MontaVista Software, Inc.
* Copyright (c) 2007 Anton Vorontsov
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* Cross-compile with cross-gcc -I/path/to/cross-kernel/include
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static const char *device = "/dev/spidev1.0";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;
static void transfer(int fd)
{
int ret;
//发送数据区域
uint8_t tx[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
};
//接收数据区域
uint8_t rx[ARRAY_SIZE(tx)] = {0, };
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");
for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
if (!(ret % 6))
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
}
static void print_usage(const char *prog)
{
printf("Usage: %s [-DsbdlHOLC3]\n", prog);
puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -b --bpw bits per word \n"
" -l --loop loopback\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\n"
" -3 --3wire SI/SO signals shared\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1) {
static const struct option lopts[] = {
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "lsb", 0, 0, 'L' },
{ "cs-high", 0, 0, 'C' },
{ "3wire", 0, 0, '3' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ NULL, 0, 0, 0 },
};
int c;
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
if (c == -1)
break;
switch (c) {
case 'D':
device = optarg;
break;
case 's':
speed = atoi(optarg);
break;
case 'd':
delay = atoi(optarg);
break;
case 'b':
bits = atoi(optarg);
break;
case 'l':
mode |= SPI_LOOP;
break;
case 'H':
mode |= SPI_CPHA;
break;
case 'O':
mode |= SPI_CPOL;
break;
case 'L':
mode |= SPI_LSB_FIRST;
break;
case 'C':
mode |= SPI_CS_HIGH;
break;
case '3':
mode |= SPI_3WIRE;
break;
case 'N':
mode |= SPI_NO_CS;
break;
case 'R':
mode |= SPI_READY;
break;
default:
print_usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
parse_opts(argc, argv);
fd = open(device, O_RDWR);
if (fd < 0)
pabort("can't open device");
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
pabort("can't get spi mode");
/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't get bits per word");
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't get max speed hz");
printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
transfer(fd);
close(fd);
return ret;
}