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.

[参考译文] EK-TM4C123GXL:外部串行 NOR 闪存+ SSI + UDMA

Guru**** 2479915 points


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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/695770/ek-tm4c123gxl-external-serial-nor-flash-ssi-udma

器件型号:EK-TM4C123GXL

您好!

我已经有一个正常工作的中断驱动 SSI 来与 Winbond 闪存通信、最近我已经进入了 DMA 的领域。 我认为最好将其与 Winbond 代码相结合、以加快数据传输速度并降低 CPU 负载。 有人以前做过吗? 我在论坛上似乎找不到任何有关这方面的信息。

目前、我已经创建了一个 SPI_TRANSFCTION 函数、调用该函数时、将首先设置和传输命令/地址字节。 然后、在 DMA 传输完成后、SSI ISR 将被触发。 触发后、ISR 将设置另一个 DMA 传输、以便写入/读取我们感兴趣的数据字节。

例如、如果我要读取 Winbond 闪存的器件 ID 和制造商 ID。 闪存要求主器件发送 Wb_read_ID、后跟3个0x00字节、然后闪存将输出器件 ID +制造商 ID。 下面是一个用于说明传输的图:

XX =无关

//传输4个数据字节,需要两个字节的设备 id +制造商 id

TX                                RX

WB_READ_ID          XX --
0x00                            XX     |___ 在第一个 DMA 传输期间传输
0x00                            XX     |
0x00                            XX --

//主设备需要驱动时钟以允许将 man_id 和 dev_id 移出

0x00                            man_id --______ 在第二个 DMA 传输期间传输
0x00                            DEV_ID  __|

我的代码如下:

//通过首先传输 cmd / addr 字节来启动传输

//
//
//通过传输命令/地址字节来启动与闪存的通信
//
*************
void SSI_transfer (uint8_t * cmd_buf、uint32_t cmd_count、uint8_t * tx_buf、
uint32_t TX_COUNT、uint8_t * rx_Buf、uint32_t rx_count){

G_ui8SSITxBuf = TX_Buf;
G_ui8SSIRxBuf = Rx_Buf;

G_ui32SSITxCount = TX_COUNT;
G_ui32SSIRxCount = Rx_COUNT;

//
//为 TX 和 RX 通道启用 uDMA 接口。
//
SSIDMAEnable (SSI0_BASE、SSI_DMA_RX | SSI_DMA_TX);

//
//uDMA SSI0 RX
//

//
//将 UDMA SSI0RX 通道的属性置于已知状态。 这些
默认情况下、//应已禁用。
//
uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0RX、
UDMA_ATTR_USEBURST |
UDMA_ATTR_ALTSELECT |
(uDMA_attr_high_priority | uDMA_attr_REQMASK);

//
//为配置主控制结构体的控制参数
// SSIORX 通道。
//
uDMAChannelControlSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、
UDMA_SIZE_8 | UDMA_SRC_INC_NONE |
UDMA_DST_INC_8 | UDMA_ARB_4);
//
//设置 SSI0RX 通道的传输参数
//
uDMAChannelTransferSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、
UDMA_MODE_BASIC、
(void *)(SSI0_BASE + SSI_O_DR)、
CMD_BUF、
CMD_COUNT);


//
//uDMA SSI0 TX
//

//
//将 UDMA SSI0TX 通道的属性置于已知状态。 这些
默认情况下、//应已禁用。
//
uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0TX、
UDMA_ATTR_ALTSELECT |
UDMA_ATTR_HIGH_PRIOR|
UDMA_ATTR_REQMASK);

//
//配置 SSI0 TX 的控制参数。
//
uDMAChannelControlSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、
UDMA_SIZE_8 | UDMA_SRC_INC_8 |
UDMA_DST_INC_NONE | UDMA_ARB_4);


