- 2024-12-10
-
加入了学习《Follow me第二季第3期演示视频》,观看 Follow me第二季第3期演示视频
-
回复了主题帖:
【Follow me第二季第3期】EK-RA6M5所有任务汇总
最后用到的是简易示波器啦
- 2024-11-20
-
发表了主题帖:
【Follow me第二季第3期】EK-RA6M5所有任务汇总
本帖最后由 fastjs 于 2024-11-20 20:10 编辑
很荣幸参与【Follow me第二季第3期】,这期活动的开发板为EK-RA6M5,是一款由瑞萨官方开发的评估套件。
一、演示视频
Follow me第二季第3期演示视频-Follow me第二季第3期演示视频-EEWORLD大学堂
二、任务介绍
任务简介
入门任务:搭建环境,下载调试示例程序,Blink,按键;
基础任务:quad-spi flash和octo-spi flash配置及读写速度测试;DAC配置生成波形及性能测试;
进阶任务:示例程序中新增命令打印信息;
扩展任务:设计一个类似信号发生器功能的例程。通过命令或按键,设置DAC输出波形,可通过flash存储历史波形等信息。
物料清单
物料为EK-RA6M5开发板和MicroUSB线缆。
设计思路
通过USB虚拟串口传输指令,从而实现各个功能,比如调整DAC输出电压的值,以生成波形;存储波形数据,读取波形数据。通过接收命令,可以调整DAC的输出参数,例如增大或减小波形参数的值(范围为0-4095),从而动态生成所需的波形。指令 a 和 s 用于实时增减波形参数,便于快速调整输出。存储功能使用 Quad-SPI 闪存,指令 w 将当前波形数据写入闪存以便保存历史记录,指令r 则从闪存中加载存储的波形数据到内存缓冲区,实现快速回放。启动功能通过 g 指令激活波形发生器,开始输出设定的波形数据。
三、软件流程图
四、实现细节
示例程序为ek_ra6m5/_quickstart下的quickstart_ek_ra6m5_ep工程。
任务一 入门任务
开发环境搭建e2 studio
打开示例程序编译后,右键工程,选择Debug As -> Renesas GDB Hardware Debugging下载程序
编译项目时,由于示例代码所使用的FSP版本问题,可能会报错,修改BSP_CLOCKS_PLL_MUL_10_0为BSP_CLOCKS_PLL_MUL(10, 0)即可。
下载好示例程序后,会发现LED初始设置为
LED1 (蓝色):以1 Hz频率闪烁,亮度为10%。
LED2 (绿色):常亮且亮度最大。
LED3 (红色):关闭。
LED5 (调试LED):忽略其橙色闪烁或点亮状态。
此时按下用户按钮S1,会改变LED1的亮度,每次按下依次切换为10%、50%和90%循环;按下用户按钮S2:改变LED1的闪烁频率,每次按下依次切换为1 Hz、5Hz和10 Hz循环。
点灯代码,其中代码中的GPT 是指 General Purpose Timer(通用定时器)
// 更改LED亮度
void gpt_blue_callback(timer_callback_args_t * p_args)
{
/* Void the unused params */
FSP_PARAMETER_NOT_USED(p_args);
switch (s_blueled_flashing)
{
case ON:
{
if ((s_intense++ ) < s_duty)
{
TURN_BLUE_ON
}
else
{
TURN_BLUE_OFF
}
if (s_intense >= 100)
{
s_intense = 0;
s_duty = g_pwm_dcs[g_board_status.led_intensity];
}
break;
}
default:
{
TURN_BLUE_OFF
s_intense = 0;
s_duty = g_pwm_dcs[g_board_status.led_intensity];
}
}
}
// 更改LED闪烁频率
{
if ((s_ux_bits & (STATUS_UPDATE_FREQ_INFO)) == (STATUS_UPDATE_FREQ_INFO))
{
R_GPT_PeriodSet (g_blinker.p_ctrl, g_pwm_rates[g_board_status.led_frequency]);
/* Clear Event */
xEventGroupClearBits (g_update_console_event, (STATUS_UPDATE_FREQ_INFO));
}
}
按键控制灯代码,按键由外部中断实现
/* SW 1 */
void button_irq10_callback(external_irq_callback_args_t *p_args)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t xResult = pdFAIL;
EventBits_t uxBits;
/* Void the unused args */
FSP_PARAMETER_NOT_USED(p_args);
uxBits = xEventGroupGetBitsFromISR (g_update_console_event);
if ((uxBits & (STATUS_UPDATE_INTENSE_INFO)) != (STATUS_UPDATE_INTENSE_INFO))
{
/* Cast, as compiler will assume calc is int */
g_board_status.led_intensity = (uint16_t) ((g_board_status.led_intensity + 1) % 3);
xResult = xEventGroupSetBitsFromISR(g_update_console_event, STATUS_UPDATE_INTENSE_INFO,
&xHigherPriorityTaskWoken);
/* Was the message posted successfully? */
if (pdFAIL != xResult)
{
/* If xHigherPriorityTaskWoken is now set to pdTRUE then a context
switch should be requested. The macro used is port specific and will
be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - refer to
the documentation page for the port being used. */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
}
/* SW 2 */
void button_irq9_callback(external_irq_callback_args_t *p_args)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t xResult = pdFAIL;
EventBits_t uxBits;
/* Void the unused args */
FSP_PARAMETER_NOT_USED(p_args);
uxBits = xEventGroupGetBitsFromISR (g_update_console_event);
if ((uxBits & (STATUS_UPDATE_FREQ_INFO)) != (STATUS_UPDATE_FREQ_INFO))
{
/* Cast, as compiler will assume calc is int */
g_board_status.led_frequency = (uint16_t) ((g_board_status.led_frequency + 1) % 3);
xResult = xEventGroupSetBitsFromISR(g_update_console_event, STATUS_UPDATE_FREQ_INFO, &xHigherPriorityTaskWoken);
/* Was the message posted successfully? */
if (pdFAIL != xResult)
{
/* If xHigherPriorityTaskWoken is now set to pdTRUE then a context
switch should be requested. The macro used is port specific and will
be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - refer to
the documentation page for the port being used. */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
}
任务二 基础任务
任务2的flash测试功能已经在示例代码中实现了,这里展示配置及测试的核心代码,测试效果如下
quad-spi flash配置代码为示例代码menu.ext.c中的qpi_init(void)函数
octo-spi flash配置代码为示例代码ospi_test.c中configure_dopi_ospi()函数
quad-spi flash和octo-spi flash读写速度测试代码
print_to_console((uint8_t *)
"\r\nWriting the text block to external Quad-SPI and Octo-SPI flash memories...\r\n");
uint32_t ospi_performance_write_result = 0;
uint32_t ospi_performance_read_result = 0;
uint32_t timer_frequency;
R_GPT_InfoGet(g_memory_performance.p_ctrl, &timer_info);
timer_frequency = timer_info.clock_frequency;
ospi_performance_test (block_size_actual, &ospi_performance_write_result, &ospi_performance_read_result);
ospi_write_result = ((100000000 / timer_frequency) * ospi_performance_write_result) / 100;
qspi_write_result = ((100000000 / timer_frequency) * qspi_write_test(block_size_actual)) / 100;
print_to_console((uint8_t *)"Writing to flash completed\r\n");
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;
print_to_console((uint8_t *)"Reading from flash completed\r\n");
R_GPT_Close(g_memory_performance.p_ctrl);
对于DAC波形生成,使用DAC的示例文件
DAC输出端口P014,对应Arduino_A4
ADC输入端口P000,对应MIKROBUS_AN_ARDUINO_A0
用杜邦线连接P014和P000,下载代码后运行效果如下
观察到ADC通道一读取的值随着设定的DAC的增加而增加。
DAC配置生成波形及性能测试代码
static fsp_err_t dac_adc_operations(int32_t * input)
{
fsp_err_t err = FSP_SUCCESS; // Error status
/* Write value to DAC module */
err = R_DAC_Write (&g_dac_ctrl, (uint16_t) (* input));
/* handle error */
if (FSP_SUCCESS != err)
{
/* dac Write Failure message */
APP_ERR_PRINT("** DAC Write API failed ** \r\n");
return err;
}
/* Start DAC conversion */
err = R_DAC_Start (&g_dac_ctrl);
/* handle error */
if (FSP_SUCCESS != err)
{
/* dac start failure message */
APP_ERR_PRINT("** DAC Start API failed ** \r\n");
return err;
}
/* Start the ADC scan in Single scan mode*/
err = R_ADC_ScanStart (&g_adc_ctrl);
/* handle error */
if (FSP_SUCCESS != err)
{
/* ADC Scan Failure message */
APP_ERR_PRINT("** ADC ScanStart API failed ** \r\n");
return err;
}
/* Stop and start DAC conversion for consecutive user input values*/
err = R_DAC_Stop(&g_dac_ctrl);
/* Handle Error */
if (FSP_SUCCESS != err)
{
/* DAC stop failure message */
APP_ERR_PRINT("** DAC Stop API failed ** \r\n");
}
return err;
}
任务三 进阶任务
新增命令打印信息
初始打印信息为
添加打印信息后,删除了暂时用不到的打印命令,显示效果如下,
按4,进入新增的第四个选项,输入字符串,按下Tab键后,将打印该字符串。
此部分核心代码如下。
block_sz_ndx = 0;
memset(&s_block_sz_str, 0, INPUT_BUFFER);
while (false == valid_block_size)
{
print_to_console("input: ");
while ((CONNECTION_ABORT_CRTL != c))
{
c = input_from_console ();
if (block_sz_ndx < block_sz_limit)
{
s_block_sz_str[block_sz_ndx] = (char_t)c;
block_sz_ndx++;
}
else
{
s_block_sz_str[block_sz_ndx] = MENU_ENTER_RESPONSE_CRTL;
c = MENU_ENTER_RESPONSE_CRTL;
}
if (MENU_ENTER_RESPONSE_CRTL == c)
{
print_to_console("\r\noutput: ");
s_block_sz_str[block_sz_ndx - 1] = '\r';
s_block_sz_str[block_sz_ndx] = '\n';
print_to_console((void*)s_block_sz_str);
block_sz_ndx = 0;
memset(&s_block_sz_str, 0, INPUT_BUFFER);
break;
}
if (MENU_EXIT_CRTL == c)
{
valid_block_size = true;
block_size_actual = 0;
break;
}
if (CARRAGE_RETURN != c)
{
sprintf(s_print_buffer, "%c", (char_t)c);
print_to_console((void*)s_print_buffer);
}
}
if ((MENU_EXIT_CRTL == c) || (CONNECTION_ABORT_CRTL == c))
{
break;
}
}
任务四 扩展任务
QSPI FLASH型号为MX25L25645G,OSPI FLASH型号为MX25LM51245GM,本次任务将波形信息存储到QSPI FLASH中。QSPI 使用6 个信号连接Flash,分别是四个数据线QIO0~QIO3,一个时钟输出SCLK,一个片选输出(低电平有效)CS#。
设置默认dac输出值为{ 2048, 2460, 2856, 3218, 3532, 3786, 3969, 4072, 4093, 4031, 3887, 3668, 3382, 3042, 2661, 2255, 1841, 1435, 1054, 714, 428, 209, 65, 3, 24, 127, 310, 564, 878, 1240, 1636, 2048 };
这将输出一个正弦波,当输入指令时,其中的值发生增大或减小固定的值,但每个值范围在0-4095间,指令设计为
'a' - 增加波形参数
's' - 减少波形参数
'r' - 从 Quad-SPI 闪存读取波形数据到内存缓冲区
'w' - 将当前波形数据写入 Quad-SPI 闪存存储
'g' - 启动波形发生器
键入指令后,输入Tab键即可执行对应功能。
波形发生器界面
设置DAC输出波形
void DAC_Init()
{
R_DAC_Open(&g_dac0_ctrl, &g_dac0_cfg);
R_DAC_Start(&g_dac0_ctrl);
}
void DAC_SinWave_Cycle(uint32_t time_interval)
{
for(uint32_t i = 0 ; i < (sizeof(custom_var)/sizeof(custom_var[0])); i++)
{
R_DAC_Write(&g_dac0_ctrl, custom_var[i]);
R_BSP_SoftwareDelay(time_interval, BSP_DELAY_UNITS_MILLISECONDS);
}
}
在flash存储历史波形
/**
* @brief 将波形数据存储到 Quad Flash
* @param buffer 需要存储的波形
* @retval 无
*/
void Write_Waveform_To_Flash(uint16_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;
/* Cast to req type */
p_mem_addr = (uint8_t *)QSPI_DEVICE_START_ADDRESS;
/* 打开 QSPI 模块 */
// err = R_QSPI_Open(&g_qspi_ctrl, &g_qspi_cfg);
// if (FSP_SUCCESS != err)
// {
// sprintf(s_print_buffer, "Failed to open QSPI module\r\n");
// return;
// }
/* 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;
}
/* 擦除 QSPI 的指定扇区 */
err = R_QSPI_Erase(&g_qspi_ctrl, p_mem_addr, SECTOR_SIZE);
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "Failed to erase QSPI flash\r\n");
return;
}
/* 等待擦除完成 */
err = get_flash_status();
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "Failed to get flash status after erase\r\n");
return;
}
/* 逐页写入波形数据 */
while (((page_write_count * PAGE_WRITE_SIZE) < sizeof(custom_var)) && (FSP_SUCCESS == err))
{
err = R_QSPI_Write(&g_qspi_ctrl, &buffer[page_write_count * PAGE_WRITE_SIZE / sizeof(uint16_t)], p_mem_addr, PAGE_WRITE_SIZE);
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "Failed to write data to QSPI flash\r\n");
}
else
{
err = get_flash_status();
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "Failed to get flash status after write\r\n");
}
}
p_mem_addr += PAGE_WRITE_SIZE;
page_write_count++;
}
/* 关闭 QSPI 模块 */
// err = R_QSPI_Close(&g_qspi_ctrl);
// if (FSP_SUCCESS != err)
// {
// sprintf(s_print_buffer, "Failed to close QSPI module\r\n");
// }
/* close QSPI module */
deinit_qspi(current_spi_mode);
}
读取上一次存储的波形
/**
* @brief 从 Quad Flash 读取波形数据
* @param buffer 存储波形的缓冲区
* @retval 无
*/
void Read_Waveform_From_Flash(uint16_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;
/* 打开 QSPI 模块 */
// err = R_QSPI_Open(&g_qspi_ctrl, &g_qspi_cfg);
// if (FSP_SUCCESS != err)
// {
// sprintf(s_print_buffer, "Failed to open QSPI module\r\n");
// return;
// }
/* 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;
}
/* 逐页读取波形数据 */
while ((page_read_count * PAGE_WRITE_SIZE) < sizeof(custom_var))
{
memcpy(&buffer[page_read_count * PAGE_WRITE_SIZE / sizeof(uint16_t)], p_mem_addr, PAGE_WRITE_SIZE);
p_mem_addr += PAGE_WRITE_SIZE;
page_read_count++;
}
// /* 关闭 QSPI 模块 */
// err = R_QSPI_Close(&g_qspi_ctrl);
// if (FSP_SUCCESS != err)
// {
// sprintf(s_print_buffer, "Failed to close QSPI module\r\n");
// }
/* close QSPI module */
deinit_qspi(current_spi_mode);
}
调整DAC参数
void Adjust_Waveform(int32_t step)
{
for (uint32_t i = 0; i < sizeof(custom_var) / sizeof(custom_var[0]); i++)
{
// 调整值,限制范围在 MIN_VALUE 和 MAX_VALUE 之间
if (custom_var[i] + step > MAX_VALUE)
{
custom_var[i] = MAX_VALUE;
}
else if (custom_var[i] + step < MIN_VALUE)
{
custom_var[i] = MIN_VALUE;
}
else
{
custom_var[i] += step;
}
}
}
指令输入解析
block_sz_ndx = 0;
memset(&s_block_sz_str, 0, INPUT_BUFFER);
while (false == response_flag)
{
print_to_console("input: ");
while ((CONNECTION_ABORT_CRTL != c))
{
c = input_from_console();
if (block_sz_ndx < block_sz_limit)
{
s_block_sz_str[block_sz_ndx] = (char_t)c;
block_sz_ndx++;
}
else
{
s_block_sz_str[block_sz_ndx] = MENU_ENTER_RESPONSE_CRTL;
c = MENU_ENTER_RESPONSE_CRTL;
}
if (MENU_ENTER_RESPONSE_CRTL == c)
{
if (s_block_sz_str[block_sz_ndx - 2] == 'a')
{
sprintf(s_print_buffer, "\r\nIncrease waveform parameters\r\n");
print_to_console((void*)s_print_buffer);
Adjust_Waveform(STEP_SIZE); // 增加波形值
sprintf(s_print_buffer, "custom_var contains:\r\n");
print_to_console((void *)s_print_buffer);
for (uint32_t i = 0; i < 32; i++)
{
sprintf(s_print_buffer, "custom_var[%u] = %u\r\n", i, custom_var[i]);
print_to_console((void *)s_print_buffer);
}
}
if (s_block_sz_str[block_sz_ndx - 2] == 's')
{
sprintf(s_print_buffer, "\r\nDecrease waveform parameters\r\n");
print_to_console((void*)s_print_buffer);
Adjust_Waveform(-STEP_SIZE); // 减少波形值
sprintf(s_print_buffer, "custom_var contains:\r\n");
print_to_console((void *)s_print_buffer);
for (uint32_t i = 0; i < 32; i++)
{
sprintf(s_print_buffer, "custom_var[%u] = %u\r\n", i, custom_var[i]);
print_to_console((void *)s_print_buffer);
}
}
if (s_block_sz_str[block_sz_ndx - 2] == 'r') // 读取波形数据
{
sprintf(s_print_buffer, "\r\nRead the waveform data from Quad-SPI flash into memory buffer\r\n");
print_to_console((void*)s_print_buffer);
Read_Waveform_From_Flash(custom_var);
is_custom_wave = true;
sprintf(s_print_buffer, "After Reading, custom_var contains:\r\n");
print_to_console((void *)s_print_buffer);
for (uint32_t i = 0; i < 32; i++)
{
sprintf(s_print_buffer, "custom_var[%u] = %u\r\n", i, custom_var[i]);
print_to_console((void *)s_print_buffer);
}
}
else if (s_block_sz_str[block_sz_ndx - 2] == 'w') // 写入波形数据
{
sprintf(s_print_buffer, "\r\nWrite the current waveform data to Quad-SPI flash storage\r\n");
print_to_console((void*)s_print_buffer);
sprintf(s_print_buffer, "Before Writing, var contains:\r\n");
print_to_console((void *)s_print_buffer);
for (uint32_t i = 0; i < 32; i++)
{
sprintf(s_print_buffer, "var[%u] = %u\r\n", i, custom_var[i]);
print_to_console((void *)s_print_buffer);
}
Write_Waveform_To_Flash(custom_var);
}
else if (s_block_sz_str[block_sz_ndx - 2] == 'g') // 写入波形数据
{
sprintf(s_print_buffer, "\r\nStart Generator...\r\n");
print_to_console((void *)s_print_buffer);
while(1)
{
DAC_SinWave_Cycle(10);
}
}
block_sz_ndx = 0;
memset(&s_block_sz_str, 0, INPUT_BUFFER);
break;
}
if (MENU_EXIT_CRTL == c)
{
response_flag = true;
block_size_actual = 0;
break;
}
if (CARRAGE_RETURN != c)
{
sprintf(s_print_buffer, "%c", (char_t)c);
print_to_console((void*)s_print_buffer);
}
// 输出波形
DAC_SinWave_Cycle(1);
}
if ((MENU_EXIT_CRTL == c) || (0x00 == c))
{
break;
}
}
向flash写入一个周期的波形数据
从flash读取一个周期的波形数据,可以看到读取的波形数据和写入的是一致的。
默认输出波形:
修改DAC输出后波形:
五、心得体会
本次活动非常棒,通过本次活动,我对在瑞萨平台上的开发有了更加深入的了解。整个任务过程设计合理,循序渐进,从环境搭建到功能扩展,不仅让我熟悉了开发流程,还提升了实际动手能力。在扩展任务中,熟悉了spi接口操作flash和dac波形生成。此外,EEworld工作人员的全程答疑提供了很大的帮助,解决了活动中的疑问。再次感谢活动组织方和工作人员的精心安排!
六、代码下载
Follow me第二季第3期任务代码-嵌入式开发相关资料下载-EEWORLD下载中心
-
上传了资料:
Follow me第二季第3期任务代码
-
加入了学习《FollowMe 第二季:3 - EK_RA6M5 开发板入门》,观看 EK-RA6M5 开发板入门
- 2024-11-14
-
加入了学习《FollowMe 第二季: 1 Adafruit Circuit Playground Express及任务讲解》,观看 Adafruit Circuit Playground Express 及任务讲解
-
加入了学习《直播回放: FollowMe 4 W5500-EVB-Pico 使用入门》,观看 W5500-EVB-Pico 使用入门