1、背景介绍

复旦微ZYNQ通过SPI配置国产JEM5396,框图如下:

现在需要在linux下的应用程序内配置JEM5396的寄存器。其中FMQL和进口的XILINX ZYNQ类似,JEM5396和进口的BCM5396兼容。因此可以参考进口ZYNQ在linux下配置BCM5396过程。Zynq-Linux移植学习笔记之41-linux下通过SPI访问broadcom 5396交换芯片_bcm5396-CSDN博客

2、内核配置

内核中将spidev.c编到内核内,同时在spidev.c中添加jem5396设备

3、设备树配置

设备树中在spi节点下添加jem5396设备

4、应用修改

由于复旦微FMQL中采用的SPI控制器不是SPI-CADENCE IP,所以不需要和进口ZYNQ那样改驱动。但是需要在应用APP中修改。参考JEM5396提供的差异说明

我司配套SPI驱动:使用ARM STM32F107VC型芯片内建的SPI IP实现SPI Master与5396 SPI Slave的通信。配置时需注意以下几点:

1.经过测试,5396 spi的CPHA选择必须为2Egde(CPOL可以为High或Low),才能保证与5396正常通信。

    2. 由于内部设计原因导致spi接口的cs控制与sck时钟信号,两者可能出现信号对齐或大体重合,继而导致采样数据不准。因此,在时序上需要 cs触发后的第一个时钟沿向后延几个相位。如红框图位置

应用代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#define NREAD	(0x60)
#define NWRITE	(0x61)

#define SIO (0xF0)
#define STS (0xFE)
#define SPG (0xFF)

#define SPIF	(0x80)
#define RACK	(0x20)
#define RXRDY	(0x02)
#define TXRDY	(0x01)

uint8_t mode = SPI_CPHA|SPI_CPOL;
uint8_t bits = 8;
uint32_t speed = 2000000;
uint16_t delay=0;


unsigned int readBCM5396Reg(unsigned char page,unsigned char offset,unsigned char regType)
{
	unsigned char wr_buf[32],rd_buf[32],*bp;
	int len, status;
	struct spi_ioc_transfer xfer[2];
	int file;
	unsigned int result;
	int ret=0;

	file = open("/dev/spidev2.0", O_RDWR);
	if (file<0) {
		printf("open 5396 error\n");
		return 1;
	}

	if (ioctl(file, SPI_IOC_WR_MODE, &mode) < 0)
	{
		printf("SPI_IOC_WR_MODE failed\n");
	}
	if (ioctl(file, SPI_IOC_RD_MODE, &mode) < 0)
	{
		printf("SPI_IOC_RD_MODE failed\n");
	}

	memset(wr_buf, 0, sizeof(wr_buf));
	memset(rd_buf, 0, sizeof(rd_buf));
	memset(xfer,0,sizeof(xfer));

	wr_buf[0] = NREAD;
	wr_buf[1] = STS;
	xfer[0].tx_buf = (unsigned long) wr_buf;
	xfer[0].rx_buf = (unsigned long) rd_buf;
	xfer[0].len = 3;
	status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);

	printf("#1 status is %d\n",status);
	usleep(10000);

	printf("#1 NREAD response(%d): ", status);
	for (bp = rd_buf; len; len--)
			printf("%02x ", *bp++);
	printf("\n");

	wr_buf[0] = NWRITE;
	wr_buf[1] = SPG;
	wr_buf[2] = page;
	xfer[0].tx_buf = (unsigned long) wr_buf;
	xfer[0].rx_buf = (unsigned long) rd_buf;
	xfer[0].len = 3;

	status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);
	//printf("#2 status is %d\n",status);
	//usleep(10000);

	printf("#2 NWRITE response(%d): ", status);
	for (bp = rd_buf; len; len--)
			printf("%02x ", *bp++);
	printf("\n");


	wr_buf[0] = NREAD;
	wr_buf[1] = offset;
	xfer[0].tx_buf = (unsigned long) wr_buf;
	xfer[0].rx_buf = (unsigned long) rd_buf;
	xfer[0].len = 3;
	status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);
	//printf("#3 status is %d\n",status);
	//usleep(10000);

	printf("#3 NREAD response(%d): ", status);
	for (bp = rd_buf; len; len--)
			printf("%02x ", *bp++);
	printf("\n");

	wr_buf[0] = NREAD;
	wr_buf[1] = STS;
	xfer[0].tx_buf = (unsigned long) wr_buf;
	xfer[0].rx_buf = (unsigned long) rd_buf;
	xfer[0].len = 3;
	status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);
	//printf("#4 status is %d\n",status);
	//usleep(10000);

	printf("#4 NREAD response(%d): ", status);
	for (bp = rd_buf; len; len--)
			printf("%02x ", *bp++);
	printf("\n");

	wr_buf[0] = NREAD;
	wr_buf[1] = SIO;
	xfer[0].tx_buf = (unsigned long) wr_buf;
	xfer[0].rx_buf = (unsigned long) rd_buf;
	xfer[0].len = 2+regType/8;
	status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);
	//printf("#5 status is %d\n",status);
	len=status;
	if (status < 0) {
		perror("SPI_IOC_MESSAGE");
		return -1;
	}

	printf("#5 NREAD response(%d): ", status);
	for (bp = rd_buf; len; len--)
		printf("%02x ", *bp++);
	printf("\n");

	bp = rd_buf;
	memcpy(&result,bp+2,regType/8);
	printf("read result is 0x%x\n",result);
	close(file);
	return result;
}


