本帖最后由 御坂10032号 于 2024-12-13 00:12 编辑
简介
大家好,接下来我将对项目的所有任务进行整合和汇总,并且介绍每个任务功能的核心代代码。
任务/项目介绍
本次项目采用的开发板是来自瑞萨电子的EK-RA6M5开发板, 它基于Arm®Cortex®-M33内核, 最高主频可以跑到200MHZ. 它一共有176个引脚。它不仅本身性能强悍,同时预留了很多其他生态的接口。 比如说 2个Seeed Grove®系统(I2C/模拟)连接器 ,SparkFun®Qwiic®连接器, 2个Digilent PmodTM(SPI和UART)连接器,ArduinoTM(Uno R3)连接器,和MikroElektronikaTM mikroBUS连接器。
本期一共有四个任务, 分别是下述。
- 入门任务:搭建环境,下载调试示例程序,Blink,按键;
- 基础任务:quad-spi flash和octo-spi flash配置及读写速度测试;DAC配置生成波形及性能测试;
- 进阶任务:示例程序中新增命令打印信息;
- 扩展任务:设计一个类似信号发生器功能的例程。可在示例程序上修改。通过命令或按键,设置DAC输出波形,可通过flash存储历史波形等信息。
实现思路:
- 入门任务:入门任务的主要实现思路是学习如何搭建环境信息, 以及如何使用E2studio创建工程,并且将示例程序使用debug接口下载到开发板上。 同时学习如何调整Demo的代码信息, 比如说增加额外的LED频率和占空比等等,以及如何读取IO输入。
- 基础任务:基础任务的Q-SPI falsh 和O-SPI flash的配置和读写速度测试,我们可以参照官方提供的函数进行实现。 对于DAC的配置如果不想在原本的项目上改的话, 可以在configuration.yml 中来快速配置, 并且快速的生成stack。然后使用Developer Assistance 来辅助我们快速完成。
- 进阶任务:进阶任务需要我们自己来创建一个菜单选项,并且集成在主菜单界面中。使其打印自定义的数据信息。
- 扩展任务:拓展任务实现起来相对复杂一点,但是其功能则是整合了上述所有的功能在一起。通过命令或者按键来生成不同的波形,同时可以将波形数据保存到Flash里,进行历史数据的保存或者直接输出。
软件流程图
由于上述的程序主要是在RTOS中运行的, 现在我来简单的解释一下上面的流程图。 首先在程序的初始化完成之后。 显示程序的菜单界面。 之后根据用户的输入进入到不同的子菜单选项。比如说板载的信息显示。 自定义命令。 或者QSPI操作DAC输出等。 而对于某些非本次任务的子菜单选项这次并没有额外的画出。 比如说Webserver的子菜单。 USB相关的等以便于理解。
主要功能、核心代码展示
入门任务核心代码及其功能演示:
入门任务的核心在于如何来追踪已经有的LED的频率和占空比信息等。我们可以非常方便的使用e2 studio 对 menu_kit.c 中相关的变量进行追踪和修改。
修改LED的相关信息
效果演示
基础任务核心代码及其功能演示:
基础任务的核心在于读懂如何使用官方的SPI-flash的读写测试部分, 虽然测试代码我们并不需要自己直接写,但是在后续的SPI flash 数据保存环节我们还需要继续使用。 所以能够看懂官方的代码也是至关重要的一项。 对于DAC的测试的重点有两个 ,1 如何使用FSP结合stack快速生成对应的库文件和初始化文件。 2 - 如何使用DAC相关的函数输出。
SPI-Flash 测试核心代码
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);
}
相关的DAC配置信息
效果如下所示:
DAC输出的效果
进阶任务核心代码及其功能演示
进阶任务的核心在于如何读懂原本的命令菜单方面的代码。 如果读懂了的话,我们可以非常方便的在原本的功能上面进行拓展。
主菜单核心代码
/* Table of menu functions */
static st_menu_fn_tbl_t s_menu_items[] =
{
{"Kit Information" , kis_display_menu},
{"Hello world" , ns_display_hello_world},
{"QSPI Operation" , ns_display_qspi_write},
{"Web Server" , eth_emb_display_menu},
{"Network Name Lookup" , eth_www_display_menu},
{"Quad-SPI and Octo-SPI Speed Comparison" , ext_display_menu},
{"Cryptography and USB High speed (MSC)" , enc_display_menu},
{"Next Steps", ns_display_menu },
{"", NULL }
};
子菜单核心代码
/*
* menu_text.c
*
* Created on: 2024年12月1日
* Author: 23391
*/
#include "FreeRTOS.h"
#include "FreeRTOSConfig.h"
#include "semphr.h"
#include "queue.h"
#include "task.h"
#include "common_init.h"
#include "common_utils.h"
#include "menu_text.h"
#define CONNECTION_ABORT_CRTL (0x00)
#define MENU_EXIT_CRTL (0x20)
static char_t s_print_buffer[BUFFER_LINE_LENGTH] = {};
#define MODULE_NAME "\r\n%d. Hello world!\r\n"
test_fn ns_display_hello_world (void)
{
int8_t c = -1;
print_to_console((void*)s_print_buffer);
sprintf (s_print_buffer, MODULE_NAME, g_selected_menu);
print_to_console((void*)s_print_buffer);
sprintf (s_print_buffer, MENU_RETURN_INFO);
while ((CONNECTION_ABORT_CRTL != c))
{
c = input_from_console ();
if ((MENU_EXIT_CRTL == c) || (CONNECTION_ABORT_CRTL == c))
{
break;
}
}
return (0);
}
效果演示
拓展任务核心代码及其功能演示
拓展任务实际上是将上述的所有任务整合在了一起,通过将上述的所有任务进行整合,从而来达到我们任务的目的。 拓展任务中我们需要将历史波形信息保存到Flash, 然后根据从Flash里读取的波形来驱动DAC输出不同的波形(正弦波和三角波等)
生成正弦波代码
void generate_sine_wave(uint16_t *sine_wave_array, size_t size)
{
for (size_t i = 0; i < size; i++)
{
// Generate sine wave: scale to 0 - MAX_AMPLITUDE
sine_wave_array[i] = (uint16_t) ((sin ((double) i * 2.0 * 3.1415926 / size) + 1.0) * (MAX_AMPLITUDE / 2.0));
}
}
生成三角波代码
void generate_triangle_wave(uint16_t *triangle_wave_array, size_t size)
{
size_t half_period = size / 2; // Half period of the triangle wave
for (size_t i = 0; i < size; i++)
{
if (i < half_period)
{
// Rising edge: 0 to MAX_AMPLITUDE
triangle_wave_array[i] = (uint16_t) ((double) i / (half_period - 1) * MAX_AMPLITUDE);
}
else
{
// Falling edge: MAX_AMPLITUDE to 0
triangle_wave_array[i] = (uint16_t) ((double) (size - i - 1) / (half_period - 1) * MAX_AMPLITUDE);
}
}
}
SPI Flash 的保存和读取
void saveToFlash(uint8_t *buffer)
{
fsp_err_t err = FSP_SUCCESS;
uint32_t page_write_count = 0;
uint8_t *p_mem_addr = (uint8_t*) QSPI_DEVICE_START_ADDRESS;
spi_flash_protocol_t current_spi_mode;
p_mem_addr = (uint8_t*) QSPI_DEVICE_START_ADDRESS;
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;
}
/* 擦除 QSPI 的指定扇区 */
err = R_QSPI_Erase (&g_qspi_ctrl, p_mem_addr, 4096U);
if (FSP_SUCCESS != err)
{
sprintf (s_print_buffer, "Failed to erase QSPI flash\r\n");
print_to_console ((void*) s_print_buffer);
return;
}
/* 等待擦除完成 */
err = get_flash_status ();
if (FSP_SUCCESS != err)
{
sprintf (s_print_buffer, "Failed to get flash status after erase\r\n");
print_to_console ((void*) s_print_buffer);
return;
}
err = R_QSPI_Write (&g_qspi_ctrl, &buffer[0], p_mem_addr, 1);
if (FSP_SUCCESS != err)
{
sprintf (s_print_buffer, "Failed to write data to QSPI flash\r\n");
print_to_console ((void*) s_print_buffer);
}
else
{
err = get_flash_status ();
if (FSP_SUCCESS != err)
{
sprintf (s_print_buffer, "Failed to get flash status after write\r\n");
print_to_console ((void*) s_print_buffer);
}
}
deinit_qspi (current_spi_mode);
}
void readFromFlash(uint8_t *buffer)
{
fsp_err_t err = FSP_SUCCESS;
uint32_t page_read_count = 0;
uint8_t *p_mem_addr = (uint8_t*) QSPI_DEVICE_START_ADDRESS;
spi_flash_protocol_t current_spi_mode;
/* 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;
}
memcpy (&buffer[0], p_mem_addr, 1);
deinit_qspi (current_spi_mode);
}
效果展示
所有功能的实现思路和效果演示请查看每个帖子,或者是总结视频,总结视频中详细讲解了每个任务的实现步骤。
总结和建议
非常感谢得捷电子和电子工程世界提供的这次来之不易的活动机会。 我个人觉得本次的活动安排的非常好, 每个任务和衔接非常严谨。最后又通过一个完成的demo设计将上述所有的任务整合在了一起, 让我在本次活动中所学到的知识都联系了起来。 希望得捷电子和电子工程世界越办越好。再次感谢!
成功视频展示
代码附件信息
代码下载链接(点击跳转, 已经包含了每个部分的完整代码实现)
|