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.
工具/软件:Linux
我正在使用 DMA 为 MCASP 提供服务、并使用已知的正弦波输入来验证设置。 我在波形中以随机间隔获得不连续性、这看起来像是丢失的数据。 发生这种情况时、MCASP 不会指示溢出或 DMA 错误。
我看到了以下描述类似行为的文章:
https://e2e.ti.com/support/dsp/omap_applications_processors/f/42/t/150666
上述帖子的解决方案是修复初始化序列中遗漏的内容。
在本例中、我在使用发送时钟时将数据计时到 MCASP 中(ACLKXCTL 异步位被清零)。 TRM 章节22.3.12.2指示接收寄存器应在发送寄存器之前置位。 但是、它还指示是否使用外部时钟、它必须在初始化之前运行。 如果发送部分为接收部分计时、是否应首先配置接收部分? 在这个序列中的哪个点应该配置 RFIFOCTL 和 REVTCTL?
Mark、
感谢您的回答。 根据您链接的示例、我非常确定我使用的初始化代码是正确的。
有关我的设置的更多信息。 我使用 ARM 内核配置 DMA、将数据从 MCASP 移动到 PRU 共享存储器中。 DMA 回调用于中断 PRU、以通知其数据已就绪。 PRU 会处理数据并将其移至 PRU 共享存储器中的另一个缓冲区。 达到某个阈值后、PRU 会配置并启动 DMA 传输、以将处理的数据移动到 DDR 中。 McASP DMA 的初始化基于 sound/soc/cDaVinci / Davinci-McAP.c、而 PRU DMA 设置基于 PRU 软件支持包中的 examples/AM335x/PRU_edmaConfig。 MCAASP 和 PRU 都使用 EDMA TC0。 ARM 内核和 PRU 之间的通信由 Remoteproc/RPMsg 完成。
这在99%的时间内都能正常工作、但我确实会不断地出现间歇性数据丢失的情况。 下图显示了三个压降组合在一起、形成了35000个点的完美运行。
Nick、
我已验证 PRU 到 DDR DMA 传输在具有代表性的工作条件下是否正常运行。 我是通过在程序分配后将 DDR 内容归零来实现这一点的。 McASP 到 PRU DMA 的运行正常、但我将已知值和模式写入传出的 PRU 缓冲区、而不是处理传入数据。 PRU 至 DDR DMA 被正常触发。 采集完成后、我将结果与已知输入进行了比较。 数据符合我的预期。
我的一位同事帮助我验证了操作例程是否按预期运行且不会损坏数据。
因此、这会留下 McASP 到 PRU DMA 的传输。 我已使用与输出不匹配的已知输入。 但是、为了尝试其他操作、我开始使用 PRU 周期计数器来计时采集。 我在开始时启动周期计数器、并在接收到最后一个 McASP 到 PRU 传输后读取它。 我看到的是高于预期的周期计数、在较长的运行时间内更明显。 我知道、由于 PRU 必须轮询中断、处理数据以及与 EDMA 进行交互、因此会有一些额外的延迟。 但是、我的假设是、此延迟很小、不同规模的采集之间应相当一致。 例如、我进行了一次采集、需要20016 MCASP 到 PRU DMA 传输、但周期计数器指示20129传输发生了足够的时间。 需要2016年转账的跑步仅需2016年转账。 在我看来、这表明在较长的采集过程中从未发生过某些转移。 我确实启用了 RDMAERR 和 ROVRN 中断、但它们永远不会被触发。 我还应该尝试其他什么?
我提前对这本小说表示歉意。 我一直在尝试找到一种方法
描述我的设置、而不会出现完整的信息过载。 以下是
不完整、但我认为这应该让您对我的设置有一个很好的了解。 我是
包括一些来自内核模块和 PRU 的代码、我认为这些代码很有用。
时钟源可以是内部24MHz 功能时钟、也可以是外部时钟
36.864MHz 振荡器。 正如我之前提到的、我们使用的是传输时钟
由于引脚复用限制、AHCLKX/ACLKX/FSX 取代了接收时钟。 。
通过从器件解析的简单 GPIO 线启用或禁用振荡器
树。 无论时钟源如何、我都看到相同的行为、即使是使用
已移除振荡器的主板。
有关 DMA 设置的信息。 我正在使用位于 PRU 共享存储器中的环形缓冲器
包含16个位置。 从 McASP 到 PRU 的 DMA 被配置为循环
使用标准 dmaEngine API 进行传输。 循环传输的时间
缓冲段有一个专用的 EDMA 参数、此参数被连接至下一个缓冲器
位置。 这些传输将运行、直到显式停止。 我有 DMA
配置为在每次传输完成后中断。 回调函数
将完成的 DMA 传输总数写入 PRU 共享中的某个位置
存储器、然后写入 PRU INTC SISR 寄存器以通知 PRU 数据
就绪。
每个 DMA 传输都包含10个帧、其中包含6个32位 tdm 插槽。 最大数据
速率为144kHz、因此在最坏的情况下、我们得到的 DMA 传输为240字节
(10帧* 6个插槽* 4字节)每~69us 一次。 在环形缓冲器中具有16段
旧值将在~1.1ms 内被覆盖。 PRU 的时钟频率为200MHz
这会在每个传入传输中提供1388个时钟周期的最坏情况、以解码和
将数据移动到 PRU 共享存储器中的环形缓冲器中。 缓冲段时
包含已处理数据已满 PRU 发起 DMA 传输以移动数据
从 PRU 共享存储器流出并进入 DDR 存储器。
PRU 维护两个 DDR 地址表。 一个表加载时
处理的数据 PRU 通过 rpmsg 通知 ARM 内核并切换到另一个内核
表。 然后、ARM 内核使用有效的 DDR 地址和来重新填充空表
使用 rpmsg 告知 PRU 已加载新的 DDR 地址。
内核模块通过以下函数控制 McASP:
#define words 每帧 6 #define DMA_XFR_FRAes 10 #define DMA_XFR_WORD_SIZE sizeof (int32_t) #define DMA_XFR_words (dma_XFR_FRAMES * word_per_FRAME) #define dma_XFR_Bytes (dma_XFR_WORD * dma_XFR_WORD_SIZE) #define dma_ring 缓冲区插槽16 #define dma_ring 缓冲区字(dma_XFR_WORD * dma_ring 缓冲区插槽) #define dma_ring 缓冲区字节(dma_XFR_boos_docks *) static cirt_clus_clus_clus_clus_cockpet_clus_clocks (dma_clus_clus_clus_clus_clus_clus_clus_clus_clust ) 16 否则,如果(ASP->xosc.present){ GPIO_SET_VALUE (ASP->xosc.GPIO,0); }/* 1. 软件复位*/ McASP_WRITE (ASP、GBLCTL_OFFSET、0); /* 2a。 设置调试行为*/ McASP_SET_BITS (ASP、PWRIDLESYSCR1D_OFFSET、IDLEMODE (1)); /* 2b。 RX 寄存器*/ McASP_WRITE (ASP、RMASK_OFFSET、0xFFFF); McASP_WRITE (ASP、RFMT_OFFSET、 XRRVRS | XRSSZ (0xF); McASP_WRITE (ASP、AFESRCTL_OFFSET、XRMOD (8)| FSXRM); McASP_WRITE (ASP、 ACLKRCTL_OFFSET、CLKXRP | CLKXRM | CLKXRDIV (aclkdiv); McASP_WRITE (ASP、AHCLKRCTL_OFFSET、HCLKXRM | HCLKXRDIV (ahclkdiv))); IF (USE_EXL_osc) McASP_WRITE (ASP、HCLKRRM) AHCLKXCTL_OFFSET、0); McASP_WRITE (ASP、RTDM_OFFSET、MCASP_TDM_SLOT_MASK); McASP_WRITE (ASP、RINTCTL_OFFSET、XRDMAERR | ROVRN); // 2c。 TX 寄存器*/ McASP_WRITE (ASP、XMASK_OFFSET、0xffffffff); McASP_WRITE (ASP、XFMT_OFFSET、 XRSSZ (0xF); McASP_WRITE (ASP、AFSXCTL_OFFSET、XRMOD (8)| FSXRM); McASP_WRITE (ASP、 ACLKXCTL_OFFSET、CLKXRM | CLKXRDIV (aclkdiv); McASP_WRITE (ASP、AHCLKXCTL_OFFSET、HCLKXRM | HCLKXRDIV (ahclkdiv)); if (use_ext_osc) McASP_write (ASP、AHCLKXCTL_offset、0); /* 2D。 串行器*/ McASP_WRITE (ASP、SRCTL_OFFSET (0)、DISMOD (2)| SRMOD (2)); McASP_WRITE (ASP、SRCTL_OFFSET (1)、 0); McASP_WRITE (ASP、SRCTL_OFFSET (2)、0); McASP_WRITE (ASP、 SRCTL_OFFSET (3)、0); /* 2e。 全局寄存器*/ McASP_CLR_BITS (ASP、PFUNC_OFFSET、AHCLKX | ACLKX | AFSX); McASP_SET_BITS (ASP、Pdir_offset、ACLKX | AFSX); IF (!use_ext_osc) McASP_SET_BITS (ASP、 Pdir_offset、AHCLKX); /* 2f。 跳过 dit 寄存器*/ /* 3. 启动高频时钟*/ McASP_SET_gblctl_bits (ASP、GBLCTL_OFFSET、XHCLKRST | RHCLKRST); // 4. 起始位时钟*/ McASP_SET_gblctl_bits (ASP、GBLCTL_OFFSET、XCLKRST | RCLKRST); // 5. 设置 DMA */ McASP_CLR_BITS (ASP、REVTCTL_OFFSET、XRDATDMA); McASP_WRITE (ASP、RFIFOCTL_OFFSET、 XRNUMEVT (DMA_XFR_COMS)| XRNUMDMA (1)); } 静态空 McASP_START (结构 McASP * ASP) { //清除然后启用 RFIFO */ McASP_CLR_BITS (ASP、RFIFOCTL_OFFSET、XRENA); McASP_SET_BITS (ASP、RFIFOCTL_OFFSET、XRENA) XRENA); /* 6. 激活串行器*/ McASP_WRITE (ASP、XSTAT_OFFSET、0xFFFF); McASP_WRITE (ASP、RSTAT_OFFSET、 0xFFFF); McASP_SET_gblctl_bits (ASP、GBLCTL_OFFSET、RSRCLR); McASP_WRITE (ASP、 RBUF_OFFSET、0); /* 7. 跳过*/ /* 8. 将状态机从复位中释放*/ McASP_SET_gblctl_bits (ASP、GBLCTL_OFFSET、RSMRST | XSMRST); /* 9. 从复位中释放帧同步发生器*/ McASP_SET_gblctl_bits (ASP、GBLCTL_OFFSET、RFRST | XFRST); } 静态空 McASP_STOP (struct McASP *ASP) { McASP_WRITE (ASP、CTL_OFFSET、0); McASP_WRITE (GBL、RSTAT_OFFSET、 0xFFFF); /* AHCLKX 在 GBLCTL 复位后未选通。 为了防止时钟泄漏、 *将所有 McASP 引脚重新配置为输入。 */ McASP_WRITE (ASP、Pdir_offset、0); IF (ASP->clksrc =CLKSRC_XOSC && ASP->xosc.present){ GPIO_set_value (ASP->xosc.GPIO、0); }
内核模块 DMA 设置如下所示:
静态空 Rx_DMA_callback (void *数据) { struct pru_rproc * PRU =(struct PRU_rproc *) data; PRU->DMA_XFRS++; writel_relaxed (PRU->DMA_XFRS、PRU->XFR_count); writel_relaxed (PRU->sys_relaxed、event) PRU->INTC_sir); } 静态空 Rx_DMA_STOP (struct McASP *ASP) { if (ASP->dma.chan){ dmaenginer_terminal_sync (proc->dma.chan); dma_release_channel (ASP->dma.chan); ASP->dma.chan = 0; } 静态&cookie_dma_start(struct Mcdma_terminal_sync->dma_delease_channels(asdma_daSP->dma_daSP_channels_dass->dma_dass_channels_dass_channels){>dma_dma_dma_dma_dma_dasps_dass->dma_dass_channels\cates\cates_dass_dma_dma_da return -EINVAL; } dma->chan = dma_request_chan (asp->dev、"rx"); if (is_ERR (dma->chan)){ if (ptr_ERR (dma->chan)!=-ENODEV)}{ dev_err (asp->dev、"ca 不能获得 sizRx 通道 (\n");ptr (dma-set);dma (dma);edma-config);tr (edma-set);tma (dma) CONFIG.src_addr = asp->L3_addr + RBUF_offset; CONFIG.src_addr_width = dma_XFR_word_size; CONFIG.src_maxburst = dma_XFR_words; if (dmaenger_slave_config (dma->chan、&config)))){ DEV_err (asp->dev、 "dmaenginer_slave_config 失败\n"); dma_release_channel (dma->chan); return -EINVAL; } desc = dmaengine_prep_dma_dma_dma_ciral (dma->chan、 dma->dma->dma->dma->dma-> dest、 dma_ring 缓冲区_Bytes、 dma_XFR_Bytes、dma_dma_dev_TO dma_prep_interrupt); if (!desc){ dev_err (asp->dev、"dmaenger_prep_dma_cyclic ()失败\n"); return -ENOMEM; } desc->callback = rx_dma_callback; desc->terminate_param = PRU; PRU->cha_XfRS = 0 (dma_domer_return);dmaid_dma-dma引擎(dma_dma_domb);(dmaid_dma_dome+ dmaid_dma-dma-dma-dma-dma-dma-dma_domt);dma-dma-dma-dma-dma-dma-dma-dma-dma- dma_async_issend_pending (dma->chan); 返回0; }
处理数据的 PRU 代码如下所示。
void acque_data (void) { uint32_t queue = ring _buff _queue (); if (!acquisition _running ()){ acquisition _abort (error_input_abort); return; } while (queue &!ring _buff _finished()){ //* buff data 并将其从 PRU SRAM */abort (error_transfer *)(error_r_sram *)(! return )(sram (!cr_r_rm)(if))(!sram (sram (sram!return))(!r_r_r_r_rs (if)))(! return; } /*将数据从 PRU SRAM 移出到 DDR */ dma_start_transfer (sRAM_buff addr ()、DDR_dst_addr (sRAM_buff 插槽())); sRAM_buff 插槽_update (); if (sRAM_buff 插槽()= 0) ; if (sRAM_buff (&sram) sram (sRAM_tum_transfer ();sram (&sRAM_tal_dum_rs);finished ();if (sram (sram (xr_bu_r_r_r_r_rs_rs);sram (&sram (&sram (xr_r_rs_rs);r_rs_ return; } }否则(sRAM_buff_SLOT_overload()){ acquisition _abort (error_buffer_overload); return; } queue -; } void parse_command (void) { while (PRU_rpmsg_receive (&transport、&src、&dst、有效负载、&len)= PRU_RPMSG_Success){ if (pru_rpmsg_transport )、}(pile (&rmsg_transport)、pile (&rmsg)、payload (&rus)、}(payload uint32_t * cmd =(uint32_t *)有效载荷; switch (cmd[0]){ case arm_send_pru_DDR_ADDR_ADDR_loaded: DDR_validate_addresses (cmd[1]); break; case arm_send_PRU_ACQ_CONF: ring_buff ((uint8_t);init_3[t_rmd );init_3[t_rmd (inuint8_1 );intru_rmd (uint8_inu_1)、intruCM_1)、inu_1 (intru1.[t_ind) dma_init (); pru_send_arm_mem_addr (); acquisition _start (); break; case arm_send_pru_echo_cmds: pru_configure_echo (cmd[1]); break; case ARM_SEND_PRU_RESET: PRU_RESET(); break; } } void enable_data_ready_IRQ (void) { //*将系统事件20映射到通道1 */ ct_intc.cmr5_bit.ch_map_20 = 1; //*将通道1映射到主机中断1 */ ct_intc.hint */translation_translation_translation_ind.it.translation_event1 ;* /*在系统事件上启用主机中断1 */ CT_INTC.HIEISR_BIT.HIN_SET_IDX = 1; //启用所有主机中断*/ CT_INTC.GER_BIT.EN_HIN_ANY = 1; //清除任何现有系统事件*/ CT_INTC.SICR_BIT.STS_CLR_BIT_DATA = 1; }SYST_VIDX = READY void main (void) { ////允许 PRU 访问 OCP 主端口,以便 PRU 可以读取外部存储器*/ CT_CFG.SYSCFG_BIT.STANDBY_INIT = 0; //清除 ARM 将用于“启动”的 PRU-ICSS 系统事件的状态*/ CT_INTC.STR_BIT.STS_STS_STS_SYIDX =来自 ARM; /*确保 Linux 驱动程序已准备好进行 RPMsg 通信*/ status =&resourceTable.rpmsg_vdev.status; while (!(* status & virtio_config_S_driver_OK); //初始化 RPMsg 传输结构*/ PRU_rpmsg_init (&transport、rmsgTable.sysgv_driver_OK);从 sysrgvwt_rpwt_ring1初始化到 sysrgor_rings_rrrring1;sysrgwt_rrrg_rgrg_rrrr /*使用传输结构在 PRU 和 ARM 用户空间之间创建 RPMsg 通道。 // while (PRU_rpmsg_channel (RPMSG_NS_create、&transport、CHAN_NAME、CHAN_DESC、CHAN_PORT)!= PRU_RPMSG_SUCCESS); ENABLE_DATA_READY_IRQ (); while (1){ if (__R31 & HOST0_INT){ CT_INTC.SICR_BIT.STS_CLR_IDX = SYSEVT_FIT_ARM; parse_command (); } if (__R31 & host1_INT){CT_INTC.SICR_IDX = SYSEVT_PACE_ST_DATA ( });SYSEVT_PACE_STR_PACE_STR_PACE_ST_READY (})
Nick、
遗憾的是、没有我无法解决问题。 由于其他一些工作优先事项,我将不得不让这种情况保持一段时间。
我的初始问题与 McASP 初始化序列有关。 Mark 表示 TRM 中包含的序列是正确的、这就是我要遵循的顺序。 从这个角度来看、我不介意将其标记为已解决。 如果我有时间再看一下、我可以根据需要打开一个新的或相关的问题。