176|1

58

帖子

2

TA的资源

一粒金砂(中级)

楼主
 

【Follow me第二季第3期】作品提交 [复制链接]

  本帖最后由 CoderX9527 于 2024-12-15 16:56 编辑
 
致谢
非常感谢 EEWORLD && DigiKey 联合举办的 FollowMe 活动,我非常幸运地参与了此次活动,学到了不少东西。以前没接触过 Renesas MCU,也没有接触过 Renesas 的开发环境,经过这次的学习,我学会了 DAC 输出波形,通过 QSPI 读写 Flash。受益良多。
 
视频
 

 
 
 
代码
 
 
 
任务简介
此次活动总共有4个任务:
  1. 入门任务:搭建环境,下载调试例程,Blink,按键;
  2. 基础任务:quid-spi flash 和 octo-spi flash 配置以及读写速度测试;DAC配置生成波形以及性能测试;
  3. 进阶任务:示例程序中新增命令打印信息;
  4. 扩展任务:设计一个类似信号发生器功能的例程。通过命令或者按键,设置 DAC 输出波形,可通过 flash 存储历史波形等信息。

 

物料
此次活动仅使用一个物料,即 EK-RA6M5 板卡。
 

1. 入门任务
其中入门任务,已经在如下帖子中分享我的实践: https://bbs.eeworld.com.cn/thread-1301289-1-1.html


此次作品提交贴分三个部分,分别介绍基础任务、进阶任务和扩展任务。

 

2. 基础任务:QSPI-Flash 和 OSPI-Flash 速度测试,以及 DAC 配置生成波形和性能测试

 
QSPI-Flash 和 OSPI-Flash 速度测试
QSPI-Flash和OSPI-Flash 速度测试在 quick_start 工程中已经有了,这里理清楚它们的实现思路并实测演示。
 
实现思路
从下面的菜单项可以看到QSPI-Flash和OSPI-Flash速度测试的入口函数是 ext_display_menu()
串口打印输出如下,用户输入整数为2的倍数,最大值为64,指定文本块大小,然后开始测试。
 
主流程
OSPI-Flash 读写测试流程
 
QSPI-Flash 写测试流程
 
QSPI-Flash 读测试流程
 
速度测试
 
块大小 2KB
块大小 4KB
块大小 8KB
块大小 16KB
块大小 32KB
块大小 64KB
 
总结
 
从上图可知:
  1. 无论QSPI还是OSPI,块大小翻倍,读写耗时都会翻倍,大约是2倍;
  2. 同样的块大小写操作,OSPI耗时基本是 QSPI 的一半,即速度大约是 QSPI 的2倍,符合预期;
  3. 同样的块大小读操作,OSPI耗时基本是 QSPI 的1/6,即速度大约是 QSPI的6倍。
 
DAC配置生成波形和性能测试
RA6M5 提供了一个带输出放大器的 12bit DAC,有两个输出通道。DA0 管脚是 DAC的通道0输出管脚,DA1 是 DAC 的通道1输出管脚。
 
三角波
由于 DAC 位宽是12位,数值范围是[0, 4095],因此三角波一个周期内的数值变化趋势是 0逐步增加到 4095,然后又逐步减少到0,周期循环。
这里为此单独写了一个生成三角波的模块,每次调用函数 triangle_wave_get_next() 都会获取三角波中的下一个点。
C++
typedef struct {
int16_t step; /**< 斜率 */
int32_t val;
} triangle_wave_t;

static triangle_wave_t m_triangle_wave = {
.step = 1 // 默认斜率为1
};

/**
* @brief 设置三角波斜率,可选值 [-2048, 2048]
*
* @param step
*/
void triangle_wave_cfg(int16_t step)
{
if ((step < -2048) || (step > 2048)) {
return;
}

m_triangle_wave.step = step;
}

/**
* @brief 获取三角波中的下一个点
*
* @return int32_t
*/
int32_t triangle_wave_get_next(void)
{
int32_t temp = 0;

temp = m_triangle_wave.val + m_triangle_wave.step;

if (m_triangle_wave.step >= 0) {
if (temp > 4095) {
m_triangle_wave.step = 0 - m_triangle_wave.step; // step 变负数
}
} else {
if (temp < 0) {
m_triangle_wave.step = 0 - m_triangle_wave.step; // step 变正数
}
}
m_triangle_wave.val += m_triangle_wave.step;

return m_triangle_wave.val;
}
 
DAC 配置
 
