This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] MSPM0G3507:如何在 DMA 模式下使用 SPI 接收大块数据?

Guru**** 2379620 points
Other Parts Discussed in Thread: MSPM0G3507
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1474595/mspm0g3507-how-to-receive-large-block-of-data-using-spi-in-dma-mode

器件型号:MSPM0G3507

工具与软件:

各位专家、您好!

我必须通过 SPI + DMA 将 MSPM0G3507与 FRAM (MB85RS256B)开发通信。 FRAM 数据表的第9页包含文本"..."RISED CS 将终止写入命令。 但是、如果在 CS 上升之前继续发送每8位写入数据、则可以继续以自动地址递增进行写入。

我能够在没有 DMA 的情况下向 FRAM 写入单个值或从 FRAM 读取单个值。

我开始使用示例 spi_controller_repeed_fifo_dma_interrupts_LP_MSPM0G3507_nortos_ticlang、唯一的更改是:SPI 时钟、SPI 引脚和设置3线 SPI (使用 GPIO 控制 CS)。 我停止所有 DMA 配置。

引用布鲁斯·麦肯尼的话、我知道"SPI 本质上是全双工的--对于你传输的每一个字节、你都会收到一个;而在这种情况下、如果你想要接收一个字节、你必须发送一个字节。 我们指的是"仅传输"和"仅接收"交易、但这些交易仅意味着另一端对数据不感兴趣。"

我的应用程序将需要写和读取大量的数据,但目前我正在用30个样本进行测试(婴儿步骤)。 因此、我将我的代码分为两部分:

第1)部分)将数据写入 FRAM:我调整了 gTxPacket 以纳入操作码(WREN 和写入)、起始地址和需要写入的数据。 此外、gTxPacket 采用单字节格式(uint8_t)。
我对来自 RXFIFO 及其回收站的数据不感兴趣。
写入部分看起来可以、我有一个在传输开始时计数为1的计时器。 我在示波器上检查了 CS、CLK 和 PICO。 CLK 很难看出、CS 在低电平时持续~16us、我知道在没有 DMA 的情况下写入16位需要~6.6us。

第2部分)从 FRAM 读取数据:我调整了 gTxDummy、以合并操作码(读取)、起始地址和需要写入的虚拟数据、从而生成时钟。 此外、gTxDummy 采用一字节格式(uint8_t)。
读取部分看起来不正常。 我对来自 RXFIFO 的数据非常感兴趣,但当我检查我的 gRxPacket 时,它在每个位置都有255 (0xFF)(我在 main ()的开头清除它)。 它应具有与 gTxPacket 相同的内容。

