[开发板] 【CW32L031CxTx StartKit评估板测评】6.SPI的使用

[复制链接]
 楼主| yuyy1989 发表于 2023-9-29 13:22 | 显示全部楼层 |阅读模式
#申请原创# @21小跑堂  

串行外设接口(SPI)是一种同步串行数据通信接口,常用于 MCU 与外部设备之间进行同步串行通信,CW32L031 内部集成 1 个串行外设 SPI 接口,在开发板上SPI接了一颗W25Q64的FLASH
QQ截图20230926181737.png
开发板上实际焊接的是芯源自家的CW25Q64,在https://www.whxy.com/support/filelist/23这里能下载到手册
先来尝试用SPI读取FLAH的ID并用串口打印出来,初始化SPI接口
  1. void spi_init()
  2. {
  3.     SPI_InitTypeDef  SPI_InitStructure = {0};
  4.     GPIO_InitTypeDef GPIO_InitStructure = {0};
  5.     __RCC_GPIOA_CLK_ENABLE();
  6.     __RCC_SPI1_CLK_ENABLE();
  7.    
  8.     PA10_AFx_SPI1SCK();
  9.     PA11_AFx_SPI1MISO();
  10.     PA12_AFx_SPI1MOSI();
  11.     PA15_AFx_SPI1CS();
  12.    
  13.     GPIO_InitStructure.Pins = GPIO_PIN_10|GPIO_PIN_12|GPIO_PIN_15;
  14.     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  15.     GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
  16.    
  17.     GPIO_InitStructure.Pins = GPIO_PIN_11;
  18.     GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
  19.     GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
  20.    
  21.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;    // 双线全双工
  22.     SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                         // 主机模式
  23.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                     // 帧数据长度为8bit
  24.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                           // 时钟空闲电平为高
  25.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                          // 第二个边沿采样
  26.     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                             // 片选信号由SSI寄存器控制
  27.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;    // 波特率为PCLK的8分频
  28.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                    // 最高有效位 MSB 收发在前
  29.     SPI_InitStructure.SPI_Speed = SPI_Speed_High;                         // 高速SPI

  30.     SPI_Init(CW_SPI1, &SPI_InitStructure);
  31.     SPI_Cmd(CW_SPI1, ENABLE);
  32.    
  33.     SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
  34. }
读Flash id,需要注意的是在全双工模式下读之前要先发送,不然无法接受从设备发过来的数据
QQ截图20230927200156.png
  1. uint8_t spi_sendbyte(SPI_TypeDef* SPIx,uint8_t data)
  2. {
  3.     while(SPI_GetFlagStatus(SPIx, SPI_FLAG_TXE) == RESET);
  4.     SPI_SendData(SPIx,data);
  5.     while(SPI_GetFlagStatus(SPIx, SPI_FLAG_RXNE) == RESET);
  6.     return SPI_ReceiveData(SPIx)&0xFF;
  7. }

  8. uint8_t spi_readbyte(SPI_TypeDef* SPIx)
  9. {
  10.     return spi_sendbyte(SPIx,0x00);
  11. }

  12. void cw25q64_readflashid()
  13. {
  14.     uint8_t data[8] = {0},i=0;
  15.     SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
  16.     spi_sendbyte(CW_SPI1,0x90);
  17.     spi_sendbyte(CW_SPI1,0x00);
  18.     spi_sendbyte(CW_SPI1,0x00);
  19.     spi_sendbyte(CW_SPI1,0x00);
  20.     data[0] = spi_readbyte(CW_SPI1);
  21.     data[1] = spi_readbyte(CW_SPI1);
  22.     SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
  23.     printf("Flash read Manufacturer/Device ID %02X %02X\n",data[0],data[1]);
  24.    
  25.     SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
  26.     spi_sendbyte(CW_SPI1,0x9F);
  27.     data[0] = spi_readbyte(CW_SPI1);
  28.     data[1] = spi_readbyte(CW_SPI1);
  29.     data[2] = spi_readbyte(CW_SPI1);
  30.     SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
  31.     printf("Flash read JEDEC ID %02X %02X%02X\n",data[0],data[1],data[2]);
  32.    
  33.     SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
  34.     spi_sendbyte(CW_SPI1,0x5A);
  35.     spi_sendbyte(CW_SPI1,0x00);
  36.     spi_sendbyte(CW_SPI1,0x00);
  37.     spi_sendbyte(CW_SPI1,0xF8);
  38.     spi_sendbyte(CW_SPI1,0x00);
  39.     while(i<8)
  40.     {
  41.         data[i] = spi_readbyte(CW_SPI1);
  42.         i++;
  43.     }
  44.     SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
  45.     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]);
  46. }
