【Follow me第二季第3期】基础任务quad-spi 和octo-spi flash对比和 DAC生成波形
[复制链接]
本帖最后由 一只小跳帽 于 2024-12-8 21:16 编辑
今天来完成第二个基础任务首先完成quad-spi 和octo-spi flash
下面首先介绍一下quad-spi
Quad-SPI(也称为QSPI)是一种在现代微控制器中广泛采用的外围设备接口,主要用于与外部闪存芯片进行高速通信。以下是对Quad-SPI的详细介绍:
一、基本概念
Quad-SPI是一个串行接口,它通过四条数据线(I0、I1、I2、I3)实现数据的读取、写入和擦除操作。与传统的SPI接口相比,Quad-SPI提供了更高的数据传输速率和更大的带宽,特别适用于需要处理大量内存密集型数据的应用场景,如多媒体处理和片上内存不足的情况。
二、工作原理
Quad-SPI接口的数据线是双向的,可以根据需要动态配置为输入或输出。在数据传输过程中,Quad-SPI接口通过时钟信号控制数据的同步传输。每个时钟周期可以传输多个比特(通常是4个比特),从而大大提高了数据传输速率。
Quad-SPI的通信过程通常包括以下几个阶段:指令阶段、地址阶段、交替字节阶段、空指令周期阶段和数据阶段。在指令阶段,Quad-SPI接口发送一个8位指令到闪存芯片,指定要执行的操作类型。在地址阶段,发送1~4字节的地址信息,指示操作的目标位置。交替字节阶段用于发送控制操作模式的字节。空指令周期阶段则用于准备数据线的传输方向。最后,在数据阶段,Quad-SPI接口可以从闪存芯片接收或向其发送任意数量的字节。
三、关键特性
- 双倍数据速率模式:Quad-SPI支持双倍数据速率模式,即在每个时钟周期的上升沿和下降沿都传输数据,从而实现了数据传输速率的加倍。但需要注意的是,这个功能需要微控制器和闪存芯片都支持才能实现。
- XIP功能:XIP(eXecute In Place)是Quad-SPI的一个重要特性,它允许微控制器直接从外部闪存执行代码而无需先将其复制到内部存储器。这大大提高了代码执行的效率,特别是在代码大小较大、无法存储在片上存储器中的情况下。
- 多设备连接:多个Quad-SPI设备可以链接到同一个Quad-SPI接口上,并通过芯片选择引脚来选择特定的设备进行通信。这增加了系统的灵活性和可扩展性。
四、应用领域
Quad-SPI接口因其高速、高效和灵活的特性而被广泛应用于各种嵌入式系统中。特别是在需要处理大量数据、对存储性能有较高要求的场景中,如智能手机、平板电脑、数码相机等消费电子产品中,Quad-SPI接口发挥着至关重要的作用。
五、总结
Quad-SPI是一种功能强大、灵活高效的串行接口,它通过四条数据线实现了与外部闪存芯片的高速通信。其支持的双倍数据速率模式、XIP功能和多设备连接特性使其在各种嵌入式系统中得到了广泛应用。随着技术的不断发展,Quad-SPI接口的性能和功能也将不断提升,为嵌入式系统的设计和开发提供更多的可能性。
我们这款开发板中有一个Quad-SPI flash
原理图如下
下面再了解一下 octo-spi
Octo-SPI(也称作OctoSPI或OSPI)是Quad-SPI(Quad Serial Peripheral Interface,四线串行外设接口)的升级版,是一种高速串行接口技术,它通过使用八条数据线(而不是Quad-SPI的四条)来进一步提高数据传输速率和带宽。以下是对Octo-SPI的详细介绍:
一、基本概念与特点
- 高速数据传输:Octo-SPI通过增加数据线的数量,实现了更高的数据传输速率,适用于需要处理大量数据的应用场景。
- 并行通信:与Quad-SPI类似,Octo-SPI也采用并行通信方式,但数据线数量加倍,从而提高了通信效率。
- 向下兼容:Octo-SPI接口通常也支持Single-SPI、Dual-SPI和Quad-SPI模式,这意味着它可以与这些较低版本的SPI接口设备兼容。
- 内存映射模式:在Octo-SPI接口中,外部存储器可以像内部内存一样被访问,这提高了系统的总线主控器(如DMA)在CPU停机的低功耗模式下自动访问外部内存的能力。
二、工作原理
Octo-SPI的工作原理与Quad-SPI相似,但数据传输能力更强。它通常包括指令阶段、地址阶段、数据阶段等,通过时钟信号同步数据的传输。在每个时钟周期,Octo-SPI可以传输多达8个比特的数据,从而实现了更高的数据传输速率。
三、关键特性
- 高吞吐量:由于使用了八条数据线,Octo-SPI能够提供比Quad-SPI更高的数据吞吐量,适用于需要快速访问大量数据的场景。
- 灵活性:Octo-SPI接口的配置非常灵活,可以根据需要启用或禁用每个数据线,以适应不同的数据传输需求。
- 支持多种存储器类型:Octo-SPI接口可以连接多种类型的外部存储器,如Flash存储器、PSRAM等,为系统设计提供了更多的选择。
四、应用领域
Octo-SPI接口因其高速、高效和灵活的特性而被广泛应用于各种高性能嵌入式系统中。特别是在需要处理大量数据、对存储性能有较高要求的场景中,如智能手机、平板电脑、数码相机、物联网设备等,Octo-SPI接口发挥着至关重要的作用。
五、发展趋势
随着技术的不断发展,Octo-SPI接口的性能和功能也在不断提升。例如,一些最新的Octo-SPI接口支持更高的时钟频率和更大的数据传输量,同时还提供了更多的配置选项和更强大的错误检测与纠正功能。这些改进使得Octo-SPI接口在高性能嵌入式系统中的应用更加广泛和深入。
总的来说,Octo-SPI作为一种高速串行接口技术,以其高效的数据传输能力和灵活的配置选项,在高性能嵌入式系统中发挥着越来越重要的作用。
我们这款开发板中有一个Octo-SPI flash
原理图如下
这里我们看到Octo-SPI 使用了更多的线 因此按照道理来说他的传输速率应该是更高的 下面我们来尝试一下对他们进行测试
由于我们对存储芯片的不熟悉因此我们使用官方的历程进行分析但是我们也要去读懂这个代码是如何实现的下面进行分析代码的实现的过程和实际的运行结果
首先我从官网下载了官方的历程并且导入工程
我们查看分析后看到这个是一个快速入门的例子 首先有一个菜单main函数我们看到里面的文件
这个应该是一个主菜单 前面对应的是字符串解释应该是在终端输入的后面是对应执行的函数看到第四个就是我们所需要的两种flash速度的对比
我们进入其中对应的函数查看
ospi_performance_test (block_size_actual, &ospi_performance_write_result, &ospi_performance_read_result);
/* Multiply uSec calcs by 100, to avoid losses due to small results in integer maths
* Scaled to fit within uint32_t */
ospi_write_result = ((100000000 / timer_frequency) * ospi_performance_write_result) / 100;
qspi_write_result = ((100000000 / timer_frequency) * qspi_write_test(block_size_actual)) / 100;
/* ignoring -Wpointer-sign is OK for a constant string */
print_to_console((uint8_t *)"Writing to flash completed\r\n");
/* ignoring -Wpointer-sign is OK for a constant string */
print_to_console((uint8_t *)"\r\nReading the text block from external Quad-SPI and Octo-SPI flash memories...\r\n");
ospi_read_result = ((100000000 / timer_frequency) * ospi_performance_read_result) / 100;
qspi_read_result = ((100000000 / timer_frequency) * qspi_read_test(block_size_actual)) / 100;
/* ignoring -Wpointer-sign is OK for a constant string */
print_to_console((uint8_t *)"Reading from flash completed\r\n");
R_GPT_Close(g_memory_performance.p_ctrl);
其中最主要的代码就是这一部分
qspi_write_test(block_size_actual)
qspi_read_test(block_size_actual))
ospi_performance_test (block_size_actual, &ospi_performance_write_result, &ospi_performance_read_result);
这两个函数就是其中测试的内容使用两种方法去测试我们再去分析每一个方法
ospi_performance_test
void ospi_performance_test(uint32_t data_size,
uint32_t *ospi_performance_write_result,
uint32_t *ospi_performance_read_result)
{
fsp_err_t err;
uint32_t i = 1;
if (R_CGC_Open (g_cgc.p_ctrl, g_cgc.p_cfg) != FSP_SUCCESS)
{
__asm("bkpt");
}
while (i)
{
err = R_OSPI_Open(g_ospi.p_ctrl, g_ospi.p_cfg);
if (FSP_SUCCESS != err)
{
__asm("bkpt");
}
#if HIGH_SPEED_MODE
configure_dopi_ospi();
ospi_test_wait_until_wip();
#endif
*ospi_performance_write_result = write_dopi_ospi(data_size);
ospi_test_wait_until_wip();
*ospi_performance_read_result = read_dopi_ospi(data_size);
ospi_test_wait_until_wip();
erase_dopi_ospi();
ospi_test_wait_until_wip();
#if HIGH_SPEED_MODE
configure_spi_ospi();
ospi_test_wait_until_wip();
#endif
err = R_OSPI_Close(g_ospi.p_ctrl);
if (FSP_SUCCESS != err)
{
__asm("bkpt");
}
i--;
}
}
这个中就是使用
*ospi_performance_write_result = write_dopi_ospi(data_size);
ospi_test_wait_until_wip();
*ospi_performance_read_result = read_dopi_ospi(data_size);
ospi_test_wait_until_wip();
先对块进行写入再进行读出进行计算各自时间
qspi_write_test
static uint32_t qspi_write_test(uint32_t block_size)
{
fsp_err_t fsp_err;
uint32_t qspi_write_result = 0;
timer_status_t status = {};
fsp_err_t err = FSP_SUCCESS;
spi_flash_protocol_t current_spi_mode;
/* Convert from kB */
block_size *= 1024;
/* The comms mode is EXTENDED_SPI by default */
current_spi_mode = SPI_FLASH_PROTOCOL_EXTENDED_SPI;
/* initialise the QSPI, and change mode to that set in FSP */
err = qpi_init();
if (FSP_SUCCESS == err)
{
/* The comms mode has changed. So if recovering, this new mode required */
current_spi_mode = g_qspi_cfg.spi_protocol;
}
uint32_t page_write_count = 0;
uint8_t * p_mem_addr;
/* Cast to req type */
p_mem_addr = (uint8_t *)QSPI_DEVICE_START_ADDRESS;
while (((page_write_count * SECTOR_SIZE) < block_size)
&& ( FSP_SUCCESS == err ) )
{
/* Erase Flash for one sector */
err = R_QSPI_Erase(&g_qspi_ctrl, p_mem_addr, SECTOR_SIZE);
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "R_QSPI_Erase Failed\r\n");
}
else
{
err = get_flash_status();
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "Failed to get status for QSPI operation\r\n");
}
/* Verify the erased block data */
uint32_t count;
for (count = 0; count < SECTOR_SIZE; count++ )
{
if (DEFAULT_MEM_VAL != p_mem_addr[count])
{
/* Verification failed, perhaps the ERASE failed */
err = FSP_ERR_NOT_ERASED;
}
}
}
p_mem_addr += SECTOR_SIZE;
page_write_count++;
}
/* Start the test timer */
fsp_err = R_GPT_Start(g_memory_performance.p_ctrl);
/* Handle error */
if (FSP_SUCCESS != fsp_err)
{
/* Fatal error */
SYSTEM_ERROR
}
/* Cast to req type */
p_mem_addr = (uint8_t *)QSPI_DEVICE_START_ADDRESS;
page_write_count = 0;
while (((page_write_count * PAGE_WRITE_SIZE) < block_size)
&& (FSP_SUCCESS == err))
{
if (FSP_SUCCESS == err)
{
/* Write data to QSPI Flash */
/* Each block begins one character shifted along the source text. To avoid regular striping in memory */
err = R_QSPI_Write(&g_qspi_ctrl, &(sp_source[page_write_count]), p_mem_addr, PAGE_WRITE_SIZE);
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "R_QSPI_Write Failed\r\n");
}
else
{
err = get_flash_status();
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "Failed to get status for QSPI operation\r\n");
}
}
}
p_mem_addr += PAGE_WRITE_SIZE;
page_write_count++;
}
/* close QSPI module */
deinit_qspi(current_spi_mode);
fsp_err = R_GPT_Stop(g_memory_performance.p_ctrl);
/* Handle error */
if (FSP_SUCCESS != fsp_err)
{
/* Fatal error */
SYSTEM_ERROR
}
fsp_err = R_GPT_StatusGet(g_memory_performance.p_ctrl, &status);
/* Handle error */
if (FSP_SUCCESS != fsp_err)
{
/* Fatal error */
SYSTEM_ERROR
}
fsp_err = R_GPT_Reset(g_memory_performance.p_ctrl);
/* Handle error */
if (FSP_SUCCESS != fsp_err)
{
/* Fatal error */
SYSTEM_ERROR
}
qspi_write_result = status.counter;
return (qspi_write_result);
}
/**********************************************************************************************************************
End of function qspi_write_test
static uint32_t qspi_read_test(uint32_t block_size)
{
fsp_err_t fsp_err;
fsp_err_t err = FSP_SUCCESS;
uint32_t qspi_read_result = 0;
timer_status_t status = {};
spi_flash_protocol_t current_spi_mode;
uint8_t * p_dma_read_buffer;
uint32_t page_read_count;
uint8_t * p_mem_addr;
/* Convert from kB */
block_size *= 1024;
p_dma_read_buffer = pvPortMalloc(block_size);
if (NULL == p_dma_read_buffer)
{
HeapStats_t pxHeapStats;
vPortGetHeapStats(&pxHeapStats);
sprintf(s_print_buffer, "\r\nQSPI malloc operation Failed - Max free mem: %dbytes\r\n",
pxHeapStats.xSizeOfLargestFreeBlockInBytes);
/* Verification failed, perhaps the ERASE failed */
err = FSP_ERR_NOT_ERASED;
}
/* The comms mode of the FLASH device is EXTENDED_SPI by default */
current_spi_mode = SPI_FLASH_PROTOCOL_EXTENDED_SPI;
/* initialise the QSPI, and change mode to that set in FSP */
err = qpi_init();
if (FSP_SUCCESS == err)
{
/* The comms mode has changed. So if recovering, this new mode required */
current_spi_mode = g_qspi_cfg.spi_protocol;
}
/* Start the test timer */
fsp_err = R_GPT_Start(g_memory_performance.p_ctrl);
/* Handle error */
if (FSP_SUCCESS != fsp_err)
{
/* Fatal error */
SYSTEM_ERROR
}
page_read_count = 0;
/* cast to req type */
p_mem_addr = (uint8_t *)QSPI_DEVICE_START_ADDRESS;
while (((page_read_count * PAGE_WRITE_SIZE) < block_size)
&& (FSP_SUCCESS == err))
{
/* Verify the written data */
/* Each block begins one character shifted along the source text. To avoid regular striping in memory */
if ((fsp_err_t) (memcmp (p_mem_addr, &(sp_source[page_read_count]), PAGE_WRITE_SIZE)) != FSP_SUCCESS)
{
err = FSP_ERR_NOT_ERASED;
sprintf(s_print_buffer, "\r\nQSPI operation Failed -> Data read does not match with written data\r\n");
}
p_mem_addr += PAGE_WRITE_SIZE;
page_read_count++;
}
fsp_err = R_GPT_Stop(g_memory_performance.p_ctrl);
/* close QSPI module */
deinit_qspi(current_spi_mode);
/* Handle error */
if (FSP_SUCCESS != fsp_err)
{
/* Fatal error */
SYSTEM_ERROR
}
fsp_err = R_GPT_StatusGet(g_memory_performance.p_ctrl, &status);
/* Handle error */
if (FSP_SUCCESS != fsp_err)
{
/* Fatal error */
SYSTEM_ERROR
}
fsp_err = R_GPT_Reset(g_memory_performance.p_ctrl);
/* Handle error */
if (FSP_SUCCESS != fsp_err)
{
/* Fatal error */
SYSTEM_ERROR
}
qspi_read_result = status.counter;
vPortFree(p_dma_read_buffer);
return (qspi_read_result);
}
/**********************************************************************************************************************
End of function qspi_read_test
这两个函数就是分别进行读和写
我们只需要知道这么多就可以了下面进行运行和测试
下载进行后使用USB-fs的端口进行打印和交互
输入 4
输入64
这里可以看到 Octa-Spi 确实比较快与我们开头的猜测是想符合的
下面完成第二个任务DAC配置生成波形及性能测试;
这里我使用的方法是使用我们的开发板进行输出DAC的正弦波 再使用第二期的arduino R4 的开发板进行采集输出波形并且进行打印查看波形
首先新建工程并且进行配置
打开后发现有红色代表冲突我们查看这个端口P014是被配置为 是什么功能
这里为模拟输入我们改为DAC输出
将ADC0 的第12通道改为None即可
加入DAC的栈
对其进行设置
选择默认即可
代码如下
float map_float(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
/*******************************************************************************************************************//**
* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
* is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
float a=0;
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
//打开DAC模块
R_DAC_Open(&g_dac0_ctrl, &g_dac0_cfg);
R_DAC_Start(&g_dac0_ctrl);
while(1)
{
a+= 0.1;
if(a>4096)
a=0;
float out=map_float(sin(2*a),-1,1,0,4096);
// R_BSP_SoftwareDelay(50,BSP_DELAY_UNITS_MILLISECONDS);
R_DAC_Write(&g_dac0_ctrl, (uint16_t)out);
// out = sin(a);
}
}
我们查看打印出来的波形
波形是这种有规律的杂波是因为我们使用的ARDUINO R4 的性能没有我们这个开发板的强这个从侧面印证了我们DAC的强大为了看出波形我们加入延迟函数
void hal_entry(void)
{
/* TODO: add your own code here */
float a=0;
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
//打开DAC模块
R_DAC_Open(&g_dac0_ctrl, &g_dac0_cfg);
R_DAC_Start(&g_dac0_ctrl);
while(1)
{
a+= 0.1;
if(a>4096)
a=0;
float out=map_float(sin(2*a),-1,1,0,4096);
R_BSP_SoftwareDelay(20,BSP_DELAY_UNITS_MILLISECONDS);
R_DAC_Write(&g_dac0_ctrl, (uint16_t)out);
// out = sin(a);
}
}
波形就很有规律了
下面是本个实验的完整视频具体的过程和细节大家可以查看视频
|