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);