//
//设置 uDMA SSI0 TX 通道的传输参数。 这将会
//配置传输源和目的以及传输大小。
//使用基本模式是因为外设正在进行 UDMA 传输
//请求。 源是 TX 缓冲区、目的是 SSI0
//数据寄存器。
//
uDMAChannelTransferSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、
UDMA_MODE_BASIC、
CMD_BUF、
(void *)(SSI0_BASE + SSI_O_DR)、
CMD_COUNT);

//开始发送命令/地址字节并弹出返回字节
CHIP_SELECT (~GPIO_PIN_3);

//
//现在,UDMA SSI0 TX 和 RX 通道都要以启动 A 为底
//传输。 一旦启用通道、外设将会启动
//发出传输请求,数据传输将开始。
//
uDMAChannelEnable (UDMA_CHANGE_SSI0RX);
uDMAChannelEnable (UDMA_CHANGE_SSI0TX);

//
//启用 SSI0 DMA TX/RX 中断。 
//我是否还需要包括 SSI_DMATX? // SSIIntEnable (SSI0_BASE、SSI_DMARX); //等待 SSI 芯片选择无效、表示结束 //传输。 while (!(GPIOPinRead (GPIO_Porta_base、GPIO_PIN_3)& GPIO_PIN_3)); }

// ISR

