SPI使用参考
1. 概述¶
本文介绍如何在linux用户层使用SPI驱动程序。
2. SPI控制¶
2.1. SPI Pads¶
SPI pad modes 可设定的mode 和引脚对应如下表
Spi Group | Mode | SPI0_CZ | SPI0_CK | SPI0_DI(MOSI) | SPI0_DO(MISO) | DEV |
---|---|---|---|---|---|---|
SPI 0 | 4 | PAD_KEY10 | PAD_KEY11 | PAD_KEY12 | PAD_KEY13 | /dev/spidev0.0 |
SPI 1 | 5 | PAD_KEY0 | PAD_KEY1 | PAD_KEY2 | PAD_KEY3 | /dev/spidev1.0 |
6 | PAD_KEY10 | PAD_KEY11 | PAD_KEY12 | PAD_KEY13 | ||
8 | PAD_GPIO5 | PAD_GPIO6 | PAD_GPIO7 | PAD_GPIO8 | ||
PSPI1 | 6 | PAD_KEY0 | PAD_KEY1 | PAD_KEY2 & PAD_KEY3 | 无 | /dev/mi_pspi |
7 | PAD_KEY10 | PAD_KEY11 | PAD_KEY12 & PAD_KEY13 | 无 | ||
9 | PAD_GPIO5 | PAD_GPIO6 | PAD_GPIO7 & PAD_GPIO8 | 无 |
2.2. SPI0的 dts 配置,以 mode4为例¶
-
如下红色方框 use-dma表示是否使用DMA,修改 pioneer3.dtsi:
-
添加 SPI0 padmux 复用
修改 pioneer3-ssc020a-s01a-demo-padmux.dtsi
SPI0 选择 Mode 4(PAD_KEY10/PAD_KEY11/PAD_KEY12/PAD_KEY13)进行输出,没有在其他组输出的话,需要将其他组注释掉。
2.3. SPI1的 dts 配置,以 mode11为例¶
-
如下红色方框 use-dma表示是否使用DMA,修改 pioneer3.dtsi:
-
添加 SPI1 padmux 复用
修改 pioneer3-ssc020a-s01a-demo-padmux.dtsi
SPI1选择 Mode 11(PAD_KEY0/PAD_KEY1/PAD_KEY2/PAD_KEY3)进行输出,没有在其他组输出的话,需要将其他组注释掉。
2.4. PSPI1的 dts 配置,以 mode11为例¶
-
修改 pioneer3 -demo.dtsi:
-
添加PSPI1 padmux 复用
修改 pioneer3-ssc020a-s01a-demo-padmux.dtsi
PSPI1选择Mode 9(PAD_GPIO5/PAD_GPIO6/PAD_GPIO7/PAD_GPIO8)进行输出,没有在其他组输出的话,需要将其他组注释掉。
3. Kernel配置¶
-
打开 kernel 对 SPI 的配置
-
打开 SStar SPI 驱动配置
4. 用户demo¶
4.1. SPI device¶
" /dev/spidev1.0” //根据使用哪一组spi确认节点
4.2. Sample code¶
static const char *device = "/dev/spidev1.0"; //根据使用哪一组spi确认节点 static uint8_t mode = 0; /* SPI通信使用全双工,设置CPOL=0,CPHA=0。 */ static uint8_t bits = 8; /* 8bits读写,MSB first。*/ static uint32_t speed = 60*1000*1000;/* 设置传输速度 */ static uint16_t delay = 0; static int g_SPI_Fd = 0; static void pabort(const char *s) { perror(s); abort(); } /** * 功 能:同步数据传输 * 入口参数 : * TxBuf -> 发送数据首地址 * len -> 交换数据的长度 * 出口参数: * RxBuf -> 接收数据缓冲区 * 返回值:0 成功 * 开发人员:Lzy 2013-5-22 */ int SPI_Transfer(const uint8_t *TxBuf, uint8_t *RxBuf, int len) { int ret; int fd = g_SPI_Fd; struct spi_ioc_transfer tr ={ .tx_buf = (unsigned long) TxBuf, .rx_buf = (unsigned long) RxBuf, .len =len, .delay_usecs = delay, }; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) perror("can't send spi message\n"); else { #if SPI_DEBUG int i; printf("nsend spi message Succeed\n"); printf("nSPI Send [Len:%d]: \n", len); for (i = 0; i < len; i++) { if (i % 8 == 0) printf("nt\n"); printf("0x%02X \n", TxBuf[i]); } printf("n"); printf("SPI Receive [len:%d]:\n", len); for (i = 0; i < len; i++) { if (i % 8 == 0) printf("nt\n"); printf("0x%02X \n", RxBuf[i]); } printf("\n"); #endif } return ret; } /** * 功 能:发送数据 * 入口参数 : * TxBuf -> 发送数据首地址 *len -> 发送与长度 *返回值:0 成功 * 开发人员:Lzy 2013-5-22 */ int SPI_Write(uint8_t *TxBuf, int len) { int ret; int fd = g_SPI_Fd; ret = write(fd, TxBuf, len); if (ret < 0) perror("SPI Write error\n"); else { #if SPI_DEBUG int i; printf("SPI Write [Len:%d]: \n", len); for (i = 0; i < len; i++) { if (i % 8 == 0) printf("\n\t"); printf("0x%02X \n", TxBuf[i]); } printf("\n"); #endif } return ret; } /** * 功 能:接收数据 * 出口参数: * RxBuf -> 接收数据缓冲区 * rtn -> 接收到的长度 * 返回值:>=0 成功 * 开发人员:Lzy 2013-5-22 */ int SPI_Read(uint8_t *RxBuf, int len) { int ret; int fd = g_SPI_Fd; ret = read(fd, RxBuf, len); if (ret < 0) printf("SPI Read error\n"); else { #if SPI_DEBUG int i; printf("SPI Read [len:%d]:\n", len); for (i = 0; i < len; i++) { if (i % 8 == 0) printf("\n\t"); printf("0x%02X \n", RxBuf[i]); } printf("\n"); #endif } return ret; } /** * 功 能:打开设备 并初始化设备 * 入口参数 : * 出口参数: * 返回值:0 表示已打开 0XF1 表示SPI已打开 其它出错 * 开发人员:Lzy 2013-5-22 */ int SPI_Open(void) { int fd; int ret = 0; if (g_SPI_Fd != 0) /* 设备已打开 */ return 0xF1; fd = open(device, O_RDWR); if (fd < 0) pabort("can't open device\n"); else printf("SPI - Open Succeed. Start Init SPI...\n"); g_SPI_Fd = fd; /* * spi mode */ ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); if (ret == -1) pabort("can't set spi mode\n"); ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); if (ret == -1) pabort("can't get spi mode\n"); /* * bits per word */ ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); if (ret == -1) pabort("can't set bits per word\n"); ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); if (ret == -1) pabort("can't get bits per word\n"); /* * max speed hz */ ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); if (ret == -1) pabort("can't set max speed hz\n"); ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); if (ret == -1) pabort("can't get max speed hz\n"); printf("spi mode: %d\n", mode); printf("bits per word: %d\n", bits); printf("max speed: %d KHz (%d MHz)\n", speed / 1000, speed / 1000 / 1000); return ret; } /** * 功 能:关闭SPI模块 */ int SPI_Close(void) { int fd = g_SPI_Fd; if (fd == 0) /* SPI是否已经打开*/ return 0; close(fd); g_SPI_Fd = 0; return 0; } /** * 功 能:自发自收测试程序 * 接收到的数据与发送的数据如果不一样 ,则失败 * 说明: * 在硬件上需要把输入与输出引脚短跑 * 开发人员:Lzy 2013-5-22 */ int SPI_LookBackTest(void) { int ret, i; const int BufSize = 16; uint8_t tx[BufSize], rx[BufSize]; bzero(rx, sizeof(rx)); for (i = 0; i < BufSize; i++) tx[i] = i; printf("nSPI - LookBack Mode Test...\n"); ret = SPI_Transfer(tx, rx, BufSize); if (ret > 1) { ret = memcmp(tx, rx, BufSize); if (ret != 0) { printf("tx:\n"); for (i = 0; i < BufSize; i++) { printf("%d ", tx[i]); } printf("\n"); printf("rx:\n"); for (i = 0; i < BufSize; i++) { printf("%d ", rx[i]); } printf("\n"); perror("LookBack Mode Test error\n"); } else printf("SPI - LookBack Mode OK\n"); } return ret; }