Win 10,STM32Cubeide 1.8.0
STM32F746发现板
MCU:STM32F746(,)
Flash:MT25QL128ABA1EW9-0SIT(
https://eu.mouser.com/datasheet/2/2/671/mict_s_a000484955_1-1-2290852.pdf示波器,低端逻辑分析仪带有解码器
我要实现的目标:我希望能够通过间接写入(工作正常)发送命令,并用间接阅读读取注册(在线上持续垃圾失败),《避风港》' T甚至尝试读取/写入实际的内存。
连接(来自发现板示意图):
有趣的是,STMicoelectronics提供的示例本身也无法正常工作。稍后再详细介绍。
最初,我阅读了参考手册,并试图自己弄清楚这些东西,就像我学会操作新的外围时一样。它没有完全奏效,因此我使用touchGFX生成的代码比较配置(它使用内存映射模式,但是我至少可以检查时钟和GPIO设置,这是正确的),然后我也找到了几乎唯一的我做的其他文章。我很亲密,但是文章中有一些无法解释的(未用参考手册中涵盖)使它起作用。 ()
我仅使用了早期代码。到“初始化”部分下的代码的第一位,但没有其他内容。我以闪光灯尺寸(128毫米)进行了调整。
我将安全地假设我的时钟和GPIO初始化是正确的,因为它与使用QSPI Flash的TouchGFX代码以及STM32F7软件包中的示例程序匹配。
我正在使用1MHz时钟配置QSPI。虽然它并没有完全涵盖Flash IC的规格,但在我的范围和逻辑分析仪上,它都具有100MHz为单据的上限,但在那里并不是真正可用的。我还尝试使用108MHz,这是一个已记录的闪存IC规格,我仍然在那里得到垃圾(通过调试输出找到)。
QSPI设置:
void qspi_setup_indirect_mode(void) {
/* ------------ QSPI Initialization --------------- */
/*
* Make sure QSPI is disabled
* */
QUADSPI->CR &= ~(QUADSPI_CR_EN);
/*
* Flash size 128Mb=16MB=2^24 bytes
* 24-bit Address
*
* */
QUADSPI->DCR = 0x00; //reset
QUADSPI->CCR = 0x00; //reset
QUADSPI->DCR |= (23U << QUADSPI_DCR_FSIZE_Pos);
QUADSPI->CCR |= (2U << QUADSPI_CCR_ADSIZE_Pos);
/*
* Sample shift 1/2 clock cycle
* Prescaler = 2 (216MHz/216 = 1MHz)
*
* */
QUADSPI->CR = 0x00; //reset
QUADSPI->CR |= (QUADSPI_CR_SSHIFT | (215U << QUADSPI_CR_PRESCALER_Pos));
/*
* Make sure all flags are cleared
*
* */
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF;
/*
* Enable peripheral
* */
//QUADSPI->CR |= (QUADSPI_CR_EN); (enable later for every transmission)
}
然后有函数,设置命令模式。它设置了访问模式(间接写入,读取,轮询,内存映射),以及传输了多少数据指令,地址等(从无到4)和虚拟周期。没有什么幻想,与示例中的一个非常相似。
void qspi_set_command_mode(uint8_t fmode, uint8_t imode, uint8_t admode, uint8_t abmode, uint8_t dcyc, uint8_t dmode) {
/*
* Make sure QSPI is disabled
* */
QUADSPI->CR &= ~(QUADSPI_CR_EN);
/*
* Communication configuration register
* First, reset all mode values
* Set new values
* */
QUADSPI->CCR = QUADSPI->CCR & ~(QUADSPI_CCR_FMODE) & ~(QUADSPI_CCR_IMODE) & ~(QUADSPI_CCR_ADMODE) & ~(QUADSPI_CCR_ABMODE) & ~(QUADSPI_CCR_DCYC)
& ~(QUADSPI_CCR_DMODE);
QUADSPI->CCR = QUADSPI->CCR | (fmode << QUADSPI_CCR_FMODE_Pos) | (imode << QUADSPI_CCR_IMODE_Pos) | (admode << QUADSPI_CCR_ADMODE_Pos)
| (abmode << QUADSPI_CCR_ABMODE_Pos) | (dcyc << QUADSPI_CCR_DCYC_Pos) | (dmode << QUADSPI_CCR_DMODE_Pos);
}
我尝试了对这些功能的各种次要更改,并且仅当我禁用外围,配置东西,启用,设置指令时,写作作品。如果我在设置部分中启用外围设备,则写入不起作用。这在参考手册中没有涵盖,我在文章中发现了这一点(没有指出)。
void qspi_sendCommandIndirectWrite(uint8_t command) {
QUADSPI->CR &= ~(QUADSPI_CR_EN); //disable qspi to configure
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; //clear all flags
qspi_set_command_mode(0x00, 0x01, 0x00, 0x00, 0x00, 0x00); //Set indirect write, only instruction on 1 line, everything else off
QUADSPI->CCR &= ~(0xFF << QUADSPI_CCR_INSTRUCTION_Pos); //clear instruction field
QUADSPI->CR |= (QUADSPI_CR_EN);
QUADSPI->CCR |= (command << QUADSPI_CCR_INSTRUCTION_Pos); //writing instruction starts communication
while (QUADSPI->SR & QUADSPI_SR_BUSY); // Wait for the transaction to complete, and disable the peripheral.
QUADSPI->CR &= ~(QUADSPI_CR_EN);
}
void qspi_sendCommandIndirectRead(uint8_t command, uint8_t receiveBuffer[], uint32_t length) {
QUADSPI->CR &= ~(QUADSPI_CR_EN); //disable qspi to configure
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; //clear all flags
qspi_set_command_mode(0x01, 0x01, 0x00, 0x00, 0x01, 0x01); //Set indirect write, only instruction on 1 line, , data on 1 line, 1 dummy cycle, everything else off
QUADSPI->CCR &= ~(0xFF << QUADSPI_CCR_INSTRUCTION_Pos); //clear instruction field
QUADSPI->DLR = length;
QUADSPI->CR |= (QUADSPI_CR_EN);
QUADSPI->CCR |= (command << QUADSPI_CCR_INSTRUCTION_Pos); //writing instruction starts communication
uint32_t counter = 0x00;
while (counter < length) {
while (!(QUADSPI->SR & QUADSPI_SR_TCF)); //wait while data arrives to FIFO
receiveBuffer[counter] = (uint8_t) (0xFF & QUADSPI->DR);
counter++;
}
while (QUADSPI->SR & QUADSPI_SR_BUSY); // Wait for the transaction to complete, and disable the peripheral.
QUADSPI->CR &= ~(QUADSPI_CR_EN);
}
最后,所有这些都以以下方式为主要方式:
#include "main.h"
void system_hw_setup(void);
void qspi_example(void);
int main(void) {
system_hw_setup(); //initialize hardware
system_msdelay(100U);
//qspi_sendCommandIndirectWrite(MT25QL128ABA1EW9_COMMAND_ENTER_QUAD_IO_MODE); //works OK
//qspi_example(); //example provided by STM32 w clock and GPIO setup
system_msdelay(100U);
uint8_t test[1];
while (1) {
qspi_sendCommandIndirectRead(MT25QL128ABA1EW9_COMMAND_READ_STATUS_REGISTER, test, 1);
//qspi_sendCommandIndirectRead(MT25QL128ABA1EW9_COMMAND_READ_ENHANCED_VOLATILE_CONFIGURATION_REGISTER, test, 1);
system_msdelay(100U);
toggle_stm32f746disco_ld1();
/*
test[0] = 0x00;
if (test[0] == 0x00) {
test[0] = (uint8_t) '0';
}
/usart_dma_sendArray(USART1, test, 1); */
}
}
void system_hw_setup(void) {
rcc_setup(); //clock for peripheral, clock will not be altered; therefore default HSI 16MHz
systick_setup(SYSTEM_FREQUENCY); //activate systick
gpio_setup(); //set pin modes and functions
dma_reset_flags(DMA2); //clear DMA2 flags for USART1
dma_reset_flags(DMA1); //clear DMA1 flags for I2C3
usart_dma_setup(USART1); //set control registers and settings for USART1 and its DMA connected to st-link
usart_enable(USART1); //enable uart1
usart_enable_tx(USART1); //enable tx line (wrapper)
usart_enable_rx(USART1); //enable rx line (wrapper)
qspi_setup_indirect_mode(); //enable qspi in indirect mode
nvic_setup(); //set interrupts and their priorities
}
https://i.sstatic.net/kgbol.png“ alt =” qspi Logic Analyzer“>
根据参考手册,来自IC的数据应在DQ1上,但它是没有发生。另外,有时DQ3随机上升了一段时间。时钟周期的数量很奇怪。另外,我不知道为什么那里有一些0x80数据包,我只发送指令,而别无其他。它可能与我人为降低的时钟速度有关,但是如果我将QSPI时钟设置为正确的值,同一配置也会失败。
我对自己做错了什么,我很迷失了,而MCU的参考手册部分目前没有太大帮助,而且互联网上几乎没有资源以有意义的(或任何,在这一点)方式。
我将感谢任何帮助或建议使QSPI工作的任何建议!
Win 10, STM32CubeIDE 1.8.0
STM32F746 Discovery board
MCU: STM32F746 (Datasheet, Reference Manual, Errata)
Flash: MT25QL128ABA1EW9-0SIT (Datasheet)
Equipment: Low end oscilloscope, low end logic analyzer with decoder
What I'm trying to achieve: I want to be able to send command via indirect write (works OK), read register with indirect read (fails miserably with consistent garbage on the lines), haven't even tried to read/write actual memory.
Connections (from the discovery board schematic):

Interestingly enough, the example provided by STMicoelectronics themselves also doesn't work as expected. More on that later.
Initially, I read the reference manual and tried to figure the stuff out on my own, as I always do when I learn to operate new peripheral. It didn't exactly work out, so I used TouchGFX-generated code to compare configuration (it's using memory mapped mode, but I could at least check clock and GPIO setup, and it was correct), then I also found pretty much the only other article that does what I do. I was close, but a few unexplained (not covered in reference manual) bits from the article made it work. (Article)
I used only the early code from there. Up to and including the first bit of code under "Initialization" section, but not anything further. I adjusted it for my Flash size (128Mbit).
I will safely assume my clock and GPIO initialization is correct, as it matches TouchGFX code, which utilizes QSPI Flash, as well as example program from STM32F7 package.
I'm configuring QSPI with 1MHz clock. While it's not exactly covered in specs of the Flash IC, it's easier on my scope and logic analyzer, both of which have 100MHz as documented upper bound, but they're not really usable there. I also tried to use 108MHz, which is a documented Flash IC spec, I still get garbage there (found via debugging output).
QSPI setup:
void qspi_setup_indirect_mode(void) {
/* ------------ QSPI Initialization --------------- */
/*
* Make sure QSPI is disabled
* */
QUADSPI->CR &= ~(QUADSPI_CR_EN);
/*
* Flash size 128Mb=16MB=2^24 bytes
* 24-bit Address
*
* */
QUADSPI->DCR = 0x00; //reset
QUADSPI->CCR = 0x00; //reset
QUADSPI->DCR |= (23U << QUADSPI_DCR_FSIZE_Pos);
QUADSPI->CCR |= (2U << QUADSPI_CCR_ADSIZE_Pos);
/*
* Sample shift 1/2 clock cycle
* Prescaler = 2 (216MHz/216 = 1MHz)
*
* */
QUADSPI->CR = 0x00; //reset
QUADSPI->CR |= (QUADSPI_CR_SSHIFT | (215U << QUADSPI_CR_PRESCALER_Pos));
/*
* Make sure all flags are cleared
*
* */
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF;
/*
* Enable peripheral
* */
//QUADSPI->CR |= (QUADSPI_CR_EN); (enable later for every transmission)
}
Then there is function, that sets command mode. It sets access mode (indirect write, read, polling, memory mapped), as well as on how many datalines instruction, address and so on are transmitted (from none to 4), and dummy cycles. Nothing fancy, very similar to the one from the example.
void qspi_set_command_mode(uint8_t fmode, uint8_t imode, uint8_t admode, uint8_t abmode, uint8_t dcyc, uint8_t dmode) {
/*
* Make sure QSPI is disabled
* */
QUADSPI->CR &= ~(QUADSPI_CR_EN);
/*
* Communication configuration register
* First, reset all mode values
* Set new values
* */
QUADSPI->CCR = QUADSPI->CCR & ~(QUADSPI_CCR_FMODE) & ~(QUADSPI_CCR_IMODE) & ~(QUADSPI_CCR_ADMODE) & ~(QUADSPI_CCR_ABMODE) & ~(QUADSPI_CCR_DCYC)
& ~(QUADSPI_CCR_DMODE);
QUADSPI->CCR = QUADSPI->CCR | (fmode << QUADSPI_CCR_FMODE_Pos) | (imode << QUADSPI_CCR_IMODE_Pos) | (admode << QUADSPI_CCR_ADMODE_Pos)
| (abmode << QUADSPI_CCR_ABMODE_Pos) | (dcyc << QUADSPI_CCR_DCYC_Pos) | (dmode << QUADSPI_CCR_DMODE_Pos);
}
I tried various minor changes to these functions, and write works if and only if I disable peripheral, configure the thing, enable, set the instruction. If I enable peripheral in the setup section, write doesn't work. This is not covered in reference manual, I found it in the article (where it's not pointed out).
void qspi_sendCommandIndirectWrite(uint8_t command) {
QUADSPI->CR &= ~(QUADSPI_CR_EN); //disable qspi to configure
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; //clear all flags
qspi_set_command_mode(0x00, 0x01, 0x00, 0x00, 0x00, 0x00); //Set indirect write, only instruction on 1 line, everything else off
QUADSPI->CCR &= ~(0xFF << QUADSPI_CCR_INSTRUCTION_Pos); //clear instruction field
QUADSPI->CR |= (QUADSPI_CR_EN);
QUADSPI->CCR |= (command << QUADSPI_CCR_INSTRUCTION_Pos); //writing instruction starts communication
while (QUADSPI->SR & QUADSPI_SR_BUSY); // Wait for the transaction to complete, and disable the peripheral.
QUADSPI->CR &= ~(QUADSPI_CR_EN);
}
void qspi_sendCommandIndirectRead(uint8_t command, uint8_t receiveBuffer[], uint32_t length) {
QUADSPI->CR &= ~(QUADSPI_CR_EN); //disable qspi to configure
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; //clear all flags
qspi_set_command_mode(0x01, 0x01, 0x00, 0x00, 0x01, 0x01); //Set indirect write, only instruction on 1 line, , data on 1 line, 1 dummy cycle, everything else off
QUADSPI->CCR &= ~(0xFF << QUADSPI_CCR_INSTRUCTION_Pos); //clear instruction field
QUADSPI->DLR = length;
QUADSPI->CR |= (QUADSPI_CR_EN);
QUADSPI->CCR |= (command << QUADSPI_CCR_INSTRUCTION_Pos); //writing instruction starts communication
uint32_t counter = 0x00;
while (counter < length) {
while (!(QUADSPI->SR & QUADSPI_SR_TCF)); //wait while data arrives to FIFO
receiveBuffer[counter] = (uint8_t) (0xFF & QUADSPI->DR);
counter++;
}
while (QUADSPI->SR & QUADSPI_SR_BUSY); // Wait for the transaction to complete, and disable the peripheral.
QUADSPI->CR &= ~(QUADSPI_CR_EN);
}
Finally, all of that is called in the main the following way:
#include "main.h"
void system_hw_setup(void);
void qspi_example(void);
int main(void) {
system_hw_setup(); //initialize hardware
system_msdelay(100U);
//qspi_sendCommandIndirectWrite(MT25QL128ABA1EW9_COMMAND_ENTER_QUAD_IO_MODE); //works OK
//qspi_example(); //example provided by STM32 w clock and GPIO setup
system_msdelay(100U);
uint8_t test[1];
while (1) {
qspi_sendCommandIndirectRead(MT25QL128ABA1EW9_COMMAND_READ_STATUS_REGISTER, test, 1);
//qspi_sendCommandIndirectRead(MT25QL128ABA1EW9_COMMAND_READ_ENHANCED_VOLATILE_CONFIGURATION_REGISTER, test, 1);
system_msdelay(100U);
toggle_stm32f746disco_ld1();
/*
test[0] = 0x00;
if (test[0] == 0x00) {
test[0] = (uint8_t) '0';
}
/usart_dma_sendArray(USART1, test, 1); */
}
}
void system_hw_setup(void) {
rcc_setup(); //clock for peripheral, clock will not be altered; therefore default HSI 16MHz
systick_setup(SYSTEM_FREQUENCY); //activate systick
gpio_setup(); //set pin modes and functions
dma_reset_flags(DMA2); //clear DMA2 flags for USART1
dma_reset_flags(DMA1); //clear DMA1 flags for I2C3
usart_dma_setup(USART1); //set control registers and settings for USART1 and its DMA connected to st-link
usart_enable(USART1); //enable uart1
usart_enable_tx(USART1); //enable tx line (wrapper)
usart_enable_rx(USART1); //enable rx line (wrapper)
qspi_setup_indirect_mode(); //enable qspi in indirect mode
nvic_setup(); //set interrupts and their priorities
}
which gives the following:


