【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上面下载到,
打开实例程序项目后,首先在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的配置方法,了解到了定时器配置以及中断回调函数的使用方法等。这次的项目不仅让我学习到开发板外设的使用方法,更重要的是让我获得了在项目中的思路和实际解决问题的技巧。
再次感谢论坛和得捷电子提供这样的机会,让我能够获得学习的机会,希望未来能够继续参与这样的活动,继续提高自己的技术水平。
项目的源码
项目的源码打包上传到了下载中心
|