运行结果
QQ截图20230928154737.png
Device ID的ID7 - ID0这个值实际读到的和手册不一致
QQ截图20230927194226.png
Unique Id是01开头F6结尾
QQ截图20230927202450.png
接下来尝试写入数据,先要使能写入,然后擦除,等待擦除完成后写入数据
  1. void cw25q64_waitwritefinish()
  2. {
  3.     uint8_t writebusy = 1;
  4.     SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
  5.     spi_sendbyte(CW_SPI1,0x05);
  6.     while(writebusy != 0)
  7.     {
  8.         writebusy = spi_readbyte(CW_SPI1)&0x01;
  9.     }
  10.     SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
  11.     printf("Flash write finish\n");
  12. }

  13. void cw25q64_writeenable()
  14. {
  15.     SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
  16.     spi_sendbyte(CW_SPI1,0x06);
  17.     SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
  18.     printf("Flash write enabled\n");
  19. }

  20. void cw25q64_erasesector(uint32_t secteraddr)
  21. {
  22.     SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
  23.     spi_sendbyte(CW_SPI1,0x20);
  24.     spi_sendbyte(CW_SPI1,(secteraddr >> 16)&0xFF);
  25.     spi_sendbyte(CW_SPI1,(secteraddr >> 8)&0xFF);
  26.     spi_sendbyte(CW_SPI1,secteraddr&0xFF);
  27.     SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
  28.     printf("Flash erase sector %08X\n",secteraddr);
  29. }

  30. void cw25q64_writepagedata(uint32_t pageaddr,uint8_t *datas,uint16_t len)
  31. {
  32.     uint16_t index = 0;
  33.     SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
  34.     spi_sendbyte(CW_SPI1,0x02);
  35.     spi_sendbyte(CW_SPI1,(pageaddr >> 16)&0xFF);
  36.     spi_sendbyte(CW_SPI1,(pageaddr >> 8)&0xFF);
  37.     spi_sendbyte(CW_SPI1,pageaddr&0xFF);
  38.     while(index < len)
  39.     {
  40.         spi_sendbyte(CW_SPI1,datas[index]);
  41.         index += 1;
  42.     }
  43.     SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
  44.     printf("Flash write page %08X data:\n",pageaddr);
  45.     index = 0;
  46.     while(index < len)
  47.     {
  48.         printf("%02X ",datas[index]);
  49.         index += 1;
  50.     }
  51.     printf("\n");
  52. }

  53. void cw25q64_readdata(uint32_t addr,uint8_t *datas,uint16_t len)
  54. {
  55.     uint16_t index = 0;
  56.     SPI_NSSInternalSoftwareConfig(CW_SPI1,0);
  57.     spi_sendbyte(CW_SPI1,0x03);
  58.     spi_sendbyte(CW_SPI1,(addr >> 16)&0xFF);
  59.     spi_sendbyte(CW_SPI1,(addr >> 8)&0xFF);
  60.     spi_sendbyte(CW_SPI1,addr&0xFF);
  61.     while(index < len)
  62.     {
  63.         datas[index] = spi_readbyte(CW_SPI1)&0xFF;
  64.         index += 1;
  65.     }
  66.     SPI_NSSInternalSoftwareConfig(CW_SPI1,1);
  67.     printf("Flash read %08X data:\n",addr);
  68.     index = 0;
  69.     while(index < len)
  70.     {
  71.         printf("%02X ",datas[index]);
  72.         index += 1;
  73.     }
  74.     printf("\n");
  75. }
  76. int32_t main(void)
  77. {
  78.     uint8_t flash_testdata[50] = {0},index = 0;
  79.     RCC_HSI_48M_init();
  80.     RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
  81.     RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
  82.     uart1_init();
  83.     spi_init();
  84.     cw25q64_readflashid();
  85.     while(index < 50)
  86.     {
  87.         index += 1;
  88.         flash_testdata[index] = index;
  89.     }
  90.     cw25q64_writeenable();
  91.     cw25q64_erasesector(0);
  92.     cw25q64_waitwritefinish();
  93.     cw25q64_writeenable();
  94.     cw25q64_writepagedata(0,flash_testdata,50);
  95.     cw25q64_waitwritefinish();
  96.     cw25q64_readdata(0,flash_testdata,50);
  97.     while (1)
  98.     {
  99.     }
  100. }
