RT-thread下ARM926EJS的CPU移植SPI 並掛載w25q128

1:環境
芯片:NUC977
CPU: ARM926EJS
OS:RT-Thread
在這裏插入圖片描述
在這裏插入圖片描述
2:RT-thread下架構分析
在這裏插入圖片描述
上圖時SPI所在的驅動架構
其中已經有的是SPI 的驅動 nuc97x_spi.c
SPI驅動框架需要在RT-thread下通過menuconfig進行添加

在這裏插入圖片描述
代碼中會新增下面的代碼文件
在這裏插入圖片描述

2:SPI驅動
添加SPI的驅動 向SPI的驅動框架下注冊設備SPI0
drv_spi.c

int rt_hw_spi_bus_init(void)
{
	rt_err_t result;
	for (int i = 0; i < sizeof(spi_config) / sizeof(spi_config[0]); i++) {
		spi_bus_obj[i].config = &spi_config[i];
		spi_bus_obj[i].spi_bus.parent.user_data = &spi_config[i];
		result = rt_spi_bus_register(&spi_bus_obj[i].spi_bus, spi_config[i].bus_name, &stm_spi_ops);
		RT_ASSERT(result == RT_EOK);
	}
	 return result;
}

其中result = rt_spi_bus_register(&spi_bus[0], spi_config[0].bus_name, &stm_spi_ops);
會向SPI的架構註冊SPI的名字,操作函數ops

static const struct rt_spi_ops stm_spi_ops =
{
    .configure = spi_configure,
    .xfer = spixfer,
};

其中spi_configure 會進行對SPI的配置,例如模式,速度,傳輸長度
最終調用下面的函數

static rt_err_t stm32_spi_init(struct nuc97x_spi *spi_drv, struct rt_spi_configuration *cfg)
{
	struct stm32_spi_config *config = spi_drv->config;
	rt_uint32_t fd = config->fd;
	/*初始化SPI時鐘*/
	outpw(REG_CLK_PCLKEN1, inpw(REG_CLK_PCLKEN0) | 0x10);
	/*複用GPIO PB6,7,8,9引腳*/
	outpw(REG_SYS_GPB_MFPL, (inpw(REG_SYS_GPB_MFPL) & ~0xff000000) | 0xBB000000);
	outpw(REG_SYS_GPB_MFPH, (inpw(REG_SYS_GPB_MFPH) & ~0xff) | 0xBB);
	/*SPI init*/	
//	 spiInit(0);
	 spiOpen(fd);
	
    // 設置SPI時鐘
    spiIoctl(fd, SPI_IOC_SET_SPEED, cfg->max_hz, RT_NULL);
    // 設置傳輸長度爲
    spiIoctl(fd, SPI_IOC_SET_TX_BITLEN, cfg->data_width, RT_NULL);
    // 設置傳輸模式
    spiIoctl(fd, SPI_IOC_SET_MODE, cfg->mode, RT_NULL);
	spiIoctl(fd, SPI_IOC_SET_LSB_MSB, RT_NULL, RT_NULL);
	
	return RT_EOK;

}

spixfer 是真正傳輸時會調用的函數,原型如下:
大概意思就是:上層函數會把傳輸的命令,數據,大小,是否片選放到結構體rt_spi_message 中去
然後通過,SPI_Transmit和SPI_Receive來進行數據的傳輸和接收

static rt_uint32_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
    rt_size_t message_length, already_send_length;
    rt_uint16_t send_length;
    rt_uint8_t *recv_buf;
    const rt_uint8_t *send_buf;
	RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);
    RT_ASSERT(device->bus->parent.user_data != RT_NULL);
    RT_ASSERT(message != RT_NULL);
    if (message->cs_take)
    {
            // /CS: 使能
			spiIoctl(0, SPI_IOC_ENABLE_SS, SPI_SS_SS0, 0);
    }
	message_length = message->length;
    recv_buf = message->recv_buf;
    send_buf = message->send_buf;
	
	 while (message_length)
    {
        /* the HAL library use uint16 to save the data length */
        if (message_length > 65535)
        {
            send_length = 65535;
            message_length = message_length - 65535;
        }
        else
        {
            send_length = message_length;
            message_length = 0;
        }

        /* calculate the start address */
        already_send_length = message->length - send_length - message_length;
        send_buf = (rt_uint8_t *)message->send_buf + already_send_length;
        recv_buf = (rt_uint8_t *)message->recv_buf + already_send_length;
       if (message->send_buf)
        {
				//SpiFlash_NormalPageProgram(0, send_buf);
				SPI_Transmit(0, (uint8_t *)send_buf, send_length);
			
                //state = HAL_SPI_Transmit(spi_handle, (uint8_t *)send_buf, send_length, 1000);
 
        }
        else
        {
            memset((uint8_t *)recv_buf, 0xff, send_length);
			//SpiFlash_NormalRead(0, recv_buf);
			SPI_Receive(0, (uint8_t *)recv_buf, send_length);
            //state = HAL_SPI_Receive(spi_handle, (uint8_t *)recv_buf, send_length, 1000);

        }

        }
    if (message->cs_release)
    {
        spiIoctl(0, SPI_IOC_DISABLE_SS, SPI_SS_SS0, 0);
    }

    return message->length;
}

