STM32笔记之 SPI(硬件 or 模拟实现)

您所在的位置:网站首页 模拟片是什么 STM32笔记之 SPI(硬件 or 模拟实现)

STM32笔记之 SPI(硬件 or 模拟实现)

2024-07-14 04:28| 来源: 网络整理| 查看: 265

写在前面: 本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。

目录

一、SPI协议

二、W25Qxx芯片

三、硬件的 SPI

四、模拟的 SPI

五、对 W25Q64的实际应用

一、SPI协议

在实现 SPI功能之前,我们必选先了解好 SPI协议是怎么实现的,当我们知道它是怎么来实现,那么这个就好办,只要跟着对应模式的协议走就行了

关于 SPI协议的了解,这里就不多说,可以看之前的文章:UART/ USRAT、I2C、SPI通信方式扫盲

二、W25Qxx芯片

理论总是枯燥的,实验则是愉悦的;在正式使用 SPI之前,我们需要一个载体来进行实验以加深印象,这里就用 Flash存储芯片 W25Q64来进行实验;然后在这里简单介绍一下:

SPI flash W25Qxx:

W25Q系列的 spiflash。每页(Page)256B,每16个page为一个sector(扇区=4KB),每16个扇区为一个block(块=64KB)

W25Q64 = 64Mb = 8MB = 8192KB = 128block = 2048sector = 32768page; 然后它是可以用 SPI mode 0跟 SPI mode 3来进行通讯的

它的指令表如下:

最后,放一些它的操作时序,这里就放两个吧,嗯嗯,实在是太多了,就放两个,也是最重要、最简单的

最后的最后,下面例程是用 Mode 3来进行对 W25Qxx通讯

三、硬件的 SPI

先来说硬件的 SPI,顺便了解一下 STM32的硬件结构

1、特征:

● 3线全双工同步传输

● 带或不带第三根双向数据线的双线单工同步传输

● 8或16位传输帧格式选择

● 主或从操作

● 支持多主模式

● 8个主模式波特率预分频系数(最大为fPCLK/2)

● 从模式频率 (最大为fPCLK/2)

● 主模式和从模式的快速通信

● 主模式和从模式下均可以由软件或硬件进行NSS管理:主/从操作模式的动态改变

● 可编程的时钟极性和相位

● 可编程的数据顺序,MSB在前或LSB在前

● 可触发中断的专用发送和接收标志

● SPI总线忙状态标志

● 支持可靠通信的硬件CRC

─ 在发送模式下,CRC值可以被作为最后一个字节发送

─ 在全双工模式中对接收到的最后一个字节自动进行CRC校验

● 可触发中断的主模式故障、过载以及CRC错误标志

● 支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求

2、框图结构

3、一般的单主单从接线方式

4、模式配置

主模式

从模式

5、硬件 SPI配置的部分代码

/************************************************ 函数名称 : W25Qxx_Config 功 能 : W25Qxx配置 参 数 : 无 返 回 值 : 无 *************************************************/ void W25Qxx_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; /* W25Q_SPIx IO Periph clock enable */ W25Q_IO_APBxClock_FUN(W25Q_CS_CLK | W25Q_SCK_CLK | W25Q_MISO_CLK | W25Q_MOSI_CLK, ENABLE); /* W25Q_SPIx Periph clock enable */ W25Q_SPI_APBxClock_FUN(W25Q_SPI_CLK, ENABLE); /* Configure W25Q_SPIx pins: CS, SCK, MISO and MOSI */ /* Confugure CS pin as Output Push Pull */ GPIO_InitStructure.GPIO_Pin = W25Q_CS_PINS; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(W25Q_CS_PORT, &GPIO_InitStructure); /* Confugure SCK and MOSI pins as Alternate Function Push Pull */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = W25Q_SCK_PINS; GPIO_Init(W25Q_SCK_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = W25Q_MOSI_PINS; GPIO_Init(W25Q_MOSI_PORT, &GPIO_InitStructure); /* Confugure MISO pin as Input Floating */ GPIO_InitStructure.GPIO_Pin = W25Q_MISO_PINS; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(W25Q_MISO_PORT, &GPIO_InitStructure); /* ---------- END ---------- */ W25Q_CS(HIGH); /* W25Q_SPIx configuration */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(W25Q_SPIx, &SPI_InitStructure); /* Disable W25Q_SPIx CRC calculation */ SPI_CalculateCRC(W25Q_SPIx, DISABLE); /* Enable W25Q_SPIx */ SPI_Cmd(SPI1, ENABLE); } /************************************************ 函数名称 : SPI_Flash_SendByte 功 能 : 使用SPI发送/ 返回一个字节的数据 参 数 : wData ---- 写数据 返 回 值 : rData ---- 读数据 *************************************************/ static uint8_t SPI_Flash_SendByte( uint8_t wData ) { W25Q_TimeOut = MAX_TIME_OUT; /* Wait for W25Q_SPIx Tx buffer empty */ while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_TXE) == RESET) { if(0 == (W25Q_TimeOut--)) return TimeOut_Callback(0); } /* Send byte through the W25Q_SPIx peripheral */ SPI_I2S_SendData(W25Q_SPIx, wData); W25Q_TimeOut = MAX_TIME_OUT; /* Wait for W25Q_SPIx data reception */ while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_RXNE) == RESET) { if(0 == (W25Q_TimeOut--)) return TimeOut_Callback(1); } /* Return the byte read from the W25Q_SPIx bus */ return SPI_I2S_ReceiveData(W25Q_SPIx); }

四、模拟的 SPI

模拟的 SPI其实就是我们自己利用 I/O管脚去实现电平的变化,以仿照出 SPI协议中的时序状态

因为 SPI目前有 4种模式,对于每种模式,它的时序电平都是不一样,但总体来说还是差不多

/* * MODE 0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第 1个边沿, \ * 也就是 SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据切换是在下降沿(第 2个边沿)。 * MODE 1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据采样是在第 2个边沿, \ * 也就是 SCLK由高电平到低电平的跳变,所以数据采样是在下降沿,数据切换是在上升沿(第 1个边沿)。 * MODE 2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采样是在第 1个边沿, \ * 也就是 SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据切换是在上升沿(第 2个边沿)。 * MODE 3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据采样是在第 2个边沿, \ * 也就是 SCLK由低电平到高电平的跳变,所以数据采集是在上升沿,数据切换是在下降沿(第 1个边沿)。 */ Leading EdgeTrailing EdgeSPI ModeCPOL = 0, CPHA = 0Sample (rising)Setup (falling)Mode 0CPOL = 0, CPHA = 1Setup (rising)Sample (falling)Mode 1CPOL = 1, CPHA = 0Sample (falling)Setup (rising)Mode 2CPOL = 1, CPHA = 1Setup (falling)Setup (rising)Mode 3

1、因为我们是用模拟的 SPI,所以不用纠结于必须用硬件上的 SPI接口,只要是个能正常输出电平的 I/O就可以了,这样也使得我们方便移植

/************************************************ 函数名称 : Simulate_SPI_Config 功 能 : 模拟 SPI IO配置 参 数 : 无 返 回 值 : 无 *************************************************/ void Simulate_SPI_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; SL_SPI_SCK_APBxClock_FUN(SL_SPI_SCK_CLK, ENABLE); SL_SPI_MOSI_APBxClock_FUN(SL_SPI_MOSI_CLK, ENABLE); SL_SPI_MISO_APBxClock_FUN(SL_SPI_MISO_CLK, ENABLE); GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* SCK */ GPIO_InitStructure.GPIO_Pin = SL_SPI_SCK_PINS; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(SL_SPI_SCK_PORT, &GPIO_InitStructure); /* MISO */ GPIO_InitStructure.GPIO_Pin = SL_SPI_MISO_PINS; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(SL_SPI_MISO_PORT, &GPIO_InitStructure); /* MOSI */ GPIO_InitStructure.GPIO_Pin = SL_SPI_MOSI_PINS; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(SL_SPI_MOSI_PORT, &GPIO_InitStructure); }

2、Mode 3下的模拟时序实现