新增Stack--DAC
  1. 双击工程的配置文件 configuration.xml 文件,打开配置工具;
  2. New Stack 展开子菜单,选择 DAC
 
g_dac0 属性
  1. 打开 Properties 窗口,点击 HAL/Common Stacks 窗口中的 g_dac0 DAC (r_dac) ,然后就可以在下方属性窗口看到 g_dac0 的各个设置。
  2. 属性中可知 DAC 通道0对应的实例对象名字为 g_dac0,数据格式:Right Justifed,即数据右对齐,其他选项都是默认值;
  3. 从 Pins 可以看到 DA0 没有对应管脚,即(3)是 None,我们单击(4)处的箭头就可以跳转到 Pin Configuration 窗口,配置 DA0 管脚。
展开的 Pin Configuration 窗口中,可以看到 DAC0 的 管脚还是 None,我们修改为 P014,然而还是红色标注,说明配置有错误。
 
DA0 配置 P014 为输出管脚,红色表示管脚有错误或冲突。
 
找到 ADC 中的 ADC0,可知通道 AN012 占用了 P014 管脚,修改它为 None,即可解决 DAC0 配置 P014 管脚冲突问题。
 
确认 DAC0 的 P014 管脚配置没有问题之后,还需要点击 Generate Project Content 重新生成工程。
 
DAC 输出三角波
在函数 common_init() 中添加如下代码,初始化 DAC
在 gpt_blue_callback() 中添加一行 R_DAC_Write(&g_dac0_ctrl, triangle_wave_get()) 即可输出三角波。
 
示波器测量
 
 
心得体会
  1. 熟悉了定时器操作;
  2. 熟悉了QSPI-Flash, OSPI-Flash 的操作,实测对比读写速度,对于两者的性能有更深的认识;
  3. 熟悉了DAC模块,如何在 e2studio 中添加并配置 DAC 模块,示波器测试 DAC 输出,加深了对 DAC 的认识;
  4. DAC输出波形如此简单,以前以为很难,其实就是往 DAC 输出一个数值即可。
 
3. 进阶任务目标:示例程序中新增命令打印信息
 
显示命令的代码
从 main() 开始,一路找到显示菜单的函数,流程图如下:
 
菜单数组 s_menu_items
菜单数组如下,每一个成员都是一个结构体 st_menu_fn_tbl_t,成员 p_name 表示显示的菜单名字,成员 p_func 表示此菜单的执行函数。
C++
typedef struct menu_fn_tbl
{
char_t * p_name; /*<! Name of Test */
test_fn ( * p_func)(void); /*<! Pointer to Test Function */
} st_menu_fn_tbl_t;

