[范例教程] c语言实现SPI通信源程序

[复制链接]
1646|22
 楼主| eefas 发表于 2023-7-13 21:42 | 显示全部楼层 |阅读模式
  1. *************
  2. ***************/
  3. void SPI_SendData(uint8_t data)
  4. {
  5.         uint8_t cnt;
  6.         for(cnt=0;cnt<8;cnt++)
  7.         {
  8.                 SPI_CLK_L;//拉低
  9.           Delay1us(10);//这个延时时间任意,但要大于芯片数据手册上的(纳秒级的)
  10.                 if(data &0x80)
  11.                 {
  12.                         SPI_MOSI_H;
  13.                 }
  14.                 else
  15.                 {
  16.                         SPI_MOSI_L;
  17.                 }
  18.                 data <<= 1;//
  19.                 Delay1us(10);
  20.                 SPI_CLK_H;//拉高
  21.                 Delay1us(10);
  22.         }
  23. }

  24. /*****************************************/
  25. uint8_t SPI_ReadData(void)
  26. {
  27.         uint8_t i = 0;
  28.         uint8_t value=0;
  29.         SPI_CLK_H;      
  30.         Delay1us(2);
  31.         for(i=0;i<8;i++)
  32.         {
  33.                 SPI_CLK_L;               
  34.                 value<<=1;
  35.                 if(SPI_MISO_READ())
  36.                 {
  37.                         value |= 0x01;
  38.                 }
  39.                 Delay1us(2);
  40.                 SPI_CLK_H;
  41.                 Delay1us(2);
  42.         }
  43.       
  44.         return value;
  45. }

  46. /*************
  47. 等待擦除完成
  48. **************/
  49. void SPI_WaitErase(void)
  50. {
  51.         uint8_t status = 0x01;
  52.         SPI_CS_L;
  53.         SPI_SendData(0x05); //读取状态寄存器命令
  54.   do
  55.         {
  56.                 status = SPI_ReadData();
  57.         }
  58.         while((status&0x01) == 1);
  59.         SPI_CS_H;
  60. }

  61. /************
  62. 扇区擦除
  63. 4k
  64. **************/
  65. void SPI_EraseSector(uint32_t addr)
  66. {      
  67.         SPI_CLK_L;
  68.         SPI_CS_L;
  69.         SPI_SendData(0x06);
  70.         SPI_CLK_L;
  71.         SPI_CS_H;
  72.         SPI_CLK_L;
  73.         SPI_CS_L;
  74.         SPI_SendData(0x20);
  75.         SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
  76.         SPI_SendData((addr&0xFF00)>>8);
  77.         SPI_SendData((addr&0xFF));
  78.         SPI_CLK_L;
  79.         SPI_CS_H;
  80.         SPI_WaitErase();
  81. }


  82. /************
  83. 块擦除
  84. 32k
  85. **************/
  86. void SPI_EraseBlock(uint32_t addr)
  87. {      
  88.         SPI_CLK_L;
  89.         SPI_CS_L;
  90.         SPI_SendData(0x06);
  91.         SPI_CLK_L;
  92.         SPI_CS_H;
  93.         SPI_CLK_L;
  94.         SPI_CS_L;
  95.         SPI_SendData(0x52);
  96.         SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
  97.         SPI_SendData((addr&0xFF00)>>8);
  98.         SPI_SendData((addr&0xFF));
  99.         SPI_CLK_L;
  100.         SPI_CS_H;
  101.         SPI_WaitErase();
  102. }


  103. /************
  104. 块擦除
  105. 64k
  106. **************/
  107. void SPI_Erase64kBlock(uint32_t addr)
  108. {      
  109.         SPI_CLK_L;
  110.         SPI_CS_L;
  111.         SPI_SendData(0x06);
  112.         SPI_CLK_L;
  113.         SPI_CS_H;
  114.         SPI_CLK_L;
  115.         SPI_CS_L;
  116.         SPI_SendData(0xd8);
  117.         SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
  118.         SPI_SendData((addr&0xFF00)>>8);
  119.         SPI_SendData((addr&0xFF));
  120.         SPI_CLK_L;
  121.         SPI_CS_H;
  122.         SPI_WaitErase();
  123. }

  124. /*****************
  125. 检测设备是否正确
  126. 设备ID为ID=0XEF4017
  127. 正常才继续后面的页写入和读取数据
  128. ******************/
  129. uint32_t SPI_CheckDevice(void)
  130. {
  131.         uint8_t temp0,temp1,temp2;
  132.         uint32_t Device_ID=0;
  133.         SPI_CLK_L;
  134.         SPI_CS_L;
  135.         SPI_SendData(0x9F);
  136.         temp0 = SPI_ReadData();
  137.   temp1 = SPI_ReadData();
  138.         temp2 = SPI_ReadData();
  139.         SPI_CLK_L;
  140.         SPI_CS_H;
  141.         Device_ID = (temp0<<16) | (temp1<<8) | (temp2);
  142.         return Device_ID;
  143. }


  144. /**************
  145. 读取数据
  146. readlen: 读取长度
  147. ***************/
  148. void SPI_BufferRead(uint8_t *pBuffer,uint32_t addr,uint32_t readlen)
  149. {
  150.         SPI_CLK_L;
  151.         SPI_CS_L;
  152.         SPI_SendData(0x03);//读数据命令
  153.         SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
  154.         SPI_SendData((addr&0xFF00)>>8);
  155.         SPI_SendData((addr&0xFF));
  156.         while(readlen--)
  157.         {
  158.                 *pBuffer = SPI_ReadData();
  159.                  pBuffer++;
  160.         }
  161.         SPI_CLK_L;
  162.         SPI_CS_H;
  163. }

  164. /************
  165. 页写入---写入字节数低于256可用页编程
  166. **************/
  167. void SPI_PageWrite(uint8_t *pBuffer,uint32_t addr,uint16_t writelen)
  168. {      
  169.         SPI_CLK_L;
  170.         SPI_CS_L;
  171.         SPI_SendData(0x06);
  172.         SPI_CLK_L
  173.         SPI_CS_H;
  174.         SPI_CLK_L;//拉低
  175.         SPI_CS_L;
  176.         SPI_SendData(0x02);
  177.         SPI_SendData((addr&0xFF0000)>>16);
  178.         SPI_SendData((addr&0xFF00)>>8);
  179.         SPI_SendData((addr&0xFF));
  180.         if(writelen>Page_Size)
  181.         {
  182. //                printf("\r\n页写入最大为256字节\r\n");
  183.                 writelen = Page_Size ;
  184.         }
  185.         while(writelen--)
  186.         {
  187.                 SPI_SendData(*pBuffer);
  188.                 pBuffer++;
  189.         }
  190.         SPI_CLK_L;//拉低
  191.         SPI_CS_H;
  192.         SPI_WaitErase();
  193. }


  194. /*************
  195. 写入任意长度数据
  196. *pBuffer :待写入数据
  197. addr                 :需写入flash的地址
  198. numToWrite :待写入数据长度
  199. ***************/
  200. void SPI_BufferWrite(uint8_t *pBuffer,uint32_t addr,uint32_t numToWrite)
  201. {
  202.         uint32_t count=0,numPage=0,numSingle=0,Addr=0;
  203.         Addr = addr%Page_Size;//某个page的地址
  204.         count = Page_Size - Addr;//某page剩余字节数
  205.         numPage = numToWrite/Page_Size;//待写入的数据占用page数
  206.         numSingle = numToWrite%Page_Size;//待写入数据不够一个page的字节数
  207.         if(Addr == 0)//在某page 起始位置 1
  208.         {
  209.                 if(numPage==0)//写入字节数小于一个page
  210.                 {
  211.                         SPI_PageWrite(pBuffer,addr,numToWrite);
  212.                 }
  213.                 else//写入字节数大于等于1个page
  214.                 {
  215.                         while(numPage--)
  216.                         {
  217.                                 SPI_PageWrite(pBuffer,addr,Page_Size);
  218.                                 pBuffer+=Page_Size;
  219.                                 addr+=Page_Size;
  220.                         }
  221.                         SPI_PageWrite(pBuffer,addr,numSingle);
  222.                 }
  223.         }
  224.         else //在某page的2~256位置
  225.         {
  226.                 if(numPage==0)//写入字节数小于一个page
  227.                 {
  228.                         if(numSingle>count)//剩余字节数>该page剩余位置
  229.                         {
  230.                                 SPI_PageWrite(pBuffer,addr,count);//向该page的剩余位置写入对应字节
  231.                                 pBuffer+=count;
  232.                                 addr+=count;
  233.                                 SPI_PageWrite(pBuffer,addr,(numSingle-count));//其余的写入下一个page
  234.                         }
  235.                         else
  236.                         {
  237.                                 SPI_PageWrite(pBuffer,addr,numToWrite);
  238.                         }
  239.                 }
  240.                 else//写入字节数大于等于一个page
  241.                 {
  242.                         numToWrite -= count;
  243.                         numPage = numToWrite/Page_Size;
  244.                         numSingle        =        numToWrite%Page_Size;
  245.                         SPI_PageWrite(pBuffer,addr,count);//写入该page还可写入的字节数
  246.                         addr+=count;
  247.                         pBuffer+=count;
  248.                         while(numPage--)//page个数
  249.                         {
  250.                                 SPI_PageWrite(pBuffer,addr,Page_Size);
  251.                                 pBuffer+=Page_Size;
  252.                                 addr+=Page_Size;
  253.                         }
  254.                         if(numSingle!=0)//剩余字节数
  255.                         {
  256.                                 SPI_PageWrite(pBuffer,addr,numSingle);
  257.                         }
  258.                 }
  259.         }
  260.         SPI_WaitErase();
  261. }


  262. /************
  263. 数据比较
  264. len :数据长度
  265. 不等:0
  266. 相等:1
  267. ************/
  268. uint8_t pBuffer_Cmp(uint8_t *buffer1,uint8_t *buffer2,uint32_t len)
  269. {
  270.         while(len--)
  271.         {
  272.                 if(*buffer1!=*buffer2)
  273.                 {
  274.                         return 0;
  275.                 }
  276.                 buffer1++;
  277.                 buffer2++;
  278.         }
  279.         return 1;
  280. }

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


  283. /***********
  284. 读写测试,验证是否正常
  285. ***********/
  286. void SPI_Test(void)
  287. {
  288.         uint32_t Device_ID = 0;
  289.         Device_ID = SPI_CheckDevice();
  290. //        printf("ID=0x%x \r\n", Device_ID);
  291.         if(Device_ID == 0xEF4016)
  292.         {
  293.                 if(comparm.DebugComm & F_DebugStart)
  294.                         DebgStr_FramProc("SPI FLASH设备正常!\r\n");
  295.                 SPI_EraseSector(0x001000);//sector1
  296.                 SPI_BufferWrite(Write_pBuffer,0x001000,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1));
  297.                 SPI_BufferRead(Read_pBuffer,0x001000,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1));
  298.                 if(pBuffer_Cmp(Write_pBuffer,Read_pBuffer,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1)) == 1)
  299.                 {      
  300.                        
  301.                         if(comparm.DebugComm & F_DebugStart)
  302.                                 DebgStr_FramProc("软件模拟SPI的读写数据一致!\r\n");
  303. //                        DebgStr_FramProc("\r\n 读出的数据为:%s \r\n", Read_pBuffer);
  304.                 }
  305.                 else
  306.                 {
  307.                         if(comparm.DebugComm & F_DebugStart)
  308.                                 DebgStr_FramProc("软件模拟SPI的读写数据不一致!\r\n");
  309.                 }
  310.         }
  311.         else
  312.         {
  313.                 if(comparm.DebugComm & F_DebugStart)
  314.                         DebgStr_FramProc("SPI FLASH设备异常!\r\n");
  315.         }
  316. }


