从SOD到OOD(spi_flash模块)

这篇博客介绍了如何将Xilinx SDK中的SPI闪存驱动代码进行封装和移植,以适应C++环境。通过定义一个`SpiFlashDev`结构体,封装了与SPI闪存交互的关键函数,如初始化、开始、读写操作等。移植过程中,重点关注了static变量的处理,确保了功能函数的独立性和模块化。整个过程遵循了结构化编程的设计原则,提高了代码的可维护性和复用性。
摘要由CSDN通过智能技术生成

D:\Xilinx\SDK\2019.1\data\embeddedsw\XilinxProcessorIPLib\drivers\spi_v4_4\examples

xspi_winbond_flash_quad_example.c文件,作为移植的参考。
这个例子中,
定义了一系列功能函数,它们利用BSP提供的操作函数,完成功能级的封装。

int SpiFlashWriteEnable(XSpi *SpiPtr);
int SpiFlashWrite(XSpi *SpiPtr, u32 Addr, u32 ByteCount, u8 WriteCmd);
int SpiFlashRead(XSpi *SpiPtr, u32 Addr, u32 ByteCount, u8 ReadCmd);
int SpiFlashBulkErase(XSpi *SpiPtr);
int SpiFlashSectorErase(XSpi *SpiPtr, u32 Addr);
int SpiFlashGetStatus(XSpi *SpiPtr);
int SpiFlashQuadEnable(XSpi *SpiPtr);
int SpiFlashEnableHPM(XSpi *SpiPtr);
static int SpiFlashWaitForFlashReady(void);
void SpiHandler(void *CallBackRef, u32 StatusEvent, unsigned int ByteCount);
static int SetupInterruptSystem(XSpi *SpiPtr);

在SOD风格下,定义了static的结构体,

static XIntc InterruptController;
static XSpi Spi;
volatile static int TransferInProgress;
static int ErrorCount;
static u8 ReadBuffer[PAGE_SIZE + READ_WRITE_EXTRA_BYTES + 4];
static u8 WriteBuffer[PAGE_SIZE + READ_WRITE_EXTRA_BYTES];

这些static变量,是我们封装时要关注的变量。

在C++环境下,既可以封装为class,也可以封装为struct,为了尽量多的保持SOD风格,这里封装成struct。

struct SpiFlashDev
{
	XSpi spi;
	int TransferInProgress;
	int ErrorCount;
	u8 ReadBuffer[PAGE_SIZE + READ_WRITE_EXTRA_BYTES + 4];
	u8 WriteBuffer[PAGE_SIZE + READ_WRITE_EXTRA_BYTES];
};

并为它设计相应的功能级函数。对应于example中的功能函数。

int spi_flash_init(struct SpiFlashDev* dev, int dev_id);
int spi_flash_start(struct SpiFlashDev* dev);
void SpiHandler(void *CallBackRef, u32 StatusEvent, unsigned int ByteCount);

int spi_flash_get_status(struct SpiFlashDev* dev);
int spi_flash_wait_for_flash_ready(struct SpiFlashDev* dev);
int spi_flash_write_enable(struct SpiFlashDev* dev);

int spi_flash_sector_erase(struct SpiFlashDev* dev, u32 addr);

int spi_flash_read_page(struct SpiFlashDev* dev, u32 addr);
int spi_flash_write_page(struct SpiFlashDev* dev, u32 addr);

来看看main函数,
main函数里,首先对xspi进行初始化,然后启动,然后执行了各种读写操作,完成测试。
所以,我们可以根据初始化序列,来封装所需要的spi_flash_init函数,
根据启动代码,来封装所需要的spi_flash_start函数。

int spi_flash_init(struct SpiFlashDev* dev, int dev_id)
{
	int ret;
	XSpi_Config *ConfigPtr;

	ConfigPtr = XSpi_LookupConfig(dev_id);
	if (ConfigPtr == NULL) {
		return -1;
	}

	ret = XSpi_CfgInitialize(&dev->spi, ConfigPtr, ConfigPtr->BaseAddress);
	if (ret != XST_SUCCESS) {
		return -1;
	}

	/*
	 * Setup the handler for the SPI that will be called from the interrupt
	 * context when an SPI status occurs, specify a pointer to the SPI
	 * driver instance as the callback reference so the handler is able to
	 * access the instance data.
	 */
	XSpi_SetStatusHandler(&dev->spi, dev, (XSpi_StatusHandler)SpiHandler);

	/*
	 * Set the SPI device as a master and in manual slave select mode such
	 * that the slave select signal does not toggle for every byte of a
	 * transfer, this must be done before the slave select is set.
	 */
	ret = XSpi_SetOptions(&dev->spi, XSP_MASTER_OPTION | XSP_MANUAL_SSELECT_OPTION);
	if(ret != XST_SUCCESS) {
		return -1;
	}

	/*
	 * Select the quad flash device on the SPI bus, so that it can be
	 * read and written using the SPI bus.
	 */
	ret = XSpi_SetSlaveSelect(&dev->spi, 1);
	if(ret != XST_SUCCESS) {
		return -1;
	}
	
	return 0;
}

