在之前的帖子中,使用硬件的SPI读取板载的FLASH芯片,在本次例子中,使用模拟的SPI方式读取板载的存储芯片。
一:硬件连接:
首先需要确定使用的GPIO引脚,通常包括:
SCK:时钟引脚(输出)
MOSI:主机输出从机输入引脚(输出)
MISO:主机输入从机输出引脚(输入)
SS/CS:片选引脚(输出,可选)
二:SPI的模式配置
根据设备要求选择SPI模式(时钟极性和相位):
CPOL:时钟极性(0 = 空闲低电平,1 = 空闲高电平)
CPHA:时钟相位(0 = 数据在第一个时钟沿采样,1 = 数据在第二个时钟沿采样)
常见模式:
模式0:CPOL=0, CPHA=0
模式3:CPOL=1, CPHA=1
三:软件代码:
3.1 GPIO口的定义:
sbit SPI_CE = P4^0; //PIN1 sbit SPI_SO = P4^2; //PIN2 sbit SPI_SI = P4^1; //PIN5 sbit SPI_SCK = P4^3; //PIN6
3.2 SPI的使用的延时函数
void SPI_Delay(u8 cnt)
{
while(cnt--);
}3.3 SPI的初始化部分
void SPI_init(void)
{
SPI_CE_High();
SPI_SCK = 0; // set clock to low initial state
SPI_SI = 1;
}3.4 SPI 写入一个字节函数:
void SPI_WriteByte(u8 out)
{
u8 i;
i = 8;
do{
out <<= 1;
SPI_SI = CY;
SPI_SCK = 1;
SPI_Delay(3);
SPI_SCK = 0;
}while(--i);
SPI_SI = 1;
}3.5 SPI 读取一个字节函数:
u8 SPI_ReadByte(void)
{
u8 i, in;
i = 8;
do{
in <<= 1;
if (SPI_SO) in++;
SPI_SCK = 1;
SPI_Delay(3);
SPI_SCK = 0;
}while(--i);
return in;
}3.5 擦除扇区的函数,这里需要每次擦写4k字节
void FlashSectorErase(u32 addr)
{
if(B_FlashOK)
{
FlashWriteEnable(); //使能Flash写命令
SPI_CE_Low();
if(B_FlashOK == 1)
{
SPI_WriteByte(SFC_SECTORER1); //发送扇区擦除命令
}
else
{
SPI_WriteByte(SFC_SECTORER2); //发送扇区擦除命令
}
SPI_WriteByte(((u8 *)&addr)[1]); //设置起始地址
SPI_WriteByte(((u8 *)&addr)[2]);
SPI_WriteByte(((u8 *)&addr)[3]);
SPI_CE_High();
}
}3.6 写入多个字节到存储的FLASH里面
void SPI_Write_Nbytes(u32 addr, u8 *buffer, u8 size)
{
if(size == 0) return;
if(!B_FlashOK) return;
while(CheckFlashBusy() > 0); //Flash忙检测
FlashWriteEnable(); //使能Flash写命令
SPI_CE_Low(); // enable device
SPI_WriteByte(SFC_PAGEPROG); // 发送页编程命令
SPI_WriteByte(((u8 *)&addr)[1]); //设置起始地址
SPI_WriteByte(((u8 *)&addr)[2]);
SPI_WriteByte(((u8 *)&addr)[3]);
do{
SPI_WriteByte(*buffer++); //连续页内写
addr++;
if ((addr & 0xff) == 0) break;
}while(--size);
SPI_CE_High(); // disable device
}3.6 读取多个字节到存储的FLASH里面
void SPI_Read_Nbytes(u32 addr, u8 *buffer, u16 size)
{
if(size == 0) return;
if(!B_FlashOK) return;
while(CheckFlashBusy() > 0); //Flash忙检测
SPI_CE_Low(); //enable device
SPI_WriteByte(SFC_READ); //read command
SPI_WriteByte(((u8 *)&addr)[1]); //设置起始地址
SPI_WriteByte(((u8 *)&addr)[2]);
SPI_WriteByte(((u8 *)&addr)[3]);
do{
*buffer = SPI_ReadByte(); //receive byte and store at buffer
buffer++;
}while(--size); //read until no_bytes is reached
SPI_CE_High(); //disable device
}4. 扩展功能
双工通信:发送和接收同时进行(如上例)。
仅发送/接收:可简化函数以节省时间。
高速优化:使用循环展开或汇编代码提高速度。
5. 注意事项
时序要求:确保时钟频率符合从设备要求(通过延时或NOP调整)。
中断处理:若在中断中使用,注意保护关键代码段。
多从机支持:每个从机需独立片选引脚。
我要赚赏金
