打印
[应用方案]

c语言实现SPI

[复制链接]
1767|21
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
beacherblack|  楼主 | 2023-11-26 20:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
初始化完成以下为spi程序:

/*************
***************/
void SPI_SendData(uint8_t data)
{
        uint8_t cnt;
        for(cnt=0;cnt<8;cnt++)
        {
                SPI_CLK_L;//拉低
          Delay1us(10);//这个延时时间任意,但要大于芯片数据手册上的(纳秒级的)
                if(data &0x80)
                {
                        SPI_MOSI_H;
                }
                else
                {
                        SPI_MOSI_L;
                }
                data <<= 1;//
                Delay1us(10);
                SPI_CLK_H;//拉高
                Delay1us(10);
        }
}

/*****************************************/
uint8_t SPI_ReadData(void)
{
        uint8_t i = 0;
        uint8_t value=0;
        SPI_CLK_H;      
        Delay1us(2);
        for(i=0;i<8;i++)
        {
                SPI_CLK_L;               
                value<<=1;
                if(SPI_MISO_READ())
                {
                        value |= 0x01;
                }
                Delay1us(2);
                SPI_CLK_H;
                Delay1us(2);
        }
      
        return value;
}

/*************
等待擦除完成
**************/
void SPI_WaitErase(void)
{
        uint8_t status = 0x01;
        SPI_CS_L;
        SPI_SendData(0x05); //读取状态寄存器命令
  do
        {
                status = SPI_ReadData();
        }
        while((status&0x01) == 1);
        SPI_CS_H;
}

/************
扇区擦除
4k
**************/
void SPI_EraseSector(uint32_t addr)
{      
        SPI_CLK_L;
        SPI_CS_L;
        SPI_SendData(0x06);
        SPI_CLK_L;
        SPI_CS_H;
        SPI_CLK_L;
        SPI_CS_L;
        SPI_SendData(0x20);
        SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
        SPI_SendData((addr&0xFF00)>>8);
        SPI_SendData((addr&0xFF));
        SPI_CLK_L;
        SPI_CS_H;
        SPI_WaitErase();
}


/************
块擦除
32k
**************/
void SPI_EraseBlock(uint32_t addr)
{      
        SPI_CLK_L;
        SPI_CS_L;
        SPI_SendData(0x06);
        SPI_CLK_L;
        SPI_CS_H;
        SPI_CLK_L;
        SPI_CS_L;
        SPI_SendData(0x52);
        SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
        SPI_SendData((addr&0xFF00)>>8);
        SPI_SendData((addr&0xFF));
        SPI_CLK_L;
        SPI_CS_H;
        SPI_WaitErase();
}


/************
块擦除
64k
**************/
void SPI_Erase64kBlock(uint32_t addr)
{      
        SPI_CLK_L;
        SPI_CS_L;
        SPI_SendData(0x06);
        SPI_CLK_L;
        SPI_CS_H;
        SPI_CLK_L;
        SPI_CS_L;
        SPI_SendData(0xd8);
        SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
        SPI_SendData((addr&0xFF00)>>8);
        SPI_SendData((addr&0xFF));
        SPI_CLK_L;
        SPI_CS_H;
        SPI_WaitErase();
}

/*****************
检测设备是否正确
设备ID为ID=0XEF4017
正常才继续后面的页写入和读取数据
******************/
uint32_t SPI_CheckDevice(void)
{
        uint8_t temp0,temp1,temp2;
        uint32_t Device_ID=0;
        SPI_CLK_L;
        SPI_CS_L;
        SPI_SendData(0x9F);
        temp0 = SPI_ReadData();
  temp1 = SPI_ReadData();
        temp2 = SPI_ReadData();
        SPI_CLK_L;
        SPI_CS_H;
        Device_ID = (temp0<<16) | (temp1<<8) | (temp2);
        return Device_ID;
}


/**************
读取数据
readlen: 读取长度
***************/
void SPI_BufferRead(uint8_t *pBuffer,uint32_t addr,uint32_t readlen)
{
        SPI_CLK_L;
        SPI_CS_L;
        SPI_SendData(0x03);//读数据命令
        SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
        SPI_SendData((addr&0xFF00)>>8);
        SPI_SendData((addr&0xFF));
        while(readlen--)
        {
                *pBuffer = SPI_ReadData();
                 pBuffer++;
        }
        SPI_CLK_L;
        SPI_CS_H;
}