int spi_flash_start(struct SpiFlashDev* dev)
{
	XSpi_Start(&dev->spi);
	return 0;
}

执行流程和main一致,区别在于,main中操作的对象是static的变量,而在封装函数中,替换成参数传入的对象。
在main中调用的函数SetupInterruptSystem,这里并未调用,因为这是对XINTC进行配置的函数,属于XINTC的初始化,此时并不执行,而是放在整板级别统一执行。加入如下的代码,并稍加修改即可。

/*
	 * Connect a device driver handler that will be called when an interrupt
	 * for the device occurs, the device driver handler performs the
	 * specific interrupt processing for the device
	 */
	Status = XIntc_Connect(&InterruptController,
				SPI_INTR_ID,
				(XInterruptHandler)XSpi_InterruptHandler,
				(void *)SpiPtr);
	if(Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

其中用到的spiHandler函数,只需要对example中的函数稍加改动,

void SpiHandler(void *CallBackRef, u32 StatusEvent, unsigned int ByteCount)
{
	struct SpiFlashDev* dev = (struct SpiFlashDev*)CallBackRef;
	/*
	 * Indicate the transfer on the SPI bus is no longer in progress
	 * regardless of the status event.
	 */
	dev->TransferInProgress = 0;

	/*
	 * If the event was not transfer done, then track it as an error.
	 */
	if (StatusEvent != XST_SPI_TRANSFER_DONE) {
		dev->ErrorCount++;
	}
}

至此,initialize函数,start函数,callback函数已经移植了。
然后是
int spi_flash_write_enable(struct SpiFlashDev* dev);
这个函数里,由于使用了SpiFlashWaitForFlashReady函数,所以,先移植这个函数。
而它里面又使用了SpiFlashGetStatus函数,所以,还要先移植这个函数

int spi_flash_get_status(struct SpiFlashDev* dev)
{
	int ret;

	/*
	 * Prepare the Write Buffer.
	 */
	dev->WriteBuffer[0] = COMMAND_STATUSREG_READ;

	/*
	 * Initiate the Transfer.
	 */
	dev->TransferInProgress = TRUE;
	ret = XSpi_Transfer(&dev->spi, dev->WriteBuffer, dev->ReadBuffer, STATUS_READ_BYTES);
	if(ret != XST_SUCCESS) {
		return -1;
	}

	/*
	 * Wait till the Transfer is complete and check if there are any errors
	 * in the transaction..
	 */
	while(dev->TransferInProgress);
	if(dev->ErrorCount != 0) {
		dev->ErrorCount = 0;
		return -1;
	}

	return 0;
}

可以看出,区别只是在于变量引用上的不同。
再来移植SpiFlashWaitForFlashReady函数。

int spi_flash_wait_for_flash_ready(struct SpiFlashDev* dev)
{
	int ret;
	u8 StatusReg;

	while(1) {

		/*
		 * Get the Status Register.
		 */
		ret = spi_flash_get_status(dev);
		if(ret < 0) {
			return -1;
		}

		/*
		 * Check if the flash is ready to accept the next command.
		 * If so break.
		 */
		StatusReg = dev->ReadBuffer[1];
		if((StatusReg & FLASH_SR_IS_READY_MASK) == 0) {
			break;
		}
	}

	return 0;
}

可以看出,区别只是在于变量引用上的不同,以及调用的API的不同。
为了保持模块内聚,这里使用的是刚封装的spi_flash_get_status函数。

int spi_flash_write_enable(struct SpiFlashDev* dev)
{
	int ret;

	/*
	 * Wait while the Flash is busy.
	 */
	ret = spi_flash_wait_for_flash_ready(dev);
	if(ret < 0) {
		return -1;
	}

	/*
	 * Prepare the WriteBuffer.
	 */
	dev->WriteBuffer[0] = COMMAND_WRITE_ENABLE;

	/*
	 * Initiate the Transfer.
	 */
	dev->TransferInProgress = TRUE;
	ret = XSpi_Transfer(&dev->spi, dev->WriteBuffer, NULL, WRITE_ENABLE_BYTES);
	if(ret != XST_SUCCESS) {
		return -1;
	}

	/*
	 * Wait till the Transfer is complete and check if there are any errors
	 * in the transaction..
	 */
	while(dev->TransferInProgress);
	if(dev->ErrorCount != 0) {
		dev->ErrorCount = 0;
		return -1;
	}

	return 0;
}

可以看出,区别只是在于变量引用上的不同,以及调用的API的不同。
为了保持模块内聚,这里使用的是刚封装的spi_flash_wait_for_flash_ready函数。

回到main,继续看,SpiFlashSectorErase函数,

int spi_flash_sector_erase(struct SpiFlashDev* dev, u32 addr)
{
	int ret;

	/*
	 * Wait while the Flash is busy.
	 */
	ret = spi_flash_wait_for_flash_ready(dev);
	if(ret < 0) {
		return -1;
	}

	/*
	 * Prepare the WriteBuffer.
	 */
	dev->WriteBuffer[0] = COMMAND_SECTOR_ERASE;
	dev->WriteBuffer[1] = (u8) (addr >> 16);
	dev->WriteBuffer[2] = (u8) (addr >> 8);
	dev->WriteBuffer[3] = (u8) (addr);

	/*
	 * Initiate the Transfer.
	 */
	dev->TransferInProgress = TRUE;
	ret = XSpi_Transfer(&dev->spi, dev->WriteBuffer, NULL, SECTOR_ERASE_BYTES);
	if(ret != XST_SUCCESS) {
		return -1;
	}

	/*
	 * Wait till the Transfer is complete and check if there are any errors
	 * in the transaction..
	 */
	while(dev->TransferInProgress);
	if(dev->ErrorCount != 0) {
		dev->ErrorCount = 0;
		return -1;
	}

	return 0;
}

可以看出,区别只是在于变量引用上的不同,以及调用的API的不同。
为了保持模块内聚,这里使用的是刚封装的spi_flash_wait_for_flash_ready函数。

回到main,继续看,SpiFlashWrite函数,

int spi_flash_write_page(struct SpiFlashDev* dev, u32 addr)
{
	int ret;

	/*
	 * Wait while the Flash is busy.
	 */
	ret = spi_flash_wait_for_flash_ready(dev);
	if(ret < 0) {
		return -1;
	}

	/*
	 * Prepare the WriteBuffer.
	 */
	dev->WriteBuffer[0] = COMMAND_PAGE_PROGRAM;
	dev->WriteBuffer[1] = (u8) (addr >> 16);
	dev->WriteBuffer[2] = (u8) (addr >> 8);
	dev->WriteBuffer[3] = (u8) addr;


	/*
	 * Initiate the Transfer.
	 */
	dev->TransferInProgress = TRUE;
	ret = XSpi_Transfer(&dev->spi, dev->WriteBuffer, NULL, (PAGE_SIZE + READ_WRITE_EXTRA_BYTES));
	if(ret != XST_SUCCESS) {
		return -1;
	}

	/*
	 * Wait till the Transfer is complete and check if there are any errors
	 * in the transaction.
	 */
	while(dev->TransferInProgress);
	if(dev->ErrorCount != 0) {
		dev->ErrorCount = 0;
		return -1;
	}

	return 0;
}

可以看出,区别只是在于变量引用上的不同,以及调用的API的不同。
为了保持模块内聚,这里使用的是刚封装的spi_flash_wait_for_flash_ready函数。
另外,由于只需要按页写入,不需要变长写入,所以省去了与变长相关的代码。

回到main,继续看,SpiFlashRead

int spi_flash_read_page(struct SpiFlashDev* dev, u32 addr)
{
	int ret;

	/*
	 * Wait while the Flash is busy.
	 */
	ret = spi_flash_wait_for_flash_ready(dev);
	if(ret < 0) {
		return -1;
	}

	/*
	 * Prepare the WriteBuffer.
	 */
	dev->WriteBuffer[0] = COMMAND_RANDOM_READ;
	dev->WriteBuffer[1] = (u8) (addr >> 16);
	dev->WriteBuffer[2] = (u8) (addr >> 8);
	dev->WriteBuffer[3] = (u8) addr;



	/*
	 * Initiate the Transfer.
	 */
	dev->TransferInProgress = TRUE;
	ret = XSpi_Transfer(&dev->spi, dev->WriteBuffer, dev->ReadBuffer, (PAGE_SIZE + READ_WRITE_EXTRA_BYTES));
	if(ret != XST_SUCCESS) {
		return -1;
	}

	/*
	 * Wait till the Transfer is complete and check if there are any errors
	 * in the transaction.
	 */
	while(dev->TransferInProgress);
	if(dev->ErrorCount != 0) {
		dev->ErrorCount = 0;
		return -1;
	}

	return 0;
}

可以看出,区别只是在于变量引用上的不同,以及调用的API的不同。
为了保持模块内聚,这里使用的是刚封装的spi_flash_wait_for_flash_ready函数。
另外,由于只需要按页读出,不需要变长读出,所以省去了与变长相关的代码。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值