關(guān)鍵詞:庫(kù)函數(shù),STM32F407,SPI+DMA ,SPI-DMA,SRAM , LY68L6400SLIT,STM32CubeMX
編 輯:大黃蜂
說(shuō)明:本筆記記錄 基于 STM32F407 + RT RTOS 采用 SPI接口和 SPI+DMA接口 調(diào)試 SRAM LY68L6400SLIT (8M 字節(jié) SRAM)
重點(diǎn):STM32 HAL SPI 庫(kù)函數(shù);STM32CubeMX SPI 配置;STM32CubeMX SPI 配置+DMA配置;SPI+DMA 讀寫SRAM
項(xiàng)目測(cè)試平臺(tái):STM32F407核心板 + LY68L6400SLIT (8M 字節(jié) SRAM)+ rt-thread4.0.2
調(diào)試心得:
1:調(diào)試 SRAM 時(shí)最開(kāi)始用的是 STM32CubeMX 配置的 SPI 配置代碼和 SPI 庫(kù)函數(shù),先期調(diào)試還算順利,但測(cè)試時(shí)發(fā)現(xiàn)代碼在讀寫大數(shù)據(jù)量,例如寫滿8M 耗時(shí)不記得了,讀完8M耗時(shí)在14s左右。
2:希望優(yōu)化 SRAM的讀寫速度,所以使用 SPI + DMA 讀寫,這個(gè)調(diào)試過(guò)程從最開(kāi)始的讀寫錯(cuò)誤,到后面發(fā)現(xiàn)有部分?jǐn)?shù)據(jù)是準(zhǔn)確的,再到讀寫不穩(wěn)定,最后讀寫徹底穩(wěn)定,連續(xù)讀寫15小時(shí)無(wú)錯(cuò)誤。
3:發(fā)生SPI + DMA 調(diào)試不順的問(wèn)題主要是忽略了 DMA 發(fā)送數(shù)據(jù)的機(jī)制,DMA發(fā)送是需要時(shí)間的,一開(kāi)始的測(cè)試代碼都是執(zhí)行了 DMA發(fā)送代碼就立即 關(guān)掉了SRAM的使能。代碼從執(zhí)行了DMA讀寫后就立刻關(guān)掉了使能,這時(shí)DMA實(shí)際上正在執(zhí)行讀寫操作,從而導(dǎo)致了DMA讀寫錯(cuò)誤。
4:優(yōu)化,在發(fā)現(xiàn) DMA 收發(fā)都需要時(shí)間這一問(wèn)題后,對(duì)此做了判斷 DMA 狀態(tài)的優(yōu)化,讀寫DMA都先判斷DMA就緒后再進(jìn)行下一步操作,從而完成了SPI + DMA 對(duì) SARM的讀寫測(cè)試。
5:在配置了 SPI + DMA 配置情況下,SPI直接讀寫函數(shù)和SPI+DMA函數(shù)都可以直接讀寫。
6:SPI + DMA 測(cè)試結(jié)果,SPI 時(shí)鐘頻率 40 M, 連續(xù)寫8M數(shù)據(jù)耗時(shí):1784ms ;連續(xù)讀8M數(shù)據(jù)耗時(shí):2070ms ;連續(xù)讀8M數(shù)據(jù) 每次讀 1K + CRC校驗(yàn) 耗時(shí):14245ms。以上測(cè)試數(shù)據(jù)都是讀寫函數(shù)按每次 512字節(jié)讀寫情況下測(cè)試結(jié)果。
7:SRAM 讀寫在MCU上電前先復(fù)位一下 SRAM,避免因 MCU 重啟而 SRAM 沒(méi)有復(fù)位 導(dǎo)致出現(xiàn)讀寫異常的問(wèn)題。
/* 調(diào)試遇到讀寫問(wèn)題時(shí)的代碼 */
rt_pin_write(SPI_SRAM_CS , 0); /*使能SRAM 用RT函數(shù)控制*/
HAL_SPI_Transmit_DMA(&hspi1, pData, 5); /*發(fā)送數(shù)據(jù)*/
HAL_SPI_Receive_DMA(&hspi1, ReadData, read_size); /*接收數(shù)據(jù)*/
rt_pin_write(SPI_SRAM_CS , 1); /*去使能SRAM 用RT函數(shù)控制*/
/* 優(yōu)化后正常測(cè)試的代碼 */
while(hdma_spi1_rx.State != HAL_DMA_STATE_READY ); /*等待DMA就緒*/
while(hdma_spi1_tx.State != HAL_DMA_STATE_READY ); /*等待DMA就緒*/
rt_pin_write(SPI_SRAM_CS , 0); /*使能SRAM 用RT函數(shù)控制*/
HAL_SPI_Transmit_DMA(&hspi1, pData, 5); /*發(fā)送數(shù)據(jù)*/
while(hdma_spi1_tx.State != HAL_DMA_STATE_READY ); /*等待DMA發(fā)送完成*/
HAL_SPI_Receive_DMA(&hspi1, ReadData, read_size); /*接收數(shù)據(jù)*/
while(hdma_spi1_rx.State != HAL_DMA_STATE_READY ); /*等待DMA接收完成*/
rt_pin_write(SPI_SRAM_CS , 1); /*去使能SRAM 用RT函數(shù)控制*/
1:硬件圖紙、連接
核心板資料:STM32F407VxT6 Board - LCD wiki
如圖:將SRAM直接焊接到箭頭指的FLASH封裝上, 例程測(cè)試時(shí)是把 SRAM直接背到FLASH,再把CS管腳單獨(dú)連接到LED0的驅(qū)動(dòng)腳。
?
?
?2:STM32CubeMX SPI 配置流程、工程包
2.1配置調(diào)試接口
?2.2配置時(shí)鐘
?
2.3配置SPI?
?2.4配置SPI 的 DMA
?2.5工程輸出設(shè)置
?工程輸出設(shè)置
?2.6生成代碼
?生成后的文件路徑,截圖是壓縮包壓縮后的參考路徑,實(shí)際路徑根據(jù)工程保存路徑查找。
?
3:STM32CubeMX SPI 代碼移植、代碼注釋
3.1 把STM32CubeMX生成的 SPI 和 DMA 代碼復(fù)制到 RT 工程的 board.c 文件后面
如:代碼有兩個(gè)模式 SPI 和 SPI + DMA 模式,根據(jù)宏定義選擇
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-09-15 RealThread first version
*/
//#include <rtthread.h> /* 這里把頭文件轉(zhuǎn)移到 user_cfg.h 文件下面了 */
//#include <board.h> /* 這里把頭文件轉(zhuǎn)移到 user_cfg.h 文件下面了 */
//#include <drv_common.h> /* 這里把頭文件轉(zhuǎn)移到 user_cfg.h 文件下面了 */
#include "user_cfg.h"
RT_WEAK void rt_hw_board_init()
{
extern void hw_board_init(char *clock_src, int32_t clock_src_freq, int32_t clock_target_freq);
/* Heap initialization */
#if defined(RT_USING_HEAP)
rt_system_heap_init((void *) HEAP_BEGIN, (void *) HEAP_END);
#endif
hw_board_init(BSP_CLOCK_SOURCE, BSP_CLOCK_SOURCE_FREQ_MHZ, BSP_CLOCK_SYSTEM_FREQ_MHZ);
/* Set the shell console output device */
#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
/* Board underlying hardware initialization */
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
}
/* SPI 模式 */
#if SRAM_LY64_MODE_SPI
SPI_HandleTypeDef hspi1;
/* SPI1 init function */
void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER; //主機(jī)模式
hspi1.Init.Direction = SPI_DIRECTION_2LINES; //全雙工
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; //數(shù)據(jù)位為8位
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; //CPOL=0
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; //CPHA為數(shù)據(jù)線的第一個(gè)變化沿
hspi1.Init.NSS = SPI_NSS_SOFT; //軟件控制NSS
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; //2分頻
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; //最高位先發(fā)送
hspi1.Init.TIMode = SPI_TIMODE_DISABLE; //禁用TI模式
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; //禁用CRC校驗(yàn)
hspi1.Init.CRCPolynomial = 10; //CRC值計(jì)算的多項(xiàng)式
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
INIT_COMPONENT_EXPORT(MX_SPI1_Init);
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(spiHandle->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* SPI1 clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
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_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
if(spiHandle->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspDeInit 0 */
/* USER CODE END SPI1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI1_CLK_DISABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);
/* USER CODE BEGIN SPI1_MspDeInit 1 */
/* USER CODE END SPI1_MspDeInit 1 */
}
}
#endif
/* SPI + DMA 模式 */
#if SRAM_LY64_MODE_SPI_DMA
/* USER CODE END 0 */
SPI_HandleTypeDef hspi1;
DMA_HandleTypeDef hdma_spi1_rx;
DMA_HandleTypeDef hdma_spi1_tx;
/* SPI1 init function */
void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
hspi1.Instance = SPI1; //SP1
hspi1.Init.Mode = SPI_MODE_MASTER; //設(shè)置SPI工作模式,設(shè)置為主模式
hspi1.Init.Direction = SPI_DIRECTION_2LINES; //設(shè)置SPI單向或者雙向的數(shù)據(jù)模式:SPI設(shè)置為雙線模式
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; //設(shè)置SPI的數(shù)據(jù)大小:SPI發(fā)送接收8位幀結(jié)構(gòu)
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; //串行同步時(shí)鐘的空閑狀態(tài)為高/底電平
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; //串行同步時(shí)鐘的第1個(gè)跳變沿(上升或下降)數(shù)據(jù)被采樣
hspi1.Init.NSS = SPI_NSS_SOFT; //NSS信號(hào)由硬件(NSS管腳)還是軟件(使用SSI位)管理:內(nèi)部NSS信號(hào)有SSI位控制
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; //定義波特率預(yù)分頻的值
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; //指定數(shù)據(jù)傳輸從MSB位還是LSB位開(kāi)始:數(shù)據(jù)傳輸從MSB位開(kāi)始
hspi1.Init.TIMode = SPI_TIMODE_DISABLE; //關(guān)閉TI模式
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; //關(guān)閉硬件CRC校驗(yàn)
hspi1.Init.CRCPolynomial = 10; //CRC值計(jì)算的多項(xiàng)式
if (HAL_SPI_Init(&hspi1) != HAL_OK) //初始化
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(spiHandle->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* SPI1 clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
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_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI1 DMA Init */
/* SPI1_RX Init */
hdma_spi1_rx.Instance = DMA2_Stream0; //數(shù)據(jù)流選擇
hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3; //通道選擇
hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; //外設(shè)到存儲(chǔ)器
hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE; //外設(shè)非增量模式
hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE; //存儲(chǔ)器增量模式
hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //外設(shè)數(shù)據(jù)長(zhǎng)度:8位
hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //存儲(chǔ)器數(shù)據(jù)長(zhǎng)度:8位
hdma_spi1_rx.Init.Mode = DMA_NORMAL; //外設(shè)流控模式
hdma_spi1_rx.Init.Priority = DMA_PRIORITY_LOW; //低優(yōu)先級(jí)
hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(spiHandle,hdmarx,hdma_spi1_rx); //將DMA與SPI1聯(lián)系起來(lái)(發(fā)送DMA)
/* SPI1_TX Init */
hdma_spi1_tx.Instance = DMA2_Stream3;
hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(spiHandle,hdmatx,hdma_spi1_tx);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
if(spiHandle->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspDeInit 0 */
/* USER CODE END SPI1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI1_CLK_DISABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);
/* SPI1 DMA DeInit */
HAL_DMA_DeInit(spiHandle->hdmarx);
HAL_DMA_DeInit(spiHandle->hdmatx);
/* USER CODE BEGIN SPI1_MspDeInit 1 */
/* USER CODE END SPI1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
/* DMA2_Stream3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
}
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET);
/*Configure GPIO pin : PB14 */
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
int SPI_Init_User()
{
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
return 1;
}
INIT_COMPONENT_EXPORT(SPI_Init_User);
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/******************************************************************************/
/* STM32F4xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f4xx.s). */
/******************************************************************************/
extern DMA_HandleTypeDef hdma_spi1_rx;
extern DMA_HandleTypeDef hdma_spi1_tx;
/**
* @brief This function handles DMA2 stream0 global interrupt.
*/
void DMA2_Stream0_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Stream0_IRQn 0 */
/* USER CODE END DMA2_Stream0_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi1_rx);
/* USER CODE BEGIN DMA2_Stream0_IRQn 1 */
/* USER CODE END DMA2_Stream0_IRQn 1 */
}
/**
* @brief This function handles DMA2 stream3 global interrupt.
*/
void DMA2_Stream3_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Stream3_IRQn 0 */
/* USER CODE END DMA2_Stream3_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi1_tx);
/* USER CODE BEGIN DMA2_Stream3_IRQn 1 */
/* USER CODE END DMA2_Stream3_IRQn 1 */
}
#endif
4:HAL SPI 相關(guān)函數(shù)
參考1:以下內(nèi)容摘錄:https://www.cnblogs.com/xingboy/p/9555708.html
HAL庫(kù)里的硬件SPI主要有以下幾個(gè)庫(kù)函數(shù):
? /* hspi1:spi1 硬件通道,temp_val:發(fā)送的數(shù)據(jù),re_val:接收的數(shù)據(jù),1:數(shù)據(jù)長(zhǎng)度,1000:超時(shí)時(shí)間 */
HAL_SPI_TransmitReceive(&hspi1,? &temp_val, &re_val, 1, 1000);? ?// 一邊接受一邊發(fā)送數(shù)據(jù)
HAL_SPI_Transmit(&hspi1,&temp,sizeof(temp),10); //發(fā)送數(shù)據(jù)
HAL_SPI_Receive(&hspi1,&sc1161y_sel_re,sizeof(sc1161y_sel_re),10); //接收數(shù)據(jù)
HAL_SPI_TransmitReceive_DMA(); //以DMA方式發(fā)送數(shù)據(jù)
HAL_SPI_Receive_DMA(); //以DMA方式接收數(shù)據(jù)
HAL_SPI_TransmitReceive_IT(); // 以中斷方式同時(shí)接收發(fā)送數(shù)據(jù)
HAL_SPI_Transmit_IT(); // 以中斷方式發(fā)送數(shù)據(jù)
HAL_SPI_Receive_IT(); // 以中斷方式接收數(shù)據(jù)
具體使用哪個(gè)HAL庫(kù)函數(shù)看項(xiàng)目需求。
在使用硬件SPI過(guò)程中,會(huì)出現(xiàn)的問(wèn)題可以總結(jié)為以下幾點(diǎn):
1.發(fā)送數(shù)據(jù)不成功;
2.接收數(shù)據(jù)不成功;
3.發(fā)送的數(shù)據(jù)有誤;
4.接收的數(shù)據(jù)有誤;
5.交互的數(shù)據(jù)一部分是對(duì)的,一部分有誤;
6.SPI時(shí)鐘沒(méi)有啟動(dòng)。
對(duì)于以上解決方法,我總結(jié)了一個(gè)自己調(diào)試時(shí)的方法:
1. 先確認(rèn)自己的SPI配置是否正確,是否滿足項(xiàng)目需求;
2. 確認(rèn)電路與通信IC無(wú)誤,注意信號(hào)線不要接錯(cuò);
3. 重點(diǎn):調(diào)節(jié)延時(shí),第一第二步確認(rèn)無(wú)誤后,很多時(shí)候不成功是由于延時(shí)原因造成的,
? ? 主要是一個(gè)數(shù)據(jù)交互之間的延時(shí),一幀數(shù)據(jù)發(fā)送后跟接收的延時(shí),IC片選的延時(shí),
每個(gè)數(shù)據(jù)發(fā)送間的延時(shí),IC與MCU交互間的延時(shí)。
5:SPI SRAM讀寫測(cè)試代碼
/*
* Copyright (c) 2006-2020, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-09-16 yl the first version
* .程序功能LY68L6400 讀寫
*
*/
#include "user_cfg.h"
//#include <rtthread.h>
//#include <board.h>
//#include <rtdevice.h>
//#include "drv_spi.h"
#define SPI_SRAM_NEME "spi11"
#define SPI_SRAM_CS GET_PIN(A, 1)
#define LY64_CS(X) X? (GPIOA->ODR |= (0X0001<<1)):(GPIOA->ODR &=~ (0X0001<<1)) /* IO 直接控制控制模式 PA1 輸出1 或者 0 */
#define SPI_FLASH_CS GET_PIN(B, 14)
#define SRAM_LY64_CMD_READ 0x0B /*LY68L6400 快速讀取命令 */
#define SRAM_LY64_CMD_WRITE 0x02 /*LY68L6400 寫命令 */
#define SRAM_LY64_CMD_READ_ID 0x9F /*LY68L6400 讀ID命令*/
#define SRAM_LY64_CMD_Reset_Enable 0x66 /*LY68L6400 復(fù)位使能*/
#define SRAM_LY64_CMD_Reset 0x99 /*LY68L6400 復(fù)位*/
#define SRAM_LY64_SIZE 1024*1024*8 /*LY68L6400 空間大小 字節(jié)*/
#define SRAM_LY64_ID_LEN 8 /*LY68L6400 ID長(zhǎng)度 字節(jié)*/
#define SRAM_LY64_READ_LEN 512 /*LY68L6400 讀取函數(shù)單次讀取的最大長(zhǎng)度,如果超過(guò)這個(gè)數(shù)量函數(shù)自動(dòng)分多次讀取*/
#define WR_SIZE 1024 /*LY68L6400 寫入函數(shù)單次寫取的最大長(zhǎng)度,如果超過(guò)這個(gè)數(shù)量函數(shù)自動(dòng)分多次寫入*/
#define READ_DATA_CRC 1 /*LY68L6400 讀取數(shù)據(jù)測(cè)試時(shí) CRC 校驗(yàn)開(kāi)關(guān),需要計(jì)算讀取時(shí)間時(shí)可以關(guān)閉CRC,1開(kāi)啟 0關(guān)閉*/
#define READ_DATA_PRINTF 0 /*LY68L6400 打印讀取的SRAM數(shù)據(jù) ,1開(kāi)啟 0關(guān)閉*/
/**
* @brief 描 述:讀取SRAM的ID
* @param 參數(shù)1:uint32_t Size 讀取數(shù)據(jù)量.
* @param 參數(shù)2:uint8_t *ReadData 讀取的數(shù)據(jù)存儲(chǔ)地址.
* @retval 回 復(fù):無(wú).
* @author 作 者:YL
* @date 日 期:2021.10.07
* @version 版 本:V1.0
* @warning 警 告:
*/
void Sram_LY64_Read_ID(uint32_t Size,uint8_t *ReadData)
{
uint8_t pData[4];
pData[0] = SRAM_LY64_CMD_READ_ID;
pData[1] = 0;
pData[2] = 0;
pData[3] = 0;
rt_enter_critical();/*調(diào)度器上鎖*/
rt_pin_write(SPI_SRAM_CS , 0); /*使能SRAM*/
rt_hw_us_delay(2);
HAL_SPI_Transmit(&hspi1, pData, 4, 2); /*發(fā)送數(shù)據(jù)*/
HAL_SPI_Receive(&hspi1, ReadData, Size, 2);/*接收數(shù)據(jù)*/
rt_pin_write(SPI_SRAM_CS , 1); /*去使能SRAM*/
rt_exit_critical();/*調(diào)度器解鎖*/
}
/**
* @brief 描 述:復(fù)位SRAM
* @param 參數(shù)1:
* @retval 回 復(fù):無(wú).
* @author 作 者:YL
* @date 日 期:2021.10.13
* @version 版 本:V1.0
* @warning 警 告:
*/
void Sram_LY64_Reset(void)
{
uint8_t pData[2];
pData[0] = SRAM_LY64_CMD_Reset_Enable;
pData[1] = SRAM_LY64_CMD_Reset;
rt_enter_critical();/*調(diào)度器上鎖*/
rt_pin_write(SPI_SRAM_CS , 0); /*使能SRAM*/
rt_hw_us_delay(2);
HAL_SPI_Transmit(&hspi1, pData, 2, 2); /*發(fā)送數(shù)據(jù)*/
rt_pin_write(SPI_SRAM_CS , 1); /*去使能SRAM*/
rt_exit_critical();/*調(diào)度器解鎖*/
}
/**
* @brief 描 述:讀取指定地址開(kāi)始指定數(shù)量的 SRAM數(shù)據(jù).
* @param 參數(shù)1:uint32_t addr 開(kāi)始地址.
* @param 參數(shù)2:uint32_t Size 讀取數(shù)據(jù)量.
* @param 參數(shù)3:uint8_t *ReadData 讀取的數(shù)據(jù)存儲(chǔ)地址.
* @retval 回 復(fù):無(wú).
* @author 作 者:YL
* @date 日 期:2021.10.07
* @version 版 本:V1.0
* @warning 警 告:如果讀取數(shù)量超過(guò) SRAM_LY64_READ_LEN 程序會(huì)自動(dòng)按設(shè)置好的單次讀取最大數(shù)量分多次讀取。
*/
void Sram_LY64_Read(uint32_t addr,uint32_t Size,uint8_t *ReadData)
{
uint8_t pData[5];
uint16_t read_size;
do
{
pData[0] = SRAM_LY64_CMD_READ;
pData[1] = addr>>16;
pData[2] = addr>>8;
pData[3] = addr;
pData[4] = 0x00; /* SPI Fast Read ‘h0B 命令的時(shí)序要求要等待8個(gè)時(shí)鐘周期。 */
if (Size > SRAM_LY64_READ_LEN)
{
read_size = SRAM_LY64_READ_LEN;
Size = Size - SRAM_LY64_READ_LEN;
}
else
{
read_size = Size;
Size = 0;
}
#if SRAM_LY64_MODE_SPI
//rt_enter_critical();/*調(diào)度器上鎖*/
rt_pin_write(SPI_SRAM_CS , 0); /*使能SRAM 用RT函數(shù)控制*/
//LY64_CS(0); /*使能SRAM 寄存器直接控制*/
rt_hw_us_delay(2);
HAL_SPI_Transmit(&hspi1, pData, 5, 2); /*發(fā)送數(shù)據(jù)*/
HAL_SPI_Receive(&hspi1, ReadData, read_size, 2); /*接收數(shù)據(jù)*/
#endif
#if SRAM_LY64_MODE_SPI_DMA
while(hdma_spi1_rx.State != HAL_DMA_STATE_READY ); /*等待DMA就緒*/
while(hdma_spi1_tx.State != HAL_DMA_STATE_READY ); /*等待DMA就緒*/
rt_pin_write(SPI_SRAM_CS , 0); /*使能SRAM 用RT函數(shù)控制*/
HAL_SPI_Transmit_DMA(&hspi1, pData, 5); /*發(fā)送數(shù)據(jù)*/
while(hdma_spi1_tx.State != HAL_DMA_STATE_READY ); /*等待DMA發(fā)送完成*/
HAL_SPI_Receive_DMA(&hspi1, ReadData, read_size); /*接收數(shù)據(jù)*/
while(hdma_spi1_rx.State != HAL_DMA_STATE_READY ); /*等待DMA接收完成*/
#endif
rt_pin_write(SPI_SRAM_CS , 1); /*去使能SRAM 用RT函數(shù)控制*/
//LY64_CS(1); /*去使能SRAM 寄存器直接控制*/
//rt_exit_critical();/*調(diào)度器解鎖*/
addr = addr + read_size;
ReadData = ReadData + read_size;
}while(Size);
}
/**
* @brief 描 述:寫指定數(shù)量數(shù)據(jù)到 SRAM數(shù)據(jù).
* @param 參數(shù)1:uint32_t addr 開(kāi)始地址.
* @param 參數(shù)2:uint32_t Size 數(shù)據(jù)量.
* @param 參數(shù)3:uint8_t *WriteData 待寫入數(shù)據(jù)存儲(chǔ)地址.
* @retval 回 復(fù):無(wú).
* @author 作 者:YL
* @date 日 期:2021.10.07
* @version 版 本:V1.0
* @warning 警 告:
*/
void Sram_LY64_Write(uint32_t addr,uint32_t Size,uint8_t *WriteData)
{
uint8_t *test = WriteData-4;
/* 連續(xù)寫入 */
#if SRAM_LY64_MODE_SPI
uint8_t pData[5];
rt_pin_write(SPI_SRAM_CS , 0);
rt_hw_us_delay(2);
pData[0] = SRAM_LY64_CMD_WRITE;
pData[1] = addr>>16;
pData[2] = addr>>8;
pData[3] = addr;
pData[4] = *WriteData;
HAL_SPI_Transmit(&hspi1, pData, 4, 2); /* 發(fā)送寫命令 */
HAL_SPI_Transmit(&hspi1, WriteData, Size, 2); /* 發(fā)送待寫入數(shù)據(jù) */
#endif
#if SRAM_LY64_MODE_SPI_DMA /* 把寫命令放入待發(fā)送數(shù)據(jù)中 */
*(WriteData - 4) = SRAM_LY64_CMD_WRITE; /* 這里是待寫入數(shù)據(jù)的首地址,在線程中定義了結(jié)構(gòu)體,這個(gè)地址前4個(gè)地址是預(yù)留存放寫命令用的 */
*(WriteData - 3) = addr>>16;
*(WriteData - 2) = addr>>8;
*(WriteData - 1) = addr;
while(hdma_spi1_rx.State != HAL_DMA_STATE_READY ); /* 等待DMA就緒 */
while(hdma_spi1_tx.State != HAL_DMA_STATE_READY ); /* 等待DMA就緒 */
rt_pin_write(SPI_SRAM_CS , 0);
HAL_SPI_Transmit_DMA(&hspi1, test, Size + 4); /* 發(fā)送待寫入數(shù)據(jù) */
while(hdma_spi1_tx.State != HAL_DMA_STATE_READY ); /* 等待DMA發(fā)送完成 */
#endif
//rt_kprintf("SIZE = %d,addr = %08x, %02x , %02x , %02x , %02x , %02x \r\n",Size,addr,pData[0],pData[1],pData[2],pData[3],pData[4]);
rt_pin_write(SPI_SRAM_CS , 1);
}
/* LY68L6400 讀寫測(cè)試線程,可以測(cè)試讀取指定大小數(shù)據(jù)花費(fèi)時(shí)間、測(cè)試讀寫可靠性 */
void SARM_LY64_Test_entry(void *param)
{
rt_pin_mode(SPI_SRAM_CS , PIN_MODE_OUTPUT);
rt_pin_mode(SPI_FLASH_CS , PIN_MODE_OUTPUT); /* 測(cè)試板FLASH 與 SARM 共用SPI接口 */
rt_pin_write(SPI_FLASH_CS , 1); /* 測(cè)試板FLASH 與 SARM 共用SPI接口,這里先把FLASH CS 拉高 */
rt_thread_mdelay(3000);
rt_uint8_t id[SRAM_LY64_ID_LEN] = {0}; /* SRAM id 存儲(chǔ) */
uint32_t data_addr = 0; /* 待讀寫 SRAM 首地址 */
struct spi_wdata /* 待寫入數(shù)據(jù)結(jié)構(gòu)體 */
{
uint8_t cmd[4]; /* 寫數(shù)據(jù)命令 */
uint8_t data[WR_SIZE]; /* 待寫入SRAM的數(shù)據(jù) */
} spi_wdata_1;
uint8_t *wdata = &spi_wdata_1.data[0]; /* 待寫入數(shù)據(jù)指針 */
uint8_t rdata[WR_SIZE] = {0};
uint32_t stime; /* 開(kāi)始時(shí)間 */
uint32_t etime; /* 結(jié)束時(shí)間 */
uint16_t crc_data_w; /* 寫入數(shù)據(jù)CRC*/
uint16_t crc_data_r; /* 讀出數(shù)據(jù)CRC*/
uint32_t read_crc_ok = 0; /* 單次讀出數(shù)據(jù)CRC 成功次數(shù)*/
uint32_t read_crc_err = 0; /* 單次讀出數(shù)據(jù)CRC 失敗次數(shù)*/
uint32_t read_crc_err_sum = 0; /* 總讀出數(shù)據(jù)CRC 失敗次數(shù)*/
uint16_t test_num = 1; /* 測(cè)試次數(shù) */
uint8_t i = 1;
/* 待寫入數(shù)據(jù)初始化 */
for (uint16_t var = 0; var < WR_SIZE; ++var)
{
if (i>100)
{
i = 1;
}
wdata[var] = i;
++i;
}
Sram_LY64_Reset(); /* 復(fù)位 SRAM */
while(1)
{
rt_thread_mdelay(100);
/* 讀取SRAM ID */
Sram_LY64_Read_ID(SRAM_LY64_ID_LEN,id);
rt_kprintf("Read ID is:%02x %02x %02x %02x %02x %02x %02x %02x \n", id[0],id[1],id[2],id[3], id[4], id[5],id[6],id[7]);
/* SRAM寫入數(shù)據(jù) */
data_addr = 0;
crc_data_w = yl_crc16(wdata,WR_SIZE); /* 校驗(yàn)待寫入數(shù)據(jù),以便與讀出數(shù)據(jù)做對(duì)比 */
rt_kprintf("寫入數(shù)據(jù) CRC = %04x \r\n",crc_data_w);
/* 往SRAM中寫滿數(shù)據(jù) */
stime = rt_tick_get(); /* 獲取系統(tǒng)時(shí)間 */
do
{
Sram_LY64_Write(data_addr,WR_SIZE,wdata);
data_addr = data_addr + WR_SIZE;
} while (data_addr < SRAM_LY64_SIZE);
etime = rt_tick_get(); /* 獲取系統(tǒng)時(shí)間 */
rt_kprintf("寫 開(kāi)始 = %d 結(jié)束 = %d 耗時(shí) = %d\r\n",stime,etime,etime-stime); /* 計(jì)算寫入過(guò)程耗時(shí) */
rt_thread_mdelay(10);
/* 讀取SRAM全部數(shù)據(jù) */
i = 5; /* 循環(huán)讀取測(cè)試次數(shù) */
while(i)
{
data_addr = 0;
rt_kprintf("倒數(shù)第 %d 次讀取測(cè)試...\r\n",i);
stime = rt_tick_get();
do
{
Sram_LY64_Read(data_addr,WR_SIZE,rdata);
data_addr = data_addr + WR_SIZE;
#if READ_DATA_CRC
/* CRC 校驗(yàn)讀取的數(shù)據(jù) */
crc_data_r = yl_crc16(rdata,WR_SIZE);
if (crc_data_r == crc_data_w)
{
//rt_kprintf("讀第 %d 次, 校驗(yàn)成功:地址: adr = %08x , CRC寫 = %04x , CRC讀 = %04x\r\n",i,data_addr-WR_SIZE,crc_data_w,crc_data_r);
++read_crc_ok;
}
else
{
rt_kprintf("\r\n\r\n\r\n 起始地址: %08x , %d 校驗(yàn)失?。?CRC寫 = %04x , CRC讀 = %04x \r\n\r\n\r\n",data_addr-WR_SIZE,i,crc_data_w,crc_data_r);
++read_crc_err;
++read_crc_err_sum;
}
#endif
/* 打印讀取的SRAM數(shù)據(jù) */
#if READ_DATA_PRINTF
rt_kprintf(" 讀開(kāi)始地址: adr = %08x \r\n",data_addr-WR_SIZE);
if (crc_data_r != crc_data_w)
{
for (uint16_t var = 0; var < WR_SIZE; ++var)
{
rt_kprintf("讀SRAM var= %d Data:%d %d %d %d %d %d %d %d %d %d\n",var, rdata[var+0],rdata[var+1],rdata[var+2],rdata[var+3], rdata[var+4], rdata[var+5],rdata[var+6],rdata[var+7],rdata[var+8], rdata[var+9]);
var = var + 9;
}
}
#endif
} while (data_addr < SRAM_LY64_SIZE);
etime = rt_tick_get();
rt_kprintf("讀取大小 = %d 字節(jié), 時(shí)間統(tǒng)計(jì):開(kāi)始 = %d 結(jié)束 = %d 耗時(shí) = %d\r\n",SRAM_LY64_SIZE,stime,etime,etime-stime);
rt_memset(rdata, 0, WR_SIZE); /* 把讀數(shù)據(jù)緩存清零 */
--i;
}
rt_kprintf("第 %d 次測(cè)試:寫1次,連讀 5 次 ;單次全部讀取,每次讀1K字節(jié):校驗(yàn)成功次數(shù) = %d ,校驗(yàn)失敗次數(shù) = %d ;累計(jì)校驗(yàn)失敗次數(shù) = %d\r\n",test_num,read_crc_ok,read_crc_err,read_crc_err_sum);
++test_num;
read_crc_err = 0;
read_crc_ok = 0;
rt_thread_mdelay(1000);
}
}
/*線程創(chuàng)建函數(shù)*/
int SARM_LY64_Test(void)
{
rt_thread_t tid1; /*創(chuàng)建線程控制塊指針來(lái)接收線程創(chuàng)建函數(shù)的返回值,目的是通過(guò)返回值判斷線程是否創(chuàng)建ok*/
/* 創(chuàng)建線程 1,名稱是 SARM_LY64_Test,入口是 SARM_LY64_Test_entry*/
tid1 = rt_thread_create("SARM_LY64_Test", /*線程名稱,系統(tǒng)打印線程時(shí)會(huì)顯示這個(gè)線程的名字*/
SARM_LY64_Test_entry, /*線程入口函數(shù),入口函數(shù)函數(shù)名*/
RT_NULL, /*入口參數(shù)*/
2000 + WR_SIZE*2, /*設(shè)置內(nèi)存堆棧大小*/
9, /*設(shè)置優(yōu)先級(jí)*/
200); /*時(shí)間片參數(shù),時(shí)間片是在有多個(gè)相同優(yōu)先級(jí)線程時(shí),這個(gè)線程每次被執(zhí)行多少個(gè)時(shí)間片*/
/* 如果獲得線程控制塊,啟動(dòng)這個(gè)線程 */
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
return RT_EOK;
}
INIT_APP_EXPORT(SARM_LY64_Test);
6.測(cè)試信息輸出
?
7:參考資源鏈接
優(yōu)質(zhì)參考資料:STM32 SPI通信協(xié)議詳細(xì)講解—小白入門_阿喬不想編程的博客-CSDN博客
其他參考:
https://blog.csdn.net/weixin_41294615/article/details/103233374?depth_1-
https://yngzmiao.blog.csdn.net/article/details/80318821
https://www.freesion.com/article/65571428542/
https://my.oschina.net/u/4386235/blog/3937830
https://blog.csdn.net/qq_54747686/article/details/119221405?spm=1001.2014.3001.5501
https://www.cnblogs.com/xuhaojieixbwer/p/14270116.html
https://www.cnblogs.com/xingboy/p/9555708.html
https://xfxuezhang.blog.csdn.net/article/details/108716706
https://blog.csdn.net/as480133937/article/details/104827639/?spm=1001.2101.3001.4242
https://blog.csdn.net/as480133937/article/details/105849607文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-651001.html
https://blog.csdn.net/as480133937/article/details/104827639文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-651001.html
到了這里,關(guān)于STM32 SPI+DMA 驅(qū)動(dòng) SRAM LY68L6400SLIT 應(yīng)用筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!