unsigned int writeBCM5396Reg(unsigned char page,unsigned char offset,unsigned char *pBuffer,unsigned char regType)
{
	unsigned char wr_buf[32],rd_buf[32],*bp;
	int len, status;
	struct spi_ioc_transfer xfer[2];
	int file,i;
	unsigned int result;

	file = open("/dev/spidev2.0", O_RDWR);
	if (file<0) {
		printf("open 5396 error\n");
		return 1;
	}

	if (ioctl(file, SPI_IOC_WR_MODE, &mode) < 0)
	{
		printf("SPI_IOC_WR_MODE failed\n");
	}
	if (ioctl(file, SPI_IOC_RD_MODE, &mode) < 0)
	{
		printf("SPI_IOC_RD_MODE failed\n");
	}

	memset(wr_buf, 0, sizeof(wr_buf));
	memset(rd_buf, 0, sizeof(rd_buf));
	memset(xfer,0,sizeof(xfer));

	wr_buf[0] = NREAD;
	wr_buf[1] = STS;
	xfer[0].tx_buf = (unsigned long) wr_buf;
	xfer[0].rx_buf = (unsigned long) rd_buf;
	xfer[0].len = 3;
	status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);

	wr_buf[0] = NWRITE;
	wr_buf[1] = SPG;
	wr_buf[2] = page;
	xfer[0].tx_buf = (unsigned long) wr_buf;
	xfer[0].rx_buf = (unsigned long) rd_buf;
	xfer[0].len = 3;
	status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);

	wr_buf[0] = NWRITE;
	wr_buf[1] = offset;
	for(i=0;i<regType/8;i++)
	{
		wr_buf[2+i] = pBuffer[i];
	}
	xfer[0].tx_buf = (unsigned long) wr_buf;
	xfer[0].rx_buf = (unsigned long) rd_buf;
	xfer[0].len = 2+(regType/8);
	status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);
	close(file);
	return status;
}


int main()
{
	unsigned int temp;
	unsigned char buf[10];
	int res=0;

	buf[0]=0x00;
	buf[1]=0x0f;
	buf[2]=0x00;


	temp=readBCM5396Reg(0x2,0x30,32);
	printf("page 0x2 offset 0x30 is 0x%x\n",temp);

	sleep(1);//连续读之间需要加时延
	temp=readBCM5396Reg(0x31,0x00,32);
	printf("before write page 0x31 offset 0x0 is 0x%x\n",temp);

	res=writeBCM5396Reg(0x31, 0x00, buf,32);//Port 0 - Slot 10
	if(res<0)
	{
		printf("write bcm5396 error\n");
	}

	temp=readBCM5396Reg(0x31,0x00,32);
	printf("after write page 0x31 offset 0x0 is 0x%x\n",temp);
	return 0;
}

5、测试验证

系统启动后,执行应用,可以看到能够正常读写

注意:国产JEM5396在连续读写寄存器时存在问题,连续两次读之间需要加1s延迟。具体看应用示例。

另外,调试时发现有个小工具很好用spidev_test,这样就不需要自己写代码,直接模拟SPI读写时序

spidev_test -D /dev/spidev2.0 -s 2000000 -H -O -p "\x60\xFE\x00" -v #NREAD,STS,rd_sts_spif(0x00)

spidev_test -D /dev/spidev2.0 -s 2000000 -H -O -p "\x61\xFF\x02" -v #NWRITE,SPG,page(0x02)

spidev_test -D /dev/spidev2.0 -s 2000000 -H -O -p "\x60\x30\x00" -v #NREAD,reg(0x30),rd_none

spidev_test -D /dev/spidev2.0 -s 2000000 -H -O -p "\x60\xFE\x00" -v #NREAD,STS,rd_sts_rack(0xA0)

spidev_test -D /dev/spidev2.0 -s 2000000 -H -O -p "\x60\xF0\x00" -v #NREAD,SIO,rd_5396_id(0x60)

上图为读出JEM5396的ID操作

Spidev_test具体用法如下:

当然,使用这个工具的前提是有个正确的时序,然后通过spidev_test去实现这个时序,比如spidev_test中设置-H(配置CPHA)和-O(配置CPOL),这个与芯片说明中的“经过测试,5396 spi的CPHA选择必须为2Egde(CPOL可以为High或Low),才能保证与5396正常通信 ”描述一致。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