| **银杏科技有限公司旗下技术文档发布平台** ||||
|技术支持电话|**0379-69926675-801**|||
|技术支持邮件|Gingko@vip.163.com|||
^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^
| V1.0 | 2020-07-28 | gingko | 初次建立 |
\\
\\
\\
===== STM32CubeMX教程十八——SPI实验 =====
\\
1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR。
{{ :icore4tx:icore4tx_cube_18_1.png?direct |}}
2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置,在搜索栏的下面,提供的各种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号。为:STM32H750IBKx。
{{ :icore4tx:icore4tx_cube_18_2.png?direct |}}
3.配置RCC,使用外部时钟源。
{{ :icore4tx:icore4tx_cube_18_3.png?direct |}}
4.时基源选择SysTick。
{{ :icore4tx:icore4tx_cube_18_4.png?direct |}}
5.将PA10,PB7,PB8设置为GPIO_Output。
{{ :icore4tx:icore4tx_cube_18_5.png?direct |}}
6.引脚模式配置。
{{ :icore4tx:icore4tx_cube_18_6.png?direct |}}
{{ :icore4tx:icore4tx_cube_18_7.png?direct |}}
7.设置串口。
{{ :icore4tx:icore4tx_cube_18_8.png?direct |}}
8.在NVIC Settings一栏使能接收中断。
{{ :icore4tx:icore4tx_cube_18_9.png?direct |}}
9.配置SPI。
{{ :icore4tx:icore4tx_cube_18_10.png?direct |}}
10.配置SPI DMA,选择SPI_TX,SPI_RX。
{{ :icore4tx:icore4tx_cube_18_11.png?direct |}}
11.时钟源设置,选择外部高速时钟源,配置为最大主频。
{{ :icore4tx:icore4tx_cube_18_12.png?direct |}}
{{ :icore4tx:icore4tx_cube_18_13.png?direct |}}
12.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27。
{{ :icore4tx:icore4tx_cube_18_14.png?direct |}}
13.点击Code Generator,进行进一步配置。
{{ :icore4tx:icore4tx_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 创建工程。
{{ :icore4tx:icore4tx_cube_18_16.png?direct |}}
创建成功,打开工程。
===== 实验十八:SPI实验——读写FPGA =====
==== 一、 实验目的与意义 ====
- 了解STM32 SPI结构。
- 了解STM32 SPI DMA特征。
- 掌握SPI的使用方法。
- 掌握STM32 HAL库中SPI属性的配置方法。
- 掌握KEILMDK 集成开发环境使用方法。
==== 二、 实验设备及平台 ====
- iCore4TX 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.3.29da532fLkazHH&id=614919247574|点击购买]]。
- 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主模块和与之通信的外设备时钟相位和极性应该一致。
{{ :icore4tx:icore4tx_arm_hal_18_1.png?direct |}}
=== 3.DMA工作原理: ===
* DMA 允许不同速度的硬件装置来沟通,而不需要依于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把他们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。
* DMA 传输主要地将一个内存区从一个装置复制到另外一个。当 CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存去。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。所以,DMA传输对于高效能嵌入式系统算法和网络是很重要的。
=== 4.SPI通信指令表 ===
{{ :icore4tx:icore4tx_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]
axp152.set_dcdc2(1200);//[FPGA INT]
axp152.set_dcdc3(3300);//[DCOUT3]
axp152.set_dcdc4(3300);//[DCOUT4]
axp152.set_aldo1(3300);//[BK3]
axp152.set_aldo2(3300);//[ALDOOUT2]
axp152.set_dldo1(3300);//[BK0]
axp152.set_dldo2(3300);//[BK1]
HAL_Delay(200);
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 iCore4TX!\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 位在前
==== 五、 实验步骤 ====
- 把仿真器与iCore4TX的SWD调试口相连(直接相连或者通过转接器相连);
- 把iCore4TX通过Micro USB线与计算机相连,为iCore4TX供电;
- 打开Quartus ll 开发环境,并打开本实验工程;
- 烧写程序到iCore4TX上;
- 打开Keil MDK 开发环境,并打开本实验工程;
- 烧写程序到iCore4TX上;
- 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。
==== 六、 实验现象 ====
{{ :icore4tx:icore4tx_arm_hal_18_3.png?direct |}}