/************
页写入---写入字节数低于256可用页编程
**************/
void SPI_PageWrite(uint8_t *pBuffer,uint32_t addr,uint16_t writelen)
{      
        SPI_CLK_L;
        SPI_CS_L;
        SPI_SendData(0x06);
        SPI_CLK_L
        SPI_CS_H;
        SPI_CLK_L;//拉低
        SPI_CS_L;
        SPI_SendData(0x02);
        SPI_SendData((addr&0xFF0000)>>16);
        SPI_SendData((addr&0xFF00)>>8);
        SPI_SendData((addr&0xFF));
        if(writelen>Page_Size)
        {
//                printf("\r\n页写入最大为256字节\r\n");
                writelen = Page_Size ;
        }
        while(writelen--)
        {
                SPI_SendData(*pBuffer);
                pBuffer++;
        }
        SPI_CLK_L;//拉低
        SPI_CS_H;
        SPI_WaitErase();
}


/*************
写入任意长度数据
*pBuffer :待写入数据
addr                 :需写入flash的地址
numToWrite :待写入数据长度
***************/
void SPI_BufferWrite(uint8_t *pBuffer,uint32_t addr,uint32_t numToWrite)
{
        uint32_t count=0,numPage=0,numSingle=0,Addr=0;
        Addr = addr%Page_Size;//某个page的地址
        count = Page_Size - Addr;//某page剩余字节数
        numPage = numToWrite/Page_Size;//待写入的数据占用page数
        numSingle = numToWrite%Page_Size;//待写入数据不够一个page的字节数
        if(Addr == 0)//在某page 起始位置 1
        {
                if(numPage==0)//写入字节数小于一个page
                {
                        SPI_PageWrite(pBuffer,addr,numToWrite);
                }
                else//写入字节数大于等于1个page
                {
                        while(numPage--)
                        {
                                SPI_PageWrite(pBuffer,addr,Page_Size);
                                pBuffer+=Page_Size;
                                addr+=Page_Size;
                        }
                        SPI_PageWrite(pBuffer,addr,numSingle);
                }
        }
        else //在某page的2~256位置
        {
                if(numPage==0)//写入字节数小于一个page
                {
                        if(numSingle>count)//剩余字节数>该page剩余位置
                        {
                                SPI_PageWrite(pBuffer,addr,count);//向该page的剩余位置写入对应字节
                                pBuffer+=count;
                                addr+=count;
                                SPI_PageWrite(pBuffer,addr,(numSingle-count));//其余的写入下一个page
                        }
                        else
                        {
                                SPI_PageWrite(pBuffer,addr,numToWrite);
                        }
                }
                else//写入字节数大于等于一个page
                {
                        numToWrite -= count;
                        numPage = numToWrite/Page_Size;
                        numSingle        =        numToWrite%Page_Size;
                        SPI_PageWrite(pBuffer,addr,count);//写入该page还可写入的字节数
                        addr+=count;
                        pBuffer+=count;
                        while(numPage--)//page个数
                        {
                                SPI_PageWrite(pBuffer,addr,Page_Size);
                                pBuffer+=Page_Size;
                                addr+=Page_Size;
                        }
                        if(numSingle!=0)//剩余字节数
                        {
                                SPI_PageWrite(pBuffer,addr,numSingle);
                        }
                }
        }
        SPI_WaitErase();
}


/************
数据比较
len :数据长度
不等:0
相等:1
************/
uint8_t pBuffer_Cmp(uint8_t *buffer1,uint8_t *buffer2,uint32_t len)
{
        while(len--)
        {
                if(*buffer1!=*buffer2)
                {
                        return 0;
                }
                buffer1++;
                buffer2++;
        }
        return 1;
}

uint8_t Write_pBuffer[]={"软件模拟SPI成功!"};
uint8_t Read_pBuffer[3500]={0};


/***********
读写测试,验证是否正常
***********/
void SPI_Test(void)
{
        uint32_t Device_ID = 0;
        Device_ID = SPI_CheckDevice();
//        printf("ID=0x%x \r\n", Device_ID);
        if(Device_ID == 0xEF4016)
        {
                if(comparm.DebugComm & F_DebugStart)
                        DebgStr_FramProc("SPI FLASH设备正常!\r\n");
                SPI_EraseSector(0x001000);//sector1
                SPI_BufferWrite(Write_pBuffer,0x001000,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1));
                SPI_BufferRead(Read_pBuffer,0x001000,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1));
                if(pBuffer_Cmp(Write_pBuffer,Read_pBuffer,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1)) == 1)
                {      
                       
                        if(comparm.DebugComm & F_DebugStart)
                                DebgStr_FramProc("软件模拟SPI的读写数据一致!\r\n");
//                        DebgStr_FramProc("\r\n 读出的数据为:%s \r\n", Read_pBuffer);
                }
                else
                {
                        if(comparm.DebugComm & F_DebugStart)
                                DebgStr_FramProc("软件模拟SPI的读写数据不一致!\r\n");
                }
        }
        else
        {
                if(comparm.DebugComm & F_DebugStart)
                        DebgStr_FramProc("SPI FLASH设备异常!\r\n");
        }
}