zerorobert 发表于 2023-8-9 14:55 | 显示全部楼层
如果支持中断方式的SPI通信,可以使用中断来提高效率和响应性
mollylawrence 发表于 2023-8-9 17:18 | 显示全部楼层
可以使用示波器或逻辑分析仪来监视和分析SPI信号,以验证通信的正确性。
juliestephen 发表于 2023-8-9 22:39 | 显示全部楼层
在进行SPI通信之前,需要设置SPI通信参数,包括SPI主机和从机的极性、数据位数、传输方式等。
robincotton 发表于 2023-8-14 11:21 | 显示全部楼层
在编写代码时,考虑到代码的可重用性和可维护性
kkzz 发表于 2023-8-14 12:24 | 显示全部楼层
在C语言中实现SPI通信源程序时,需要注意SPI控制器的初始化、通信参数的设置、数据的控制和错误处理等
fengm 发表于 2023-8-14 13:06 | 显示全部楼层
在程序开始时,需要对SPI控制器进行初始化,包括选择SPI模式、时钟极性、数据位数等参数。
benjaminka 发表于 2023-8-14 14:06 | 显示全部楼层
SPI通信是全双工的,意味着主设备和从设备可以同时发送和接收数据
sdCAD 发表于 2023-8-14 17:26 | 显示全部楼层
SPI通信对时序有严格要求。              
jtracy3 发表于 2023-8-14 19:39 | 显示全部楼层
SPI通信过程中,可能会出现错误,如数据传输错误、时钟同步错误等,需要对这些错误进行处理。
lzmm 发表于 2023-8-14 19:48 | 显示全部楼层
SPI通信有多种模式,包括时钟极性(CPOL)和时钟相位(CPHA)
alvpeg 发表于 2023-8-15 13:07 | 显示全部楼层
设置时钟频率、选择主从机角色以及配置其他相关参数
pixhw 发表于 2023-8-15 17:47 | 显示全部楼层
在SPI通信中,可能会发生错误,如传输错误或超时
maudlu 发表于 2023-8-15 18:31 | 显示全部楼层
使用适当的数据结构和函数来组织代码
yeates333 发表于 2023-8-15 19:09 | 显示全部楼层
选择适当的时钟速率以满足目标设备的要求,并确保主设备和从设备的时钟速率匹配。
jimmhu 发表于 2023-8-15 19:22 | 显示全部楼层
SPI通信的速率由时钟频率决定              
ingramward 发表于 2023-8-15 19:34 | 显示全部楼层
在编程之前,你需要确定SPI接口的引脚分配。通常,SPI接口有三个信号线:MOSI(主从机数据输出/输入线)、SCK(时钟线)和SS(片选线)
bartonalfred 发表于 2023-8-15 20:04 | 显示全部楼层
可以利用中断或DMA来处理SPI通信
saservice 发表于 2023-8-15 22:04 | 显示全部楼层
连接SPI设备,包括主设备和从设备之间的物理连接,以及正确的引脚配置。
mollylawrence 发表于 2023-8-18 22:37 | 显示全部楼层
需要确定合适的缓冲区大小,并确保在发送和接收之间正确管理缓冲区的数据。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

98

主题

3179

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部