- 2024-12-25
-
回复了主题帖:
2025年测评中心,DigiKey得捷赞助继续,欢迎跟帖推你期待的上线的测品啦~
希望多出一些AI系列难度中等的开发板测评或者活动
- 2024-12-13
-
加入了学习《Follow me 第二季第3期成果视频》,观看 成果展示
-
发表了主题帖:
[Follow me第二季第3期] 作品提交
本帖最后由 御坂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设计将上述所有的任务整合在了一起, 让我在本次活动中所学到的知识都联系了起来。 希望得捷电子和电子工程世界越办越好。再次感谢!
成功视频展示
代码附件信息
代码下载链接(点击跳转, 已经包含了每个部分的完整代码实现)
- 2024-12-12
-
回复了主题帖:
[Follow me第二季第3期] [扩展任务] DAC输出不同波形,FLASH保存和读取历史数据
wangerxian 发表于 2024-12-12 16:11
要是有个示波器看DAC波形,可能会更直接~
哎,哪里有示波器。 家境贫寒
- 2024-12-11
-
发表了主题帖:
[Follow me第二季第3期] [扩展任务] DAC输出不同波形,FLASH保存和读取历史数据
简介
拓展任务的要求的要求设计一个类似信号发生器功能的例程。可在示例程序上修改。通过命令或按键,设置DAC输出波形,可通过flash存储历史波形等信息。 那么对于这个任务一共分为三点.即 (1) 命令或者按键 (2)DAC输出 (3)Flash的读取和写入。
对于命令或者按键我们已经在进阶任务和入门任务中完成了。所以可以参考之前的实现逻辑。 对于DAC的输出,我们已经在基础任务中正确实现了。 而对于(3)Flash的读取和写入,我们则可以参考基础任务中的SPI写和读的相关函数。
实现过程
1 - 菜单功能实现,设计菜单和子菜单。
子菜单
2- 生成正弦波和三角波,和DAC输出函数
DAC输出生成后的波形数据
3 - QSPI Flash的读和写,操作。在这里非常感谢活动交流群内的各位大佬提供的帮助, 以及同期参加活动的小伙伴们, 我这里在操作QSPI的时候没有成功,后经过他们的帮助后成功的从QSPI读取到了数据。
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);
}
当用户输入1 或者 2 的时候,那么将当前的波形信息保存到Flash里
当用户输入3的时候使能输出。
视频效果展示
[localvideo]cc136791b1c05e4ca81ffa76a1d15ac4[/localvideo]
-
回复了主题帖:
STM32全球线上峰会,STM32N6重磅发布啦!
报名成功!
- 2024-12-10
-
回复了主题帖:
【Follow me第二季第3期】扩展任务---EK_RA6M5函数信号发生器
quansirx 发表于 2024-12-9 10:44
你参考这篇帖子https://bbs.eeworld.com.cn/thread-1299584-1-1.html
可以进行SPI Flash外设初始化和 ...
我前两天搞定了, 谢谢
- 2024-12-08
-
上传了资料:
Follow me 第三季任务代码汇总
-
回复了主题帖:
【Follow me第二季第3期】EK-RA6M5任务提交
御坂10032号 发表于 2024-12-8 21:20
跟我手里的板子一模一样
梅林雀 好哥哥。。。
-
回复了主题帖:
【Follow me第二季第3期】EK-RA6M5任务提交
aramy 发表于 2024-12-8 20:35
应该是stm芯片的。
跟我手里的板子一模一样
-
回复了主题帖:
【Follow me第二季第3期】EK-RA6M5任务提交
这个树莓派有固件吗佬
-
回复了主题帖:
[工业级智能控制MCU 匠芯创D133CBS] 3 - GPIO-IO中断
kaixx 发表于 2024-12-8 14:28
大佬 有没有单独编译自己写的应用然后放到板子上执行的办法 现在能传文件到板子上了 不知道咋单独编译应用
...
看前几个帖子, 有新建应用的教程
- 2024-12-07
-
回复了主题帖:
【Follow me第二季第3期】扩展任务---EK_RA6M5函数信号发生器
我想看一下这个保存历史波形到Flash里的相关代码,但是似乎你这个工程文件中并没有这一部分, 而你这个帖子中有
- 2024-12-01
-
回复了主题帖:
[Follow me第二季第3期] [基础任务] Q-spi 和O-spi 读写速度测试 + DAC输出和性能测试
Jacktang 发表于 2024-12-1 16:17
虽然三用表精度不够,但效果已经到位了,谢谢分享
我还有个51单片机的示波器, 今天晚点我补充一下波形
-
发表了主题帖:
[Follow me第二季第3期] [进阶任务] 示例程序中新增命令打印信息
简介
quick_start 的menu菜单是一个非常棒的功能。 那么本章节将演示如何在示例程序中新增命令的打印信息
正文
根据程序的追踪menu_main.c 是程序的主菜单入口文件。我们可以观察到上述定义了很多的菜单。 它同时还包括了一些其他的菜单。
我们可以按下ctrl + 鼠标左键来点击对应的方法来跳转到具体的某个菜单实现。
我们可以发现, 菜单的打印其实就是每次把数据写入到缓冲区里, 然后打印到串口。 同时当前的任务等待按下空格然后退出。 所以我们可以仿照这种形式来完成一个属于我们自己的一个简易菜单来打印信息。
仿照上述的格式, 首先我们定义一个菜单的头文件
menu_text.h
#ifndef MENU_TEXT
#define MENU_TEXT
extern test_fn ns_display_hello_world (void);
#endif /* MENU_ETH_WWW_H_ */
和它的具体实现
menu_text.c
/*
* 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);
}
那么之后在menu_main.c 中包含这个头文件,并且在menu菜单中多增加一行,把我们的方法放进去。
烧录并且查看实验现象。
[localvideo]eac2e63550f37de357b52e4d6f90d8f4[/localvideo]
-
加入了学习《FollowMe 第二季:3 - EK_RA6M5 开发板入门》,观看 EK-RA6M5 开发板入门
-
发表了主题帖:
[Follow me第二季第3期] [基础任务] Q-spi 和O-spi 读写速度测试 + DAC输出和性能测试
本帖最后由 御坂10032号 于 2024-12-1 06:13 编辑
简介
本章节我们来完成基础任务, 即QSPI 和 OSPI的配置以及读写测试, 和DAC的输出和精度测试。
任务1: Q-spi 和O-spi 读写速度测试
从下图我们可以看到,这块开发板板载了一块32MB的QSPI flash 和一块64MB的OSPI flash。接下来我们可以对其进行配置和读写测试。
在创建项目的时候如果我们选择是基于board的方式创建的话,那么针对当前开发板的所有的外设资源都会被正确的初始化。 我们可以在configuration.xml 中进行检查。
下图为QSPI的PIN配置
下图为OSPI的PIN配置
QUICK_Start 项目中提供了一份完整的QSPI和OSPI的对比测试。
OSPI的方法定义在ospi-test.h里, 而它的上层调用在menu_ext.c 里。
当程序调用测试方法之后, 会将OSPI的测试结果返回,然后进行QSPI的写测试,同时得到两者的时间, 读操作类似。所以我们可以在menu中来进行这个测试
输入 4, 进行对比测试。
输入最大的测试块大小。
下图为测试结果, 可以看出, 在64KB的读写测试中OSPI的速度明显要更胜一筹。无论是在读写的方面
任务2: DAC输出和性能测试
E2- studio 对RA系列的程序集成的非常好, 所有的功能等是处于可选的, 当你不选择生成的时候,这些对应外设的库文件并不会为你生成,但是当你初始化完PIN之后, 并且配置stack,那么这些库文件才会被生成到IDE里。
下面我将演示如何配置FSP生成DAC的库函数和PIN的初始化。
1- 在基于board的项目创建完毕后,点击configuration.yml 选择PIN
在上述PIN的地方初始化DAC0或者1, 但是在enable的时候会出现error,原因是这个PIN已经被使用为了ADC. 所以需要在ADC处将这个PIN给取消占用。
之后点击下面的Stacks选项
逐次点击, New Stack, Analog , DAC, 然后点击Generate Project content. 这样的话DAC的初始化配置已经完成了。 我们可以检查Ra 的 hal_data 内已经有了DAC的相关配置。
下图为DAC的相关库(FSP)
接下来打开src下的hal_entry写下我们的代码。代码如下(使其DAC从0 输出到 4095, 然后4095 到0)
#include "hal_data.h"
#include "r_dac.h"
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
void dac0_initialize(void);
/*******************************************************************************************************************//**
* 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)
{
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
/* Initialize DAC0 */
dac0_initialize();
uint16_t dac_value = 0; // Current DAC value
int step = 1; // Step direction (1 for increment, -1 for decrement)
/* Main loop */
while (1)
{
/* Write the current DAC value */
R_DAC_Write(&g_dac0_ctrl, dac_value);
/* Update the DAC value */
dac_value += step;
/* Check boundaries */
if (dac_value == 4095) // Maximum value for 12-bit DAC
{
step = -1; // Switch to decrementing
}
else if (dac_value == 0) // Minimum value
{
step = 1; // Switch to incrementing
}
/* Add a delay to control the update rate */
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
}
}
/*******************************************************************************************************************//**
* DAC0 Initialization
**********************************************************************************************************************/
void dac0_initialize(void)
{
/* Open DAC0 */
fsp_err_t err = R_DAC_Open(&g_dac0_ctrl, &g_dac0_cfg);
if (FSP_SUCCESS != err)
{
/* Handle error */
__BKPT(0);
}
/* Start DAC0 */
err = R_DAC_Start(&g_dac0_ctrl);
if (FSP_SUCCESS != err)
{
/* Handle error */
__BKPT(0);
}
/* Optional: Set the initial DAC value to 0 */
err = R_DAC_Write(&g_dac0_ctrl, 0);
if (FSP_SUCCESS != err)
{
/* Handle error */
__BKPT(0);
}
}
/*******************************************************************************************************************//**
* This function is called at various points during the startup process. This implementation uses the event that is
* called right before main() to set up the pins.
*
* @param[in] event Where at in the start up process the code is currently at
**********************************************************************************************************************/
void R_BSP_WarmStart(bsp_warm_start_event_t event)
{
if (BSP_WARM_START_RESET == event)
{
#if BSP_FEATURE_FLASH_LP_VERSION != 0
/* Enable reading from data flash. */
R_FACI_LP->DFLCTL = 1U;
/* Would normally have to wait tDSTOP(6us) for data flash recovery. Placing the enable here, before clock and
* C runtime initialization, should negate the need for a delay since the initialization will typically take more than 6us. */
#endif
}
if (BSP_WARM_START_POST_C == event)
{
/* C runtime environment and system clocks are setup. */
/* Configure pins. */
R_IOPORT_Open(&g_ioport_ctrl, g_ioport.p_cfg);
#if BSP_CFG_SDRAM_ENABLED
/* Setup SDRAM and initialize it. Must configure pins first. */
R_BSP_SdramInit(true);
#endif
}
}
#if BSP_TZ_SECURE_BUILD
FSP_CPP_HEADER
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable();
/* Trustzone Secure Projects require at least one nonsecure callable function in order to build (Remove this if it is not required to build). */
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable()
{
}
FSP_CPP_FOOTER
#endif
进行编译和烧录,然后使用万用表测试P014
将万用表的 负极接到GND, 正极接到DAC的输出 P014.
视频效果如下所示
[localvideo]9176f38e2c1ae7b2f33928304561f6db[/localvideo]
代码如下
-
回复了主题帖:
[光学传感器] X-NUCLEO-53L4A3 飞行时间 (ToF) 传感器的其他应用
lugl4313820 发表于 2024-11-30 22:48
这个只能检测一个区域,检测多个区域的是其他的型号,当时我也是以为可以检测多个区域的。
我说怎么看代码都感觉好像是这样,但是也不敢确定
- 2024-11-30
-
发表了主题帖:
[光学传感器] X-NUCLEO-53L4A3 飞行时间 (ToF) 传感器的其他应用
本帖最后由 御坂10032号 于 2024-11-30 02:32 编辑
简介
本章节将介绍TOF传感器在其他方面上的应用。在上几个章节中我们使用TOF传感器成功的读取到了距离信息(包括多个zone的距离信息。)即可以检测到多个目标的距离信息。
那么如果背测的物体始终是处于TOF传感器的测试中心的话(TOF可以检测到物体), 那么TOF可以拿到每个Zone的距离值。 比如说被测平面不平整的话,那么每一个zone的距离信息都将不相同。 我们可以通过距离信息的判断来做一些有趣的应用。
具体的方法定义在下述截图中
我们可以从每次获取测试结果的 变量中来找到一共有几个区域, 以及区域中一共有多少个目标(包括当前区域的噪声和信号强度)。信噪比同样是一个衡量当前测量结果的重要指标。 即判断当前的测量结果是否可信。 我在另一家公司的TOF传感器的教学视频里,看到它们公司是信噪比如果大于6 的话即可确定当前的测试结果是可靠的。
那么如果我们可以获取到每一个Zone的距离的话(实际上在测量的时候, 并不是每次都能获取到多个zone的结果)。比如说3*3 . 那么我们可以拿这次的结果来构建一个矩阵
如下图所示:
我们可以从右侧的数据即zone 3 6 9, 和 1 4 7 做对比, 发现这一个平面的右侧数据是高于左侧的, 即中间的物体的形状最低。假设TOF传感器位于下图所示,结合上面的数据, 这个图形的某一个平面将如下图所示 (仅仅TOF 对应的平面)。
因此,我们可以将当此的数据进行保存, 同时和下一次的数据进行对比来得到下一次测量物体相对这次的形状或者位置的变化, 比如说旋转等。
但是遗憾的是,在我实现的过程中并没有能很好的处理好每个区域的距离信息。并且不清楚到底在什么情况下才能被检测到多个目标。 在我的测试中, 在多个物体的情况下,90%的时间都是显示的两个物体。 很少很少才有检测到多个zone。 效果如下所示。
[localvideo]5355b612e11cb9f17f1ac60e0a48c489[/localvideo]
总结
综上所述, 仅仅本次所有的测评的结果上来看, TOF的其他方便的应用空间还非常大。 我也看了其他坛友发的手势识别等也是一个应用的方向。但是由于个人的能力有限没办法完成的复刻手势识别。但是这次的评测可以看出,TOF传感器在测距方面上表现尤为出色!其相应速度和精度都是一大亮点。比如应用在物体检测和避障的方面。
- 2024-11-22
-
回复了主题帖:
【Follow me第二季第3期】 入门任务 + 搭建环境,下载调试示例程序,Blink,按键
秦天qintian0303 发表于 2024-11-22 21:15
Follow me第二季第3期和第四期同步进行了
活动多多, 今年参加两季了。没有参加第四季