本帖最后由 xld0932 于 2024-3-14 15:26 编辑
1.概述
国民技术N32A455系列MCU搭载了3路SPI接口,同时还具备I2S功能;SPI允许芯片与外部设备以半/全双工、同步、串行方式通信。此接口可以被配置成主模式,并为外部从设备提供通信时钟(SCK)。接口还能以多主配置方式工作。它可用于多种用途,包括使用一条双向数据线的双线单工同步传输,还可使用CRC校验的可靠通信。
2.N32A455开发板SPI FLASH原理图
开发板板载一路SPI FLASH(W25Q128JVSIQTR),占用了SPI1外设资源,通过设置跳线J29、 J30、 J31、 J32与MCU的PA4、PA5、PA6、PA7相连接,所以在做SPI FLASH实验时,需要注意跳线的连接状态
3.SPI示例程序
我们参考官方的SPI FLASH示例工程,通过SPI1接口对SPI FLASH进行读、写、擦除等操作,通过修改代码将每个过程的数据打印出来,更加的直观:
- int main(void)
- {
-
-
- log_init();
- LedInit(GPIOE, LED1 | LED2);
- LedOff(GPIOE, LED1 | LED2);
-
-
- sFLASH_Init();
-
-
- FlashID = sFLASH_ReadID();
- printf("\r\nRead ID=0x%06x",FlashID);
-
- if ((FlashID == sFLASH_ID)||(FlashID == sFLASH_W25Q128_ID_DTR))
- {
-
- LedOn(GPIOE, LED1);
-
-
- sFLASH_EraseSector(FLASH_SectorToErase);
-
-
- sFLASH_ReadBuffer(Rx_Buffer, FLASH_ReadAddress, BufferSize);
-
- printf("\r\nRead after Erase...");
-
- for (uint32_t i = 0; i < BufferSize; i++)
- {
- if ((i % 16) == 0)
- {
- printf("\r\n");
- }
-
- printf("0x%02X ", Rx_Buffer[i]);
- }
-
- printf("\r\n");
-
-
- sFLASH_WriteBuffer(Tx_Buffer, FLASH_WriteAddress, BufferSize);
-
-
- sFLASH_ReadBuffer(Rx_Buffer, FLASH_ReadAddress, BufferSize);
-
- printf("\r\nRead after Write...");
-
- for (uint32_t i = 0; i < BufferSize; i++)
- {
- if ((i % 16) == 0)
- {
- printf("\r\n");
- }
-
- printf("0x%02X ", Rx_Buffer[i]);
- }
-
- printf("\r\n");
-
- printf("\r\n%s", Rx_Buffer);
-
- printf("\r\n");
-
-
- TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
- if(PASSED == TransferStatus1)
- printf("\r\nFlash write OK.");
- else
- printf("\r\nFlash write fail.");
-
-
-
-
-
-
-
- sFLASH_EraseSector(FLASH_SectorToErase);
-
-
- sFLASH_ReadBuffer(Rx_Buffer, FLASH_ReadAddress, BufferSize);
-
- printf("\r\nRead after Erase...");
-
- for (uint32_t i = 0; i < BufferSize; i++)
- {
- if ((i % 16) == 0)
- {
- printf("\r\n");
- }
-
- printf("0x%02X ", Rx_Buffer[i]);
- }
-
- printf("\r\n");
-
-
- for (Index = 0; Index < BufferSize; Index++)
- {
- if (Rx_Buffer[Index] != 0xFF)
- {
- TransferStatus2 = FAILED;
- break;
- }
- }
-
- if(PASSED == TransferStatus2)
- printf("\r\nSector erase OK.");
- else
- printf("\r\nSector erase fail.");
-
-
- }
- else
- {
-
- LedOn(GPIOE, LED2);
- printf("\r\nRead ID fail.");
- }
-
- while (1)
- {
- }
- }
4.运行结果
4.1.硬件环境
4.2.运行结果
5.使用SPI2操作SPI FLASH的注意事项
N32A455开发板上的PE10\PE11\PE12\PE13这几个接口作为SPI2通讯接口,正好也没有被其它板载资源所占用,所以就使用这4个引脚来验证一下SPI2操作SPI FLASH的功能,首先需要对程序进行配置
5.1.在spi_flash.h文件中对操作SPI FLASH的接口和外设进行定义:
- #if 0
- #define sFLASH_SPI SPI1
- #define sFLASH_SPI_CLK RCC_APB2_PERIPH_SPI1
- #define sFLASH_SPI_SCK_PIN GPIO_PIN_5 /* PA.05 */
- #define sFLASH_SPI_SCK_GPIO_PORT GPIOA /* GPIOA */
- #define sFLASH_SPI_SCK_GPIO_CLK RCC_APB2_PERIPH_GPIOA
- #define sFLASH_SPI_MISO_PIN GPIO_PIN_6 /* PA.06 */
- #define sFLASH_SPI_MISO_GPIO_PORT GPIOA /* GPIOA */
- #define sFLASH_SPI_MISO_GPIO_CLK RCC_APB2_PERIPH_GPIOA
- #define sFLASH_SPI_MOSI_PIN GPIO_PIN_7 /* PA.07 */
- #define sFLASH_SPI_MOSI_GPIO_PORT GPIOA /* GPIOA */
- #define sFLASH_SPI_MOSI_GPIO_CLK RCC_APB2_PERIPH_GPIOA
- #define sFLASH_CS_PIN GPIO_PIN_4 /* PA.04 */
- #define sFLASH_CS_GPIO_PORT GPIOA /* GPIOA */
- #define sFLASH_CS_GPIO_CLK RCC_APB2_PERIPH_GPIOA
- #else
- #define sFLASH_SPI SPI2
- #define sFLASH_SPI_CLK RCC_APB1_PERIPH_SPI2
- #define sFLASH_SPI_SCK_PIN GPIO_PIN_11/* PE.11 */
- #define sFLASH_SPI_SCK_GPIO_PORT GPIOE /* GPIOE */
- #define sFLASH_SPI_SCK_GPIO_CLK RCC_APB2_PERIPH_GPIOE
- #define sFLASH_SPI_MISO_PIN GPIO_PIN_12/* PA.12 */
- #define sFLASH_SPI_MISO_GPIO_PORT GPIOE /* GPIOE */
- #define sFLASH_SPI_MISO_GPIO_CLK RCC_APB2_PERIPH_GPIOE
- #define sFLASH_SPI_MOSI_PIN GPIO_PIN_13/* PA.13 */
- #define sFLASH_SPI_MOSI_GPIO_PORT GPIOE /* GPIOE */
- #define sFLASH_SPI_MOSI_GPIO_CLK RCC_APB2_PERIPH_GPIOE
- #define sFLASH_CS_PIN GPIO_PIN_10/* PE.10 */
- #define sFLASH_CS_GPIO_PORT GPIOE /* GPIOE */
- #define sFLASH_CS_GPIO_CLK RCC_APB2_PERIPH_GPIOE
- #endif
5.2.因为SPI1与SPI2这两个外设接口不在同一个总线上,所以需要对官方例程中出现外设时钟配置的地方进行修改,修改spi_flash.c文件中的函数,如下所示:
- void sFLASH_LowLevel_DeInit(void)
- {
- GPIO_InitType GPIO_InitStructure;
-
-
- SPI_Enable(sFLASH_SPI, DISABLE);
-
-
- SPI_I2S_DeInit(sFLASH_SPI);
-
-
- if (sFLASH_SPI_CLK == RCC_APB2_PERIPH_SPI1)
- {
- RCC_EnableAPB2PeriphClk(sFLASH_SPI_CLK, DISABLE);
- }
- else
- {
- RCC_EnableAPB1PeriphClk(sFLASH_SPI_CLK, DISABLE);
- }
-
-
- GPIO_InitStructure.Pin = sFLASH_SPI_SCK_PIN;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- GPIO_InitPeripheral(sFLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.Pin = sFLASH_SPI_MISO_PIN;
- GPIO_InitPeripheral(sFLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.Pin = sFLASH_SPI_MOSI_PIN;
- GPIO_InitPeripheral(sFLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.Pin = sFLASH_CS_PIN;
- GPIO_InitPeripheral(sFLASH_CS_GPIO_PORT, &GPIO_InitStructure);
- }
-
- void sFLASH_LowLevel_Init(void)
- {
- GPIO_InitType GPIO_InitStructure;
-
-
-
- RCC_EnableAPB2PeriphClk(
- sFLASH_CS_GPIO_CLK | sFLASH_SPI_MOSI_GPIO_CLK | sFLASH_SPI_MISO_GPIO_CLK | sFLASH_SPI_SCK_GPIO_CLK, ENABLE);
-
-
- if (sFLASH_SPI_CLK == RCC_APB2_PERIPH_SPI1)
- {
- RCC_EnableAPB2PeriphClk(sFLASH_SPI_CLK, ENABLE);
- }
- else
- {
- RCC_EnableAPB1PeriphClk(sFLASH_SPI_CLK, ENABLE);
-
- GPIO_ConfigPinRemap(GPIO_RMP2_SPI2, ENABLE);
- }
-
-
- GPIO_InitStructure.Pin = sFLASH_SPI_SCK_PIN;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitPeripheral(sFLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.Pin = sFLASH_SPI_MOSI_PIN;
- GPIO_InitPeripheral(sFLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.Pin = sFLASH_SPI_MISO_PIN;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- GPIO_InitPeripheral(sFLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.Pin = sFLASH_CS_PIN;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitPeripheral(sFLASH_CS_GPIO_PORT, &GPIO_InitStructure);
- }
5.3.注意事项:查看SPI2使用的引脚,需要看一下复用功能,需不需要重映射配置;上例中使用了PE10~PE13,是需要进行重映射配置,要不然就无法通讯了!
5.4.SPI2测试环境
5.5.SPI2测试结果
6.工程源代码