【Follow me第二季第3期】基础任务---QSPI、OSPI Flash及DAC测试
本帖最后由 quansirx 于 2024-11-19 23:00 编辑<p>一、QSPI Flash配置</p>
<p lang="en-US">1.1</p>
<p>使用e2 studio新建MCU自定义工程,MCU型号为R7FA6M5BH3CFC</p>
<p> </p>
<p> </p>
<p> </p>
<p>打开开发板原理图文件,找到QSPI Flash的原理图</p>
<p> </p>
<p>在FSP配置界面找到Pins栏,引脚配置与原理图一致,注意引脚组选项为_B only</p>
<p>操作模式为Quad</p>
<p> </p>
<p> </p>
<p>在stacks栏新增qspi flash</p>
<p lang="en-US"> </p>
<p> </p>
<p>Qspi flash stack属性设置保持默认</p>
<p>FSP设置完成后点击右上角的Generate Project Content</p>
<p lang="en-US">1.2</p>
<p lang="en-US"> </p>
<p> </p>
<p> </p>
<p>在工程目录src下新建bsp文件夹,并新建qspi的两个c/h驱动文件</p>
<p>bsp_qspi_flash.h头文件内容如下</p>
<pre>
<code>#ifndef QSPI_FLASH_BSP_QSPI_FLASH_H_
#define QSPI_FLASH_BSP_QSPI_FLASH_H_
#include "hal_data.h"
#include "r_qspi.h"
#define FLASH_ID_W25Q32JV 0xEF4016 // W25Q32JV
#define FLASH_ID_AT25SF321B 0x1F8701 // AT25SF321B
#define SPI_FLASH_PageSize 256
#define SPI_FLASH_PerWritePageSize 256
#define RESET_VALUE (0x00)
/*命令定义-开头*******************************/
#define WriteEnable 0x06
#define WriteDisable 0x04
#define ReadStatusReg 0x05
#define WriteStatusReg 0x01
#define ReadData 0x03
#define FastReadData 0x0B
#define FastReadDual 0x3B
#define PageProgram 0x02
#define BlockErase 0xD8
#define SectorErase 0x20
#define ChipErase 0xC7
#define PowerDown 0xB9
#define ReleasePowerDown 0xAB
#define DeviceID 0xAB
#define ManufactDeviceID 0x90
#define JedecDeviceID 0x9F
/* WIP(busy)标志,FLASH内部正在写入 */
#define WIP_Flag 0x01
#define Dummy_Byte 0xFF
/*命令定义-结尾*******************************/
void QSPI_Flash_Init(void);
uint32_t QSPI_Flash_ReadID(void);
uint32_t QSPI_Flash_ReadDeviceID(void);
void QSPI_Flash_PowerDown(void);
void QSPI_Flash_WakeUp(void);
void QSPI_Flash_WriteEnable(void);
fsp_err_t QSPI_Flash_WaitForWriteEnd(void);
void QSPI_Flash_SectorErase(uint32_t adress);
void QSPI_Flash_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); //页写入
void QSPI_Flash_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void QSPI_Flash_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
//static void qspi_d0_byte_write_standard (uint8_t byte);
fsp_err_t R_QSPI_Read(spi_flash_ctrl_t *p_ctrl, uint8_t *p_src, uint8_t *const p_dest, uint32_t byte_count);
#endif /* QSPI_FLASH_BSP_QSPI_FLASH_H_ */</code></pre>
<p> </p>
<p>bsp_qspi_flash.c文件内容如下</p>
<pre>
<code>#include "bsp_qspi_flash.h"
void QSPI_Flash_Init(void)
{
R_QSPI_Open(&g_qspi0_flash_ctrl, &g_qspi0_flash_cfg);
R_BSP_SoftwareDelay(10u, BSP_DELAY_UNITS_MILLISECONDS);
}
/**
* @brief 读取FLASH ID
* @param 无
* @retval FLASH ID
*/
uint32_t QSPI_Flash_ReadID(void)
{
unsigned char data = {};
uint32_t back;
data = JedecDeviceID;
R_QSPI_DirectWrite(&g_qspi0_flash_ctrl, &data, 1, true); //false: close the spi true: go go go
R_QSPI_DirectRead(&g_qspi0_flash_ctrl, &data, 3);
/*把数据组合起来,作为函数的返回值*/
back = (data << 16) | (data << 8) | (data);
return back;
}
/**
* @brief 读取FLASH 设备ID
* @param 无
* @retval FLASH ID
*/
uint32_t QSPI_Flash_ReadDeviceID(void)
{
unsigned char data = {};
uint32_t back;
data = DeviceID;
data = 0xff;
data = 0xff;
data = 0xff;
R_QSPI_DirectWrite(&g_qspi0_flash_ctrl, &data, 4, true); //false: close the spi true: go go go
R_QSPI_DirectRead(&g_qspi0_flash_ctrl, &data, 3);
/*把数据组合起来,作为函数的返回值*/
back = (data << 16) | (data << 8) | (data);
return back;
}
/**
* @brief 等待WIP(BUSY)标志被置0,即等待FLASH内部数据写入完毕
* @param 无
*/
fsp_err_t QSPI_Flash_WaitForWriteEnd(void)
{
spi_flash_status_t status = {.write_in_progress = true};
int32_t time_out = (INT32_MAX);
fsp_err_t err = FSP_SUCCESS;
do
{
/* Get status from QSPI flash device */
err = R_QSPI_StatusGet(&g_qspi0_flash_ctrl, &status);
if (FSP_SUCCESS != err)
{
//printf("R_QSPI_StatusGet Failed\r\n");
return err;
}
/* Decrement time out to avoid infinite loop in case of consistent failure */
--time_out;
if (RESET_VALUE >= time_out)
{
//printf("\r\n ** Timeout : No result from QSPI flash status register ** \r\n");
return FSP_ERR_TIMEOUT;
}
}
while (false != status.write_in_progress);
return err;
}
/**
* @brief 擦除FLASH扇区
* @param SectorAddr:要擦除的扇区地址
* @retval 无
*/
void QSPI_Flash_SectorErase(uint32_t adress)
{
unsigned char data = {};
data = 0x06; //write_enable_command
data = 0x20; //erase_command
data = (uint8_t)(adress >> 16);
data = (uint8_t)(adress >> 8);
data = (uint8_t)(adress);
R_QSPI->SFMCMD = 1U;
R_QSPI->SFMCOM = data;
R_QSPI_DirectWrite(&g_qspi0_flash_ctrl, &data, 4, false);
QSPI_Flash_WaitForWriteEnd();
}
/**
* @brief 对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
* @param pBuffer,要写入数据的指针
* @param WriteAddr,写入地址
* @param NumByteToWrite,写入数据长度
* @retval 无
*/
void QSPI_Flash_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
/*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/
Addr = WriteAddr % SPI_FLASH_PageSize;
/*差count个数据值,刚好可以对齐到页地址*/
count = SPI_FLASH_PageSize - Addr;
/*计算出要写多少整数页*/
NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
/*mod运算求余,计算出剩余不满一页的字节数*/
NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
/* Addr=0,则WriteAddr 刚好按页对齐 aligned */
if (Addr == 0)
{
/* NumByteToWrite < SPI_FLASH_PageSize */
if (NumOfPage == 0)
{
R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, NumByteToWrite);
QSPI_Flash_WaitForWriteEnd();
}
else /* NumByteToWrite > SPI_FLASH_PageSize */
{
/*先把整数页都写了*/
while (NumOfPage--)
{
R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, SPI_FLASH_PageSize);
QSPI_Flash_WaitForWriteEnd();
WriteAddr += SPI_FLASH_PageSize;
pBuffer += SPI_FLASH_PageSize;
}
/*若有多余的不满一页的数据,把它写完*/
R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, NumOfSingle);
QSPI_Flash_WaitForWriteEnd();
}
}
/* 若地址与 SPI_FLASH_PageSize 不对齐 */
else
{
/* NumByteToWrite < SPI_FLASH_PageSize */
if (NumOfPage == 0)
{
/*当前页剩余的count个位置比NumOfSingle小,一页写不完*/
if (NumOfSingle > count)
{
temp = NumOfSingle - count;
/*先写满当前页*/
R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, count);
QSPI_Flash_WaitForWriteEnd();
WriteAddr += count;
pBuffer += count;
/*再写剩余的数据*/
R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, temp);
QSPI_Flash_WaitForWriteEnd();
}
else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
{
R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, NumByteToWrite);
QSPI_Flash_WaitForWriteEnd();
}
}
else /* NumByteToWrite > SPI_FLASH_PageSize */
{
/*地址不对齐多出的count分开处理,不加入这个运算*/
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
/* 先写完count个数据,为的是让下一次要写的地址对齐 */
R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, count);
QSPI_Flash_WaitForWriteEnd();
/* 接下来就重复地址对齐的情况 */
WriteAddr += count;
pBuffer += count;
/*把整数页都写了*/
while (NumOfPage--)
{
R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, SPI_FLASH_PageSize);
QSPI_Flash_WaitForWriteEnd();
WriteAddr += SPI_FLASH_PageSize;
pBuffer += SPI_FLASH_PageSize;
}
/*若有多余的不满一页的数据,把它写完*/
if (NumOfSingle != 0)
{
R_QSPI_Write(&g_qspi0_flash_ctrl, pBuffer, WriteAddr, NumOfSingle);
QSPI_Flash_WaitForWriteEnd();
}
}
}
}
/**
* @brief 读取FLASH数据,减少ctrl这个标志
* @param pBuffer,存储读出数据的指针
* @param ReadAddr,读取地址
* @param NumByteToRead,读取数据长度
* @retval 无
*/
void QSPI_Flash_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
R_QSPI_Read(&g_qspi0_flash_ctrl, pBuffer, ReadAddr, NumByteToRead);
}
/**
* @brief 读取flash数据
* @param p_ctrl
* @param p_src 需要传回的数据
* @param p_dest 数据地址
* @param byte_count 数据长度
*/
void qspi_d0_byte_write_standard(uint8_t byte)
{
R_QSPI->SFMCOM = byte;
}
fsp_err_t R_QSPI_Read(spi_flash_ctrl_t *p_ctrl,
uint8_t *p_src,
uint8_t *const p_dest,
uint32_t byte_count)
{
qspi_instance_ctrl_t *p_instance_ctrl = (qspi_instance_ctrl_t *) p_ctrl;
uint32_t chip_address = (uint32_t) p_dest - (uint32_t) QSPI_DEVICE_START_ADDRESS + R_QSPI->SFMCNT1;
bool restore_spi_mode = false;
void (* write_command)(uint8_t byte) = qspi_d0_byte_write_standard;
void (* write_address)(uint8_t byte) = qspi_d0_byte_write_standard;
#if QSPI_CFG_SUPPORT_EXTENDED_SPI_MULTI_LINE_PROGRAM
/* If the peripheral is in extended SPI mode, and the configuration provided in the BSP allows for programming on
* multiple data lines, and a unique command is provided for the required mode, update the SPI protocol to send
* data on multiple lines. */
if ((SPI_FLASH_DATA_LINES_1 != p_instance_ctrl->data_lines) &&
(SPI_FLASH_PROTOCOL_EXTENDED_SPI == R_QSPI->SFMSPC_b.SFMSPI))
{
R_QSPI->SFMSPC_b.SFMSPI = p_instance_ctrl->data_lines;
restore_spi_mode = true;
/* Write command in extended SPI mode on one line. */
write_command = gp_qspi_prv_byte_write;
if (SPI_FLASH_DATA_LINES_1 == p_instance_ctrl->p_cfg->page_program_address_lines)
{
/* Write address in extended SPI mode on one line. */
write_address = gp_qspi_prv_byte_write;
}
}
#endif
/* Enter Direct Communication mode */
R_QSPI->SFMCMD = 1;
/* Send command to enable writing */
write_command(0x03);
/* Write the address. */
if ((p_instance_ctrl->p_cfg->address_bytes & R_QSPI_SFMSAC_SFMAS_Msk) == SPI_FLASH_ADDRESS_BYTES_4)
{
/* Send the most significant byte of the address */
write_address((uint8_t)(chip_address >> 24));
}
/* Send the remaining bytes of the address */
write_address((uint8_t)(chip_address >> 16));
write_address((uint8_t)(chip_address >> 8));
write_address((uint8_t)(chip_address));
/* Write the data. */
uint32_t index = 0;
while (index < byte_count)
{
/* Read the device memory into the passed in buffer */
*(p_src + index) = (uint8_t) R_QSPI->SFMCOM;
index++;
}
/* Close the SPI bus cycle. Reference section 39.10.3 "Generating the SPI Bus Cycle during Direct Communication"
* in the RA6M3 manual R01UH0886EJ0100. */
R_QSPI->SFMCMD = 1;
/* Return to ROM access mode */
R_QSPI->SFMCMD = 0;
return FSP_SUCCESS;
}</code></pre>
<p>1.3 hal_entry.c代码编写</p>
<p> </p>
<p>hal_entry.c添加内容</p>
<pre>
<code>//add***
uint32_t flag=0;
uint32_t id;
uint32_t id1;
#define FLASH_WriteAddress 0x00000
#define FLASH_ReadAddress FLASH_WriteAddress
#define FLASH_SectorToErase FLASH_WriteAddress
/* 发送缓冲区初始化 */
uint8_t Tx_Buffer[] = "Hello Digikey&EEWorld!\n";
uint8_t Rx_Buffer;
int Buffercmp(uint8_t *pBuffer1, uint8_t *pBuffer2, uint16_t BufferLength)
{
while (BufferLength--)
{
if (*pBuffer1 != *pBuffer2)
{
return 1;
}
pBuffer1++;
pBuffer2++;
}
return 0;
}
//add***</code></pre>
<p> </p>
<p>hal_entry函数添加如下内容</p>
<pre>
<code>//add***
/*******************************************************************************************************************//**
* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
* is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
//add***
// uint32_t FlashID = 0;
// uint32_t FlashDeviceID = 0;
QSPI_Flash_Init(); // 串行FLASH初始化
// FlashID = QSPI_Flash_ReadID();
// FlashDeviceID = QSPI_Flash_ReadDeviceID();
QSPI_Flash_SectorErase(FLASH_SectorToErase);
/* 将发送缓冲区的数据写到flash中 */
// 这里写一页,一页的大小为256个字节
QSPI_Flash_BufferWrite(Tx_Buffer, FLASH_WriteAddress, sizeof(Tx_Buffer));
//printf("写入的数据为:%s \r\n", Tx_Buffer);
/* 将刚刚写入的数据读出来放到接收缓冲区中 */
QSPI_Flash_BufferRead(Rx_Buffer, FLASH_ReadAddress, sizeof(Tx_Buffer));
//printf("读出的数据为:%s \r\n", Rx_Buffer);
if (Buffercmp(Tx_Buffer, Rx_Buffer, sizeof(Tx_Buffer)) == 0)
{
flag=1;
//printf("\r\n32Mbit串行Flash测试成功!\r\n");
//LED3_ON;
}
else
{
flag=0;
//printf("\r\n32Mbit串行Flash测试失败!\r\n");
//LED1_ON;
}
//add***</code></pre>
<p>编译工程后进入调试模式</p>
<p>打开Jlink RTT查看日志输出</p>
<p> </p>
<p> </p>
<p>这里往QSPI Flash的起始地址写入一行字符串,并读出该地址的字符数据,如果写入的数据与读出的数据一致,说明QSPI Flash读写测试成功</p>
<p>二、OSPI配置</p>
<p lang="en-US">2.1</p>
<p>使用e2 studio新建MCU自定义工程</p>
<p> </p>
<p> </p>
<p>打开开发板原理图文件,找到OSPI Flash的原理图</p>
<p> </p>
<p> </p>
<p> </p>
<p>在FSP配置界面找到Pins栏,引脚配置与原理图一致,注意引脚组选项为Mixed</p>
<p>操作模式为Custom</p>
<p> </p>
<p>在stacks栏新增ospi flash</p>
<p> </p>
<p>ospi flash stack属性设置保持默认,由于OPSI外设使用独立的OCTASPICLK时钟信号,需要在Clocks配置OSPI的时钟信号,时钟树配置如下</p>
<p> </p>
<p lang="en-US">2.2</p>
<p>在工程目录src下新建bsp文件夹,并新建ospi的两个c/h驱动文件</p>
<p>bsp_ospi_flash.h头文件内容如下</p>
<p lang="en-US"> </p>
<p> </p>
<p>bsp_ospi_flash.h头文件内容如下</p>
<pre>
<code>/*
* bsp_ospi_flash.h
*
* Created on: 2024年11月17日
* Author: quansirx
*/
#ifndef OSPI_FLASH_BSP_OSPI_FLASH_H_
#define OSPI_FLASH_BSP_OSPI_FLASH_H_
#include "hal_data.h"
#include "r_ospi.h"
#define RESET_VALUE (0x00)
void QSPI_Flash_Init(void);
fsp_err_t ospi_init(void);
#endif /* OSPI_FLASH_BSP_OSPI_FLASH_H_ */</code></pre>
<p>bsp_ospi_flash.c文件内容如下</p>
<pre>
<code>/*
* bsp_ospi_flash.c
*
* Created on: 2024年11月17日
* Author: quansirx
*/
#include "bsp_ospi_flash.h"
void QSPI_Flash_Init(void){
// bsp_octaclk_settings_t octaclk = {RESET_VALUE};
// octaclk.source_clock = BSP_CFG_OCTA_SOURCE; /* 200MHz */
// octaclk.divider = BSP_CLOCKS_OCTA_CLOCK_DIV_2;
// R_BSP_OctaclkUpdate(&octaclk);//更新OSI 时钟频率
//
// fsp_err_t err = FSP_SUCCESS;
// err = ospi_init();
R_OSPI_Open(&g_ospi_ctrl, &g_ospi_cfg);
}
spi_flash_cfg_t g_loc_ospi_cfg;
ospi_extended_cfg_t g_loc_ospi_extnd_cfg;
#define DEVICE_SECTOR_SIZE (4096U)
#define DEVICE_BLOCK_SIZE (65536U)
#define INITIAL_INDEX (0U)
#define OSPI_OPI_CMD_ERASE_SECTOR (0x21DEU)
#define OSPI_OPI_CMD_ERASE_BLOCK (0xDC23U)
#define OSPI_OPI_CMD_ERASE_CHIP (0xC738U)
static const spi_flash_erase_command_t opi_erase_command_list[] =
{
{ .command = OSPI_OPI_CMD_ERASE_SECTOR, .size = DEVICE_SECTOR_SIZE },
{ .command = OSPI_OPI_CMD_ERASE_BLOCK, .size = DEVICE_BLOCK_SIZE },
{ .command = OSPI_OPI_CMD_ERASE_CHIP, .size = SPI_FLASH_ERASE_SIZE_CHIP_ERASE },
};
fsp_err_t ospi_init(void)
{
fsp_err_t err = FSP_SUCCESS;
/*Configuration setup of Extended SPI configuration into local config*/
ospi_extended_cfg_t* textd = NULL;
textd = (void *)g_ospi_cfg.p_extend;
g_loc_ospi_extnd_cfg = *textd;
g_loc_ospi_cfg = g_ospi_cfg;
g_loc_ospi_cfg.p_erase_command_list = &opi_erase_command_list;
g_loc_ospi_cfg.p_extend = &g_loc_ospi_extnd_cfg;
/* In the current OSPI device model, the default status of the device is set to SPI mode.
* Therefore, it is necessary to initialize the OSPI driver module in SPI mode
* to ensure compatibility with the device's default SPI protocol.
*/
g_loc_ospi_cfg.spi_protocol = SPI_FLASH_PROTOCOL_EXTENDED_SPI;
/*Open Octa Flash device in Extended SPI Mode */
err = R_OSPI_Open(&g_ospi_ctrl, &g_loc_ospi_cfg);
if (FSP_SUCCESS != err)
{
//APP_ERR_PRINT ("\r\n** R_OSPI_Open API FAILED **\r\n");
}
return err;
}</code></pre>
<p>三、DAC波形测试</p>
<p>以快速向导工程为模版,驱动DAC产生正弦波波形</p>
<p lang="en-US">3.1</p>
<p>打开quickstart_ek_ra6m5_ep工程下FSP配置文件</p>
<p>在stack栏新增DAC外设</p>
<p> </p>
<p>pins栏配置DAC输出引脚为P014</p>
<p lang="en-US"> </p>
<p>DAC波形产生需要使用定时器,这里使用示例工程中g_gpt_blue这一定时器</p>
<p> </p>
<p>g_gpt_blue定时器已经使能定时器溢出中断,中断回调函数为gpt_blue_callback</p>
<p>使用全局搜索gpt_blue_callback,找到gpt_blue_callback的函数定义</p>
<p> </p>
<p> </p>
<p>gpt_blue_callback定义在common_init.c文件</p>
<p>在gpt_blue_callback添加以下内容</p>
<pre>
<code>#include "math.h"
#define M_PI 3.141592
/**********************************************************************************************************************
* Function Name: gpt_blue_callback
* Description : Callback function for driver g_gpt_blue.
* Argument : p_args
* Return Value : .
*********************************************************************************************************************/
void gpt_blue_callback(timer_callback_args_t * p_args)
{
/* Void the unused params */
uint16_t dacvalue=0;
static double w=0.0;
w+=M_PI/90;
if(w>=2*M_PI){
w=0.0;
}
dacvalue=4095*(cos(w)+1)/2;
R_DAC_Write(&g_dac0_ctrl, dacvalue);
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;
}
break;
}
default:
{
TURN_BLUE_OFF
s_intense = 0;
s_duty = g_pwm_dcs;
}
}
}
</code></pre>
<p> </p>
<p>进行程序调试</p>
<p>我这里没有示波器观察DAC波形,使用e2 studio内置的real-time chart观察dacvalue的变化情况</p>
<p> </p>
<p> </p>
<p> </p>
<p>因为real-time chart的刷新速率慢,所以波形会有些失真</p>
页:
[1]