#申请原创# @21小跑堂
串行外设接口(SPI)是一种同步串行数据通信接口,常用于 MCU 与外部设备之间进行同步串行通信,CW32L031 内部集成 1 个串行外设 SPI 接口,在开发板上SPI接了一颗W25Q64的FLASH
开发板上实际焊接的是芯源自家的CW25Q64,在https://www.whxy.com/support/filelist/23这里能下载到手册
先来尝试用SPI读取FLAH的ID并用串口打印出来,初始化SPI接口
void spi_init()
{
SPI_InitTypeDef SPI_InitStructure = {0};
GPIO_InitTypeDef GPIO_InitStructure = {0};
__RCC_GPIOA_CLK_ENABLE();
__RCC_SPI1_CLK_ENABLE();
PA10_AFx_SPI1SCK();
PA11_AFx_SPI1MISO();
PA12_AFx_SPI1MOSI();
PA15_AFx_SPI1CS();
GPIO_InitStructure.Pins = GPIO_PIN_10|GPIO_PIN_12|GPIO_PIN_15;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.Pins = GPIO_PIN_11;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
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_High; // 时钟空闲电平为高
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 第二个边沿采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 片选信号由SSI寄存器控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 波特率为PCLK的8分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 最高有效位 MSB 收发在前
SPI_InitStructure.SPI_Speed = SPI_Speed_High; // 高速SPI
SPI_Init(CW_SPI1, &SPI_InitStructure);
SPI_Cmd(CW_SPI1, ENABLE);
SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
}
读Flash id,需要注意的是在全双工模式下读之前要先发送,不然无法接受从设备发过来的数据
uint8_t spi_sendbyte(SPI_TypeDef* SPIx,uint8_t data)
{
while(SPI_GetFlagStatus(SPIx, SPI_FLAG_TXE) == RESET);
SPI_SendData(SPIx,data);
while(SPI_GetFlagStatus(SPIx, SPI_FLAG_RXNE) == RESET);
return SPI_ReceiveData(SPIx)&0xFF;
}
uint8_t spi_readbyte(SPI_TypeDef* SPIx)
{
return spi_sendbyte(SPIx,0x00);
}
void cw25q64_readflashid()
{
uint8_t data[8] = {0},i=0;
SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
spi_sendbyte(CW_SPI1,0x90);
spi_sendbyte(CW_SPI1,0x00);
spi_sendbyte(CW_SPI1,0x00);
spi_sendbyte(CW_SPI1,0x00);
data[0] = spi_readbyte(CW_SPI1);
data[1] = spi_readbyte(CW_SPI1);
SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
printf("Flash read Manufacturer/Device ID %02X %02X\n",data[0],data[1]);
SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
spi_sendbyte(CW_SPI1,0x9F);
data[0] = spi_readbyte(CW_SPI1);
data[1] = spi_readbyte(CW_SPI1);
data[2] = spi_readbyte(CW_SPI1);
SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
printf("Flash read JEDEC ID %02X %02X%02X\n",data[0],data[1],data[2]);
SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
spi_sendbyte(CW_SPI1,0x5A);
spi_sendbyte(CW_SPI1,0x00);
spi_sendbyte(CW_SPI1,0x00);
spi_sendbyte(CW_SPI1,0xF8);
spi_sendbyte(CW_SPI1,0x00);
while(i<8)
{
data[i] = spi_readbyte(CW_SPI1);
i++;
}
SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
printf("Flash read Unique Id %02X%02X%02X%02X%02X%02X%02X%02X\n",data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]);
}
运行结果
Device ID的ID7 - ID0这个值实际读到的和手册不一致
Unique Id是01开头F6结尾
接下来尝试写入数据,先要使能写入,然后擦除,等待擦除完成后写入数据
void cw25q64_waitwritefinish()
{
uint8_t writebusy = 1;
SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
spi_sendbyte(CW_SPI1,0x05);
while(writebusy != 0)
{
writebusy = spi_readbyte(CW_SPI1)&0x01;
}
SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
printf("Flash write finish\n");
}
void cw25q64_writeenable()
{
SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
spi_sendbyte(CW_SPI1,0x06);
SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
printf("Flash write enabled\n");
}
void cw25q64_erasesector(uint32_t secteraddr)
{
SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
spi_sendbyte(CW_SPI1,0x20);
spi_sendbyte(CW_SPI1,(secteraddr >> 16)&0xFF);
spi_sendbyte(CW_SPI1,(secteraddr >> 8)&0xFF);
spi_sendbyte(CW_SPI1,secteraddr&0xFF);
SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
printf("Flash erase sector %08X\n",secteraddr);
}
void cw25q64_writepagedata(uint32_t pageaddr,uint8_t *datas,uint16_t len)
{
uint16_t index = 0;
SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
spi_sendbyte(CW_SPI1,0x02);
spi_sendbyte(CW_SPI1,(pageaddr >> 16)&0xFF);
spi_sendbyte(CW_SPI1,(pageaddr >> 8)&0xFF);
spi_sendbyte(CW_SPI1,pageaddr&0xFF);
while(index < len)
{
spi_sendbyte(CW_SPI1,datas[index]);
index += 1;
}
SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
printf("Flash write page %08X data:\n",pageaddr);
index = 0;
while(index < len)
{
printf("%02X ",datas[index]);
index += 1;
}
printf("\n");
}
void cw25q64_readdata(uint32_t addr,uint8_t *datas,uint16_t len)
{
uint16_t index = 0;
SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
spi_sendbyte(CW_SPI1,0x03);
spi_sendbyte(CW_SPI1,(addr >> 16)&0xFF);
spi_sendbyte(CW_SPI1,(addr >> 8)&0xFF);
spi_sendbyte(CW_SPI1,addr&0xFF);
while(index < len)
{
datas[index] = spi_readbyte(CW_SPI1)&0xFF;
index += 1;
}
SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
printf("Flash read %08X data:\n",addr);
index = 0;
while(index < len)
{
printf("%02X ",datas[index]);
index += 1;
}
printf("\n");
}
int32_t main(void)
{
uint8_t flash_testdata[50] = {0},index = 0;
RCC_HSI_48M_init();
RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
uart1_init();
spi_init();
cw25q64_readflashid();
while(index < 50)
{
index += 1;
flash_testdata[index] = index;
}
cw25q64_writeenable();
cw25q64_erasesector(0);
cw25q64_waitwritefinish();
cw25q64_writeenable();
cw25q64_writepagedata(0,flash_testdata,50);
cw25q64_waitwritefinish();
cw25q64_readdata(0,flash_testdata,50);
while (1)
{
}
}
运行结果
除了读写FLASH,这块LCD12864的屏幕也可以用SPI驱动
驱动它要用到5个IO,CS RST A0 MOSI SCK,SPI只用发送模式就可以了MISO可以不用,这里换一组IO
YUYY_HS12864G18B_DEV_t hs12864_dev;
void hs12864g18b_init(void)
{
GPIO_InitTypeDef gpiodef;
hs12864_dev.cs.gpio = CW_GPIOA;
hs12864_dev.cs.pin = GPIO_PIN_4;
hs12864_dev.rst.gpio = CW_GPIOA;
hs12864_dev.rst.pin = GPIO_PIN_3;
hs12864_dev.a0.gpio = CW_GPIOA;
hs12864_dev.a0.pin = GPIO_PIN_2;
hs12864_dev.sck.gpio = CW_GPIOA;
hs12864_dev.sck.pin = GPIO_PIN_5;
hs12864_dev.mo.gpio = CW_GPIOA;
hs12864_dev.mo.pin = GPIO_PIN_7;
__RCC_GPIOA_CLK_ENABLE();
gpiodef.Pins = GPIO_PIN_5|GPIO_PIN_7|GPIO_PIN_4|GPIO_PIN_3|GPIO_PIN_2;
gpiodef.Mode = GPIO_MODE_OUTPUT_PP;
gpiodef.IT = GPIO_IT_NONE;
GPIO_Init(CW_GPIOA,&gpiodef);
#if(YUYY_HS12864G18B_USE_SOFT_SPI)
yuyy_hs12864g18b_set_spigpio(NULL,&hs12864g18b_gpio_config);
#else
__RCC_SPI1_CLK_ENABLE();;
PA05_AFx_SPI1SCK();
PA07_AFx_SPI1MOSI();
SPI_InitTypeDef spidef;
spidef.SPI_Direction = SPI_Direction_1Line_TxOnly;
spidef.SPI_Mode = SPI_Mode_Master;
spidef.SPI_DataSize = SPI_DataSize_8b;
spidef.SPI_CPOL = SPI_CPOL_High;
spidef.SPI_CPHA = SPI_CPHA_2Edge;
spidef.SPI_NSS = SPI_NSS_Soft;
spidef.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
spidef.SPI_FirstBit = SPI_FirstBit_MSB;
spidef.SPI_Speed = SPI_Speed_High;
SPI_Init(CW_SPI1,&spidef);
SPI_Cmd(CW_SPI1, ENABLE);
hs12864_dev.spix = CW_SPI1;
#endif
yuyy_hs12864g18b_init(&hs12864_dev);
}
int32_t main(void)
{
RCC_HSI_48M_init();
RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
hs12864g18b_init();
yuyy_hs12864g18b_clear_screen(&hs12864_dev);
yuyy_hs12864g18b_display_string_8x16(&hs12864_dev,0,0,0,(uint8_t *)"CW32L031TEST");
yuyy_hs12864g18b_display_string_8x16(&hs12864_dev,0,2,0,(uint8_t *)"Drivered by SPI");
yuyy_hs12864g18b_display_string_8x16(&hs12864_dev,0,4,0,(uint8_t *)"Code by yuyy1989");
while (1)
{
}
}
运行效果
另外SPI还能用来驱动WS2812或SK6812这样的幻彩灯珠,这里就先不演示了
|
|