As per reference manual, data from the IC should come on DQ1, but it's not happening. Also, sometimes DQ3 randomly goes up for some time. The number of clock cycles is strange. Also, I have no idea why there is some 0x80 packet there, I'm sending only the instruction and nothing else. It could be related to my artificially lowered clock speed, but the same configuration also miserably fails if I set QSPI clock to proper value.
I'm pretty lost at what I'm doing wrong, and the reference manual section of the MCU is not much help at this point, and there are next to no resources on the internet that cover it in a meaningful (or any, at this point) way.
I would appreciate any help or advice with making QSPI work!
发布评论
评论(1)
主要问题是访问数据寄存器。 quadspii&gt; dr是挥发性的UINT32_T。因此,每当我访问quadspi-&gt; dr时,即使我收到了1个字节,它也会从寄存器中读取4个字节,并且因此也会产生带有FIFO阈值的Gibberish。正确的解决方案是明确指定字节,半词或单词访问quadspii&gt; dr。我使用数据寄存器的地址,将其作为指向UINT8_T或UINT16_T的指针,然后将其放置:
The main problem was the access to data register. QUADSPI->DR is a volatile uint32_t. So whenever I access QUADSPI->DR, even if I received 1 byte, it reads 4 bytes from the register, and it also produces gibberish with FIFO threshold because of that. The correct solution is to explicitly specify byte, half-word or word access to the QUADSPI->DR. I take address of the data register, cast it as a pointer to uint8_t or uint16_t, and dereference it: