| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-03-02 | gingko | 初次建立 | \\ \\ \\ ===== STM32CubeMX教程十八——SPI实验 ===== \\ 1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR。 {{ :icore4t:icore4t_cube_18_1.png?direct |}} 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置,在搜索栏的下面,提供的各种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号。为:STM32H750IBKx。 {{ :icore4t:icore4t_cube_18_2.png?direct |}} 3.配置RCC,使用外部时钟源。 {{ :icore4t:icore4t_cube_18_3.png?direct |}} 4.时基源选择SysTick。 {{ :icore4t:icore4t_cube_18_4.png?direct |}} 5.将PA10,PB7,PB8设置为GPIO_Output。 {{ :icore4t:icore4t_cube_18_5.png?direct |}} 6.引脚模式配置。 {{ :icore4t:icore4t_cube_18_6.png?direct |}} {{ :icore4t:icore4t_cube_18_7.png?direct |}} 7.设置串口。 {{ :icore4t:icore4t_cube_18_8.png?direct |}} 8.在NVIC Settings一栏使能接收中断。 {{ :icore4t:icore4t_cube_18_9.png?direct |}} 9.配置SPI。 {{ :icore4t:icore4t_cube_18_10.png?direct |}} 10.配置SPI DMA,选择SPI_TX,SPI_RX。 {{ :icore4t:icore4t_cube_18_11.png?direct |}} 11.时钟源设置,选择外部高速时钟源,配置为最大主频。 {{ :icore4t:icore4t_cube_18_12.png?direct |}} {{ :icore4t:icore4t_cube_18_13.png?direct |}} 12.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27。 {{ :icore4t:icore4t_cube_18_14.png?direct |}} 13.点击Code Generator,进行进一步配置。 {{ :icore4t:icore4t_cube_18_15.png?direct |}} * **Copy all used libraries into the project folder** * **将HAL库的所有.C和.H都复制到所建工程中** * 优点:这样如果后续需要新增其他外设又可能不再用STM32CubeMX的时候便会很方便 * 缺点:体积大,编译时间很长 * **Copy only the necessary library files** * **只复制所需要的.C和.H(推荐)** * 优点:体积相对小,编译时间短,并且工程可复制拷贝 * 缺点:新增外设时需要重新用STM32CubeMX导入 * **Add necessary library files as reference in the toolchain project configuration file** * **不复制文件,直接从软件包存放位置导入.C和.H** * 优点:体积小,比较节约硬盘空间 * 缺点:复制到其他电脑上或者软件包位置改变,就需要修改相对应的路径 * 自行选择方式即可 14.然后点击GENERATE CODE 创建工程。 {{ :icore4t:icore4t_cube_18_16.png?direct |}} 创建成功,打开工程。 ===== 实验十八:SPI实验——读写FPGA ===== ==== 一、 实验目的与意义 ==== - 了解STM32 SPI结构。 - 了解STM32 SPI DMA特征。 - 掌握SPI的使用方法。 - 掌握STM32 HAL库中SPI属性的配置方法。 - 掌握KEILMDK 集成开发环境使用方法。 ==== 二、 实验设备及平台 ==== - iCore4T 双核心板。[[https://item.taobao.com/item.htm?spm=a1z10.1-c.w137644-251734891.3.5923532fDrMDOe&id=610595120319|点击购买]] - JLINK(或相同功能)仿真器。[[https://item.taobao.com/item.htm?id=554869837940|点击购买]] - Micro USB线缆。 - Keil MDK 开发平台。 - STM32CubeMX开发平台。 - 装有WIN XP(及更高版本)系统的计算机。 ==== 三、 实验原理 ==== === 1.SPI简介 === * SPI是串行外设接口(Serial PeripheralInterface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议。SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选)。 * **SPI硬件接口:** * MISO :主设备数据输入,从设备数据输出。 * MOSI :主设备数据输出,从设备数据输入。 * SCLK :时钟信号,由主设备产生。 * CS :从设备片选信号,由主设备控制。 === 2.SPI功能说明 === * SPI时钟极性和相位: * CPOL决定时钟空闲时的稳定电平,对主/从都有效。 * CPOL=0:空闲时低电平。 * CPOL=1:空闲时高电平。 * CPHA决定数据采样时刻。 * CPHA=0:第一个时钟延开始采样MSBit。 * CPHA=1:第二个时钟延开始采样MSBit。 * SPI总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。 {{ :icore4t:icore4t_arm_hal_18_1.png?direct |}} === 3.DMA工作原理: === * DMA 允许不同速度的硬件装置来沟通,而不需要依于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把他们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。 * DMA 传输主要地将一个内存区从一个装置复制到另外一个。当 CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存去。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。所以,DMA传输对于高效能嵌入式系统算法和网络是很重要的。 === 4.SPI通信指令表 === {{ :icore4t:icore4t_arm_hal_18_2.png?direct |}} * ARM与FPGA通信采用的是半双工式通信,FPGA通过识别指令完成与ARM的交互。 * 写数据指令为04h,接下来为两字节的地址指令,后为要写入的数据,数据写入完毕以伪指令00h结束数据传输。 * 读数据指令为07h,接下来为两字节的地址指令,其后为伪指令00h开始读取数据进行数据传输,第五字节以后为要读取的数据。 * 器件ID指令为01h,接下来为两字节的伪指令,第四字节仍为伪指令开始读取ID标志,第五字节为读取的器件ID。 ==== 四、 实验程序 ==== === 1.主函数 === int main(void) { int i; int temp; int error; unsigned char buffer[4096]; HAL_Init(); SystemClock_Config(); i2c.initialize(); axp152.initialize(); axp152.set_dcdc1(3500);//[ARM & FPGA BK1/2/6 &OTHER] axp152.set_dcdc2(1200);//[FPGA INT & PLL D] axp152.set_aldo1(2500);//[FPGA PLL A] axp152.set_dcdc4(3300);//[POWER_OUTPUT] axp152.set_dcdc3(3300);//[FPGA BK4][Adjustable] axp152.set_aldo2(3300);//[FPGA BK3][Adjustable] axp152.set_dldo1(3300);//[FPGA BK7][Adjustable] axp152.set_dldo2(3300);//[FPGA BK5][Adjustable] MX_GPIO_Init(); MX_DMA_Init(); MX_USART2_UART_Init(); MX_SPI4_Init(); usart2.initialize(115200); //串口波特率设置 usart2.printf("\x0c"); //清屏 usart2.printf("\033[1;32;40m"); //设置终端字体为绿色 usart2.printf("Hello,I am iCore4T!\r\n\r\n"); //串口信息输出 //SPI TEST error = 0; usart2.printf("\033[1;32;40m"); //显示绿色 usart2.printf(" *Write FPGA 10MByte & Read......"); //SPI写数据指令 buffer[0] = 0x04; //写指令 buffer[1] = 0x00; //地址 buffer[2] = 0x00; //地址 for(i = 0;i < 1024;i ++){ buffer[i+3] = i%256; //数据 } buffer[1027] = 0x00; //伪指令 temp = HAL_GetTick(); for(i = 0;i < 10240;i ++){ HAL_SPI_Transmit_DMA(&hspi4,buffer,1028); //通过SPI DMA发送数据 while(!spi4_tc_flag); spi4_tc_flag = 0; } temp = HAL_GetTick() - temp; //计算数据传输时间 memset(buffer,0,sizeof(buffer)); //清空缓存区 //SPI读数据指令 buffer[0] = 0x07; //读指令 buffer[1] = 0x00; //地址 buffer[2] = 0x00; //地址 buffer[3] = 0x00; //伪指令,开始数据传输 HAL_SPI_TransmitReceive_DMA(&hspi4,buffer,buffer,1028); //通过SPI DMA接收数据 while(!spi4_tc_flag); spi4_tc_flag = 0; for(i = 0;i < 1023;i ++){ if(buffer[i+4] != i%256){ error ++; break; } } if(error == 0){ usart2.printf("\t\t\033[1;32;40m[OK]\r\n"); usart2.printf("*Write time:%dms\r\n*Data size :10MByte\r\n",temp); //打印传输1MB数据的时间 usart2.printf("*SPI Speed :%.2fMBytes/s\r\n",10000./temp); //打印SPI传输速度 }else{ usart2.printf("\t\t\033[1;31;40m[Fail]\r\n"); } while (1) { } } === 2.SPI初始化函数 === 使能SPIx和IO口时钟,初始化IO口为复用功能并配置SPI_DMA,SPI_RX,SPI_TX。 void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(spiHandle->Instance==SPI4) { __HAL_RCC_SPI4_CLK_ENABLE(); //使能SPI时钟 __HAL_RCC_GPIOE_CLK_ENABLE(); //使能GPIO时钟 /**SPI4 GPIO Configuration PE2 ------> SPI4_SCK PE4 ------> SPI4_NSS PE5 ------> SPI4_MISO PE6 ------> SPI4_MOSI */ GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF5_SPI4; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); /* SPI4 DMA Init */ /* SPI4_TX Init */ hdma_spi4_tx.Instance = DMA1_Stream0; //stream0可以被配置成channel 0、1、2、3、4、6任意一个。 hdma_spi4_tx.Init.Request = DMA_REQUEST_SPI4_TX; //SPI4使用DMA接收 hdma_spi4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; //存储器到外设 hdma_spi4_tx.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式 hdma_spi4_tx.Init.MemInc = DMA_MINC_ENABLE; //外设增量模式 hdma_spi4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据长度:8位 hdma_spi4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_spi4_tx.Init.Mode = DMA_NORMAL; //正常模式 hdma_spi4_tx.Init.Priority = DMA_PRIORITY_LOW; //设置 DMA 的优先级别 hdma_spi4_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_spi4_tx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(spiHandle,hdmatx,hdma_spi4_tx); /* SPI4_RX Init */ hdma_spi4_rx.Instance = DMA1_Stream1; hdma_spi4_rx.Instance = DMA1_Stream1; hdma_spi4_rx.Init.Request = DMA_REQUEST_SPI4_RX; //SPI4使用DMA发送 hdma_spi4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; //外设到存储器 hdma_spi4_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_spi4_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_spi4_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_spi4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; if (HAL_DMA_Init(&hdma_spi4_rx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi4_rx); /* SPI4 interrupt Init */ HAL_NVIC_SetPriority(SPI4_IRQn, 0, 0); HAL_NVIC_EnableIRQ(SPI4_IRQn); } } 初始化SPIx,设置SPIx工作模式 void MX_SPI4_Init(void) { hspi4.Instance = SPI4; hspi4.Init.Mode = SPI_MODE_MASTER; //设置 SPI4 为主模式 hspi4.Init.Direction = SPI_DIRECTION_2LINES; //设置双线单向模式 hspi4.Init.DataSize = SPI_DATASIZE_8BIT; //设置 8位数据位 hspi4.Init.CLKPolarity = SPI_POLARITY_HIGH; //时钟极性为高 hspi4.Init.CLKPhase = SPI_PHASE_2EDGE; //时钟相位为2 hspi4.Init.NSS = SPI_NSS_HARD_OUTPUT; //NSS 硬件控制 hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB; //起始位为MSB hspi4.Init.TIMode = SPI_TIMODE_DISABLE; //帧格式关闭 hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//硬件CRC关闭 hspi4.Init.CRCPolynomial = 0x0; hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; //NSS脉冲关闭 hspi4.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; //NSS极性为低 hspi4.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; hspi4.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; hspi4.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; hspi4.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; hspi4.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; hspi4.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; hspi4.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; hspi4.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; hspi4.Init.IOSwap = SPI_IO_SWAP_DISABLE; if (HAL_SPI_Init(&hspi4) != HAL_OK) { Error_Handler(); } } === 3.SPI_InitTypeDef 的结构体 === assert_param(IS_SPI_ALL_INSTANCE(hspi->Instance)); assert_param(IS_SPI_MODE(hspi->Init.Mode)); assert_param(IS_SPI_DIRECTION(hspi->Init.Direction)); assert_param(IS_SPI_DATASIZE(hspi->Init.DataSize)); assert_param(IS_SPI_FIFOTHRESHOLD(hspi->Init.FifoThreshold)); assert_param(IS_SPI_NSS(hspi->Init.NSS)); assert_param(IS_SPI_NSSP(hspi->Init.NSSPMode)); assert_param(IS_SPI_BAUDRATE_PRESCALER(hspi->Init.BaudRatePrescaler)); assert_param(IS_SPI_FIRST_BIT(hspi->Init.FirstBit)); assert_param(IS_SPI_TIMODE(hspi->Init.TIMode)); * SPI_Direction :设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和 串行收方式 * SPI_Mode :设置 SPI 的主从模式 * SPI_DataSize :帧格式选择项,8 位还是 16 位 * SPI_CPOL :设置时钟极性 * SPI_CPHA:设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿数据被采样 * SPI_NSS :设置 NSS 信号由硬件(NSS 管脚)还是软件控制 * SPI_BaudRatePrescaler:设置 SPI 波特率预分频值 * SPI_FirstBit :设置数据传输顺序是 MSB 位在前还是 LSB 位在前 ==== 五、 实验步骤 ==== - 把仿真器与iCore4T的SWD调试口相连(直接相连或者通过转接器相连); - 把iCore4T通过Micro USB线与计算机相连,为iCore4T供电; - 打开Quartus ll 开发环境,并打开本实验工程; - 烧写程序到iCore4T上; - 打开Keil MDK 开发环境,并打开本实验工程; - 烧写程序到iCore4T上; - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 ==== 六、 实验现象 ==== {{ :icore4t:icore4t_arm_hal_18_3.png?direct |}}