void SPI_send(void)
{
    // Part 1: writing data to FRAM

    /*
     * Configure DMA source, destination and size to transfer data from
     * gTxPacket to TXDATA. The DMA transfer will start when TX interrupt is
     * set, which it should already be set (which indicates that the SPI is
     * ready to transfer) so the DMA will begin this transfer when the channel
     * is enabled.
     */
    DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gTxPacket[0]);
    DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)(&SPI_0_INST->TXDATA));
    DL_DMA_setTransferSize(
        DMA, DMA_CH0_CHAN_ID, sizeof(gTxPacket) / sizeof(gTxPacket[0]));

    /*
     * Configure DMA source, destination and size from RXDATA to gRxPacket.
     * The DMA transfer will start when the RX interrupt is set, which happens
     * when the device receives data.
     */
    DL_DMA_setSrcAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(&SPI_0_INST->RXDATA));
    DL_DMA_setDestAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t) &gRxPacket[0]);
    DL_DMA_setTransferSize(DMA, DMA_CH1_CHAN_ID, ARRAY_SIZE);

    DL_GPIO_clearPins(GPIO_PORT, GPIO_CS_SPI_PIN);

    /*
     * The SPI TX interrupt is already set, indicating the SPI is ready to
     * transmit data, so enabling the DMA will start the transfer
     */
    DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
    DL_DMA_enableChannel(DMA, DMA_CH1_CHAN_ID);  

    /*
     * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
     * from gTxPacket to the SPI TXFIFO, and the DMA interrupt is triggered
     */
    while (false == isDMATXDataTransferred) {
        __WFE();
    }

    /*
     * Wait until the SPI has transmitted all the data and the TXFIFO
     * is empty
     */
    while (false == isSPIDataTransmitted) {
        __WFE();
    }

    /*
     * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
     * from SPI TXFIFO to gRxPacket, and the DMA interrupt is triggered
     */
    while (false == isDMARXDataTransferred) {
        __WFE();
    }

    /*
     * Optional SW breakpoint to check results.
     * If this example is used with the
     * spi_peripheral_repeated_fifo_dma_interrupts example,
     * the expected data that will be received in gRxPacket is
     * {'x', 0x2, 0x3, 0x4}, where 'x' starts at 0x0 and
     * should increment every time the Peripheral example
     * sends a new data packet.
     */
    //        __BKPT(0);

    isSPIDataTransmitted   = false;
    isDMATXDataTransferred = false;
    isDMARXDataTransferred = false;

    DL_GPIO_setPins(GPIO_PORT, GPIO_CS_SPI_PIN);

    // Part 2: reading data from FRAM

    // Sugestion from Chris e2e.ti.com/.../mspm0g3507-cant-get-spi-to-automatically-clock-in-receive-data-in-dma-mode
    
    /*
     * Configure DMA source, destination and size to transfer data from
     * tx_pbuf to TXDATA. The DMA transfer will start when TX interrupt is
     * set, which it should already be set (which indicates that the SPI is
     * ready to transfer) so the DMA will begin this transfer when the channel
     * is enabled.
     */
    DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gTxDummy[0]);
    DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)(&SPI_0_INST->TXDATA));
        DL_DMA_setTransferSize(
        DMA, DMA_CH0_CHAN_ID, sizeof(gTxDummy) / sizeof(gTxDummy[0]));

    NVIC_EnableIRQ(SPI_0_INST_INT_IRQN);

    // enable chip select
    DL_GPIO_setPins(GPIO_PORT, GPIO_CS_SPI_PIN);
   
    /*
     * The SPI TX interrupt is already set, indicating the SPI is ready to
     * transmit data, so enabling the DMA will start the transfer
     */
    DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);

    /*
     * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
     * from tx_pbuf to the SPI TXFIFO, and the DMA interrupt is triggered
     */
    while (false == isDMATXDataTransferred) {
        __WFE();
    }

    /*
     * Wait until the SPI has transmitted all the data and the TXFIFO
     * is empty
     */
    while (false == isSPIDataTransmitted) {
        __WFE();
    }

    // dummy read clears spi data waiting interrupt caused by spi tx
    DL_SPI_receiveData8(SPI_0_INST);
   
    /*
     * Configure DMA source, destination and size from RXDATA to rx_pbuf.
     * The DMA transfer will start when the RX interrupt is set, which happens
     * when the device receives data.
     */
    DL_DMA_setSrcAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(&SPI_0_INST->RXDATA));
    DL_DMA_setDestAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t) &gRxPacket[0]);
    DL_DMA_setTransferSize(DMA, DMA_CH1_CHAN_ID, ARRAY_SIZE);
    DL_DMA_enableChannel(DMA, DMA_CH1_CHAN_ID);

    /*
     * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
     * from SPI TXFIFO to rx_pbuf, and the DMA interrupt is triggered
     */
    /*while ((false == gDMARXDataTransferred) && (ARRAY_SIZE > 0)) {
        // generates sclks until ARRAY_SIZE requested number of bytes have been read
        DL_SPI_transmitData8(SPI_0_INST, 0x00);
    }*/
}

void SPI_0_INST_IRQHandler(void)
{
    switch (DL_SPI_getPendingInterrupt(SPI_0_INST)) {
        case DL_SPI_IIDX_DMA_DONE_TX:
            /* DMA is done transferring data from gTxPacket to TXFIFO */
            isDMATXDataTransferred = true;
            break;
        case DL_SPI_IIDX_TX_EMPTY:
            /* SPI is done transmitting data and TXFIFO is empty */
            isSPIDataTransmitted = true;
            break;
        case DL_SPI_IIDX_DMA_DONE_RX:
            /* DMA is done transferring data from RXFIFO to gRxPacket*/
            isDMARXDataTransferred = true;
        default:
            break;
    }
}

我查看了此处的其他文章(e2e.ti.com/.../mspm0g3507-cant-get-spi-to-automatically-clock-in-receive-data-in-dma-mode)。 我实施了它,但解决方案克里斯善意地建议也不工作。

我还检查了:

e2e.ti.com/.../mspm0g3507-dma-enabled-spi-communication-with-ads8684a