這兩個函數,一個向SPI的數據寄存器寫數據,一個從SPI數據寄存器讀取數據

static rt_uint32_t SPI_Transmit(rt_uint32_t fd, uint8_t *pData, uint16_t Size)
{
	uint32_t i;

	// 寫數據
    for(i=0;i<Size;i++)
    {
        spiWrite(fd, TX_RX_0_OFFSET, pData[i]);
        spiIoctl(fd, SPI_IOC_TRIGGER, RT_NULL, RT_NULL);
        while(spiGetBusyStatus(RT_NULL));
    }
	return RT_EOK;

}

static rt_uint32_t SPI_Receive(rt_uint32_t fd, uint8_t *pData, uint16_t Size)
{
	uint32_t i;
    for(i=0; i<Size; i++) {
        spiWrite(fd, TX_RX_0_OFFSET, ZERO_DATA);
        spiIoctl(fd, SPI_IOC_TRIGGER, RT_NULL, RT_NULL);
        while(spiGetBusyStatus(RT_NULL));
        pData[i] = spiRead(fd, TX_RX_0_OFFSET);  
    }
	return RT_EOK;

}

3:編寫W25Q128的驅動

#include "w25q128bv.h"

#define TEST_LENGTH 256 /* length */
#define W25Q_SPI_DEVICE_NAME     "spi01"
rt_uint8_t W25QXX_BUFFER[4096] ={0};
struct rt_spi_device *spi_dev_w25q;

rt_uint8_t w25q_init(void)
{
	char name[RT_NAME_MAX];
	struct rt_spi_configuration cfg;
	
	rt_strncpy(name, W25Q_SPI_DEVICE_NAME, RT_NAME_MAX);
	spi_dev_w25q = (struct rt_spi_device *)rt_device_find(name);
	if(!spi_dev_w25q) {
		rt_kprintf("spi sample run failed! can't find %s device!\n", name);
		return RT_ERROR;
	}else{

         cfg.data_width = 8;
         cfg.mode = RT_SPI_MODE_0;//| RT_SPI_MSB; /* SPI Compatible: Mode 0 and Mode 3 */
         cfg.max_hz = 2 * 1000 * 1000 ; /* 50M */
         rt_spi_configure(spi_dev_w25q, &cfg);
		 return RT_EOK;
	}
}

static void read_spi_flash_id(void)
{
	RT_ASSERT(spi_dev_w25q != RT_NULL);
	rt_uint8_t w25x_read_id = W25X_ManufactDeviceID;
	rt_uint8_t id[5] = {0};			
	struct rt_spi_message msg1, msg2;
	
	msg1.send_buf = &w25x_read_id;
	msg1.recv_buf = RT_NULL;
	msg1.length = 1;
	msg1.cs_take = 1;
	msg1.cs_release = 0;
	msg1.next = &msg2;
		
	msg2.send_buf = RT_NULL;
	msg2.recv_buf = id;
	msg2.length = 5;
	msg2.cs_take = 0;
	msg2.cs_release = 1;
	msg2.next = RT_NULL;
		
	rt_spi_transfer_message(spi_dev_w25q, &msg1);
	rt_kprintf("use rt_spi_transfer_mesage() read w25q ID is :%x%x\n", id[3], id[4]);

}
MSH_CMD_EXPORT(read_spi_flash_id, spi_w25q_sample samole);

