鱼豆腐车仔面

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

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

    本帖最后由 鱼豆腐车仔面 于 2024-12-15 15:53 编辑 从收到开发板到完成任务已经一个月了,终于完成了所有的任务,这次是我第一次参加Follow me的活动,我觉得这真是个很好的活动,为我这种想学习嵌入式的人提供了一个机会。这一期的开发板是瑞萨的RA6M5系列的开发板,版上集成度相当高,各种接口、存储器都有,相信以后还能用在其他的地方。另外,还要感谢论坛和得捷电子提供的机会,让我可以有机会参加这样的项目。     项目任务汇总视频 这个项目的任务主要有七个,分别是: (1)例程的演示 (2)Blink与按键的演示 (3)QSPI的配置与Quad Flash的测速 (4)OSPI的配置与Octa Flash的测速 (5)DAC的配置与性能测试 (6)命令接收与菜单打印 (7)信号发生器的实现   所有的项目都在视频里进行展示:       物料展示 这次能够购买的物料仅有开发板本体,任务所需的全部功能均使用开发板上的器件完成           任务成果展示   入门任务:搭建环境,下载调试示例程序、Blink、按键 开发环境 RA6M5使用瑞萨的e2studio进行开发,使用官方的IDE,可以很方便地通过图形化的界面完成时钟树、引脚功能、中断、ELC事件、各种模块栈的配置,类似于CubeMX。而且借助于FSP库,通过调用API接口,能快速完成任务的需求。        1.新建项目时,选择对应的开发板编号或MCU编号,是否使用RTOS,是否打开TrustZone等选项,完成项目的建立 2.在FSP Configuration图形化配置界面,根据所需的功能,添加对应的模块 3.在编写程序时调用对应的FSP库API接口,快速完成程序的编写   下载调试示例程序 官方的示例程序可以在Github上面下载到,https://github.com/renesas/ra-fsp-examples/tree/master/example_projects/ek_ra6m5 打开实例程序项目后,首先在FSP Configuration界面点击生成项目文件,IDE会根据所选择的配置生成对应的源文件和头文件,然后点击编译与烧录,即可将程序烧录到片上   写入程序后,打开IDE的终端,使用附带的USB将开发板与电脑连接,通过串口完成交互   通过串口发送命令,可以做到: (1)查看开发板状态 (2)打开Web服务器,使用网线连接到路由器之后,可以在浏览器打开对应的页面 (3)获取开发板的IP地址,并Ping“www.renesas.com" (4)Flash读写时间对比   并且在开发板上,通过两个用户按键,可以切换板载LED的亮度以及闪烁频率     Blink与按键 通过按键控制板载LED的开关,这个任务非常简单,只需要查看电路图找到LED和开关对应的引脚就可以了,查看图纸可知用户按键1对应P005,LED对应P006     创建一个空白工程,在图形化配置页面完成IO口的配置     程序代码如下: void hal_entry(void) { /* TODO: add your own code here */ bsp_io_level_t state; while (1) { R_IOPORT_PinRead (&g_ioport_ctrl, BSP_IO_PORT_00_PIN_06, &state); //读取LED1端口状态 if (Key_Scan (BSP_IO_PORT_00_PIN_05) == 1) //扫描按键 { if (state == BSP_IO_LEVEL_HIGH) //电平翻转 { R_IOPORT_PinWrite (&g_ioport_ctrl, BSP_IO_PORT_00_PIN_06, BSP_IO_LEVEL_LOW); } else { R_IOPORT_PinWrite (&g_ioport_ctrl, BSP_IO_PORT_00_PIN_06, BSP_IO_LEVEL_HIGH); } } } } //按键扫描函数 uint32_t Key_Scan(bsp_io_port_pin_t pin) { bsp_io_level_t state; R_IOPORT_PinRead(&g_ioport_ctrl, pin, &state); if(state == BSP_IO_LEVEL_HIGH) { return NG;//按键没有被按下 } else { do//直到没有按下按键后才跳出循环 { R_IOPORT_PinRead(&g_ioport_ctrl, pin, &state); }while(state == BSP_IO_LEVEL_LOW); } return EN;//按键被按下 }   基础任务一:Quad-SPI Flash配置与读写测速 任务中使用到的Flash是来自MACRONIX的MX25L25645G Quad-SPI Flash,芯片容量32MB,Flash的手册也一并附上。   QSPI使用六线与Flash连接,分别是片选线、时钟线以及四条数据线。SPI一个时钟周期只能发送接收1位的数据,发送1字节数据需要8个时钟周期,QSPI可以在1个时钟周期发送4位数据,2个时钟周期就可以发送1字节的数据,提高了传输的速度。   QSPI能够工作在多种模式下,在不同模式下的命令阶段、地址阶段和数据阶段传输的位数不一样,需要根据实际情况进行调整。 而且QSPI的工作模式要与Flash芯片的模式相对应,如果QSPI处于QPI模式时,则无法与处于extended-SPI模式的Flash芯片进行通讯。 注意,Flash在重新上电后是处于extended-SPI模式的,需要将QSPI的工作模式改为extended-SPI模式后,对Flash的寄存器进行配置,将Flash设置为QPI模式,才能在QPI模式下进行读写。   这个任务的大致思路是: (1)将Flash设置为QPI模式,因为Flash在重新上电后是处于extended-SPI模式的,需要先改变QSPI的协议,完成Flash的设置后,再将QSPI的协议改回来; (2)准备好256字节的数据,写入到Flash芯片中; (3)使用定时器计算Flash读写时所需的时间,然后根据读写的数据量与时间,计算出Quad-SPI Flash读写的速度。   流程图:   代码: void qspi_test(void) { /* TODO: add your own code here */ //写入与读取缓存 uint8_t data_write[PAGE_SIZE_QSPI] = {0}; uint8_t data_read[PAGE_SIZE_QSPI] = {0}; //写入flash的status与configuration寄存器的数值,以及读写的地址 uint8_t data_sreg[3] = {WRSR, STATUS_REGISTER_CONTENT, CONFIG_REGISTER_CONTENT}; uint8_t *p_mem_addr = (uint8_t*)FLASH_START_ADDRESS; //计时器状态结构体 timer_status_t timer_status_write; timer_status_t timer_status_read; fsp_err_t err = FSP_SUCCESS; APP_PRINT("This is a QSPI test program\r\n"); R_GPT_Open(&g_timer_performance_ctrl, &g_timer_performance_cfg);//开启计数器 //若qspi设置为QPI模式,则改为extend qspi模式 if(SPI_FLASH_PROTOCOL_QPI == g_qspi0_cfg.spi_protocol) { spi_flash_cfg_t temp_qspi0_cfg; memcpy((spi_flash_cfg_t *)&temp_qspi0_cfg, (spi_flash_cfg_t *)&g_qspi0_cfg, sizeof(spi_flash_cfg_t)); temp_qspi0_cfg.spi_protocol = SPI_FLASH_PROTOCOL_EXTENDED_SPI; APP_PRINT("The default set of QSPI is QPI mode\r\n"); err = R_QSPI_Open(&g_qspi0_ctrl, &temp_qspi0_cfg); if(FSP_SUCCESS != err) { APP_ERR_PRINT("QSPI open failed.\r\n"); APP_ERR_TRAP(err); } } else//如qspi设置为extend qspi模式,则直接启动 { APP_PRINT("The default set of QSPI is extended-SPI mode\r\n\r\n"); err = R_QSPI_Open(&g_qspi0_ctrl, &g_qspi0_cfg); if(FSP_SUCCESS != err) { APP_ERR_PRINT("QSPI open failed.\r\n"); APP_ERR_TRAP(err); } } //设置Flash的状态寄存器(status register)与配置寄存器(configuration register) err = R_QSPI_DirectWrite(&g_qspi0_ctrl, &g_qspi0_cfg.write_enable_command, 1, false); if(FSP_SUCCESS != err) { APP_ERR_PRINT("Flash write enable failed\r\n"); APP_ERR_TRAP(err); } err = get_flash_status(); if(FSP_SUCCESS != err) { APP_PRINT("Can't get flash status\r\n"); } //将设定值写入status寄存器 err = R_QSPI_DirectWrite(&g_qspi0_ctrl, data_sreg, 3, false); if(FSP_SUCCESS != err) { APP_ERR_PRINT("Flash write register failed\r\n"); APP_ERR_TRAP(err); } get_flash_status(); //检查寄存器的值(status register) uint8_t _register_data = 0; err = R_QSPI_DirectWrite(&g_qspi0_ctrl, &g_qspi0_cfg.status_command, 1, true); err = R_QSPI_DirectRead(&g_qspi0_ctrl, &_register_data, 1); if(FSP_SUCCESS != err) { APP_ERR_PRINT("Flash write register failed\r\n"); APP_ERR_TRAP(err); } get_flash_status(); if(_register_data !=0x40) { APP_ERR_PRINT("The status register can't be set correctly.\r\n"); APP_ERR_TRAP(err); } //如果一开始设置为QPI模式,现在将MCU与Flash恢复成QPI if(g_qspi0_cfg.spi_protocol == SPI_FLASH_PROTOCOL_QPI) { uint8_t data = OPEN_FLASH_QPI; err = R_QSPI_DirectWrite(&g_qspi0_ctrl, &g_qspi0_cfg.write_enable_command, 1, false); if(FSP_SUCCESS != err) { APP_ERR_PRINT("Flash write enable failed\r\n"); APP_ERR_TRAP(err); } get_flash_status(); //设置Flash为QPI模式 err = R_QSPI_DirectWrite(&g_qspi0_ctrl, &data, 1, false); if(FSP_SUCCESS != err) { APP_ERR_PRINT("Flash set qpi mode failed\r\n"); APP_ERR_TRAP(err); } err = R_QSPI_SpiProtocolSet(&g_qspi0_ctrl, SPI_FLASH_PROTOCOL_QPI); if(FSP_SUCCESS != err) { APP_ERR_PRINT("QSPI protocol change failed\r\n"); APP_ERR_TRAP(err); } } //准备写入数据 for (uint16_t index = 0; index < PAGE_SIZE_QSPI; index++) { data_write[index] = (uint8_t) index; } //开始计时 err = R_GPT_Start(&g_timer_performance_ctrl); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Timer start failed\r\n"); flash_quit_qpi (); APP_ERR_TRAP(err); } //擦除一个扇区 err = R_QSPI_Erase(&g_qspi0_ctrl, p_mem_addr, 4096U); if(FSP_SUCCESS != err) { APP_ERR_PRINT("Flash erase failed\r\n"); flash_quit_qpi(); APP_ERR_TRAP(err); } get_flash_status(); //写入数据 err = R_QSPI_Write(&g_qspi0_ctrl, data_write, p_mem_addr, PAGE_SIZE_QSPI); if(FSP_SUCCESS != err) { APP_ERR_PRINT("Flash program failed\r\n"); flash_quit_qpi(); APP_ERR_TRAP(err); } get_flash_status(); err = R_GPT_Stop(&g_timer_performance_ctrl);//结束计时 if (FSP_SUCCESS != err) { APP_ERR_PRINT("Timer stop failed\r\n"); flash_quit_qpi (); APP_ERR_TRAP(err); } R_GPT_StatusGet (&g_timer_performance_ctrl, &timer_status_write); R_GPT_Reset (&g_timer_performance_ctrl); err = R_GPT_Start(&g_timer_performance_ctrl); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Timer start failed\r\n"); flash_quit_qpi (); APP_ERR_TRAP(err); } //读取Flash数据,地址为0x60000000 memcpy(data_read,p_mem_addr,PAGE_SIZE_QSPI); err = R_GPT_Stop (&g_timer_performance_ctrl); //结束计时 if (FSP_SUCCESS != err) { APP_ERR_PRINT("Timer stop failed\r\n"); flash_quit_qpi (); APP_ERR_TRAP(err); } R_GPT_StatusGet (&g_timer_performance_ctrl, &timer_status_read); R_GPT_Reset (&g_timer_performance_ctrl); flash_quit_qpi (); //Flash退出QPI模式 R_QSPI_Close (&g_qspi0_ctrl); //关闭qspi //打印从flash读取的内容 APP_PRINT("The data written to the flash is:\r\n"); for(uint16_t column = 0; column<16;column++) { for(uint16_t row = 0; row<16;row++) { APP_PRINT("%4d",data_read[row+16*column]); R_BSP_SoftwareDelay(50, BSP_DELAY_UNITS_MICROSECONDS); } APP_PRINT("\r\n"); } APP_PRINT("\r\n"); //因RTT_viewer不能打印浮点类型数据,所以将浮点型用两个整型分别表示整数与小数点后部分 uint16_t WR_int; uint16_t WR_dec; uint16_t RE_int; uint16_t RE_dec; float temp_speed; temp_speed = 250000000 / (10 * (float)timer_status_write.counter); WR_int = (uint16_t) temp_speed; WR_dec = (uint16_t) ((temp_speed - WR_int) * 100); temp_speed = 250000000 / (10 * (float)timer_status_read.counter); RE_int = (uint16_t) temp_speed; RE_dec = (uint16_t) ((temp_speed - RE_int) * 100); APP_PRINT("Write speed: %d.%d KB/s.\r\n", WR_int, WR_dec); APP_PRINT("Read speed: %d.%d KB/s.\r\n", RE_int, RE_dec); APP_PRINT("\r\n\r\n"); } /*Flash退出QPI模式*/ void flash_quit_qpi(void) { uint8_t data = CLOSE_FLASH_QPI; R_QSPI_DirectWrite(&g_qspi0_ctrl, &data, 1, false); } //获取Flash读写状态 fsp_err_t get_flash_status(void) { spi_flash_status_t status = {.write_in_progress = true}; volatile uint16_t time_out = (0xFFFF); fsp_err_t err = FSP_SUCCESS; do { /* Get status from QSPI flash device */ err = R_QSPI_StatusGet(&g_qspi0_ctrl, &status); if (FSP_SUCCESS!= err) { return err; } /* Decrement time out to avoid infinite loop in case of consistent failure */ --time_out; if ( 0 >= time_out) { return FSP_ERR_TIMEOUT; } }while (false != status.write_in_progress); return err; }   读写速度测试结果:     基础任务二:Octa-SPI Flash配置与读写测速 任务中使用到的Octa-SPI Flash如下图,型号为MX25LM51245G,该Flash容量为64MB。     程序思路大致与Quad-SPI Flash的一样,只是Octa-SPI的配置相对复杂,需要使用一个结构体去写入和读取数据,如下: spi_flash_direct_transfer_t g_ospi_direct_transfer [OSPI_COMMAND_MAX] = { [OSPI_COMMAND_WRITE_ENABLE_SPI_MODE] = { .command = OSPI_WRITE_ENABLE_COMMAND_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_ZERO, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_WRITE_DISABLE_SPI_MODE] = { .command = OSPI_WRITE_DISABLE_COMMAND_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_ZERO, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_READ_STATUS_SPI_MODE] = { .command = OSPI_READ_STATUS_COMMAND_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_ZERO, .data_length = LENGTH_ONE, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_SECTOR_ERASE_SPI_MODE] = { .command = OSPI_ERASE_COMMAND_SECTOR_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_FOUR, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_SECTOR_ERASE_3SPI_MODE] = { .command = OSPI_ERASE_COMMAND_SECTOR_3SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_THREE, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_BLOCK_ERASE_SPI_MODE] = { .command = OSPI_ERASE_COMMAND_BLOCK_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_FOUR, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_BLOCK_ERASE_3SPI_MODE] = { .command = OSPI_ERASE_COMMAND_BLOCK_3SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_THREE, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_WRITE_CR2_SPI_MODE] = { .command = OSPI_COMMAND_WCR2_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_FOUR, .data_length = LENGTH_ONE, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_WRITE_CR2_3SPI_MODE] = { .command = OSPI_COMMAND_WCR2_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_FOUR, .data_length = LENGTH_ONE, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_WRITE_CR2_OPI_MODE] = { .command = OSPI_COMMAND_WCR2_OPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_TWO, .address_length = LENGTH_FOUR, .data_length = LENGTH_ONE, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_READ_CR2_SPI_MODE] = { .command = OSPI_COMMAND_RCR2_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_FOUR, .data_length = LENGTH_ONE, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_READ_CR2_3SPI_MODE] = { .command = OSPI_COMMAND_RCR2_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_FOUR, .data_length = LENGTH_ONE, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_READ_CR2_OPI_MODE] = { .command = OSPI_COMMAND_RCR2_OPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_TWO, .address_length = LENGTH_FOUR, .data_length = LENGTH_ONE, .dummy_cycles = LENGTH_FOUR }, [OSPI_COMMAND_WRITE_ENABLE_OPI_MODE] = { .command = OSPI_WRITE_ENABLE_COMMAND_OPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_TWO, .address_length = LENGTH_ZERO, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_WRITE_DISABLE_OPI_MODE] = { .command = OSPI_WRITE_DISABLE_COMMAND_OPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_TWO, .address_length = LENGTH_ZERO, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_SECTOR_ERASE_OPI_MODE] = { .command = OSPI_ERASE_COMMAND_SECTOR_OPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_TWO, .address_length = LENGTH_FOUR, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_BLOCK_ERASE_OPI_MODE] = { .command = OSPI_ERASE_COMMAND_BLOCK_OPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_TWO, .address_length = LENGTH_FOUR, .data_length = LENGTH_ZERO, .dummy_cycles = LENGTH_ZERO }, [OSPI_COMMAND_READ_STATUS_OPI_MODE] = { .command = OSPI_READ_STATUS_COMMAND_OPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_TWO, .address_length = LENGTH_FOUR, .data_length = LENGTH_ONE, .dummy_cycles = LENGTH_FOUR } }; 每当要对Flash芯片的寄存器进行读写操作时,就要用到上面这个结构体数组,通过调用数组中的不同元素,完成Flash芯片寄存器的读写操作。 另外,OSPI在extended-SPI模式与OSPI模式下,所用到外设时钟频率和dummy cycles也不一样,具体需要查看用户手册对应章节。   代码: #include "common_utils.h" #include "ospi_commands.h" #include "ospi.h" spi_flash_cfg_t local_ospi0_cfg = {RESET_VALUE}; ospi_extended_cfg_t local_ospi0_extend_cfg = {RESET_VALUE}; extern spi_flash_direct_transfer_t g_ospi_direct_transfer[OSPI_COMMAND_MAX]; void ospi_dopi_test(void) { APP_PRINT("This is an OSPI test program\r\n"); APP_PRINT("OSPI will be set from extended-SPI mode to DOPI mode\r\n"); fsp_err_t err = FSP_SUCCESS; uint8_t data_write[PAGE_SIZE] = { RESET_VALUE }; uint8_t data_read[PAGE_SIZE] = { RESET_VALUE }; bsp_octaclk_settings_t octaclk = { RESET_VALUE }; uint8_t *p_mem_addr = (uint8_t*) 0x70000000; timer_status_t write_status = { RESET_VALUE }; timer_status_t read_status = { RESET_VALUE }; R_GPT_Open (&g_timer_performance_ctrl, &g_timer_performance_cfg); //重置Flash芯片 flash_reset (); //设置OSPI外设时钟为50MHz,对应extended spi模式 octaclk.source_clock = BSP_CFG_OCTA_SOURCE; /* 200MHz */ octaclk.divider = BSP_CLOCKS_OCTA_CLOCK_DIV_2; R_BSP_OctaclkUpdate (&octaclk); //打开OSPI及协议调整 err = ospi_init (); if (FSP_SUCCESS != err) { APP_ERR_PRINT("OSPI init failed\r\n"); APP_ERR_TRAP(err); } //Flash芯片寄存器配置 err = flash_configure (); //设置OSPI外设时钟为100MHz,对应DOPI模式 octaclk.source_clock = BSP_CFG_OCTA_SOURCE; /* 200MHz */ octaclk.divider = BSP_CLOCKS_OCTA_CLOCK_DIV_1; R_BSP_OctaclkUpdate (&octaclk); err = ospi_spi_to_dopi (); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Setting extended-SPI mode to DOPI mode failed\r\n"); APP_ERR_TRAP(err); } //准备写入的数据 for (uint16_t index = 0; index < PAGE_SIZE; index++) { data_write[index] = (uint8_t) index; } //开始计时 err = R_GPT_Start (&g_timer_performance_ctrl); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Start counting failed\r\n"); APP_ERR_TRAP(err); } //擦除一个扇区 err = R_OSPI_Erase (&g_ospi0_ctrl, p_mem_addr, 4096U); wait_operation (); //写入数据 err = R_OSPI_Write (&g_ospi0_ctrl, data_write, p_mem_addr, PAGE_SIZE); wait_operation (); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Flash writing failed\r\n"); APP_ERR_TRAP(err); } //结束计时并读出写入时间 R_GPT_Stop (&g_timer_performance_ctrl); R_GPT_StatusGet (&g_timer_performance_ctrl, &write_status); R_GPT_Reset (&g_timer_performance_ctrl); /****************************************************************************/ err = R_GPT_Start (&g_timer_performance_ctrl); //开始计时 if (FSP_SUCCESS != err) { APP_ERR_PRINT("Start counting failed\r\n"); APP_ERR_TRAP(err); } //从闪存芯片读取 memcpy (data_read, p_mem_addr, PAGE_SIZE); //结束计时 R_GPT_Stop (&g_timer_performance_ctrl); R_GPT_StatusGet (&g_timer_performance_ctrl, &read_status); R_GPT_Reset (&g_timer_performance_ctrl); R_OSPI_Close(&g_ospi0_ctrl);//关闭ospi //打印读取内容 APP_PRINT("The data written to the flash is:\r\n"); for (uint16_t column = 0; column < 16; column++) { for (uint16_t row = 0; row < 16; row++) { APP_PRINT("%4d", data_read[row + 16 * column]); R_BSP_SoftwareDelay (50, BSP_DELAY_UNITS_MICROSECONDS); } APP_PRINT("\r\n"); } APP_PRINT("\r\n"); //因RTT_viewer不能打印浮点类型数据,所以将浮点型用两个整型分别表示整数与小数点后部分 uint16_t WR_int; uint16_t WR_dec; uint16_t RE_int; uint16_t RE_dec; float temp_speed; temp_speed = 250000000 / (10 * (float)write_status.counter); WR_int = (uint16_t) temp_speed; WR_dec = (uint16_t) ((temp_speed - WR_int) * 100); temp_speed = 250000000 / (10 * (float)read_status.counter); RE_int = (uint16_t) temp_speed; RE_dec = (uint16_t) ((temp_speed - RE_int) * 100); APP_PRINT("Write speed: %d.%d KB/s.\r\n", WR_int, WR_dec); APP_PRINT("Read speed: %d.%d KB/s.\r\n", RE_int, RE_dec); APP_PRINT("\r\n\r\n"); } //Flash芯片重置 void flash_reset(void) { R_IOPORT_PinWrite (&g_ioport_ctrl, RESET_PIN, BSP_IO_LEVEL_LOW); R_BSP_SoftwareDelay (DELAY_TIME, BSP_DELAY_UNITS_MICROSECONDS); R_IOPORT_PinWrite (&g_ioport_ctrl, RESET_PIN, BSP_IO_LEVEL_HIGH); R_BSP_SoftwareDelay (DELAY_TIME, BSP_DELAY_UNITS_MICROSECONDS); } //打开OSPI以及协议配置调整 fsp_err_t ospi_init(void) { fsp_err_t err = FSP_SUCCESS; ospi_extended_cfg_t *p_temp = NULL; p_temp = (void *)g_ospi0_cfg.p_extend; local_ospi0_extend_cfg = *p_temp; //修改local_ospi0_cfg的配置 local_ospi0_cfg = g_ospi0_cfg; local_ospi0_cfg.p_extend = &local_ospi0_extend_cfg; local_ospi0_cfg.p_erase_command_list = &spi_erase_command_list[INITIAL_INDEX]; local_ospi0_cfg.spi_protocol = SPI_FLASH_PROTOCOL_EXTENDED_SPI; err = R_OSPI_Open(&g_ospi0_ctrl, &local_ospi0_cfg); if(FSP_SUCCESS != err) { APP_ERR_PRINT("Open ospi failed\r\n"); APP_ERR_TRAP(err); } return err; } //设置flash芯片,对应extended-spi模式 fsp_err_t flash_configure(void) { fsp_err_t err = FSP_SUCCESS; spi_flash_direct_transfer_t ospi_transfer_struct = { .command = OSPI_COMMAND_WCR2_SPI_MODE, .address = OSPI_CR2_ADDRESS_HEX_0, .data = OSPI_CR2_DATA_HEX_00, .command_length = LENGTH_ONE, .address_length = LENGTH_FOUR, .data_length = LENGTH_ONE, .dummy_cycles = LENGTH_ZERO }; err = flash_write_enable(); //打开flash的extended spi 模式 err = R_OSPI_DirectTransfer(&g_ospi0_ctrl, &ospi_transfer_struct, SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); if(FSP_SUCCESS != err) { APP_ERR_PRINT("Open extended spi mode failed\r\n"); APP_ERR_TRAP(err); } //设置dummy cycles ospi_transfer_struct.address = OSPI_CR2_ADDRESS_HEX_300; ospi_transfer_struct.data = OSPI_CR2_DATA_HEX_07; err = flash_write_enable(); err = R_OSPI_DirectTransfer(&g_ospi0_ctrl, &ospi_transfer_struct, SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Changing Flash's dummy cycles in spi mode failed\r\n"); APP_ERR_TRAP(err); } return err; } //将外设与flash芯片都设置为DOPI模式 fsp_err_t ospi_spi_to_dopi(void) { fsp_err_t err = FSP_SUCCESS; //修改ospi配置变量 local_ospi0_cfg.p_erase_command_list = &opi_erase_command_list[INITIAL_INDEX]; local_ospi0_cfg.spi_protocol = SPI_FLASH_PROTOCOL_DOPI; err = flash_write_enable(); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Write enable failed\r\n"); APP_ERR_TRAP(err); } //修改DQS g_ospi_direct_transfer[OSPI_COMMAND_WRITE_CR2_SPI_MODE].address = OSPI_CR2_ADDRESS_HEX_200; g_ospi_direct_transfer[OSPI_COMMAND_WRITE_CR2_SPI_MODE].data = OSPI_CR2_DATA_HEX_00; err = R_OSPI_DirectTransfer (&g_ospi0_ctrl, &g_ospi_direct_transfer[OSPI_COMMAND_WRITE_CR2_SPI_MODE], SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Setting DQS failed\r\n"); APP_ERR_TRAP(err); } /***************************************************************************/ err = flash_write_enable (); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Write enable failed\r\n"); APP_ERR_TRAP(err); } //修改dummy cycles g_ospi_direct_transfer[OSPI_COMMAND_WRITE_CR2_SPI_MODE].address = OSPI_CR2_ADDRESS_HEX_300; g_ospi_direct_transfer[OSPI_COMMAND_WRITE_CR2_SPI_MODE].data = OSPI_CR2_DATA_HEX_05; err = R_OSPI_DirectTransfer (&g_ospi0_ctrl, &g_ospi_direct_transfer[OSPI_COMMAND_WRITE_CR2_SPI_MODE], SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Setting dummy cycles failed\r\n"); APP_ERR_TRAP(err); } /*****************************************************************************/ err = flash_write_enable (); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Write enable failed\r\n"); APP_ERR_TRAP(err); } //打开DOPI模式 g_ospi_direct_transfer[OSPI_COMMAND_WRITE_CR2_SPI_MODE].address = OSPI_CR2_ADDRESS_HEX_0; g_ospi_direct_transfer[OSPI_COMMAND_WRITE_CR2_SPI_MODE].data = OSPI_CR2_DATA_HEX_02; err = R_OSPI_DirectTransfer (&g_ospi0_ctrl, &g_ospi_direct_transfer[OSPI_COMMAND_WRITE_CR2_SPI_MODE], SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Setting DOPI mode failed\r\n"); APP_ERR_TRAP(err); } err = R_OSPI_SpiProtocolSet(&g_ospi0_ctrl, SPI_FLASH_PROTOCOL_DOPI); err = R_OSPI_Close(&g_ospi0_ctrl); err = R_OSPI_Open(&g_ospi0_ctrl, &local_ospi0_cfg); err = R_OSPI_SpiProtocolSet(&g_ospi0_ctrl, SPI_FLASH_PROTOCOL_DOPI); return err; } fsp_err_t flash_write_enable(void) { fsp_err_t err = FSP_SUCCESS; if(SPI_FLASH_PROTOCOL_EXTENDED_SPI == g_ospi0_ctrl.spi_protocol) { err = R_OSPI_DirectTransfer(&g_ospi0_ctrl, &g_ospi_direct_transfer[OSPI_COMMAND_WRITE_ENABLE_SPI_MODE], SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); return err; } else { err = R_OSPI_DirectTransfer(&g_ospi0_ctrl, &g_ospi_direct_transfer[OSPI_COMMAND_WRITE_ENABLE_OPI_MODE], SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE); return err; } } void wait_operation(void) { uint32_t timeout = UINT32_MAX; spi_flash_status_t status; status.write_in_progress = true; while ((status.write_in_progress) && (--timeout > RESET_VALUE)) { R_OSPI_StatusGet(&g_ospi0_ctrl, &status); } /*check if timeout occured*/ if(timeout == RESET_VALUE) { APP_PRINT("Timeout Occurred"); } }   读写速度测试结果:     基础任务三:DAC配置生成波形与性能测试 RA6M5配备了一个双通道的12位DA模块,通过该模块,可以输出0-3.3V的电压信号。   程序思路: (1)通过定时器溢出中断触发DAC输出值更新,保证DAC有精确的采样频率 (2)由于没有示波器,需要用ADC去测量DAC的输出电压值,所以也使用定时器溢出中断去触发ADC采样,保证ADC的采样率 (3)配置输出波形的频率、幅值、偏置,以及DAC的采样频率(通过调整定时器周期实现),去测试DAC的性能 (4)由于没有示波器,使用SEGGER J-Scope去查看ADC采集的波形   所需的模块: (1)GPT定时器 (2)DAC (3)ADC   流程图:   为了固定ADC和DAC的采样频率,要用到定时器的溢出中断,需要事先配置好定时器的通道、周期、对应的中断回调函数和中断优先等级   代码: #include "ad_da.h" #include "qspi.h" #define M_PI 3.14159265358979323846 uint16_t wave; //波形生成参数 uint32_t index=0; uint16_t frequency = 1; uint16_t amplitude = 0; uint16_t offset = 0; uint32_t sample_rate = DAFAULT_SAMPLE_RATE; uint32_t sample_num = 0; //切换波形 uint8_t waveform_change = 0; //ADC完成转换标志 volatile bool adc_complete_flag = false; void dac_test(void) { uint16_t data = 0; bsp_io_level_t state; //计算一个周期采样点数 sample_num = sample_rate / frequency; //打开DAC R_DAC_Open(&g_dac0_ctrl, &g_dac0_cfg); R_DAC_Start(&g_dac0_ctrl); //打开DAC采样定时器 R_GPT_Open (&g_timer_dac_ctrl, &g_timer_dac_cfg); R_GPT_Start (&g_timer_dac_ctrl); //打开ADC R_ADC_Open (&g_adc0_ctrl, &g_adc0_cfg); R_ADC_ScanCfg (&g_adc0_ctrl, &g_adc0_channel_cfg); //打开ADC采样定时器 R_GPT_Open (&g_timer_adc_ctrl, &g_timer_adc_cfg); R_GPT_Start (&g_timer_adc_ctrl); APP_PRINT("\r\nInput the frequency, or input 0 to quit\r\n"); while(1) { //输入生成的波形的参数 data = process_input_data(); if(data != 0) { frequency = data; APP_PRINT("\r\nThe frequency of sine wave is %d\r\n",frequency); APP_PRINT("\r\nInput the amplitude\r\n"); amplitude = process_input_data(); APP_PRINT("\r\nThe amplitude of sine wave is %d\r\n",amplitude); APP_PRINT("\r\nInput the offset\r\n"); offset = process_input_data(); APP_PRINT("\r\nThe offset of sine wave is %d\r\n",offset); APP_PRINT("\r\nInput the sample rate\r\n"); sample_rate = process_input_data (); APP_PRINT("\r\nThe sample rate of sine wave is %d\r\n", sample_rate); sample_num = sample_rate / frequency; R_GPT_PeriodSet(&g_timer_dac_ctrl, (uint32_t)(100000000/sample_rate)); APP_PRINT("\r\nNow the DAC is running\r\n"); APP_PRINT("\r\nPress user sw1 to stop DAC\r\n"); while(1) { //等待ADC采样完成 while (!adc_complete_flag) { } R_ADC_Read (&g_adc0_ctrl, ADC_CHANNEL_0, &wave); adc_complete_flag = false; //按下SW1退出循环 R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_05, &state); if(state == 0) { APP_PRINT("\r\nInput the frequency, or input 0 to quit\r\n"); break; } } } else if(data == 0) { break; } } //关闭外设 R_DAC_Close (&g_dac0_ctrl); R_ADC_Close (&g_adc0_ctrl); R_GPT_Close (&g_timer_dac_ctrl); R_GPT_Close (&g_timer_adc_ctrl); wave = 0; } //DAC更新波形值的中断函数 void timer_dac_callback(timer_callback_args_t *p_args) { FSP_PARAMETER_NOT_USED(p_args); index++; if(index > sample_num) { index = 0; } switch(waveform_change) { case 0://正弦波 { R_DAC_Write (&g_dac0_ctrl, (uint16_t) (amplitude * sin (2 * M_PI * frequency * index / sample_rate) + offset)); break; } case 1://三角波 { R_DAC_Write (&g_dac0_ctrl, (uint16_t) (2*amplitude * asin(sin (2 * M_PI * frequency * index / sample_rate))/M_PI + offset)); break; } case 2: { if(index <= (sample_num/2)) { R_DAC_Write (&g_dac0_ctrl,amplitude+offset); } else { R_DAC_Write (&g_dac0_ctrl,-amplitude+offset); } } } } //触发ADC开始采样的中断 void timer_adc_callback(timer_callback_args_t *p_args) { FSP_PARAMETER_NOT_USED(p_args); R_ADC_ScanStart(&g_adc0_ctrl); } //ADC采样完成中断 void adc_callback(adc_callback_args_t *p_args) { FSP_PARAMETER_NOT_USED(p_args); adc_complete_flag = true; }   DAC测试结果: 为了使用ADC测量DAC输出电压信号,需要使用到一根杜邦线,将P000引脚与P014引脚相连 调整了波形的参数和DAC的采样率之后,可以看到波形有明显的变化     进阶任务:命令打印信息 实现了使用菜单进行交互的功能,将QSPI测试、OSPI测试、DAC测试以及信号发生器功能集成到一个菜单里,使用菜单选择不同的功能,进入下一级菜单后,又能选择其他的选项。 主菜单页面 信号发生器菜单页面 程序思路: 在while循环中嵌套多层switch函数,检测输入来进入不同的功能模块,功能模块中也有对应的菜单。功能模块里设置有相应的退出条件,达到退出条件后,就可以返回主菜单并进行下一次的循环。   流程图:   代码: 主菜单 void hal_entry(void) { /* TODO: add your own code here */ APP_PRINT("Select the function:\r\n" "\r\n" "1.QSPI speed test\r\n" "2.OSPI speed test\r\n" "3.DAC test\r\n" "4.signal generator\r\n\r\n\r\n"); while (1) { switch (process_input_data ()) { case QSPI_TEST: { qspi_test (); break; } case OSPI_TEST: { ospi_dopi_test (); break; } case DAC_TEST: { dac_test(); break; } case SIGNAL_GENERATOR: { signal_generator(); break; } default: { APP_PRINT("\r\n Invalid input. Provide a valid input\r\n\r\n"); break; } } APP_PRINT("Select the function:\r\n" "\r\n" "1.QSPI speed test\r\n" "2.OSPI speed test\r\n" "3.DAC test\r\n" "4.signal generator\r\n\r\n\r\n"); } }   DAC测试菜单: //忽略了前后段的外设配置程序,只展示了菜单的实现部分 DAC_test_menu(void) { while(1) { data = process_input_data(); if(data != 0) { //逐个输入参数 frequency = data; APP_PRINT("\r\nThe frequency of sine wave is %d\r\n",frequency); APP_PRINT("\r\nInput the amplitude\r\n"); amplitude = process_input_data(); APP_PRINT("\r\nThe amplitude of sine wave is %d\r\n",amplitude); APP_PRINT("\r\nInput the offset\r\n"); offset = process_input_data(); APP_PRINT("\r\nThe offset of sine wave is %d\r\n",offset); APP_PRINT("\r\nInput the sample rate\r\n"); sample_rate = process_input_data (); APP_PRINT("\r\nThe sample rate of sine wave is %d\r\n", sample_rate); sample_num = sample_rate / frequency; R_GPT_PeriodSet(&g_timer_dac_ctrl, (uint32_t)(100000000/sample_rate)); APP_PRINT("\r\nNow the DAC is running\r\n"); APP_PRINT("\r\nPress user sw1 to stop DAC\r\n"); while(1) { //等待ADC采样完成 while (!adc_complete_flag) { } R_ADC_Read (&g_adc0_ctrl, ADC_CHANNEL_0, &wave); adc_complete_flag = false; //按下SW1退出循环 R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_05, &state); if(state == 0) { APP_PRINT("\r\nInput the frequency, or input 0 to quit\r\n"); break; } } } else if(data == 0)//返回上一级菜单 { break; } } }   信号发生器菜单: APP_PRINT("\r\nChoose the mode\r\n" "\r\n1.Write parameter to flash\r\n" "2.Read parameter from flash\r\n" "3.Input the parameter and turn on DAC directly\r\n"); switch (process_input_data ()) { case WRITE_TO_FLASH://写入参数到flash { //写入参数的代码 break; } case READ_FROM_FLASH://从flash读取参数 { //读取参数的代码 break; } case DIRECT_INPUT://直接使用输入参数启动DAC { //直接输入参数的代码 break; } } APP_PRINT("\r\nOpen DAC or go back to main menu\r\n" "\r\n1.Open DAC" "\r\n2.Back to main menu\r\n"); switch(process_input_data()) { case 1://打开DAC { //配置DAC与ADC的代码 break; } case 2://返回主菜单 { break; } }     扩展任务:信号发生器,通过命令或按键设置DAC波形,通过Flash存储波形信息 实现了正弦波、三角波、方波的生成,并且可以将波形参数存到Flash中。   程序思路: (1)通过DAC生成波形,ADC采样波形并在电脑屏幕显示(由于没有示波器) (2)DAC与ADC通过GPT定时器溢出触发,以固定的频率进行波形生成与采样 (3)通过按键外部中断,切换DAC生成波形的种类,实现在波形生成中的波形切换 (4)使用QSPI-Flash存储波形信息,在启动DAC之前可以选择提前将参数写入到Flash中,并在以后进行读取   流程图:   代码: 信号发生器主程序 void signal_generator(void) { fsp_err_t err = FSP_SUCCESS; bsp_io_level_t state;//io口状态 uint8_t data_buffer[2] = {0};//缓冲 uint8_t data_sreg[3] = { WRSR, STATUS_REGISTER_CONTENT, CONFIG_REGISTER_CONTENT };//flash设置 uint8_t *p_mem_addr = NULL;//写入地址指针 sample_rate = DAFAULT_SAMPLE_RATE;//重置采样率 //初始化QSPI和flash if (SPI_FLASH_PROTOCOL_QPI == g_qspi0_cfg.spi_protocol) { spi_flash_cfg_t temp_qspi0_cfg; memcpy ((spi_flash_cfg_t*) &temp_qspi0_cfg, (spi_flash_cfg_t*) &g_qspi0_cfg, sizeof(spi_flash_cfg_t)); temp_qspi0_cfg.spi_protocol = SPI_FLASH_PROTOCOL_EXTENDED_SPI; err = R_QSPI_Open (&g_qspi0_ctrl, &temp_qspi0_cfg); if (FSP_SUCCESS != err) { APP_ERR_PRINT("QSPI open failed.\r\n"); APP_ERR_TRAP(err); } } else //如qspi设置为extend qspi模式,则直接启动 { err = R_QSPI_Open (&g_qspi0_ctrl, &g_qspi0_cfg); if (FSP_SUCCESS != err) { APP_ERR_PRINT("QSPI open failed.\r\n"); APP_ERR_TRAP(err); } } //设置Flash的状态寄存器(status register)与配置寄存器(configuration register) err = R_QSPI_DirectWrite (&g_qspi0_ctrl, &g_qspi0_cfg.write_enable_command, 1, false); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Flash write enable failed\r\n"); APP_ERR_TRAP(err); } err = get_flash_status (); if (FSP_SUCCESS != err) { APP_PRINT("Can't get flash status\r\n"); } //将设定值写入status寄存器 err = R_QSPI_DirectWrite (&g_qspi0_ctrl, data_sreg, 3, false); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Flash write register failed\r\n"); APP_ERR_TRAP(err); } get_flash_status (); //检查寄存器的值(status register) uint8_t _register_data = 0; err = R_QSPI_DirectWrite (&g_qspi0_ctrl, &g_qspi0_cfg.status_command, 1, true); err = R_QSPI_DirectRead (&g_qspi0_ctrl, &_register_data, 1); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Flash write register failed\r\n"); APP_ERR_TRAP(err); } get_flash_status (); if (_register_data != 0x40) { APP_ERR_PRINT("The status register can't be set correctly.\r\n"); APP_ERR_TRAP(err); } //如果一开始设置为QPI模式,现在将MCU与Flash恢复成QPI if (g_qspi0_cfg.spi_protocol == SPI_FLASH_PROTOCOL_QPI) { uint8_t data = OPEN_FLASH_QPI; err = R_QSPI_DirectWrite (&g_qspi0_ctrl, &g_qspi0_cfg.write_enable_command, 1, false); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Flash write enable failed\r\n"); APP_ERR_TRAP(err); } get_flash_status (); //设置Flash为QPI模式 err = R_QSPI_DirectWrite (&g_qspi0_ctrl, &data, 1, false); if (FSP_SUCCESS != err) { APP_ERR_PRINT("Flash set qpi mode failed\r\n"); APP_ERR_TRAP(err); } err = R_QSPI_SpiProtocolSet (&g_qspi0_ctrl, SPI_FLASH_PROTOCOL_QPI); if (FSP_SUCCESS != err) { APP_ERR_PRINT("QSPI protocol change failed\r\n"); APP_ERR_TRAP(err); } } APP_PRINT("\r\nChoose the mode\r\n" "\r\n1.Write parameter to flash\r\n" "2.Read parameter from flash\r\n" "3.Input the parameter and turn on DAC directly\r\n"); //菜单与功能选择 switch (process_input_data ()) { case WRITE_TO_FLASH://写入参数到flash { APP_PRINT("\r\nInput the frequency:\r\n"); frequency = process_input_data (); APP_PRINT("\r\nInput the amplitude:\r\n"); amplitude = process_input_data (); APP_PRINT("\r\nInput the offset:\r\n"); offset = process_input_data (); //擦除扇区 p_mem_addr = (uint8_t*) 0x60001000; err = R_QSPI_Erase (&g_qspi0_ctrl, p_mem_addr, 4096U); if (FSP_SUCCESS != err) { flash_quit_qpi (); APP_ERR_PRINT("DAC-QSPI erase failed\r\n"); APP_ERR_TRAP(err); } get_flash_status(); //写入频率 p_mem_addr = (uint8_t*) FREQUENCY_ADDRESS; data_buffer[0] = (uint8_t) (frequency & 0xff); //取低字节 data_buffer[1] = (uint8_t) ((frequency >> 8) & 0xff); //取高字节 err = R_QSPI_Write (&g_qspi0_ctrl, data_buffer, p_mem_addr, 2); if (FSP_SUCCESS != err) { flash_quit_qpi (); APP_ERR_PRINT("DAC-QSPI write frequency failed\r\n"); APP_ERR_TRAP(err); } get_flash_status(); //写入幅值 p_mem_addr = (uint8_t*) AMPLITUDE_ADDRESS; data_buffer[0] = (uint8_t) (amplitude & 0xff); //取低字节 data_buffer[1] = (uint8_t) ((amplitude >> 8) & 0xff); //取高字节 err = R_QSPI_Write (&g_qspi0_ctrl, data_buffer, p_mem_addr, 2); if (FSP_SUCCESS != err) { flash_quit_qpi (); APP_ERR_PRINT("DAC-QSPI write amplitude failed\r\n"); APP_ERR_TRAP(err); } get_flash_status(); //写入偏置 p_mem_addr = (uint8_t*) OFFSET_ADDRESS; data_buffer[0] = (uint8_t) (offset & 0xff); //取低字节 data_buffer[1] = (uint8_t) ((offset >> 8) & 0xff); //取高字节 err = R_QSPI_Write (&g_qspi0_ctrl, data_buffer, p_mem_addr, 2); if (FSP_SUCCESS != err) { flash_quit_qpi (); APP_ERR_PRINT("DAC-QSPI write offset failed\r\n"); APP_ERR_TRAP(err); } get_flash_status(); flash_quit_qpi (); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); err = R_QSPI_Close(&g_qspi0_ctrl); break; } case READ_FROM_FLASH://从flash读取参数 { p_mem_addr = (uint8_t*) FREQUENCY_ADDRESS; memcpy (data_buffer, p_mem_addr, 2); frequency = (data_buffer[0] | (data_buffer[1] << 8)); p_mem_addr = (uint8_t*) AMPLITUDE_ADDRESS; memcpy (data_buffer, p_mem_addr, 2); amplitude = (data_buffer[0] | (data_buffer[1] << 8)); p_mem_addr = (uint8_t*) OFFSET_ADDRESS; memcpy (data_buffer, p_mem_addr, 2); offset = (data_buffer[0] | (data_buffer[1] << 8)); flash_quit_qpi (); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); R_QSPI_Close(&g_qspi0_ctrl); APP_PRINT("\r\nfrequency = %d\r\n" "amplitude = %d\r\n" "offset = %d\r\n",frequency ,amplitude ,offset); break; } case DIRECT_INPUT://直接使用输入参数启动DAC { flash_quit_qpi (); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); R_QSPI_Close (&g_qspi0_ctrl); APP_PRINT("\r\nInput the frequency:\r\n"); frequency = process_input_data (); APP_PRINT("\r\nInput the amplitude:\r\n"); amplitude = process_input_data (); APP_PRINT("\r\nInput the offset:\r\n"); offset = process_input_data (); break; } } APP_PRINT("\r\nOpen DAC or go back to main menu\r\n" "\r\n1.Open DAC" "\r\n2.Back to main menu\r\n"); switch(process_input_data()) { case 1://打开DAC { sample_num = sample_rate / frequency; //打开P004外部中断,用于切换波形 R_ICU_ExternalIrqOpen(&g_external_irq0_ctrl, &g_external_irq0_cfg); R_ICU_ExternalIrqEnable(&g_external_irq0_ctrl); //打开DAC R_DAC_Open (&g_dac0_ctrl, &g_dac0_cfg); R_DAC_Start (&g_dac0_ctrl); //打开DAC采样定时器 R_GPT_Open (&g_timer_dac_ctrl, &g_timer_dac_cfg); R_GPT_PeriodSet(&g_timer_dac_ctrl, (uint32_t)(100000000/sample_rate)); R_GPT_Start (&g_timer_dac_ctrl); //打开ADC R_ADC_Open (&g_adc0_ctrl, &g_adc0_cfg); R_ADC_ScanCfg (&g_adc0_ctrl, &g_adc0_channel_cfg); //打开ADC采样定时器 R_GPT_Open (&g_timer_adc_ctrl, &g_timer_adc_cfg); R_GPT_Start (&g_timer_adc_ctrl); APP_PRINT("\r\nDAC is running.\r\n" "\r\nPress SW2 to switch waveform\r\n" "Press SW1 to quit\r\n"); while (1) { //等待ADC采样完成 while (!adc_complete_flag) { } R_ADC_Read (&g_adc0_ctrl, ADC_CHANNEL_0, &wave); adc_complete_flag = false; //按下SW1退出循环 R_IOPORT_PinRead (&g_ioport_ctrl, BSP_IO_PORT_00_PIN_05, &state); if (state == 0) { break; } } R_DAC_Close (&g_dac0_ctrl); R_ADC_Close (&g_adc0_ctrl); R_GPT_Close (&g_timer_dac_ctrl); R_GPT_Close (&g_timer_adc_ctrl); wave = 0; break; } case 2://返回主菜单 { break; } } }   DAC波形更新(定时器溢出中断回调) void timer_dac_callback(timer_callback_args_t *p_args) { FSP_PARAMETER_NOT_USED(p_args); index++; if(index > sample_num) { index = 0; } switch(waveform_change) { case 0://正弦波 { R_DAC_Write (&g_dac0_ctrl, (uint16_t) (amplitude * sin (2 * M_PI * frequency * index / sample_rate) + offset)); break; } case 1://三角波 { R_DAC_Write (&g_dac0_ctrl, (uint16_t) (2*amplitude * asin(sin (2 * M_PI * frequency * index / sample_rate))/M_PI + offset)); break; } case 2://方波 { if(index <= (sample_num/2)) { R_DAC_Write (&g_dac0_ctrl,amplitude+offset); } else { R_DAC_Write (&g_dac0_ctrl,-amplitude+offset); } } } }   ADC开始转换(定时器溢出中断回调函数) 以及 ADC采样转换完成回调函数 //ADC定时开始采样 void timer_adc_callback(timer_callback_args_t *p_args) { FSP_PARAMETER_NOT_USED(p_args); R_ADC_ScanStart(&g_adc0_ctrl); } //ADC采样转换完成 void adc_callback(adc_callback_args_t *p_args) { FSP_PARAMETER_NOT_USED(p_args); adc_complete_flag = true; }   波形切换,通过外部中断实现 void irq_waveform_callback(external_irq_callback_args_t *p_args) { FSP_PARAMETER_NOT_USED(p_args); waveform_change++; if(waveform_change > 2) { waveform_change = 0; } }   演示: 为了采集DAC输出电压信号,与DAC测试一样,需要将P000与P014相连 波形生成: 生成正弦波   生成三角波   生成方波 在DAC生成波形中,按下用户按键2,触发中断,就可以完成波形的转换     总结     在这次活动里面,我学习到了EK-RA6M5这块开发板上各种外设功能的配置和使用方法,然后又将这些外设应用到不同的项目任务中。在实现这些任务的过程中,我了解到了QSPI、OSPI Flash的工作原理,了解到DAC、ADC的配置方法,了解到了定时器配置以及中断回调函数的使用方法等。这次的项目不仅让我学习到开发板外设的使用方法,更重要的是让我获得了在项目中的思路和实际解决问题的技巧。     再次感谢论坛和得捷电子提供这样的机会,让我能够获得学习的机会,希望未来能够继续参与这样的活动,继续提高自己的技术水平。     项目的源码 项目的源码打包上传到了下载中心 https://download.eeworld.com.cn/detail/%E9%B1%BC%E8%B1%86%E8%85%90%E8%BD%A6%E4%BB%94%E9%9D%A2/635244  

  • 上传了资料: 【Follow me第二季第3期】EK-RA6M5代码汇总

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

  • 2024-11-24
  • 回复了主题帖: 【Follow me第二季第3期】1入门任务:搭建环境,下载调试示例程序,Blink,按键

    bigbat 发表于 2024-11-24 15:19 UP的原理图能传一份吗?"ek-ra6m5-v1-designpackage.zip",   我的原理图是怎样滴,我也没 ... 用edge打开就会这样,换个软件试试

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

  • 回复了主题帖: 【Follow me第二季第3期】哪位真的按照视频指南完成了E2 STUDIO环境搭建?

    编译实例程序之前,要看实例程序FSP库版本号是否与e2studio的FSP库版本号一致 如果版本不一致的话会出错

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

  • 2024-10-29
  • 加入了学习《直播回放: FollowMe 4 W5500-EVB-Pico 使用入门》,观看 W5500-EVB-Pico 使用入门

  • 2024-10-20
  • 加入了学习《直播回放:Fluke 示波器的基础知识及其校准》,观看 示波器的基础知识及其校准

  • 加入了学习《直播回放: 英飞凌针对电动工具的高功率、高效率以及高可靠性解决方案》,观看 英飞凌针对电动工具的高功率、高效率以及高可靠性解决方案

最近访客

< 1/1 >

统计信息

已有1人来访过

  • 芯积分:49
  • 好友:--
  • 主题:1
  • 回复:2

留言

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


现在还没有留言