e2e.ti.com/.../mspm0g3507-spi-dma-receive-abnormal

e2e.ti.com/.../ads1258-ep-using-dma-with-ads1258

请,有人可以给我任何建议或建议,以修复我的阅读部分代码?
谢谢!
Andrea

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    WREN 需要在单独的事务中发送(由/CS 构成)。 从您的描述来看、这听起来您在一次交易中即可发送所有内容。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    嗨、Bruce、  

    嗯,我分离了 WREN ,仍然不使用克里斯的建议(从他的帖子)。

    然后、我开始做一些测试、我重复我用于写入以读取的相同代码(除了我使用 gTxDummy)。

    我附上一个数字,我"想"我可以看到写作部分的过程,但不是阅读。

    我将代码附在这里、如果您能运行代码并留下您的印象、那将非常好。 您可以保留该示例中的 syscfg 文件、只需添加一个计时器(每1秒中断一次)。

    #include "FRAM_SPI.h"
    #include "ti/driverlib/dl_adc12.h"
    #include "ti/driverlib/dl_comp.h"
    #include "ti/driverlib/dl_gpio.h"
    #include "ti/driverlib/dl_timera.h"
    #include "ti/driverlib/dl_timerg.h"
    //#include "ti/driverlib/dl_uart_main.h"
    #include "ti_msp_dl_config.h"
    #include <stdio.h>
    #include <string.h>
    
    #define ARRAY_SIZE 30
    
    void cleargRxPacket();
    void creategTxPacket();
    void creategTxDummy();
    void FRAM_WREN();
    void SPI_send();
    
    uint8_t gTxPacket[ARRAY_SIZE];
    uint8_t gRxPacket[ARRAY_SIZE];
    uint8_t gTxDummy[ARRAY_SIZE];
    uint8_t trash = 0;
    
    volatile bool isSPIDataTransmitted, isDMATXDataTransferred,
        isDMARXDataTransferred, isTransmitReady;
    
    int main(void)
    {
    
        isSPIDataTransmitted   = false;
        isDMATXDataTransferred = false;
        isDMARXDataTransferred = false;
        isTransmitReady = false;
    
        SYSCFG_DL_init();
    
        cleargRxPacket();
        creategTxPacket();
        creategTxDummy();
    
        DL_GPIO_clearPins(GPIO_PORT, GPIO_LED_1_PIN | GPIO_EN_MUX_PIN | GPIO_SEL_MUX_PIN); // necessary to access the FRAM
    
        DL_GPIO_setPins(GPIO_PORT, GPIO_CS_SPI_PIN);
        
        NVIC_EnableIRQ(SPI_TIMER_INST_INT_IRQN);
        NVIC_EnableIRQ(SPI_0_INST_INT_IRQN);
    
        while(1)
        { 
                      
            if(isTransmitReady){
                isTransmitReady = false;
                SPI_send();
            } else {
                __WFI();
            }
    
        }
    
    }
    
    // clear gRxPacket
    void cleargRxPacket(void) {
      for (uint16_t i = 0; i < ARRAY_SIZE; i++) {
        gRxPacket[i] = 0;
      }
    }
    
    // create gTxPacket
    void creategTxPacket(void) {
    
        gTxPacket[0] = 0x02; // op-code write
        gTxPacket[1] = 0x00; // LSB startAddress
        gTxPacket[2] = 0x00; // MSB startAddress
    
        uint16_t offset = 3;
        for (uint16_t i = offset; i < ARRAY_SIZE/2 ; i ++) {
    
            uint16_t aux = i;
            gTxPacket[2 * i - offset] = ((aux - offset) >> 8) & 0xFF; // MSB
            gTxPacket[2 * i - offset + 1] = (aux - offset) & 0xFF; // LSB
        }
    }
    
    // create gTxDummy
    void creategTxDummy(void) {
      for (uint16_t i = 3; i < ARRAY_SIZE; i++) {
    
        gTxDummy[0] = 0x03; // op-code read
        gTxDummy[1] = 0x00; // LSB startAddress
        gTxDummy[2] = 0x00; // MSB startAddress
        gTxDummy[i] = 0xFF; // dummy
      }
    }
    
    void FRAM_WREN(void)
    {
        uint8_t notUsed = 0;
    	DL_GPIO_clearPins(GPIO_PORT, GPIO_CS_SPI_PIN);
        DL_SPI_transmitDataBlocking8(SPI_0_INST, 0x06);
        while (DL_SPI_isBusy(SPI_0_INST));
        notUsed = DL_SPI_receiveDataBlocking8(SPI_0_INST);
    	DL_GPIO_setPins(GPIO_PORT, GPIO_CS_SPI_PIN);
    }
    
    void SPI_send(void)
    {
        // Part 1: writing data to FRAM
    
        FRAM_WREN(); // Send WREN first
    
        /*
         * Configure DMA source, destination and size to transfer data from
         * gTxPacket to TXDATA. The DMA transfer will start when TX interrupt is
         * set, which it should already be set (which indicates that the SPI is
         * ready to transfer) so the DMA will begin this transfer when the channel
         * is enabled.
         */
        DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gTxPacket[0]);
        DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)(&SPI_0_INST->TXDATA));
        DL_DMA_setTransferSize(
            DMA, DMA_CH0_CHAN_ID, sizeof(gTxPacket) / sizeof(gTxPacket[0]));
    
        /*
         * Configure DMA source, destination and size from RXDATA to gRxPacket.
         * The DMA transfer will start when the RX interrupt is set, which happens
         * when the device receives data.
         */
        DL_DMA_setSrcAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(&SPI_0_INST->RXDATA));
        DL_DMA_setDestAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t) &gRxPacket[0]);
        DL_DMA_setTransferSize(DMA, DMA_CH1_CHAN_ID, ARRAY_SIZE);
    
        DL_GPIO_clearPins(GPIO_PORT, GPIO_CS_SPI_PIN);
    
        /*
         * The SPI TX interrupt is already set, indicating the SPI is ready to
         * transmit data, so enabling the DMA will start the transfer
         */
        DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
        DL_DMA_enableChannel(DMA, DMA_CH1_CHAN_ID);  
    
        /*
         * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
         * from gTxPacket to the SPI TXFIFO, and the DMA interrupt is triggered
         */
        while (false == isDMATXDataTransferred) {
            __WFE();
        }
    
        /*
         * Wait until the SPI has transmitted all the data and the TXFIFO
         * is empty
         */
        while (false == isSPIDataTransmitted) {
            __WFE();
        }
    
        /*
         * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
         * from SPI TXFIFO to gRxPacket, and the DMA interrupt is triggered
         */
        while (false == isDMARXDataTransferred) {
            __WFE();
        }
    
        isSPIDataTransmitted   = false;
        isDMATXDataTransferred = false;
        isDMARXDataTransferred = false;
    
        DL_GPIO_setPins(GPIO_PORT, GPIO_CS_SPI_PIN);
    
        // Part 2: reading data from FRAM
       
        /*
         * Configure DMA source, destination and size to transfer data from
         * gTxDummy to TXDATA. The DMA transfer will start when TX interrupt is
         * set, which it should already be set (which indicates that the SPI is
         * ready to transfer) so the DMA will begin this transfer when the channel
         * is enabled.
         */
        DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gTxDummy[0]);
        DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)(&SPI_0_INST->TXDATA));
        DL_DMA_setTransferSize(
            DMA, DMA_CH0_CHAN_ID, sizeof(gTxDummy) / sizeof(gTxDummy[0]));
    
        /*
         * Configure DMA source, destination and size from RXDATA to gRxPacket.
         * The DMA transfer will start when the RX interrupt is set, which happens
         * when the device receives data.
         */
        DL_DMA_setSrcAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(&SPI_0_INST->RXDATA));
        DL_DMA_setDestAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t) &gRxPacket[0]);
        DL_DMA_setTransferSize(DMA, DMA_CH1_CHAN_ID, ARRAY_SIZE);
    
        DL_GPIO_clearPins(GPIO_PORT, GPIO_CS_SPI_PIN);
    
        /*
         * The SPI TX interrupt is already set, indicating the SPI is ready to
         * transmit data, so enabling the DMA will start the transfer
         */
        DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
        DL_DMA_enableChannel(DMA, DMA_CH1_CHAN_ID);  
    
        /*
         * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
         * from gTxPacket to the SPI TXFIFO, and the DMA interrupt is triggered
         */
        while (false == isDMATXDataTransferred) {
            __WFE();
        }
    
        /*
         * Wait until the SPI has transmitted all the data and the TXFIFO
         * is empty
         */
        while (false == isSPIDataTransmitted) {
            __WFE();
        }
    
        /*
         * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
         * from SPI TXFIFO to gRxPacket, and the DMA interrupt is triggered
         */
        while (false == isDMARXDataTransferred) {
            __WFE();
        }
    
        DL_GPIO_setPins(GPIO_PORT, GPIO_CS_SPI_PIN);
    }
    
    void SPI_0_INST_IRQHandler(void)
    {
        switch (DL_SPI_getPendingInterrupt(SPI_0_INST)) {
            case DL_SPI_IIDX_DMA_DONE_TX:
                /* DMA is done transferring data from gTxPacket to TXFIFO */
                isDMATXDataTransferred = true;
                break;
            case DL_SPI_IIDX_TX_EMPTY:
                /* SPI is done transmitting data and TXFIFO is empty */
                isSPIDataTransmitted = true;
                break;
            case DL_SPI_IIDX_DMA_DONE_RX:
                /* DMA is done transferring data from RXFIFO to gRxPacket*/
                isDMARXDataTransferred = true;
            default:
                break;
        }
    }
    
    void SPI_TIMER_INST_IRQHandler(void)
    {
        switch (DL_TimerG_getPendingInterrupt(SPI_TIMER_INST)) {
            case DL_TIMERG_INTERRUPT_ZERO_EVENT:
                isTransmitReady = true;
                DL_TimerG_stopCounter(SPI_TIMER_INST); // not sure it is necessary
                DL_TimerG_startCounter(SPI_TIMER_INST); // start counter 
                //printf("isTransmitReady! \n"); 
                printf("gTxPacket[24] = %d \n", gTxPacket[24]);
                printf("gRxPacket[24] = %d \n", gRxPacket[24]);
                break;
            default:
                break;
        }
    }

    我知道、您没有 FRAM、但有任何想法都会有所帮助!

    谢谢!

    Andrea

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您是否也尝试过如代码中所示的那样写入 FRAM、然后逐个读取每个字节、看看使用 DMA 将所有字节作为一个大块发送是否成功、然后再尝试读取一个大块?

    可能值得一看。 我看到,你已经尝试了单独的读写,因为块读和块写不能一起工作,一次尝试一个来隔离问题。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    我在做更多的测试、发现使用断点时有一些有趣的事情。

    第一次运行 SPI_SEND()时、写入和读取运行正常(下图)、 gRxPacket  与 gTxPacket 相同  (第191行)。

    第二次运行  SPI_SEND()时 、我正在   gRxPacket 中存储"trase" (第131行)。  我知道,这是愚蠢的。 我创建了一个 gRxTrash 来接收这些数据。 它现在是固定的。

    最有趣的是,在第一次跑步后,它不再起作用。 我在读取部分看不到 CS、CLK 和 POCI。 示波器数据与我之前发送的图类似。

    请,有人可以提供一些帮助来尝试修复为什么它只能工作一次?

    再次感谢、

    Andrea

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您是否正在使用商业分线板? 我对/wp 和/hold 的连接方式特别感兴趣。

    在 Adafruit 板上、他们被拉起来了、但几周前我们有人在这里做了一次突破、讨论的是浮动的。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    看起来您没有清除 读取事务之后的完成布尔(尤其是 isDMARXDataTransferred)。 这就意味着在对 SPI_send 的第二次调用时、您过早地提高/CS、这会截断写入、但 DMA/SPI 会继续运行。  

    我没有找到语句、但根据 DMA 与 MSP430的相似性、我期望 DMA 控制寄存器在 DMA 繁忙时有效地处于只读状态、因此绝不会发生读取事务。

    解决方法(如您所料)是在读取事务之后将这些变量设置回 false。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我一直在根据 TI 的应用手册 SLAA208为 SPI EEPROM 构建一个小库、因此我认为现在可以改进并发布它。

    它尚未大量使用、但已在 MB85RS1和 M95P32上运行。 也许您可以在这里使用:

    https://github.com/brucemckenney/spi-eeprom-mspm0

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    嗨、Bruce、

    "你,你是我的女人。" 我没进行猜测、您是对的、我忘记将变量设置回"false"。

    只需回答您的上一个问题、我们有自己的电路板、WP 和 HOLD 具有连接至3V3的上拉电阻器。

    非常感谢!

    Andrea

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    感谢 Bruce 的分享、
    看起来很酷、我将深入研究一下、看看我能用多少钱。 看起来像高级编码,我会尝试一下;)
    再次感谢、
    Andrea