void SSI0IntHandler (void){
GPIOPinWrite (GPIO_PORTB_BASE、GPIO_PIN_2、GPIO_PIN_2);//指示 ISR 条目 
uint32_t ui32Status; uint32_t ui32模式; ui32Status = SSIIntStatus (SSI0_BASE、1); SSIIntClear (SSI0_BASE、ui32Status); if (g_cmd_sent){ CHIP_SELECT (GPIO_PIN_3); } 否则{ //在此处输入意味着命令/地址字节已发送 //为我们感兴趣的数据设置另一个传输 G_CMD_SENT = 1;//发送命令和 addr 的信号,以便在接下来发送 // ISR 入口将拉高 SS 线路 ui32Mode = uDMAChannelModeGet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT); // RX DMA 完成 //如果是写入、g_rx_dst_inc 将为 UDMA_dst_inc_none //如果读取、g_rx_dst_inc = uDMA_dst_inc_8 if (ui32Mode = udma_mode_stop){ uDMAChannelControlSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、 UDMA_SIZE_8 | UDMA_SRC_INC_NONE | G_Rx_DST_inc | UDMA_ARB_4); uDMAChannelTransferSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、 UDMA_MODE_BASIC、 (void *)(SSI0_BASE + SSI_O_DR)、 G_ui8SSIRxBuf、 g_ui32SSIRxCount); uDMAChannelEnable (UDMA_CHANGE_SSI0RX); } // TX DMA 完成 //如果是写入、g_TX_DST_inc 将为 UDMA_SRC_INC_8 //如果读取、g_TX_src_inc = UDMA_SRC_INC_NONE if (!uDMAChannelIsEnabled (UDMA_CHANGE_SSI0TX)){ uDMAChannelControlSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、 UDMA_SIZE_8 | G_TX_src_inc | UDMA_DST_INC_NONE | UDMA_ARB_4); uDMAChannelTransferSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、 UDMA_MODE_BASIC、 G_ui8SSITxBuf、 (void *)(SSI0_BASE + SSI_O_DR)、 g_ui32SSITxCount); uDMAChannelEnable (UDMA_CHANGE_SSI0TX); } } GPIOPinWrite (GPIO_PORTB_BASE、GPIO_PIN_2、~GPIO_PIN_2);//指示 ISR 退出 }

因此、我的问题是、当我单步调试程序时、我将获得正确的 man_id 和 dev_id (0xef 和0x17)、但当我执行完整运行时、ID 是错误的(0x90、0xff)。 以下是逻辑分析仪上完整运行的屏幕截图:




如屏幕截图中所示、SS 线路被拉至高电平的时间太短、我不确定原因。

为了确保 DMA 工作正常、我还尝试在一个 DMA 传输中传输所有数据、而不是在两个 DMA 传输中传输(如前所述)、这为我提供了正确的结果、如下面的屏幕截图所示:

我是否缺少或不正确理解 UDMA?

此致、提前感谢您

Jacky

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    只是第一个帖子的后续操作、我想要拆分 cmd/addr 和实际数据传输的原因是我不希望来自 cmd/ addr 字节的返回数据字节与目标数据缓冲区发生混乱。 例如、如果在闪存上执行读取操作、如果我们在单个 DMA 传输中传输 cmd/addr 和实际的相关数据、则用户提供的读取目标缓冲区将包含4个额外字节(从 cmd/addr 返回的字节)、这有点不方便。

    这是我提出的使 API 更易于使用的解决方案。 是否有更好的方法来解决这个问题? 我真的想听到别人的声音。

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

    为什么您的接收通道使用 cmd_Buf 和 cmd_count? 我认为 CMD_BUF 和 CMD_COUNT 用于 TX 通道。

    uDMAChannelTransferSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、
    UDMA_MODE_BASIC、
    (void *)(SSI0_BASE + SSI_O_DR)、
    CMD_BUF、
    CMD_COUNT);

    在我看来、尽管您当前的设置应该也能正常工作、但 UDMA 外设散聚模式似乎非常适合您的应用要求、只要您只想读取制造和器件 ID 即可。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Charles、

    我对 RX 使用 cmd_Buf、因为我不想创建额外的变量来存储从 cmd/addr 传输返回的字节。 此外、由于我们接收的数据字节数量与我们传输的数据字节数量相同、因此 TX 和 RX 共享相同的 CMD_COUNT 传输大小。

    BTW、在我将所有内容放入一个 DMA 传输中时、它为什么会起作用、而在分解为多个 DMA 传输时、它会失败? (如原始帖子中显示的屏幕截图所示)
    或者、我想我应该问、我的 DMA 设置或配置中是否有任何会导致传输问题的东西。

    在平均时间内、我将研究散聚方法并保持帖子的更新。

    谢谢、

    Jacky

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

    [引用用户="Charles Tsaa"]

    在我看来、尽管您当前的设置应该也能正常工作、但 UDMA 外设散聚模式似乎非常适合您的应用要求、只要您只想读取制造和器件 ID 即可。

    [/报价]

    您是否说过、如果我要读取/写入大量数据、那么我的方法将不会那么高效?

    谢谢、

    Jacky

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

    我想、如果您只想读取制造和器件 ID、那么最简单的方法是、您最初只需将一个 DMA 通道配置为传输六个字节的数据。 您只需忽略来自 SPI 的 MISO 上的前4个字节、因为前4个字节与 cmd 周期相对应。 ID 位于第五个字节和第六个字节上。 为什么要将其分解为代码中所示的两个不同的 DMA 传输?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Charles、

    我还打算对闪存执行读取/写入/擦除操作。 ID 读数只是为了确保我的基本 SSI 传输函数正常工作。

    如果我计划这样做、您会建议什么? 我是否可以使用基本模式完成读取/写入/擦除?


    谢谢、

    Jacky

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我不熟悉您的特定 NOR 闪存。 如您所示、读取制造/器件 ID 需要六个字节的 cmd 和数据周期。 写入操作和擦除操作怎么样? 您如何为这些操作提供命令和数据? 读取/写入/擦除是三个独立的操作、我认为外设散聚非常适合。 对于这三个操作、您将首先在存储器中定义三个副 DMA 控制结构体、当检测到来自外设的请求时、μ μDMA 控制器将使用主控制结构体从列表中复制一个条目到副控制结构体、然后执行传输。 有关详细信息、请参阅数据表。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    [引用用户="Charles Tsaa"]

    为什么要将其分解为代码中所示的两个不同的 DMA 传输?

    [/报价]

    因此、我希望将其分解为两个不同的 DMA 传输的原因是 、我不希望从发送 cmd / addr 字节返回的数据字节与目标数据缓冲区相混乱。 例如、在我的示例中、闪存读取函数必须如下所示(接受用户的三个参数并返回 void。 只要读取函数参数和返回类型保持不变、就可以修改函数中的内容)

    void my_SPI_read (uint32_t addr、uint32_t num_Byte、uint8_t * dst){
    uint8_t cmd[4];
    
    CMD[0]= WW_READ_DATA;
    CMD[1]=(addr >> 16)& 0xFF;
    CMD[2]=(addr >> 8)& 0xFF;
    CMD[3]=(addr >> 0)& 0xFF;
    
    G_Rx_DST_inc = UDMA_DST_INC_8;
    G_TX_src_inc = UDMA_SRC_INC_NONE;
    
    uint8_t TX_dummy = 0x00;
    
    SSI_transfer (cmd、4、&TX_dummy、num_Byte、dst、 num_Byte);
    
    while (chip_busy()& 1);
    } 

    我的想法是、如果我将 cmd / addr 传输放入一个单独的 DMA 传输中、那么我的目标数据缓冲区将不包含从发送 cmd / addr 返回的字节。

    我希望此函数的用户只需在闪存中输入起始地址、要读取的字节数、并提供一个目标数据缓冲区(缓冲区大小=要读取的字节数)来存储读取的数据。

    例如、 用户希望从 addr 0x00开始读取256字节、那么目标数据缓冲区将只包含256字节的数据(不包含发送 cmd / addr 的返回字节)

    我希望这能让我的问题更清楚。 在这种情况下、您是否认为散聚方法更好(可能将 cmd/addr 作为一个任务、而将其余任务作为另一个任务?) 或者、您还有其他方法可以解决这个问题吗? 如果是、您将如何解决此问题?

    非常感谢您的帮助!

    此致、

    Jacky

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Jacky、
    如果您将来需要更复杂的传输要求、散聚模式将起作用、并且将成为更好的解决方案。 现在、您可能需要查看乒乓模式。 在乒乓模式下、您仍将设置主控制结构体和副控制结构体。 在主结构中、目标是将 CMD/ADDRESS 传输到闪存器件。 您将为主结构体设置一个目标缓冲区(让我们将其称为 bufferA)、但存储在 bufferA 中的数据将被忽略。 主结构体完成后、UDMA 将自行触发副控制结构体。 副控制结构体被设置为传输将被存储在 bufferB 中的 N 个数据字节。 bufferA 和 bufferB 是分开的、如果这是您的担忧、则无需担心将它们混淆在一起。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Charles、

    因此、我认为我之前发布的代码不起作用是因为 DMA 处于基本模式、这会在重新加载和下一次传输之间留下死区时间?

    此致、

    Jacky

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

    您好、Charles、

    我已经为 DMA 传输设置了两个主要结构(一个用于 cmd / addr、一个用于实际数据)和两个备用结构(一个用于 cmd / addr、一个用于实际数据)、并且我与闪存的通信正常。

    假设我的数据长度不会超过1024、这意味着我不会重新加载、正确退出 DMA 的最佳方法是什么? 到目前为止、我正在检查备用结构体的 SSIRX 通道、以在 ISR 中获取停止模式信号、如下所示:

    void SSI0IntHandler (void){
    GPIOPinWrite (GPIO_PORTB_BASE、GPIO_PIN_2、GPIO_PIN_2);//指示 ISR 条目
    
    uint32_t ui32Status;
    uint32_t ui32模式;
    
    ui32Status = SSIIntStatus (SSI0_BASE、1);
    
    SSIIntClear (SSI0_BASE、ui32Status);
    
    ui32Mode = uDMAChannelModeGet (UDMA_CHANGE_SSI0RX | UDMA_ALT_SELECT);
    
    //数据传输完成,将 CS 拉至高电平
    if (ui32Mode = udma_mode_stop){
    IntDisable (INT_UDMAERR);
    IntDisable (INT_SSI0);
    CHIP_SELECT (GPIO_PIN_3);
    }
    
    GPIOPinWrite (GPIO_PORTB_BASE、GPIO_PIN_2、~GPIO_PIN_2);//指示 ISR 条目
    } 

    这是一种安全的方法、还是应该在实际停止之前(通过将 SS 线路拉为高电平)检查所有通道是否处于停止模式?

    谢谢、

    Jacky

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

    至于用您拥有的数据结束 DMA 传输、我没有发现任何问题。 我唯一的问题是为什么要设置两个不同的通道? 我认为一个乒乓通道就足够了、在主通道传输 cmd/addr、而副通道传输数据。 也许您的要求与简单的 NOR 闪存读取命令不同。 如果是这种情况、则如果您证明它可以正常工作、则它应该起作用。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Charles、

    好的。 我认为我不能正确理解 DMA 交替模式。 下面是我的当前设置:

    TX (UDMA_CHANGE_SSI0TX)                               RX (UDMA_CHANGE_SSI0RX)

    主结构(发送命令/地址)   --- >  主结构(从发送 cmd/addr 接收返回字节)   

    备用结构(发送 N 个字节的数据)--- > 备用结构(接收 N 个字节的数据)

    下面是我的 DMA 设置代码:

    void SSI_transfer (uint8_t * cmd_buf、uint32_t cmd_count、uint8_t * tx_buf、
    uint8_t * rx_Buf、uint32_t data_count){
    
    //
    //为 TX 和 RX 通道启用 uDMA 接口。
    //
    SSIDMAEnable (SSI0_BASE、SSI_DMA_RX | SSI_DMA_TX);
    
    //
    //uDMA SSI0 RX
    //
    
    //
    //将 UDMA SSI0RX 通道的属性置于已知状态。 这些
    默认情况下、//应已禁用。
    //
    uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0RX、
    UDMA_ATTR_USEBURST |
    UDMA_ATTR_ALTSELECT |
    (uDMA_attr_high_priority | uDMA_attr_REQMASK);
    
    //
    //为配置主控制结构体的控制参数
    // SSIORX 通道。
    //
    uDMAChannelControlSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、
    UDMA_SIZE_8 | UDMA_SRC_INC_NONE |
    UDMA_DST_INC_8 | UDMA_ARB_4);
    
    //
    //为的替代控制结构配置控制参数
    // SSIORX 通道。
    //
    uDMAChannelControlSet (UDMA_CHANGE_SSI0RX | UDMA_ALT_SELECT、
    UDMA_SIZE_8 | UDMA_SRC_INC_NONE |
    G_Rx_DST_inc | UDMA_ARB_4);
    
    //
    //设置 SSI0RX 通道(主)的传输参数
    //
    uDMAChannelTransferSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、
    UDMA_MODE_PINGONG、
    (void *)(SSI0_BASE + SSI_O_DR)、
    CMD_BUF、
    CMD_COUNT);
    
    //
    //设置 SSI0RX 通道的传输参数(备选)
    //
    uDMAChannelTransferSet (UDMA_CHANGE_SSI0RX | UDMA_ALT_SELECT、
    UDMA_MODE_PINGONG、
    (void *)(SSI0_BASE + SSI_O_DR)、
    RX_Buf、
    DATA_COUNT);
    
    
    //
    //uDMA SSI0 TX
    //
    
    //
    //将 UDMA SSI0TX 通道的属性置于已知状态。 这些
    默认情况下、//应已禁用。
    //
    uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0TX、
    UDMA_ATTR_ALTSELECT |
    UDMA_ATTR_HIGH_PRIOR|
    UDMA_ATTR_REQMASK);
    
    //
    //配置 SSI0 TX 的控制参数。
    //
    uDMAChannelControlSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、
    UDMA_SIZE_8 | UDMA_SRC_INC_8 |
    UDMA_DST_INC_NONE | UDMA_ARB_4);
    
    //
    //配置 SSI0 TX 的控制参数。
    //
    uDMAChannelControlSet (UDMA_CHANGE_SSI0TX | UDMA_ALT_SELECT、
    UDMA_SIZE_8 | G_TX_src_inc |
    UDMA_DST_INC_NONE | UDMA_ARB_4);
    
    //
    //设置 uDMA SSI0 TX 通道的传输参数。 这将会
    //配置传输源和目的以及传输大小。
    //使用基本模式是因为外设正在进行 UDMA 传输
    //请求。 源是 TX 缓冲区、目的是 SSI0
    //数据寄存器。
    //
    uDMAChannelTransferSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、
    UDMA_MODE_PINGONG、
    CMD_BUF、
    (void *)(SSI0_BASE + SSI_O_DR)、
    CMD_COUNT);
    
    uDMAChannelTransferSet (UDMA_CHANGE_SSI0TX | UDMA_ALT_SELECT、
    UDMA_MODE_PINGONG、
    TX_Buf、
    (void *)(SSI0_BASE + SSI_O_DR)、
    DATA_COUNT);
    
    //发送命令/地址字节和弹出返回字节
    CHIP_SELECT (~GPIO_PIN_3);
    
    //
    //现在,UDMA SSI0 TX 和 RX 通道都要以启动 A 为底
    //传输。 一旦启用通道、外设将会启动
    //发出传输请求,数据传输将开始。
    //
    uDMAChannelEnable (UDMA_CHANGE_SSI0RX);
    uDMAChannelEnable (UDMA_CHANGE_SSI0TX);
    
    //等待 SSI 芯片选择无效、表示结束
    //传输。
    while (!(GPIOPinRead (GPIO_Porta_base、GPIO_PIN_3)& GPIO_PIN_3));
    } 

    [引用用户="Charles Tsaa"]

    我唯一的问题是为什么要设置两个不同的通道? 我认为一个乒乓通道就足够了、在主通道传输 cmd/addr、而副通道传输数据。

    [/报价]

    我并不是完全理解这将如何使用一个通道来工作。 您是否说一个结构能够同时进行发送/接收? 我是否在 ISR 中重新设置传输? 请给我启迪!

    此致、

    Jacky

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Jacky、
    我看到您在做什么、它应该起作用。 对于 TX 端、您只需为副控制结构体发送虚拟数据。 N 字节是否为固定长度? 您之前尝试仅读取2字节的制造/设备 ID。 如果需要读取可变字节、则需要使用正确的传输大小重新配置替代结构。 我认为您还需要根据您要向闪存器件提供的命令重新配置 cmd/addr 的主结构。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Charles、

    N 字节是变量。 我的目标是使闪存读取、写入、擦除... ETC API 利用 DMA 传输来降低 CPU 负载。 如果它运行良好、我甚至会考虑在闪存文件系统上使用这些 API!

    例如、我的闪存读取 API 会让用户输入读取的起始地址(addr)、要读取的字节数(num_Bytes)以及提供一个用于存储数据读取(dst)的目标缓冲区。  

    void my_SPI_read (uint32_t addr、uint32_t num_Byte、uint8_t * dst){
    
    uint8_t cmd[4];
    
    CMD[0]= WW_READ_DATA;
    CMD[1]=(addr >> 16)& 0xFF;
    CMD[2]=(addr >> 8)& 0xFF;
    CMD[3]=(addr >> 0)& 0xFF;
    
    G_Rx_DST_inc = UDMA_DST_INC_8;
    G_TX_src_inc = UDMA_SRC_INC_NONE;
    
    uint8_t TX_dummy = 0x00;
    
    SSI_transfer (cmd、4、&TX_dummy、dst、num_Byte);
    
    while (chip_busy()& 1);
    } 

    如果字节数超过1024、我只需在 ISR 中重新加载即可。

    那么、我想、我的 DMA 设置对于我要做的事情来说是可以的? 您是否会发现我应该关注的目标可能存在的问题?

    此致、

    Jacky

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Jacky、
    我想不出您的建议有什么问题。 如果你已经尝试过你的建议,如果它在工作,我认为你很好。