#elif (1 == _CPOL && 1 == _CPHA) /* ----- MODE 3 ----- */ /************************************************ 函数名称 : Write_SPI_Byte 功 能 : SPI写读一个字节 参 数 : Byte ---- 数据 返 回 值 : Byte ---- 数据 *************************************************/ uint8_t Write_SPI_Byte( uint8_t Byte ) { uint8_t i; SPI_SCK(HIGH); for(i = 0;i < 8;i++) { SPI_SCK(LOW); SPI_Delay_us(WAIT_TIME); // 空等待 #if 0 SPI_MOSI((Byte & 0x80) >> 7); #else if(Byte & 0x80) { SPI_MOSI(HIGH); } else { SPI_MOSI(LOW); } #endif Byte W25Q_PAGE_SIZE */ { /* 地址不对齐多出的 count分开处理,不加入这个运算 */ Len -= count; NumOfPage = Len / W25Q_PAGE_SIZE; NumOfSingle = Len % W25Q_PAGE_SIZE; if(count != 0) { /* 先写完count个数据,为的是让下一次要写的地址对齐 */ W25Qxx_Page_Program(pBuffer, Address, count); /* 接下来就重复地址对齐的情况 */ Address += count; pBuffer += count; } /* 把整数页都写了 */ while(NumOfPage--) { W25Qxx_Page_Program(pBuffer, Address, W25Q_PAGE_SIZE); Address += W25Q_PAGE_SIZE; pBuffer += W25Q_PAGE_SIZE; } /* 若有多余的不满一页的数据,把它写完*/ if(NumOfSingle != 0) { W25Qxx_Page_Program(pBuffer, Address, NumOfSingle); } } } } /************************************************ 函数名称 : W25Qxx_Read_Flash 功 能 : 读 W25Qxx闪存数据 参 数 : pBuffer ---- 数据 Address ---- 地址 Len ---- 长度 返 回 值 : 无 *************************************************/ void W25Qxx_Read_Flash( uint8_t *pBuffer, uint32_t Address, uint16_t Len ) { W25Q_CS(LOW); SPI_Flash_SendByte(W25Q_READ_DATA); SPI_Flash_SendByte((Address & 0xFF0000) >> 16); SPI_Flash_SendByte((Address & 0xFF00) >> 8); SPI_Flash_SendByte(Address & 0xFF); /* 读取数据 */ while(Len--) { *pBuffer = SPI_Flash_SendByte(W25Q_DUMMY_BYTE); pBuffer++; } W25Q_CS(HIGH); } /************************************************ 函数名称 : W25Qxx_Sector_Erase 功 能 : FLASH扇区擦除 参 数 : Address ---- 擦除地址 返 回 值 : 无 *************************************************/ void W25Qxx_Sector_Erase( uint32_t Address ) { W25Qxx_Write_Enable(); W25Q_CS(LOW); SPI_Flash_SendByte(W25Q_SECTOR_ERASE); SPI_Flash_SendByte((Address & 0xFF0000) >> 16); SPI_Flash_SendByte((Address & 0xFF00) >> 8); SPI_Flash_SendByte(Address & 0xFF); W25Q_CS(HIGH); W25Qxx_Busy_Wait(); } /************************************************ 函数名称 : W25Qxx_Chip_Erase 功 能 : FLASH整片擦除(为了安全起见,若要调用,请先调用 W25Qxx_Write_Enable函数) 参 数 : 无 返 回 值 : 无 *************************************************/ void W25Qxx_Chip_Erase(void) { W25Q_CS(LOW); SPI_Flash_SendByte(W25Q_CHIP_ERASE); W25Q_CS(HIGH); W25Qxx_Busy_Wait(); } /************************************************ 函数名称 : W25Qxx_Write_Enable 功 能 : W25Qxx写使能 参 数 : 无 返 回 值 : 无 *************************************************/ void W25Qxx_Write_Enable(void) { uint8_t flash_status = 0; W25Q_CS(LOW); SPI_Flash_SendByte(W25Q_WRITE_ENABLE); W25Q_CS(HIGH); W25Q_CS(LOW); /* 等待写使能位置 1 */ do { flash_status = SPI_Flash_SendByte(W25Q_STATUS_REG1); }while(!(flash_status & BIT_WEL)); W25Q_CS(HIGH); } /************************************************ 函数名称 : W25Qxx_Write_Disable 功 能 : W25Qxx写失能 参 数 : 无 返 回 值 : 无 *************************************************/ void W25Qxx_Write_Disable(void) { uint8_t flash_status = 0; W25Q_CS(LOW); SPI_Flash_SendByte(W25Q_WRITE_DISABLE); W25Q_CS(HIGH); W25Q_CS(LOW); /* 等待写使能清 0 */ do { flash_status = SPI_Flash_SendByte(W25Q_STATUS_REG1); }while(!(flash_status & BIT_WEL)); W25Q_CS(HIGH); } /************************************************ 函数名称 : W25Qxx_Power_Down 功 能 : W25Qxx掉电 参 数 : 无 返 回 值 : 无 *************************************************/ void W25Qxx_Power_Down(void) { W25Q_CS(LOW); SPI_Flash_SendByte(W25Q_POWER_DOWN); W25Q_CS(HIGH); } /************************************************ 函数名称 : W25Qxx_Release_PowerDown 功 能 : W25Qxx唤醒 参 数 : 无 返 回 值 : 无 *************************************************/ void W25Qxx_Release_PowerDown(void) { W25Q_CS(LOW); SPI_Flash_SendByte(W25Q_RELEASE_POWER_DOWN); W25Q_CS(HIGH); } /************************************************ 函数名称 : W25Qxx_Config 功 能 : W25Qxx配置 参 数 : 无 返 回 值 : 无 *************************************************/ void W25Qxx_Config(void) { #if USE_SIMULATE_SPI GPIO_InitTypeDef GPIO_InitStructure; W25Q_CS_APBxClock_FUN(W25Q_CS_CLK, ENABLE); /* Confugure CS pin as Output Push Pull */ GPIO_InitStructure.GPIO_Pin = W25Q_CS_PINS; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(W25Q_CS_PORT, &GPIO_InitStructure); /* Smiulate IO Config */ Simulate_SPI_Config(); W25Q_CS(HIGH); SPI_SCK(HIGH); SPI_MOSI(HIGH); #else GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; /* W25Q_SPIx IO Periph clock enable */ W25Q_IO_APBxClock_FUN(W25Q_CS_CLK | W25Q_SCK_CLK | W25Q_MISO_CLK | W25Q_MOSI_CLK, ENABLE); /* W25Q_SPIx Periph clock enable */ W25Q_SPI_APBxClock_FUN(W25Q_SPI_CLK, ENABLE); /* Configure W25Q_SPIx pins: CS, SCK, MISO and MOSI */ /* Confugure CS pin as Output Push Pull */ GPIO_InitStructure.GPIO_Pin = W25Q_CS_PINS; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(W25Q_CS_PORT, &GPIO_InitStructure); /* Confugure SCK and MOSI pins as Alternate Function Push Pull */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = W25Q_SCK_PINS; GPIO_Init(W25Q_SCK_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = W25Q_MOSI_PINS; GPIO_Init(W25Q_MOSI_PORT, &GPIO_InitStructure); /* Confugure MISO pin as Input Floating */ GPIO_InitStructure.GPIO_Pin = W25Q_MISO_PINS; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(W25Q_MISO_PORT, &GPIO_InitStructure); /* ---------- END ---------- */ W25Q_CS(HIGH); /* W25Q_SPIx configuration */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(W25Q_SPIx, &SPI_InitStructure); /* Disable W25Q_SPIx CRC calculation */ SPI_CalculateCRC(W25Q_SPIx, DISABLE); /* Enable W25Q_SPIx */ SPI_Cmd(SPI1, ENABLE); #endif /* USE_SIMULATE_SPI */ } /************************************************ 函数名称 : W25Qxx_Init 功 能 : W25Qxx初始化 参 数 : 无 返 回 值 : 无 *************************************************/ void W25Qxx_Init(void) { uint32_t FlashID = 0; W25Qxx_Config(); #if(_W25Q_DUBUG) FlashID = W25Qxx_Read_JEDECID(); W25Q_DUBUG_PRINTF("FlashID is 0x%X,Manufacturer Device ID is 0x%X\r\n", \ FlashID, W25Qxx_Read_DeviceID()); if(FlashID != JEDEC_ID) { /* 读取错误处理 */ W25Q_DUBUG_PRINTF("SPI read-write Error, please check the connection between MCU and SPI Flash\n"); } else { /* 读取成功处理 */ W25Q_DUBUG_PRINTF("SPI read-write succeed\n"); // uint8_t Tx_buff[] = "FLASH读写测试实验\r\n"; // uint8_t Rx_buff[] = "FLASH读写测试实验\r\n"; // W25Qxx_Sector_Erase(0x0000); // W25Qxx_Write_Flash(Tx_buff, 0x0000, (sizeof(Tx_buff) / sizeof(*(Tx_buff)))); // W25Qxx_Read_Flash(Rx_buff, 0x0000, (sizeof(Tx_buff) / sizeof(*(Tx_buff)))); // W25Q_DUBUG_PRINTF("读出的数据:%s\n", Rx_buff); } #endif /* _W25Q_DUBUG */ } /*---------------------------- END OF FILE ----------------------------*/

代码:BasicDriver/W25Qxx at master · Arachnid-97/BasicDriver · GitHub



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3