/* Table of menu functions */
static st_menu_fn_tbl_t s_menu_items[] =
{
{"Kit Information" , kis_display_menu},
{"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 }
};
如第一个菜单项,菜单名字为 "Kit Information",菜单的执行函数为 kis_display_menu()。这个菜单执行函数打印开发板的基本信息,包括板卡名字,part number,128-比他UID,芯片温度,蓝色LED闪烁频率、亮度等级等。
我新增一个命令,菜单名字为 "Welcome",菜单执行函数为 kit_welcome(),此菜单打印MCU的温度信息。
 
菜单数组更新
C++
/* Table of menu functions */
static st_menu_fn_tbl_t s_menu_items[] =
{
{"Welcome" , kit_welcome}, // 新增命令
{"Kit Information" , kis_display_menu},
{"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 }
};
 
kit_welcome() 实现
此函数实现如下,最关键的就是第10行代码,把字符串输出到 s_print_buffer,然后调用函数 print_to_console((void *)s_print_buffer) 输出到终端。
C++
test_fn kit_welcome(void)
{
int8_t c = -1;

sprintf (s_print_buffer, "%s%s", gp_clear_screen, gp_cursor_home);

/* ignoring -Wpointer-sign is OK when treating signed char_t array as as unsigned */
print_to_console((void*)s_print_buffer);

sprintf (s_print_buffer, "\r\nWelcome, EEWORLD & DigiKey FollowMe 2-3 by CoderX9527\r\n");

/* ignoring -Wpointer-sign is OK when treating signed char_t array as as unsigned */
print_to_console((void*)s_print_buffer);

/* ignoring -Wpointer-sign is OK when treating signed char_t array as as unsigned */
sprintf (s_print_buffer, MENU_RETURN_INFO);
print_to_console((void*)s_print_buffer);

while (CONNECTION_ABORT_CRTL != c)
{
c = input_from_console ();
if ((MENU_EXIT_CRTL == c) || (CONNECTION_ABORT_CRTL == c))
{
break;
}
}

xEventGroupClearBits (g_update_console_event, STATUS_DISPLAY_MENU_KIS);
return (0);
}
 
运行演示
按下1进入 Welcome 命令界面,如下图所示:
按下空格键返回到主菜单。
 
心得体会
  1. 通过串口输出打印信息也可以玩的很花,在打印的字符串中加入特殊的控制字符,例如 \x1b[2m 等可以控制输出的格式,以及输出文字的颜色,但是需要串口中断支持;
 
4. 扩展任务:设计一个类似信号发生器功能的历程。可以在示例程序上修改。通过命令或者按键,设置DAC输出波形,可以通过 Flash 存储历史波形等信息。
 
设计思路
  1. 信号发生器,支持输出三角波、正弦波。
  2. 命令界面,t -- 三角波,s -- 正弦波,w -- 保存波形类型到Flash,r -- 从 Flash 读取波形类型。
 
实现步骤
  1. 设计2种波形,分别是三角波、正弦波,能输出对应的波形;
  2. 菜单中增加命令,进入信号发生器菜单界面,可选择子菜单项,总共4个选项;
  3. 分别实现4个子菜单项;
  4. 分别验证4个子菜单项;
 
菜单设计
在主菜单中新增一个菜单项,名字为 Signal Generator,执行函数为 kit_signal_generator()。
C
/* Table of menu functions */
static st_menu_fn_tbl_t s_menu_items[] =
{
{"Welcome" , kit_welcome},
{"Signal Generator" , kit_signal_generator}, // 信号发生器
{"Kit Information" , kis_display_menu},
{"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 }
};
 
函数 kit_signal_generator()
执行函数在串口打印如下信息,用户输入
  • t 输出三角波
  • s 输出正弦波
  • w 保存当前的波形类型到 Flash
  • r 从 Flash 读取波形类型,并立刻输出波形
  • 空格键,返回主菜单
C
test_fn kit_signal_generator(void)
{
int8_t c = -1;

sprintf(s_print_buffer, "%s%s", gp_clear_screen, gp_cursor_home);
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, OPT_HELP);
print_to_console(s_print_buffer);

sprintf(s_print_buffer, OPT_TRIANGLE);
print_to_console(s_print_buffer);

sprintf(s_print_buffer, OPT_SINE);
print_to_console(s_print_buffer);

sprintf(s_print_buffer, OPT_WRITE);
print_to_console(s_print_buffer);

sprintf(s_print_buffer, OPT_READ);
print_to_console(s_print_buffer);

sprintf(s_print_buffer, MENU_RETURN_INFO);
print_to_console((void*)s_print_buffer);

while (CONNECTION_ABORT_CRTL != c) {
c = input_from_console();

if (c == 't') {
triangle_wave_cfg(20);
wave_type_set(WAVE_TYPE_TRIANGLE);
print_to_console("t --> Force wave type: Triangle\r\n");
continue;
}

if (c == 's') {
wave_type_set(WAVE_TYPE_SINE);
print_to_console("s --> Force wave type: Sine\r\n");
continue;
}

if (c == 'w') {
sprintf(s_print_buffer, "w --> Save wave type: %u \r\n", (uint32_t)wave_type_get());
print_to_console(s_print_buffer);

save_wave_type(wave_type_get());
continue;
}

if (c == 'r') {
uint32_t wt = 0;
if (restore_wave_type(&wt) == FSP_SUCCESS) {
wave_type_set(wt);
}

sprintf(s_print_buffer, "r --> Restore wave type: %u \r\n", (uint32_t)wave_type_get());
print_to_console(s_print_buffer);
continue;
}

if ((MENU_EXIT_CRTL == c) || (CONNECTION_ABORT_CRTL == c)) {
break;
}
}

xEventGroupClearBits(g_update_console_event, STATUS_DISPLAY_MENU_KIS);
return (0);
}
 
产生三角波
产生三角波的代码见入门任务,这里仅粘贴代码。重点在函数 triangle_wave_get_next(),每调用一次即可得到下一个点。
C
typedef struct {
int16_t step; /**< 斜率 */
int32_t val;
} triangle_wave_t;

static triangle_wave_t m_triangle_wave = {
.step = 1 // 默认斜率为1
};

/**
* @brief 设置三角波斜率,可选值 [-2048, 2048]
*
* @param step
*/
void triangle_wave_cfg(int16_t step)
{
if ((step < -2048) || (step > 2048)) {
return;
}

m_triangle_wave.step = step;
}

/**
* @brief 获取三角波中的下一个点
*
* @return int32_t
*/
int32_t triangle_wave_get_next(void)
{
int32_t temp = 0;

temp = m_triangle_wave.val + m_triangle_wave.step;

if (m_triangle_wave.step >= 0) {
if (temp > 4095) {
m_triangle_wave.step = 0 - m_triangle_wave.step; // step 变负数
}
} else {
if (temp < 0) {
m_triangle_wave.step = 0 - m_triangle_wave.step; // step 变正数
}
}
m_triangle_wave.val += m_triangle_wave.step;

return m_triangle_wave.val;
}
 
产生正弦波
产生正弦波的代码也很简单,只需要调用 sine_wave_get_next() 即可获取下一个点。
C
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define SAMPLE_N (500) // sin 周期 500 个点
#define X_FACT ((2 * M_PI) / SAMPLE_N) // X系数

static int sine_t = 0; // 时间变量

double sine_wave_get_next(void)
{
double y = (sin(X_FACT * sine_t) + 1) / 2 * 4095;
sine_t++;

return y;
}
 
保存波形、读取波形
QSPI-Flash 的读写操作参见 QSPI-Flash 读写速度测试,修改 qspi_write_test() 和 qspi_read_test() 函数即可分别实现写 Flash 和读 Flash 的操作。
 
保存波形 save_wave_type()
入口参数 wt 表示波形类型,参见 wave_type_e。
  1. 先调用 qpi_init() 初始化 QSPI ;
  2. R_QSPI_Erase() 擦除一个扇区;
  3. R_QSPI_Write() 把 wt 写入到第一个扇区首地址上;
  4. deinit_qspi() 反初始化 QSPI
C

/**
* @brief 保存波形类型到 Flash 上。
*
* @param wt 参见 wave_type_e
* @return
*/
static fsp_err_t save_wave_type(uint32_t wt)
{
fsp_err_t fsp_err = FSP_SUCCESS;
fsp_err_t err = FSP_SUCCESS;
spi_flash_protocol_t current_spi_mode;
uint32_t block_size = 4; // 4KB

/* 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;

// NOTE: Erase Sector
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");
print_to_console(s_print_buffer);
} else {
err = get_flash_status();
if (FSP_SUCCESS != err) {
sprintf(s_print_buffer, "Failed to get status for QSPI operation\r\n");
print_to_console(s_print_buffer);
}

/* Verify the erased block data */
for (uint32_t 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++;
}

/* 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;

// NOTE: Write
fsp_err = R_QSPI_Write(&g_qspi_ctrl, &wt, p_mem_addr, sizeof(wt));

/* close QSPI module */
deinit_qspi(current_spi_mode);

/* Handle error */
if (FSP_SUCCESS != fsp_err) {
/* Fatal error */
SYSTEM_ERROR
}

return (fsp_err);
}
 
读取波形 restore_wave_type()
从 Flash 读取波形类型保存到入口参数 wt 中。
  1. qpi_init() 初始化 QSPI;
  2. memcpy() 读取 QSPI_DEVICE_START_ADDRESS 前1个word 并保存到 wt 地址上;
  3. deinit_qspi() 反初始化 QSPI;
C

/**
* @brief 从 Flash 读取 wave_type
*
* @param wt
* @return 0 表示成功,其他值表示失败
*/
static fsp_err_t restore_wave_type(uint32_t* wt)
{
fsp_err_t err = FSP_SUCCESS;

spi_flash_protocol_t current_spi_mode;

uint8_t* p_mem_addr;

/* 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;
}

// NOTE: Read wave type from Flash
p_mem_addr = (uint8_t*)QSPI_DEVICE_START_ADDRESS;
memcpy((void*)wt, p_mem_addr, sizeof(wt));

/* close QSPI module */
deinit_qspi(current_spi_mode);

return (err);
}
视频演示
 
心得体会
  1. 学会了如何生成三角波、正弦波;
  2. 模块化编程,有利于分解各个功能,减少串联出错的风险;
  3. 学习 QSPI-Flash 的读写操作;

 

最新回复

是比较 牛!!!!!!!!!  详情 回复 发表于 7 天前
点赞(1) 关注
 
 

回复
举报

3

帖子

0

TA的资源

一粒金砂(初级)

沙发
 
是比较 牛!!!!!!!!!
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表