wiwhh

  • 2024-12-10
  • 加入了学习《【Follow me第二季第3期】任务汇总》,观看 【Follow me第二季第3期】任务汇总

  • 加入了学习《【Follow me第二季第3期】所有任务汇总》,观看 【Follow me第二季第3期】所有任务汇总

  • 上传了资料: 【Follow me第二季第3期】任务4

  • 上传了资料: 【Follow me第二季第3期】任务2、3

  • 上传了资料: 【 Follow me第二季第3期】任务1

  • 2024-12-08
  • 加入了学习《【Follow me第二季第3期】LED程序修改及编译,Blink及按键测试》,观看 【Follow me第二季第3期】LED程序修改及编译,Blink及按键测试

  • 加入了学习《【Follow me第二季第4期】任务汇报》,观看 【Follow me第二季第4期】任务汇报

  • 2024-12-05
  • 发表了主题帖: 【Follow me第二季第3期】EK_RA6M5任务汇总

    本帖最后由 wiwhh 于 2024-12-10 16:05 编辑 【Follow me第二季第3期】EK_RA6M5任务汇总 0 开箱环节 第一次收到国外的快递非常的兴奋,感谢得捷和EEWorld两位父亲的支持。包装非常高级环保,是用一个没见过的纸做箱子的填充,包装用料非常足哈。接下来说说板子,首先板子非常的大哈,给的线材也非常多,还贴心的给了一个USB转接线。板子用料非常足啊,不仅有以太网模块、两块不同接口的flash,最重要的还板载了Jlink,这大大减少了桌面上的一堆线,调试起来非常的方便哈。                   1 入门任务:搭建环境,下载调试示例程序,Blink,按键 1.1搭建环境 下载e2 studio/RASC 获取e2 studio 的安装包,项目地址为:https://github.com/renesas/fsp/releases     ​​​​​​​     如果使用e2 studio开发,仅仅需要下载setup_fsp_v5_5_0_e2s_v2024-07.exe文件即可,如果习惯使用keil或者其他工具开发,就需要下载另外两个文件。例如官方给出的例程是e2 studio工程,我们下载e2 studio安装包即可。 安装e2 studio 双击打开setup_fsp_v5_5_0_e2s_v2024-07.exe 更改安装目录,不能含有空格 直接统统下一步等待安装完成,同时设置桌面快捷方式 1.2 Blink,按键 新建keil工程 打开rasc,填写工程名称和工程路径。 为了能够快速开发,我们选则该板子的工程模板。 3.选择FreeRTOS工程,方便后续开发。     ​​​​​​​     4.工程创建完成后,检查是否有下载算法,如果没有,将工程切换为其他芯片,再切换回来就会有下载算法。     ​​​​​​​     LED闪烁 /* Blinky Thread entry function */ void blinky_thread_entry (void * pvParameters) { FSP_PARAMETER_NOT_USED(pvParameters); /* LED type structure */ bsp_leds_t leds = g_bsp_leds; /* If this board has no LEDs then trap here */ if (0 == leds.led_count) { while (1) { ; // There are no LEDs on this board } } /* Holds level to set for pins */ bsp_io_level_t pin_level = BSP_IO_LEVEL_LOW; while (1) { /* Enable access to the PFS registers. If using r_ioport module then register protection is automatically * handled. This code uses BSP IO functions to show how it is used. */ R_BSP_PinAccessEnable(); /* Update all board LEDs */ for (uint32_t i = 0; i < leds.led_count; i++) { /* Get pin to toggle */ uint32_t pin = leds.p_leds[i]; /* Write to this pin */ R_BSP_PinWrite((bsp_io_port_pin_t) pin, pin_level); } /* Protect PFS registers */ R_BSP_PinAccessDisable(); /* Toggle level for next write */ if (BSP_IO_LEVEL_LOW == pin_level) { pin_level = BSP_IO_LEVEL_HIGH; } else { pin_level = BSP_IO_LEVEL_LOW; } vTaskDelay(configTICK_RATE_HZ); } } 以下是这段代码的主要逻辑: 初始化:函数首先检查板子上是否有LED,如果leds.led_count为0(即没有LED),则进入一个无限循环,什么也不做。 设置引脚电平:定义了一个变量pin_level来保存要设置给LED引脚的电平状态,初始值为低电平BSP_IO_LEVEL_LOW。 主循环:  在每次循环开始时,调用R_BSP_PinAccessEnable()启用对PFS(Pin Function Select)寄存器的访问,这通常是必要的,以确保能够修改引脚的功能。 接着通过一个for循环遍历所有LED,并使用R_BSP_PinWrite()函数将当前的pin_level写入到每个LED对应的引脚上,从而控制LED的状态(亮或灭)。 循环结束后,调用R_BSP_PinAccessDisable()禁用对PFS寄存器的访问,以保护这些配置不被意外改变。 最后,根据当前的pin_level值进行取反操作,以便在下一次循环中切换LED的状态。 使用vTaskDelay(configTICK_RATE_HZ)让线程延时一段时间,这样LED就会以一定的频率闪烁。configTICK_RATE_HZ定义了延时的时间长度,通常它表示系统的一个tick周期。 整个过程不断地重复,导致LED以固定的间隔时间(由vTaskDelay中的参数决定)在亮与灭之间切换,从而实现了LED闪烁的效果。 按键 按键功能我们通过中断实现,打开rasc配置工程 首先就是配置引脚功能 接着在stacks中添加对应的stack,也就是外部中断9和10 配置他们的属性,包括中断名称,触发方式,中断优先级和回调函数 我们新建一个Key_driver.c文件来实现按键的功能。主要就两个对外的接口,一个就是中断的初始化函数。另外一个就是在中断函数中注册回调函数。这样就能实现按键功能的解耦。下面就是该文件的代码。 #include "driver_key.h" /* KEY 外部中断初始化函数 */ void Key_IRQ_Init(void) { fsp_err_t err = FSP_SUCCESS; /* Open ICU module */ err = R_ICU_ExternalIrqOpen(&g_external_irq9_ctrl, &g_external_irq9_cfg); err = R_ICU_ExternalIrqOpen(&g_external_irq10_ctrl, &g_external_irq10_cfg); /* 允许中断 */ err = R_ICU_ExternalIrqEnable(&g_external_irq9_ctrl); err = R_ICU_ExternalIrqEnable(&g_external_irq10_ctrl); } // 注册回调函数的接口 void key_register_callbacks(callback_func_t key0_cb, callback_func_t key1_cb) { callbacks.key0_cb = key0_cb; callbacks.key1_cb = key1_cb; } // key0 的中断处理函数 void key0_callback(external_irq_callback_args_t *p_args) { if (callbacks.key0_cb != NULL) { callbacks.key0_cb(); } } // key1 的中断处理函数 void key1_callback(external_irq_callback_args_t *p_args) { if (callbacks.key1_cb != NULL) { callbacks.key1_cb(); } } 在LED线程中调用按键的接口,注册回调函数来调节LED的闪烁频率。 static uint8_t Led_frequencyindex = 0; static uint32_t Led_frequency[] = {10, 300, 500, 1000, 1300}; void my_key0_handler(void) { Led_frequencyindex = (Led_frequencyindex+1)%5; } void my_key1_handler(void) { if(Led_frequencyindex == 0) { Led_frequencyindex = sizeof(Led_frequency)/sizeof(Led_frequency[0]) - 1; } else { Led_frequencyindex = (Led_frequencyindex-1)%(sizeof(Led_frequency)/sizeof(Led_frequency[0])); } } void blinky_thread_entry (void * pvParameters) { FSP_PARAMETER_NOT_USED(pvParameters); /* LED type structure */ bsp_leds_t leds = g_bsp_leds; Key_IRQ_Init(); key_register_callbacks(my_key0_handler, my_key1_handler); /* If this board has no LEDs then trap here */ if (0 == leds.led_count) { while (1) { ; // There are no LEDs on this board } } /* Holds level to set for pins */ bsp_io_level_t pin_level = BSP_IO_LEVEL_LOW; while (1) { /* Enable access to the PFS registers. If using r_ioport module then register protection is automatically * handled. This code uses BSP IO functions to show how it is used. */ R_BSP_PinAccessEnable(); /* Update all board LEDs */ for (uint32_t i = 0; i < leds.led_count; i++) { /* Get pin to toggle */ uint32_t pin = leds.p_leds[i]; /* Write to this pin */ R_BSP_PinWrite((bsp_io_port_pin_t) pin, pin_level); } /* Protect PFS registers */ R_BSP_PinAccessDisable(); /* Toggle level for next write */ if (BSP_IO_LEVEL_LOW == pin_level) { pin_level = BSP_IO_LEVEL_HIGH; } else { pin_level = BSP_IO_LEVEL_LOW; } vTaskDelay(Led_frequency[Led_frequencyindex]); } } 全局变量定义:定义了两个全局变量Led_frequencyindex和Led_frequency。Led_frequencyindex用于记录当前选择的闪烁频率索引,而Led_frequency数组存储了五个不同的闪烁频率(单位为毫秒),分别为10ms、300ms、500ms、1000ms和1300ms。 按键处理函数: my_key0_handler:当按下键0时,Led_frequencyindex递增1,然后取模5,确保其值始终在0到4之间循环,对应于Led_frequency数组中的五个频率。 my_key1_handler:当按下键1时,如果Led_frequencyindex已经是0,则将其设置为最大索引值(即4),否则减1并取模5,同样保证索引值在有效范围内。 线程入口函数blinky_thread_entry 初始化按键中断和注册按键回调函数my_key0_handler和my_key1_handler,这样当用户按下键0或键1时,会分别调用这两个函数来更新Led_frequencyindex。 1.3 实现效果 2. 基础任务:qspi flash和ospi flash读写速度测试及DAC波形设置 2.1 QSPI和OSPI配置 首先就是通过原理图确认QSPI使用的引脚。从图中可以看出主要就四根数据线、一根时钟线和一个片选信号。OSPI使用了8根数据线。     ​​​​​​​           ​​​​​​​       接下来打开配置界面,确认pin脚配置是否正确     ​​​​​​​         2.2 QSPI和OSPI读写测试 QSPI QSPI的测试代码我们可以从官方的quickstart例程中阅读,分别是qspi_write_test()和qspi_read_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); } 首先调用err = qpi_init(); 该函数就是调用R_QSPI_Open函数对外设进行初始化。还进行了一系列的读写测试。 接下来就是调用R_QSPI_Erase对flash进行擦除,然后读取spi的状态等待擦除完毕。 接着就是启动定时器,然后开始写入数据,等待写入完成后,停止定时器,并读取定时器的计数,以获取时间。 qspi_read_test()和qspi_write_test()函数类似,这里不做过多分析。 OSPI OSPI调用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--; } } 首先打开时钟门控控制R_CGC_Open,因为后面高速模式需要配置时钟, 打开OSPI控制器。R_OSPI_Open 如果定义了HIGH_SPEED_MODE,就会配置高速模式。 write_dopi_ospi和read_dopi_ospi分别对读写速度进行了测试。   2.3 读写测试结果 电脑通过USB连接开发板     ​​​​​​​       打开xshell,通过串口连接。     ​​​​​​​       通过命令4开始执行上面的代码,结果打印在控制台。可以看到OSPI的通信速率要比QSPI快很多。  我尝试了将HIGH_SPEED_MODE定义为0,结果程序卡死了。     ​​​​​​​       2.4 DAC测试 添加DAC设备,设置通道0,同时设置引脚。     ​​​​​​​       因为没有示波器,我们也添加一个ADC设备     ​​​​​​​       通过杜邦线将两个引脚连接起来     ​​​​​​​       编写测试代码 /**初始化DAC*/ fsp_err_t common_init(void) { fsp_err_t fsp_err = FSP_SUCCESS; fsp_err = R_DAC_Open(&g_dac0_ctrl, &g_dac0_cfg); fsp_err = R_DAC_Start(&g_dac0_ctrl); fsp_err = adc_initialize(); .... } /**初始化ADC*/ static fsp_err_t adc_initialize(void) { fsp_err_t fsp_err = FSP_SUCCESS; fsp_err = R_ADC_Open (&g_adc_ctrl, &g_adc_cfg); if (FSP_SUCCESS != fsp_err) { return fsp_err; } fsp_err = R_ADC_ScanCfg (&g_adc_ctrl, &g_adc_channel_cfg); if (FSP_SUCCESS != fsp_err) { return fsp_err; } fsp_err = R_ADC_ScanStart (&g_adc_ctrl); if (FSP_SUCCESS != fsp_err) { return fsp_err; } /* Read TSN cal data (value written at manufacture, does not change at runtime) */ //fsp_err = R_ADC_InfoGet (&g_adc_ctrl, &g_adc_info_rtn); return fsp_err; } uint16_t Read_ADC_Voltage_Value(void) { uint16_t adc_data; (void)R_ADC_ScanStart(&g_adc_ctrl); while (!scan_complete_flag) //等待转换完成标志 { ; } scan_complete_flag = false; //重新清除标志位 /* 读取通道0数据 */ R_ADC_Read(&g_adc_ctrl, ADC_CHANNEL_0, &adc_data); return adc_data; } void gpt_blue_callback(timer_callback_args_t * p_args) { /* Void the unused params */ FSP_PARAMETER_NOT_USED(p_args); double angle = 2 * M_PI * i / TABLE_SIZE; double sine_value = sin(angle); int mapped_value = (sine_value + 1.0) * 0.5 * MAX_VALUE; R_DAC_Write(&g_dac0_ctrl, mapped_value); i = (++i)%TABLE_SIZE; SEGGER_RTT_printf(0, "adcvalue :%d\r\n",Read_ADC_Voltage_Value()); ..... } 打印结果显示     ​​​​​​​       3 进阶任务:示例程序中新增命令打印信息 3.1 例程中命令实现原理         在示例程序中,定义了一个名为 menu_fn_tbl 的命令结构体,用于封装每个命令的名称及其对应的功能实现。此结构体包含两个成员:一个是命令名称的字符串指针 p_name,另一个是指向函数的指针 p_func,该函数负责执行与命令相关的操作。       同时,示例程序中还定义了一个名为 s_menu_items[] 的结构体变量数组。每一个数组元素都是一个 menu_fn_tbl 类型的实例,代表菜单中的一个条目。要添加新的命令到菜单中,只需在这个数组中加入相应的命令名称和关联的功能实现即可。       当用户在控制台选择特定命令时,程序会查找 s_menu_items[] 数组,找到匹配项后调用其 p_func 成员指向的函数来执行相应功能。 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 } }; 3.2 具体命令功能实现 本命令的目标是在控制台窗口中显示通过ADC采集到的数据。在之前的小任务中,我们已经完成了ADC功能的实现,因此在这个任务中,我们将直接读取ADC的值,并将其输出到控制台上。为了达到这个目的,定义了一个名为 showADCdata 的函数,它是一个测试函数 (test_fn),负责执行以下操作: 清屏并将光标移至屏幕起始位置。 在一个无限循环中: 将当前ADC值格式化为字符串,并发送到控制台进行显示。 暂停500毫秒,以防止更新速度过快导致显示混乱。 再次清屏并重置光标位置,确保每次更新时数据都在相同的位置显示。 test_fn showADCdata(void) { int8_t c = -1; uint16_t temp[50]; uint8_t i; uint16_t adcvalue; 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); while(1) { sprintf (s_print_buffer, "%s:%d\r\n", "ADC:", g_adcvalue); print_to_console((void*)s_print_buffer); sprintf (s_print_buffer, "%s%s", gp_clear_screen, gp_cursor_home); vTaskDelay(500); print_to_console((void*)s_print_buffer); } return (0); } 3.3 功能展示     我们将ADC引脚连接至3.3V,可以看到控制台正确输出了adc的值。   4 扩展任务:信号发生器 4.1 功能流程 简单的功能流程图如下图所示,详细的功能介绍会在代码部分详解。     ​​​​​​​     4.2 工程配置     为了方便功能的实现,我有连接了一个额外的按键模块   通过RASC配置初始工程,用到的模块如图左边所示。 4.3 代码部分 main 函数 void hal_entry(void) { /* TODO: add your own code here */ uint16_t i = 0; uint16_t j = 0; uint32_t FlashID = 0; uint32_t FlashDeviceID = 0; uint16_t u16temp; Debug_UART4_Init(); // SCI4 UART 调试串口初始化 printf("hellow world\r\n"); QSPI_Flash_Init(); // 串行FLASH初始化 adc_init(); dac_init(); R_ICU_ExternalIrqOpen(&g_external_irq9_ctrl, &g_external_irq9_cfg); R_ICU_ExternalIrqOpen(&g_external_irq10_ctrl, &g_external_irq10_cfg); /* 允许中断 */ R_ICU_ExternalIrqEnable(&g_external_irq9_ctrl); R_ICU_ExternalIrqEnable(&g_external_irq10_ctrl); while(1) { u16temp = getadcvalue(); writebuf[i++] = u16temp; printf("line1=%d\r\n",u16temp); if(i == 1000) { i = 0; QSPI_Flash_Write((uint8_t *)writebuf, 0x000000, 2000); } if(readflag == 1) { readflag = 0; QSPI_Flash_BufferRead((uint8_t *)readbuf,0x000000,2000); for( j = 0; j<1000; j++) { printf("line2=%d\r\n",readbuf[j]); } } R_BSP_SoftwareDelay(1,BSP_DELAY_UNITS_MICROSECONDS); } #if BSP_TZ_SECURE_BUILD /* Enter non-secure code */ R_BSP_NonSecureEnter(); #endif } // key0 的中断处理函数 void key0callback(external_irq_callback_args_t *p_args) { if(get_wave_type() == WAVE_SINE) { set_wave_type(WAVE_SQUARE); } else { set_wave_type(WAVE_SINE); } } // key1 的中断处理函数 void key1callback(external_irq_callback_args_t *p_args) { readflag = 1; } uint8_t amplitude_index; // key2 的中断处理函数 void key2callback(external_irq_callback_args_t *p_args) { set_wave_amplitude(amplitude_index++); amplitude_index = amplitude_index%4; } uint32_t g_frequency = 500; // key3 的中断处理函数 void key3callback(external_irq_callback_args_t *p_args) { set_wave_frequency(g_frequency); g_frequency+=200; g_frequency = g_frequency%1500; } 该工程没有使用操作系统,只有主函数一个线程,这部分代码主要实现以下功能: 初始化外设,包含以下这些外设: 初始化uart4,用于输出调式信息和用于输出ADC波形。 初始化QSPI,主要用于存储历史波形,存储的数据为adc采集到的波形。 初始化DAC,用于输出0-3.3v的自定义波形,设置的波形目前只有正弦波和方波。 初始化ADC,由于手头没有示波器工具,只能使用板载的ADC,由于测试使用ADC的功能在主函数中实现,采样率只有勉强1khz。 开启外部中断,实现按键功能,用于调整波形和输出历史波形。 开启一个1ms执行一次的循环,实现的功能如下: 获取ADC采样的数据,adc的值存储在一个全局变量中。 将ADC的值存储到一个数组中,当数组存了大概1s的数据后,存储到外部flash中,该过程可能比较耗时,因为擦除这个过程花费很多时间,会稍微影响其他任务,但不影响DAC的波形输出。 如果检测到读取历史波形的按键按下,会读取flash中的数据,并打印出来。 定时器中断 DAC的功能主要依赖定时器中断,下面列出这部分代码 volatile uint32_t timer_ticks = 0; // 用于计数定时器中断次数 uint32_t wave_frequency = 1000; // 波形频率,默认1kHz uint64_t pclkd_freq_hz //时钟频率 uint16_t wave_amplitude = 2048; // DAC最大值 WaveType current_wave_type = WAVE_SINE; // 默认为正弦波 uint16_t dac_value = 0; // 定时器中断回调函数 void dac_timercallback(timer_callback_args_t * p_args) { FSP_PARAMETER_NOT_USED(p_args); switch (current_wave_type) { case WAVE_SINE: // 正弦波的计算 dac_value = (wave_amplitude * (1 + sin(2 * M_PI * timer_ticks / wave_frequency))) / 2; break; case WAVE_SQUARE: // 方波的计算 dac_value = (timer_ticks < wave_frequency / 2) ? wave_amplitude : 0; break; } R_DAC_Write(&g_dac0_ctrl,dac_value); timer_ticks++; if (timer_ticks >= wave_frequency) { timer_ticks = 0; } } // 设置波形频率 void set_wave_frequency(uint32_t freq) { // 重新配置定时器以适应新的频率 uint32_t period_counts =(uint32_t) (((uint64_t) pclkd_freq_hz ) / freq)-1; // 这里需要根据实际使用的定时器进行相应的配置 R_GPT_PeriodSet(&dac_timer_ctrl, period_counts); } // 设置波形幅值 void set_wave_amplitude(Amplitude amp) { switch(amp) { case AMP_500: wave_amplitude = 500; break; case AMP_1500: wave_amplitude = 1500; break; case AMP_2500: wave_amplitude = 2500; break; case AMP_3500: wave_amplitude = 3500; break; } } // 设置波形类型 void set_wave_type(WaveType type) { current_wave_type = type; } WaveType get_wave_type(void) { return current_wave_type; } 在dac_timercallback定时器中断回调函数中实现的功能如下。 根据用户选择的波形计算DAC输出值 正弦波 使用 sin() 函数生成周期性的波动。 将时间(timer_ticks)映射到正弦波的一个完整周期。 调整输出范围以匹配DAC的输出值,确保输出在0到最大幅值之间。 方波 根据定时器计数值 (timer_ticks) 判断当前是否处于半个周期内。 如果是前半个周期,则输出最大幅值; 否则输出0,形成高、低电平的交替。 同时,我们还提供了以下的几个外部接口,用以调整波形 set_wave_frequency:设置波形,主要利用R_GPT_PeriodSet接口来设置定时器的周期,通过R_FSP_SystemClockHzGet接口可以获取定时器的时钟频率,利用时钟频率和需求的定时器频率就可以计算出定时器的溢出值。 set_wave_amplitude:设置幅值,这部分简单,调整wave_amplitude变量就能实现DAC波形的变化。 set_wave_type:设置波形的类型,即用以输出正弦波和方波。 adc中断 uint16_t getadcvalue(void) { return g_adcvalue; } void g_adc0_callback(adc_callback_args_t * p_args) { FSP_PARAMETER_NOT_USED(p_args); R_ADC_Read(&g_adc0_ctrl, ADC_CHANNEL_0, &g_adcvalue); (void)R_ADC_ScanStart(&g_adc0_ctrl); } 该中断的任务比较简单,就是在ADC转换完成后,读取adc的值存储到g_adcvalue变量中,并重新开启adc转换。 同时提供外部一个接口getadcvalue用以获取g_adcvalue中的值。 4.4 功能展示 我们使用串口打印adc的值,利用串口工具将数值通过波形展示,       蓝色的线条为输出的历史波形,目前只做了保存1s。   总结 参加这项活动还是非常开心的,能够和很多大佬一起交流,得捷提供的开发板不得不说性能非常强大,我只是简单测试了一些基本的功能,希望以后能够好好利用这款开发板,进一步学习这块开发板。 视频链接 【Follow me第二季第3期】任务汇总-【Follow me第二季第3期】任务汇总-EEWORLD大学堂 代码链接 【 Follow me第二季第3期】任务1-嵌入式开发相关资料下载-EEWORLD下载中心 【Follow me第二季第3期】任务2、3-嵌入式开发相关资料下载-EEWORLD下载中心 【Follow me第二季第3期】任务4-嵌入式开发相关资料下载-EEWORLD下载中心  

  • 2024-12-03
  • 加入了学习《【Follow me第二季第3期】扩展任务---EK_RA6M5函数信号发生器》,观看 【Follow me第二季第3期】扩展任务---EK_RA6M5函数信号发生器

  • 2024-11-28
  • 加入了学习《FollowMe 第二季:3 - EK_RA6M5 开发板入门》,观看 EK-RA6M5 开发板入门

  • 加入了学习《Follow me第二季第3期演示视频》,观看 Follow me第二季第3期演示视频

  • 2024-11-22
  • 回复了主题帖: 入围名单公布:嵌入式工程师AI挑战营(进阶)的挑战者们,领取板卡啦

    补充内容: InsightFace简述: InsightFace 是一个开源的人脸识别算法,用于人脸检测、定位、对齐和特征提取并识别,具备高精度和高性能的特点。使用5个特征点进行人脸检测,这使得它在资源消耗上相对较低,适合在嵌入式系统上部署。 部署实现思路: 1. 首先就是选择合适的模型,下载现成的预训练好的开源模型,可以直接使用或者对使用自己的数据二次训练。 2. 接着就是RKNN Toolkit,将 InsightFace 的 PyTorch 或 MXNet 模型转换为 RV1106 可运行的 RKNN 模型 3. 搭建开发环境,确保 RV1106 开发板可以使用常用的一些库,如 Python、OpenCV、pytorch等。确保pytorch的版本大于1.6或者3.x。 4. 用 InsightFace 的人脸检测算法如RetinaFace、SCRFD等方法检测视频流中的所有人脸,并对检测到的人脸进行对齐,支持的方法有SDUNets、SimpleRegression,最后就是用人脸识别模型输出特征,并于保存的人脸数据比对,判断人脸数据是否通过验证。

  • 2024-11-21
  • 回复了主题帖: 入围名单公布:嵌入式工程师AI挑战营(进阶)的挑战者们,领取板卡啦

    个人信息已确认,领取板卡,可继续完成任务。

  • 回复了主题帖: 入围名单公布:嵌入式工程师AI挑战营(进阶)的挑战者们,领取板卡啦

    补充内容: InsightFace简述: InsightFace 是一个开源的人脸识别算法,用于人脸检测、对齐和特征提取,具备高精度和高性能的特点。InsightFace 支持2D和3D人脸分析,并在多个基准测试中达到了先进的性能。使用5个特征点进行人脸检测,这使得它在资源消耗上相对较低,适合在嵌入式系统上部署。 部署实现思路: 1. 选择合适的模型:根据 RV1106 的计算能力和 NPU 兼容性选择轻量化的模型。 2. 模型转换:使用 Rockchip 提供的工具链(如 RKNN Toolkit),将 InsightFace 的 PyTorch 或 MXNet 模型转换为 RV1106 可运行的 RKNN 模型 3. 开发环境搭建:确保 RV1106 开发板已经安装了必要的依赖,如 Python、OpenCV、NumPy 等。 4. 人脸检测与对齐:利用 InsightFace 的人脸检测算法检测视频流中的所有人脸,并对检测到的人脸进行对齐。 5. 结果展示:将识别结果和轨迹追踪信息实时展示在界面上,或者通过其他方式输出。

  • 2024-11-20
  • 回复了主题帖: 嵌入式工程师AI挑战营(进阶):在RV1106部署InsightFace算法的多人实时人脸识别实战

    申请理由: 对InsightFace人脸识别项目非常感兴趣。同时对于luckyfox提供的开发板,我也被他的小尺寸十分吸引。我觉得不仅仅是用来用来人脸识别,这块开发板还可以用在其他各行各业。我再硕士期间学习过深度学习和python,一直都是研究模型的训练评估,但是没有部署的经验,希望借此机会,对深度学习有进一步认识。 打算部署的应用: 我计划在RV1106 Linux开发板上部署一个智能门禁系统,该系统能够实时识别进入人员的身份,并根据预设的权限控制门的开启。

最近访客

< 1/1 >

统计信息

已有7人来访过

  • 芯积分:54
  • 好友:--
  • 主题:1
  • 回复:4

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言