串行外设接口(SPI)是一种同步串行数据通信接口,常用于 MCU 与外部设备之间进行同步串行通信。CW32L083 内部集成 2 个串行外设 SPI 接口,支持双向全双工、单线半双工和单工通信模式,可配置 MCU 作为 主机或从机,支持多主机通信模式,支持直接内存访问(DMA)。
一、主要功能
• 支持主机模式、从机模式
• 支持全双工、单线半双工、单工
• 可选的 4 位到 16 位数据帧宽度
• 支持收发数据 LSB 或 MSB 在前
• 可编程时钟极性和时钟相位
• 主机模式下通信速率高达 PCLK/2
• 从机模式下通信速率高达 PCLK/4
• 支持多机通信模式
• 8 个带标志位的中断源
• 支持直接内存访问 (DMA)
1.功能框图
SPI 一般通过 4 个引脚与外部设备相连:
• MOSI 主机输出 / 从机输入,用于主机模式下的数据发送和从机模式下的数据接收;
• MISO 主机输入 / 从机输出,用于主机模式下的数据接收和从机模式下的数据发送;
• SCK 同步串行时钟,主机时钟输出和从机时钟输入,发送或接收主机同步时钟;
• CS 从机选择,也用于多机通信时总线冲突检测
CW32L083 支持用户灵活选择 GPIO 作为 SPI 通信引脚,通过 AFR 功能复用实现,具体 SPI 引脚请参考数据手册中的引脚定义。
2.全双工模式
SPI 支持全双工通信模式,在该模式下,主机和从机的移位寄存器通过 MOSI、MISO 两条单向数据线进行连接, 在主机提供的 SCK 时钟信号的控制下同时进行两个方向的数据传输。设置控制寄存器 SPIx_CR1 的 MODE 位域为 0x0,使 SPI 工作于全双工通信模式。
二、FLASH存储器及电路连接
W25Q64JVSSIQ为华邦NOR型FLASH,存储容量为8Mbytes,每页256bytes,总共有32768个可编程页,最大一次可编程大小为256bytes。一次擦除大小可以为4K、32K、64K字节(K=1024)或者全擦除。支持标准SPI、Dual SPI、Quad SPI,最大操作时钟为133MHz。
芯片SPI接口时序要求如下:
由接口时序图可知,空闲电平可高可低,数据在第1个时钟上升沿采样。对应的可以选择用模式0或者模式3和MCU进行对接。
三、实例演示
本实例演示MCU(CW32L083)芯片对外部SPI FLASH(W25Q64JVSSIQ)进行读写访问,SPI接口选择模式0。
1.配置RCC和GPIO
void RCC_Configuration(void) { RCC_HSI_Enable(RCC_HSIOSC_DIV2); //SYSCLK = HSI = 24MHz = HCLK = PCLK //外设时钟使能 RCC_AHBPeriphClk_Enable(DEBUG_UART_GPIO_CLK | RCC_AHB_PERIPH_GPIOC, ENABLE); DEBUG_UART_APBClkENx(DEBUG_UART_CLK, ENABLE); } void GPIO_Configuration(void)//包含UART TX RX 复用和LED开关 { GPIO_InitTypeDef GPIO_InitStructure = {0}; DEBUG_UART_AFTX; //UART TX RX 复用 DEBUG_UART_AFRX; GPIO_InitStructure.Pins = DEBUG_UART_TX_GPIO_PIN; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_Init(DEBUG_UART_TX_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.Pins = DEBUG_UART_RX_GPIO_PIN; GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP; GPIO_Init(DEBUG_UART_RX_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.Pins = GPIO_PIN_3 | GPIO_PIN_2; //PC3 LED1 / PC2 LED2 GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_Init(CW_GPIOC, &GPIO_InitStructure); PC03_SETLOW();//LED灭 PC02_SETLOW(); }
2.配置UART
void UART_Configuration(void) { UART_InitTypeDef UART_InitStructure = {0}; UART_InitStructure.UART_BaudRate = DEBUG_UART_BaudRate; // 波特率 UART_InitStructure.UART_Over = UART_Over_16; // 采样方式 UART_InitStructure.UART_Source = UART_Source_PCLK; // 传输时钟源UCLK UART_InitStructure.UART_UclkFreq = DEBUG_UART_UclkFreq; // 传输时钟UCLK频率 UART_InitStructure.UART_StartBit = UART_StartBit_FE; // 起始位判定方式 UART_InitStructure.UART_StopBits = UART_StopBits_1; // 停止位长度 UART_InitStructure.UART_Parity = UART_Parity_No ; // 校验方式 UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None; //硬件流控 UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx; // 发送/接收使能 UART_Init(DEBUG_UARTx, &UART_InitStructure); }
3.配置SPI接口
void SPI_FLASH_Init(void) { SPI_InitTypeDef SPI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; /************************GPIO Configuration***********************/ RCC_AHBPeriphClk_Enable(FLASH_SPI_SCK_GPIO_CLK | FLASH_SPI_MISO_GPIO_CLK | FLASH_SPI_MOSI_GPIO_CLK | FLASH_SPI_CS_GPIO_CLK, ENABLE); FLASH_SPI_APBClkENx(FLASH_SPI_CLK, ENABLE); //SPI SCK MOSI MISO 复用 FLASH_SPI_AF_SCK; FLASH_SPI_AF_MISO; FLASH_SPI_AF_MOSI; //CS GPIO_InitStructure.Pins = FLASH_SPI_CS_GPIO_PIN; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.IT = GPIO_IT_NONE; GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; GPIO_Init(FLASH_SPI_CS_GPIO_PORT, &GPIO_InitStructure); //SCK GPIO_InitStructure.Pins = FLASH_SPI_SCK_GPIO_PIN; GPIO_InitStructure.IT = GPIO_IT_NONE; GPIO_Init(FLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure); //MOSI GPIO_InitStructure.Pins = FLASH_SPI_MOSI_GPIO_PIN; GPIO_InitStructure.IT = GPIO_IT_NONE; GPIO_Init(FLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure); //MISO GPIO_InitStructure.Pins = FLASH_SPI_MISO_GPIO_PIN; GPIO_InitStructure.Mode = GPIO_MODE_INPUT; GPIO_InitStructure.IT = GPIO_IT_NONE; GPIO_Init(FLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure); //拉高CS FLASH_SPI_CS_HIGH(); /************************SPI Configuration***********************/ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 双线全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主机模式 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 帧数据长度为8bit SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 时钟空闲电平为低 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 第1边沿采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 片选信号由SSI寄存器控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; // 波特率为PCLK的8分频 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 最高有效位 MSB 收发在前 SPI_InitStructure.SPI_Speed = SPI_Speed_High; // 低速SPI SPI_Init(FLASH_SPIx, &SPI_InitStructure); }
4.比较两个缓冲区数据
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength) { while(BufferLength--) { if(*pBuffer1 != *pBuffer2) { return FAILED; } pBuffer1++; pBuffer2++; } return PASSED; }
5.将C库printf函数重新定位到UART
PUTCHAR_PROTOTYPE { UART_SendData_8bit(DEBUG_UARTx, (uint8_t)ch);// 通过UARTx发送一个数据(8bit) while (UART_GetFlagStatus(DEBUG_UARTx, UART_FLAG_TXE) == RESET); return ch; } size_t __write(int handle, const unsigned char * buffer, size_t size) { size_t nChars = 0; if (buffer == 0) {//应该刷新内部缓冲区 "handle" == -1 return 0; } for (size != 0; --size) { UART_SendData_8bit(DEBUG_UARTx, *buffer++); while (UART_GetFlagStatus(DEBUG_UARTx, UART_FLAG_TXE) == RESET); ++nChars; } return nChars; }
6.主程序实现SPI对FLASH的读写(具体对FLASH读写可参考cw32_eval_spi_flash.c)
int32_t main(void) { RCC_Configuration();//配置RCC GPIO_Configuration();//配置GPIO UART_Configuration();//配置UART SPI_FLASH_Init();//配置SPI printf("\r\nCW32L083 SPI FLASH Example\r\n"); DeviceID = SPI_FLASH_DeviceID();//读取Device ID ManufactDeviceID = SPI_FLASH_ManufactDeviceID();//读取Manufacturer / Device ID JedecID = SPI_FLASH_JedecID();//读取JEDEC ID SPI_FLASH_UniqueID(UniqueID);// 读取Unique ID (64bit) printf("\r\nDeviceID = 0x%X\r\nManufactDeviceID = 0x%X\r\nJedecID = 0x%X", DeviceID, ManufactDeviceID, JedecID); printf("\r\nUniqueID = 0x "); for(uint8_t i = 0; i<8; i++) { printf("%X ", UniqueID[i]); } if(JedecID == sJedecID) /* Check JEDEC ID */ { printf("\r\n\nFLASH Detected\r\n");; SPI_FLASH_SectorErase(FLASH_SectorToEraseAddress); //擦除扇区 4KB SPI_FLASH_BufferWrite(TxBuffer, FLASH_WriteAddress, BufferSize); //写数据 printf("\r\n写入的数据为:%s\r\n", TxBuffer); SPI_FLASH_BufferRead(RxBuffer, FLASH_ReadAddress, BufferSize); //读数据 printf("\r\n读出的数据为:%s\r\n", RxBuffer); //检查写入的数据与读出的数据是否一致 TransferStatus = Buffercmp(TxBuffer, RxBuffer, BufferSize); if(TransferStatus == PASSED) { PC03_SETHIGH(); printf("\r\nFLASH Success\r\n"); } else { PC02_SETHIGH(); printf("\r\nFLASH Error 1\r\n"); } SPI_FLASH_SectorErase(FLASH_SectorToEraseAddress); //擦除扇区 4KB SPI_FLASH_BufferRead(RxBuffer, FLASH_ReadAddress, BufferSize); //读数据 for(uint8_t j = 0; j < BufferSize; j++) { if(RxBuffer[j] != 0xFF) { PC02_SETHIGH(); printf("\r\nFLASH Error 2\r\n"); } } } else// (FlashID != sFLASH_ID) { PC02_SETHIGH(); printf("\r\nFLASH Error 3\r\n"); } while(1) { } }
7.演示说明
程序读外部FLASH的DeviceID、ManufactDeviceID、JedecID、UniqueID并打印读取到的芯片相关信息,然后依次进行写入数据、读取数据、比较数据、擦除SECTOR、读取数据等操作,当读写正确时,通过UART打印“FLASH Success”,LED1亮。当读取的JedecID不正确,写入读取内容不一致,擦除后读取数据不为全FF时,打印“FLASH Error 2”,LED2亮。如下图所示:
关于CW32更多详细信息,请访问官方网站www.whxy.com。
来源:武汉芯源半导体
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。