串行外设接口(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)。