STM32(H7S7)学习记录-9:XSPI Octal Flash内存映射
最近在使用STM32H7S78-DK这块板的时候,发现官方只给了PSRAM(hexa spi)的内存映射的例子,但是官方没有给Octal_flash的内存映射例子。在此记录一下。目前STM32H7RS软件包还是V1.0。cubeMX版本是6.11.1 (cubemx生成的STM32H7S系列的代码还是不太完整,需要参考官方例程以及查找官方文档)。
简单介绍一下XSPI的一些知识(想要知道更多的话可以看一些具有XSPI芯片的参考手册)。XSPI支持两个协议,regular-command / hyperbus protocol,三个模式,间接、间状态轮询(hyperbus protocol 没有这个模式)、内存映射。XSPI可以配置成single_SPI、Dual_SPI、Quad_SPI、Octal_SPI、hexa_SPI、hyperbus。
regular-command protocol有以下阶段:
• instruction phase
• address phase
• alternate-byte phase
• dummy-cycle phase
• data phase
注意:Only the data phase uses 16 bits. Instruction, address, and alternate phases use only the eight LSB of the bus as for octal configuration.
支持SDR\DTR模式。
对目前cubemx里面的XSPI参数进行解释:
Fifo threshold :设置的是Fifo阈值的大小。
memory mode:此处不太确定(没找到相应的寄存器)。应该是指协议regular-command protocol /HyperBus protocol。看官方例程都是disable。
memory type:这里是 你所使用的内存厂家。
memory size:你所使用的memory大小。
Schip select high time cycle:查阅STM32H7S7手册:当空闲时,ncs引脚是高电平。也就是与外部设备进行各项事务之间保持高电平的时间。
free running clock :手册里面的解释(翻译了一下)→自由时钟 自由时钟(free running clock),即运行时时钟频率、占空比等不随时间发生变化的时钟,通俗来讲线上一直有稳定的时钟信号,比如 I2S 的MCLK、BCLK 非自由时钟 非自由时钟(Non-free running clock),即在运行时,时钟信号不是稳定产生的,只有发送、接受数据时才需要时钟信号,比如 I2C 、SPI 的时钟信号。
clock mode:0: CLK必须保持在低电平,而NCS是高电平(芯片选择释放),称为时钟模式0。 1: CLK必须保持在高电平,而NCS是高电平(芯片选择释放),称为时钟模式3。(没有时钟模式2!)
wrap size:(这个意思其实我没太理解好)表示配置内存的包装大小。针对需要包装指令的内存。这个位字段表示与XSPI_WPIR中保存的命令相关联的包装大小
clock prescaler:预分频数(实际XSPI时钟频率=XSPI的频率/预分频数+1)。
sample shift :默认情况下,数据由外部设备驱动后,XSPI采样CLK周期的1/2。允许稍后对数据进行采样,以便考虑外部信号的延迟(STR)。
delay holde quarter cycle:当外部DQS被用作采样时钟时,对于所有在freq_min以上的频率,它被精确地移动SPI时钟周期的四分之一,以补偿产品嵌入时“高速接口”中的数据传播延迟。
chip select boundary:例如,如果CSBOUND[4:0] = 0x4,则将边界设置为2^4 = 16字节。因此,每次LSB地址等于0xF时,以及每次发出一个新事务以处理下一个数据时,都会释放NCS。
maximum transfer:是否启用通信调节功能。当另一个XSPI请求访问总线时,每一个最大的+1时钟周期都会释放NCS
refresh rate:启用刷新率功能。在写入时,NCS每次时钟周期+1进行刷新,在读取时,每时钟周期+4进行刷新。当在单、双或四spi模式下的字节传输期间发生刷新时,这个值可以用少数时钟周期进行扩展,因为字节( a byte)传输必须完成。
memory select: 选择片选信号,NCS1 或者NCS2对应芯片不同引脚!(对于H7S7来说有两个port,每个port都有NCS1与NCS2,当XSPI_1 与 XSPI_2用同一个port 会有仲裁器判断)。
以下是配置Octal_SPI_FLASH例子(使用的是XSPI_2对应地址0x7000 0000;如果你是用的是XSPI_1的话对应地址0x9000 0000):
记得配置时钟,我这配置了100MHz。
我这里还开了一个串口,方便调试。
以下是主要代码:
int main(void)
{
/* USER CODE BEGIN 1 */
XSPI_RegularCmdTypeDef sCommand = {0};
XSPI_MemoryMappedTypeDef sMemMappedCfg={0};
uint32_t v=0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Update SystemCoreClock variable according to RCC registers values. */
SystemCoreClockUpdate();
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_UART7_Init();
MX_XSPI2_Init();
/* USER CODE BEGIN 2 */
XSPI_NOR_OctalDTRModeCfg(&hxspi2);
//XSPI_WriteEnable(&hxspi2);
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_16_BITS;
sCommand.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_ENABLE;
sCommand.AddressMode = HAL_XSPI_ADDRESS_8_LINES;
sCommand.AddressWidth = HAL_XSPI_ADDRESS_32_BITS;
sCommand.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE;
sCommand.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand.DataMode = HAL_XSPI_DATA_8_LINES;
sCommand.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE;
sCommand.DQSMode = HAL_XSPI_DQS_ENABLE;
sCommand.OperationType = HAL_XSPI_OPTYPE_READ_CFG;
sCommand.Instruction = OCTAL_IO_DTR_READ_CMD;
sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_OCTAL;
if (HAL_XSPI_Command(&hxspi2, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
sCommand.OperationType = HAL_XSPI_OPTYPE_WRITE_CFG;
sCommand.Instruction = OCTAL_PAGE_PROG_CMD;
sCommand.DummyCycles = 0;
if (HAL_XSPI_Command(&hxspi2, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
sMemMappedCfg.TimeOutActivation = HAL_XSPI_TIMEOUT_COUNTER_ENABLE;
sMemMappedCfg.TimeoutPeriodClock = 0x34;
if (HAL_XSPI_MemoryMapped(&hxspi2, &sMemMappedCfg) != HAL_OK)
{
Error_Handler();
}
volatile uint32_t *pAddress = (volatile uint32_t *)0x70000000;
v= * pAddress;
HAL_UART_Transmit(&huart7,(uint8_t*)&v,4,0xff);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
static void XSPI_WriteEnable(XSPI_HandleTypeDef *hxspi)
{
XSPI_RegularCmdTypeDef sCommand ={0};
uint8_t reg[2];
/* Enable write operations ------------------------------------------ */
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.Instruction = OCTAL_WRITE_ENABLE_CMD;
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_16_BITS;
sCommand.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_ENABLE;
sCommand.AddressMode = HAL_XSPI_ADDRESS_NONE;
sCommand.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand.DataMode = HAL_XSPI_DATA_NONE;
sCommand.DummyCycles = 0;
sCommand.DQSMode = HAL_XSPI_DQS_DISABLE;
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
/* Configure automatic polling mode to wait for write enabling ---- */
sCommand.Instruction = OCTAL_READ_STATUS_REG_CMD;
sCommand.Address = 0x0;
sCommand.AddressMode = HAL_XSPI_ADDRESS_8_LINES;
sCommand.AddressWidth = HAL_XSPI_ADDRESS_32_BITS;
sCommand.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE;
sCommand.DataMode = HAL_XSPI_DATA_8_LINES;
sCommand.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE;
sCommand.DataLength = 2;
sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_OCTAL;
sCommand.DQSMode = HAL_XSPI_DQS_ENABLE;
do
{
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (HAL_XSPI_Receive(hxspi, reg, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
} while((reg[0] & WRITE_ENABLE_MASK_VALUE) != WRITE_ENABLE_MATCH_VALUE);
}
/**
* @brief This function read the SR of the memory and wait the EOP.
* @param hxspi: XSPI handle
* @retval None
*/
static void XSPI_AutoPollingMemReady(XSPI_HandleTypeDef *hxspi)
{
XSPI_RegularCmdTypeDef sCommand={0};
uint8_t reg[2];
/* Configure automatic polling mode to wait for memory ready ------ */
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.Instruction = OCTAL_READ_STATUS_REG_CMD;
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_16_BITS;
sCommand.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_ENABLE;
sCommand.Address = 0x0;
sCommand.AddressMode = HAL_XSPI_ADDRESS_8_LINES;
sCommand.AddressWidth = HAL_XSPI_ADDRESS_32_BITS;
sCommand.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE;
sCommand.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand.DataMode = HAL_XSPI_DATA_8_LINES;
sCommand.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE;
sCommand.DataLength = 2;
sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_OCTAL;
sCommand.DQSMode = HAL_XSPI_DQS_ENABLE;
do
{
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (HAL_XSPI_Receive(hxspi, reg, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
} while((reg[0] & MEMORY_READY_MASK_VALUE) != MEMORY_READY_MATCH_VALUE);
}
/**
* @brief This function configure the memory in Octal DTR mode.
* @param hxspi: XSPI handle
* @retval None
*/
static void XSPI_NOR_OctalDTRModeCfg(XSPI_HandleTypeDef *hxspi)
{
uint8_t reg = 0;
XSPI_RegularCmdTypeDef sCommand = {0};
XSPI_AutoPollingTypeDef sConfig = {0};
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
sCommand.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE;
sCommand.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE;
sCommand.DummyCycles = 0;
sCommand.DQSMode = HAL_XSPI_DQS_DISABLE;
sConfig.MatchMode = HAL_XSPI_MATCH_MODE_AND;
sConfig.AutomaticStop = HAL_XSPI_AUTOMATIC_STOP_ENABLE;
sConfig.IntervalTime = 0x10;
/* Enable write operations */
sCommand.Instruction = WRITE_ENABLE_CMD;
sCommand.DataMode = HAL_XSPI_DATA_NONE;
sCommand.AddressMode = HAL_XSPI_ADDRESS_NONE;
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
/* Reconfigure XSPI to automatic polling mode to wait for write enabling */
sConfig.MatchMask = 0x02;
sConfig.MatchValue = 0x02;
sCommand.Instruction = READ_STATUS_REG_CMD;
sCommand.DataMode = HAL_XSPI_DATA_1_LINE;
sCommand.DataLength = 1;
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (HAL_XSPI_AutoPolling(hxspi, &sConfig, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
/* Write Configuration register 2 (with Octal I/O SPI protocol) */
sCommand.Instruction = WRITE_CFG_REG_2_CMD;
sCommand.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
sCommand.AddressWidth = HAL_XSPI_ADDRESS_32_BITS;
sCommand.Address = 0;
reg = 0x2;
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (HAL_XSPI_Transmit(hxspi, ®, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
sCommand.Instruction = READ_STATUS_REG_CMD;
sCommand.DataMode = HAL_XSPI_DATA_1_LINE;
sCommand.DataLength = 1;
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (HAL_XSPI_AutoPolling(hxspi, &sConfig, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
}
一些定义:
/* Flash commands */
#define OCTAL_IO_READ_CMD 0xEC13
#define OCTAL_IO_DTR_READ_CMD 0xEE11
#define OCTAL_PAGE_PROG_CMD 0x12ED
#define OCTAL_READ_STATUS_REG_CMD 0x05FA
#define OCTAL_SECTOR_ERASE_CMD 0x21DE
#define OCTAL_WRITE_ENABLE_CMD 0x06F9
#define READ_STATUS_REG_CMD 0x05
#define WRITE_CFG_REG_2_CMD 0x72
#define WRITE_ENABLE_CMD 0x06
/* Dummy clocks cycles */
#define DUMMY_CLOCK_CYCLES_READ 6
#define DUMMY_CLOCK_CYCLES_READ_REG 4
#define DUMMY_CLOCK_CYCLES_READ_OCTAL 6
/* Auto-polling values */
#define WRITE_ENABLE_MATCH_VALUE 0x02
#define WRITE_ENABLE_MASK_VALUE 0x02
#define MEMORY_READY_MATCH_VALUE 0x00
#define MEMORY_READY_MASK_VALUE 0x01
#define AUTO_POLLING_INTERVAL 0x10
/* Memory registers address */
#define CONFIG_REG2_ADDR1 0x0000000
#define CR2_DTR_OPI_ENABLE 0x02
#define CONFIG_REG2_ADDR3 0x00000300
#define CR2_DUMMY_CYCLES_66MHZ 0x07
/* Memory delay */
#define MEMORY_REG_WRITE_DELAY 40
#define MEMORY_PAGE_PROG_DELAY 2
/* End address of the OSPI memory */
#define OSPI_END_ADDR (1 << OSPI_FLASH_SIZE)
/* Size of buffers */
#define BUFFERSIZE 256
运行效果:
串口打印的内容:
debug模式下,查看内存:
作者:做只小小jet