void W25QXX_Write_Page(rt_uint8_t* pBuffer,rt_uint32_t WriteAddr,rt_uint16_t NumByteToWrite)
{
	RT_ASSERT(spi_dev_w25q != RT_NULL);
	rt_uint8_t addr[3] = {0};
	struct rt_spi_message msg1, msg2,msg3, msg4;
	rt_uint8_t w25x_write_id = W25X_WriteEnable;
	rt_uint8_t w25x_page_id = W25X_PageProgram;
	rt_uint32_t StartAddress = WriteAddr;

	addr[0] = (StartAddress>>16) & 0xFF;
	addr[1] = (StartAddress>>8) & 0xFF;
	addr[2] = StartAddress & 0xFF;
			
	msg1.send_buf = &w25x_write_id;
	msg1.recv_buf = RT_NULL;
	msg1.length = 1;	
	msg1.cs_take = 1;
	msg1.cs_release = 1;
	msg1.next = &msg2;
		
	msg2.send_buf = &w25x_page_id;
	msg2.recv_buf = RT_NULL;
	msg2.length = 1;
	msg2.cs_take = 1;
	msg2.cs_release = 0;
	msg2.next = &msg3;
		
	msg3.send_buf = addr;
	msg3.recv_buf = RT_NULL;
	msg3.length = 3;
	msg3.cs_take = 0;
	msg3.cs_release = 0;
	msg3.next = &msg4;
				
	msg4.send_buf = pBuffer;
	msg4.recv_buf = RT_NULL;
	msg4.length = NumByteToWrite;
	msg4.cs_take = 0;
	msg4.cs_release = 1;
	msg4.next = RT_NULL;
	
	rt_spi_transfer_message(spi_dev_w25q, &msg1);

	SpiFlash_WaitReady();
}

void W25QXX_Read(rt_uint8_t* pBuffer,rt_uint32_t ReadAddr,rt_uint32_t NumByteToRead)
{
	RT_ASSERT(spi_dev_w25q != RT_NULL);
	rt_uint8_t addr[3] = {0};
	rt_uint8_t w25x_read_id = 0x03;
	rt_uint32_t StartAddress = ReadAddr;
	struct rt_spi_message msg1, msg2, msg3;

	addr[0] = (StartAddress>>16) & 0xFF;
	addr[1] = (StartAddress>>8) & 0xFF;
	addr[2] = StartAddress & 0xFF;	
		
	rt_kprintf("read addr 0x%02x......... \n", StartAddress);
	msg1.send_buf = &w25x_read_id;
	msg1.recv_buf = RT_NULL;
	msg1.length = 1;
	msg1.cs_take = 1;
	msg1.cs_release = 0;
	msg1.next = &msg2;
		
	msg2.send_buf = addr;
	msg2.recv_buf = RT_NULL;
	msg2.length = 3;
	msg2.cs_take = 0;
	msg2.cs_release = 0;
	msg2.next = &msg3;
				
	msg3.send_buf = RT_NULL;
	msg3.recv_buf = pBuffer;
	msg3.length = NumByteToRead;
	msg3.cs_take = 0;
	msg3.cs_release = 1;
	msg3.next = RT_NULL;
	rt_spi_transfer_message(spi_dev_w25q, &msg1);
	
}



void W25QXX_Erase_Sector(u32 Dst_Addr)
{
	RT_ASSERT(spi_dev_w25q != RT_NULL);
	uint8_t addr[3] = {0};
	struct rt_spi_message msg1, msg2, msg3;
	rt_uint8_t w25x_erase_sector_id = W25X_SectorErase;
	rt_uint8_t w25x_write_id = 0x06;
	rt_uint32_t StartAddress  = Dst_Addr*4096;

	addr[0] = (StartAddress>>16) & 0xFF;
	addr[1] = (StartAddress>>8) & 0xFF;
	addr[2] = StartAddress & 0xFF;	
	
	rt_kprintf("erase 0x%02x...\n",StartAddress);

	msg1.send_buf = &w25x_write_id;
	msg1.recv_buf = RT_NULL;
	msg1.length = 1;
	msg1.cs_take = 1;
	msg1.cs_release = 1;
	msg1.next = &msg2;
		
	msg2.send_buf = &w25x_erase_sector_id;
	msg2.recv_buf = RT_NULL;
	msg2.length = 1;
	msg2.cs_take = 1;
	msg2.cs_release = 0;
	msg2.next = &msg3;
		
	msg3.send_buf = addr;
	msg3.recv_buf = RT_NULL;
	msg3.length = 3;
	msg3.cs_take = 0;
	msg3.cs_release = 1;
	msg3.next = RT_NULL;
		
	rt_spi_transfer_message(spi_dev_w25q, &msg1);
	SpiFlash_WaitReady();
}

void erase_spi_flash_all(void)
{
	//擦除FLASH
   SpiFlash_ChipErase();

   //等待操作完成
   SpiFlash_WaitReady();
}
MSH_CMD_EXPORT(erase_spi_flash_all, erase_spi_flash_all);

