中级会员
 
- 积分
- 252
- 金钱
- 252
- 注册时间
- 2026-1-29
- 在线时间
- 22 小时
|
针对您遇到的 STM32F103ZET6 通过 PWM DMA 驱动 WS2812B 时逻辑分析仪无法抓取波形的问题,以下是系统性分析和解决方案:
一、核心原因分析
1. DMA 传输方向配置错误
现象:若使用 DMA_MEM_TO_MEM 方向(内存到内存),实际应为 从 GPMC 外设(FPGA BRAM)到内存(即 DMA_DEV_TO_MEM)。
后果:DMA_MEM_TO_MEM 会将 src_addr(可能是 CPU 映射的 GPMC 地址)当作普通内存地址拷贝,而非通过 GPMC 总线发起真实读操作,导致数据无效或错乱。
验证方法:通过 readl() 直接读取 GPMC 地址空间,确认是否能获得正确数据。
2. GPMC 时序与 FPGA BRAM 不匹配
关键参数:需根据 FPGA BRAM 手册调整 GPMC 的访问时间(access-time)、突发长度(burst-length)等参数。
典型配置示例(DTSI 文件):
dts
&gpmc {
ti,chip-select = <0>;
ti,burst-length = <4>; /* 突发长度(根据 BRAM 宽度调整) */
ti,access-time = <2>; /* 访问时间(单位:时钟周期) */
ti,page-release = <true>; /* 允许页释放优化 */
};
验证方法:用示波器测量 GPMC 的 CS#、WE#、RD# 信号,确认时序是否符合 FPGA 要求。
3. 地址对齐问题
风险点:若 src_addr 或 dst_addr 未按总线宽度(如 32 位)对齐,可能导致数据错位。
解决方案:使用 ALIGN() 宏强制对齐地址:
uint32_t aligned_src = ALIGN(cfg.src_addr, 4);
uint32_t aligned_dst = ALIGN(cfg.dst_addr, 4);
4. 缓存一致性问题
潜在影响:若目标缓冲区位于缓存中,DMA 写入后未刷新缓存,CPU 读取时可能命中旧数据。
解决方法:使用 dma_alloc_coherent 分配非缓存缓冲区:
void *dma_buf = dma_alloc_coherent(&pdev->dev, size, &dma_handle, GFP_KERNEL);
二、针对性解决方案
1. 修正 DMA 传输方向
// 修改前
cfg.direction = DMA_MEM_TO_MEM;
// 修改后
cfg.direction = DMA_DEV_TO_MEM; // 从设备(GPMC)到内存
同时确保 src_addr 是 GPMC 寄存器的物理地址(通过 ioremap 映射):
void __iomem *gpmc_base = ioremap(0x80000000, SZ_4K);
cfg.src_addr = gpmc_base + FPGA_BRAM_OFFSET; // FPGA BRAM 在 GPMC 空间中的偏移
2. 调整 GPMC 时序参数
关键参数说明:
access-time:从地址有效到数据有效的延迟(需 ≥ FPGA BRAM 的 tACC)。
burst-length:若 BRAM 支持突发读取,设置为对应值;否则设为 1。
示例配置(基于 STM32CubeMX):
hgpmc.Init.AccessTime = HAL_GPMC_ACCESS_TIME_2CYCLES;
hgpmc.Init.BurstLength = HAL_GPMC_BURST_LENGTH_4;
3. 强制地址对齐
// 示例:保证 src_addr 是 4 字节对齐
uint32_t aligned_src = ALIGN(cfg.src_addr, 4);
uint32_t aligned_dst = ALIGN(cfg.dst_addr, 4);
并在 DMA 提交时使用对齐后的地址:
tx = dev->device_prep_dma_memcpy(data->m_channel,
aligned_dst,
aligned_src,
data->m_data_len * sizeof(u16),
DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
4. 禁用 CPU 缓存(临时验证)
若怀疑缓存问题,可临时禁用目标缓冲区的缓存:
// 在分配 DMA 缓冲区时使用 dma_alloc_coherent
void *dma_buf = dma_alloc_coherent(&pdev->dev, size, &dma_handle, GFP_KERNEL);
// 确保后续操作使用 dma_buf 而非普通 kmalloc 分配的内存
三、其他注意事项
DMA 通道独占性:确保 dma_request_channel 成功获取通道,且未与其他驱动冲突:
if (!channel) {
pr_err("Failed to request DMA channel\n");
return -EBUSY;
}
完成回调与同步等待:确保 fpga_dma_complete 回调确实调用了 complete():
void fpga_dma_complete(void *param) {
struct completion *wait = (struct completion *)param;
complete(wait);
}
数据长度单位:确认 data->m_data_len 的单位是 字数(word) 还是 字节数(byte)。若为字数,则无需乘 sizeof(u16);若为字节数,则需除以 sizeof(u16)。 |
|