运行结果
QQ截图20230929101911.png
除了读写FLASH,这块LCD12864的屏幕也可以用SPI驱动
微信图片_20230929131340.jpg
驱动它要用到5个IO,CS RST A0 MOSI SCK,SPI只用发送模式就可以了MISO可以不用,这里换一组IO
  1. YUYY_HS12864G18B_DEV_t hs12864_dev;
  2. void hs12864g18b_init(void)
  3. {
  4.     GPIO_InitTypeDef gpiodef;
  5.     hs12864_dev.cs.gpio = CW_GPIOA;
  6.     hs12864_dev.cs.pin = GPIO_PIN_4;
  7.     hs12864_dev.rst.gpio = CW_GPIOA;
  8.     hs12864_dev.rst.pin = GPIO_PIN_3;
  9.     hs12864_dev.a0.gpio = CW_GPIOA;
  10.     hs12864_dev.a0.pin = GPIO_PIN_2;
  11.     hs12864_dev.sck.gpio = CW_GPIOA;
  12.     hs12864_dev.sck.pin = GPIO_PIN_5;
  13.     hs12864_dev.mo.gpio = CW_GPIOA;
  14.     hs12864_dev.mo.pin = GPIO_PIN_7;
  15.    
  16.     __RCC_GPIOA_CLK_ENABLE();
  17.     gpiodef.Pins = GPIO_PIN_5|GPIO_PIN_7|GPIO_PIN_4|GPIO_PIN_3|GPIO_PIN_2;
  18.     gpiodef.Mode = GPIO_MODE_OUTPUT_PP;
  19.     gpiodef.IT = GPIO_IT_NONE;
  20.     GPIO_Init(CW_GPIOA,&gpiodef);
  21.    
  22.         #if(YUYY_HS12864G18B_USE_SOFT_SPI)
  23.     yuyy_hs12864g18b_set_spigpio(NULL,&hs12864g18b_gpio_config);
  24.     #else
  25.     __RCC_SPI1_CLK_ENABLE();;
  26.     PA05_AFx_SPI1SCK();
  27.     PA07_AFx_SPI1MOSI();
  28.     SPI_InitTypeDef spidef;
  29.     spidef.SPI_Direction = SPI_Direction_1Line_TxOnly;
  30.     spidef.SPI_Mode = SPI_Mode_Master;
  31.     spidef.SPI_DataSize = SPI_DataSize_8b;
  32.     spidef.SPI_CPOL = SPI_CPOL_High;
  33.     spidef.SPI_CPHA = SPI_CPHA_2Edge;
  34.     spidef.SPI_NSS = SPI_NSS_Soft;
  35.     spidef.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
  36.     spidef.SPI_FirstBit = SPI_FirstBit_MSB;
  37.     spidef.SPI_Speed = SPI_Speed_High;
  38.     SPI_Init(CW_SPI1,&spidef);
  39.     SPI_Cmd(CW_SPI1, ENABLE);
  40.     hs12864_dev.spix = CW_SPI1;
  41.     #endif
  42.     yuyy_hs12864g18b_init(&hs12864_dev);
  43. }

  44. int32_t main(void)
  45. {
  46.     RCC_HSI_48M_init();
  47.     RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
  48.     RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
  49.     hs12864g18b_init();
  50.     yuyy_hs12864g18b_clear_screen(&hs12864_dev);
  51.     yuyy_hs12864g18b_display_string_8x16(&hs12864_dev,0,0,0,(uint8_t *)"CW32L031TEST");
  52.     yuyy_hs12864g18b_display_string_8x16(&hs12864_dev,0,2,0,(uint8_t *)"Drivered by SPI");
  53.     yuyy_hs12864g18b_display_string_8x16(&hs12864_dev,0,4,0,(uint8_t *)"Code by yuyy1989");
  54.     while (1)
  55.     {
  56.     }
  57. }
运行效果
微信图片_20230929131349.jpg
另外SPI还能用来驱动WS2812或SK6812这样的幻彩灯珠,这里就先不演示了



您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

161

主题

815

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部