//無檢驗寫SPI FLASH 
//必須確保所寫的地址範圍內的數據全部爲0XFF,否則在非0XFF處寫入的數據將失敗!
//具有自動換頁功能 
//在指定地址開始寫入指定長度的數據,但是要確保地址不越界!
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(rt_uint8_t* pBuffer,rt_uint32_t WriteAddr,rt_uint16_t NumByteToWrite)   
{ 			 		 
	rt_uint16_t pageremain;	   
	pageremain=256-WriteAddr%256; //單頁剩餘的字節數		 	    
	if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大於256個字節
	while(1)
	{	   
		W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
		if(NumByteToWrite==pageremain)break;//寫入結束了
	 	else //NumByteToWrite>pageremain
		{
			pBuffer+=pageremain;
			WriteAddr+=pageremain;	

			NumByteToWrite-=pageremain;			  //減去已經寫入了的字節數
			if(NumByteToWrite>256)pageremain=256; //一次可以寫入256個字節
			else pageremain=NumByteToWrite; 	  //不夠256個字節了
		}
	};	    
} 

//寫SPI FLASH  
//在指定地址開始寫入指定長度的數據
//該函數帶擦除操作!
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)						
//NumByteToWrite:要寫入的字節數(最大65535)   	 
void W25QXX_Write(rt_uint8_t* pBuffer,rt_uint32_t WriteAddr,rt_uint32_t NumByteToWrite)   
{ 
	rt_uint32_t secpos;
	rt_uint16_t secoff;
	rt_uint16_t secremain;	   
 	rt_uint16_t i;    
	rt_uint8_t *W25QXX_BUF;	  
   	W25QXX_BUF=W25QXX_BUFFER;	     
 	secpos=WriteAddr/4096;//扇區地址  
	secoff=WriteAddr%4096;//在扇區內的偏移
	secremain=4096-secoff;//扇區剩餘空間大小   
 	//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//測試用
 	if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大於4096個字節
	while(1) 
	{	
		W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//讀出整個扇區的內容

		for(i=0;i<secremain;i++)//校驗數據
		{
			if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除  	  
		}
		if(i<secremain)//需要擦除
		{
			W25QXX_Erase_Sector(secpos);//擦除這個扇區
			for(i=0;i<secremain;i++)	   //複製
			{
				W25QXX_BUF[i+secoff]=pBuffer[i];
				//rt_kprintf("0x%02x ", W25QXX_BUFFER[i]);				
			}
			rt_kprintf("write sector 0x%x\n", secpos*4096);
			W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//寫入整個扇區  

		}else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//寫已經擦除了的,直接寫入扇區剩餘區間. 				   
		if(NumByteToWrite==secremain)break;//寫入結束了
		else//寫入未結束
		{
			secpos++;//扇區地址增1
			secoff=0;//偏移位置爲0 	 

		   	pBuffer+=secremain;  //指針偏移
			WriteAddr+=secremain;//寫地址偏移	   
		   	NumByteToWrite-=secremain;				//字節數遞減
			if(NumByteToWrite>4096)secremain=4096;	//下一個扇區還是寫不完
			else secremain=NumByteToWrite;			//下一個扇區可以寫完了
		}	 
	};	 
}

void W25QXX_Write_Big(rt_uint8_t* pBuffer,rt_uint32_t WriteAddr,rt_uint32_t NumByteToWrite)   
{
	rt_size_t message_length, already_send_length;
	rt_uint16_t send_length;
	message_length =NumByteToWrite;

	 while (message_length)
    {
        /* the HAL library use uint16 to save the data length */
        if (message_length > 65535)
        {
            send_length = 65535;
            message_length = message_length - 65535;
        }
        else
        {
            send_length = message_length;
            message_length = 0;
        }

        /* calculate the start address */
        already_send_length = NumByteToWrite - send_length - message_length;
        pBuffer = pBuffer + already_send_length;
		WriteAddr = WriteAddr + already_send_length;

		W25QXX_Write(pBuffer,WriteAddr,send_length);
	}
	
}

int w25q_spi_device_init()
{
    outpw(REG_CLK_PCLKEN0, inpw(REG_CLK_PCLKEN0) | 0x8);
    return rt_hw_spi_device_attach("spi0", "spi01", RT_NULL);
}

INIT_DEVICE_EXPORT(w25q_spi_device_init);


static int rt_hw_spi_flash_with_sfud_init(void)
{
    if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi01"))
    {
        return RT_ERROR;
    };
	
    return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_with_sfud_init);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章