使用特权

评论回复
沙发
maudlu| | 2023-12-2 15:33 | 只看该作者
SPI通信的时钟极性可以选择为高电平有效或低电平有效

使用特权

评论回复
板凳
jtracy3| | 2023-12-2 16:49 | 只看该作者
可以使用中断和DMA技术。中断可以在数据传输完成时触发,DMA可以在后台进行数据传输,避免CPU占用。

使用特权

评论回复
地板
wilhelmina2| | 2023-12-2 19:46 | 只看该作者
SPI通信的时钟相位可以选择为早采样或晚采样

使用特权

评论回复
5
geraldbetty| | 2023-12-2 20:05 | 只看该作者
尽量利用标准库函数来简化代码,例如使用memset函数来清零数组,这可以轻易减少大量的代码,同时不会损失太多效率。

使用特权

评论回复
6
olivem55arlowe| | 2023-12-2 20:16 | 只看该作者
在SPI通信过程中,数据传输可能受到干扰,因此需要在接收端对数据进行缓冲和处理。

使用特权

评论回复
7
sheflynn| | 2023-12-2 20:31 | 只看该作者
SPI设备在通信时,时钟由SPI主设备提供。因此,当SPI接口配置成从机模式时,对其频率进行设置的参数其实是无意义的。

使用特权

评论回复
8
abotomson| | 2023-12-2 20:45 | 只看该作者
如果需要实现实时通信,可能需要使用中断处理机制。

使用特权

评论回复
9
rosemoore| | 2023-12-2 21:53 | 只看该作者
时钟相位可以选择为早采样或晚采样

使用特权

评论回复
10
tabmone| | 2023-12-2 22:45 | 只看该作者
SPI接口的时钟频率会影响数据传输的速度和精度

使用特权

评论回复
11
wwppd| | 2023-12-4 11:48 | 只看该作者
在SPI通信中,主设备发起通信,从设备响应。确保在发送数据之前,主设备已经初始化并准备好接收数据。同时,要注意控制传输顺序,以避免数据混乱。

使用特权

评论回复
12
mnynt121| | 2023-12-5 19:19 | 只看该作者
SPI接口的数据模式是指数据传输的方式和顺序。

使用特权

评论回复
13
uiint| | 2023-12-6 18:37 | 只看该作者
了解SPI的基本概念和原理也是非常重要的,包括它是串行外设接口(Serial Peripheral Interface),支持全双工、同步操作,并且只需四根线就可以完成主从设备间的通信。

使用特权

评论回复
14
biechedan| | 2023-12-8 16:41 | 只看该作者
在SPI通信过程中,可能会出现各种错误,如数据传输错误、时钟同步错误等

使用特权

评论回复
15
uytyu| | 2023-12-9 14:38 | 只看该作者
SPI接口需要两个主机:发送器和接收器。在实现SPI时,需要正确连接发送器和接收器的引脚,并保证引脚的极性、电平等问题。

使用特权

评论回复
16
backlugin| | 2023-12-10 18:50 | 只看该作者
在C语言中实现SPI通信时,需要编写函数来实现数据的发送和接收。在发送数据时,需要将数据放在SCLK引脚上,然后在下一个时钟周期的上升沿读取MOSI引脚上的数据。在接收数据时,需要读取MOSI引脚上的数据,然后在下一个时钟周期的上升沿将数据放在SCLK引脚上。

使用特权

评论回复
17
mnynt121| | 2023-12-12 15:48 | 只看该作者
注意正确设置SPI设备的时钟分频数。例如,如果将SPI设备的时钟分频数设置为4,那么就需要根据这个参数计算出各自的SCLK频率。

使用特权

评论回复
18
uptown| | 2023-12-12 16:06 | 只看该作者
需要熟悉SPI协议,包括SPI协议的工作原理、通信模式、数据帧格式等。

使用特权

评论回复
19
hilahope| | 2023-12-13 11:06 | 只看该作者
需要根据具体的SPI设备确定其引脚定义,包括MISO、MOSI、SCLK、CS等。

使用特权

评论回复
20
ccook11| | 2023-12-13 12:55 | 只看该作者
如果SPI通信的速度较快,可能需要使用中断的方式来处理数据的发送和接收。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

14

主题

1283

帖子

1

粉丝