MADL!AR
Code is cheap, show me the PPT!
首页
分类
Fragment
关于
失败的尝试:实现PSSI HOST
分类:
硬件
发布于: 2026-05-01
stm32U5/N6等系列的很多芯片都内置了PSSI(Parallel Synchronous Slave Interface,并行同步从接口),而且协议十分简单,最小包含一条时钟线和8条(或16条)数据线,可选DE和RDY信号,可以视作一个并行的的SPI接口,数据吞吐量非常大。 这是多种工作模式的一种,即发送方和接收方都实施流控: [https://www.st.com.cn/.../rm0456-stm32u5-series-....pdf](https://www.st.com.cn/resource/zh/reference_manual/rm0456-stm32u5-series-armbased-32bit-mcus-stmicroelectronics.pdf) > 双向 PSSI_DE/PSSI_RDY 信号 如果 PSSI_CR 寄存器中的 DEPOL 和 RDYPOL 均置 1 并且 DERDYCFG 置为 111 或 100,则 可以使用单个引脚同时实现数据使能 (PSSI_DE) 和就绪 (PSSI_RDY) 功能。在这种情况下, 与所选复用功能(DERDYCFG=111 时的 PSSI_DE,或 DERDYCFG=100 时的 PSSI_RDY) 相对应的 GPIO 必须配置为漏极开路。另一个器件也必须将线路驱动为漏极开路,并且必须 对该线路应用弱上拉。 双向信号因此得以实现。如果发送器将线路驱动为低电平(以指示数据无效)或接收器将线 路驱动为低电平(以指示未对当前数据进行采样),则两个器件都知道当前周期内未进行数 据传输。  事实上,它和DCMI接口类似,都是用作数据高速通信的接口,但DCMI一般只能支持到14bit,硬件时序更加复杂,且只可以用作输入,在与FPGA、MPU通信时,PSSI还是更具有优势。难受的是,PSSI是从接口,虽然也能作为发送方,但它只能被动接受时钟,无法主动发起。于是探究了几个方法,让普通的MCU可以充当主机的角色,通过某种总线接口,连接到从机的PSSI接口实现双向通信。 ### 几种容易想到的方法 PSSI的主机其核心在于,产生合适的时钟并将数据搬运到IO,由此想到可能通过这些方式实现: 1. 使用FMC的SRAM/LCD模式 2. 使用LTDC接口 3. 使用QSPI或、Octo-SPI/Hexa-SPI接口 4. 使用mdma+TIM方式 于是逐一进行验证: #### 1. FMC的SRAM/LCD模式 STM32绝大多数的芯片都有FMC或者FSMC接口,实际上只需用到SRAM模式即可,这里需要查询一下FMC总线在内存空间中的映射关系,针对stm32H7A3: [https://www.st.com.cn/.../rm0455-stm32h7a37b3-and-stm32h7b0....pdf](https://www.st.com.cn/resource/en/reference_manual/rm0455-stm32h7a37b3-and-stm32h7b0-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf)  因此配置FMC ```BANK1 NOR/PSRAM 1```,Memory Type选择LCD Interface,Timing参数都选择为最小,在主函数中实现下述逻辑: ``` #define txBuffLen (64*64*2) uint8_t txBuff[txBuffLen]; void main() { ... MX_FMC_Init(); ... for (unsigned int i = 0; i < txBuffLen; i++) { txBuff[i] = 1 << (i%8); } HAL_StatusTypeDef state; while (1) { HAL_Delay(1000); state = HAL_SRAM_Write_8b(&hsram1, (uint32_t *)0x60000001, txBuff, 16); printf("send state: %d\n", state); ... } } ``` 其实是实现类似流水灯的效果,通过逻辑分析仪抓取波形:  通道0是NE1信号,1是NOE信号,这里非常奇怪,每个bytes输出完成之后,NOE还产生了几个没用的时钟,与NE1也对不上。这显然没有办法使用任何一条信号线用作时钟线。调整timing参数和其他参数,也无法解决这个问题。 FMC一共有4种模式,LCD type下支持A和D,但均呈现此现象,暂未排查到原因,出于有限精力,暂搁置。 补充:发现可能是MPU的配置问题 [https://forum.anfulai.cn/forum.php?mod=viewthread&tid=127182](https://forum.anfulai.cn/forum.php?mod=viewthread&tid=127182) 留待验证 #### 2. LTDC接口 按照AI和手册给出的参考,LTDC天然适合做PSSI HOST,因为有像素时钟,配合合适的timing,可以实现“完美”的时序。 在stm32需要注意window、layer等、Sync/Back Porch等设置,尤其layer行缓存跨1K的问题,应该是可以产生类似时钟的,但在实际测试时发现两个硬伤,直接宣告此方案的失败:1. LTDC最小只能配置到16bit,即RGB565,如果从机是8bit总线,则要浪费一半的IO或者RAM。2. 一旦LTDC被configured,其像素时钟就会一直启动,这个控制器就会周而复始的从RAM搬运数据到IO,根本无法终止或者控制启停边界。虽然可以借助DMA2D和软件定义消息边界等措施,解决问题2,但引入了额外的开销,显然不合适。 #### 3. 使用QSPI或、Octo-SPI/Hexa-SPI接口 虽然查看时序图有作为主机的可能性,但实际测试中发现,写入和读取过程十分繁琐,因为此类总线的主要用途为存储器通信,据流强制包含指令(Instruction)、地址(Address)、空周期(Dummy)和数据(Data)等固定阶段,带宽利用率低下。PSSI总线是没有地址和指令的概念,为了让接收方只接受纯数据的部分,要么在软件上做额外的处理(无法兼容DMA),要么在硬件时序中启用DQS或者引入其他的电路生成DE信号,并且大量传输时还要配合MDMA,配置起来十分繁琐。 当然还有一点,并非所有芯片都有OSPI,很多只有QSPI,更别提如果从机是16bit位宽的场景,OSPI更没辙了。权衡之后,还是放弃了这个方案。 #### 4. 使用DMA+TIM方式 这个方式的参考资料来自: * [https://forum.anfulai.cn/forum.php?mod=viewthread&tid=86980](https://forum.anfulai.cn/forum.php?mod=viewthread&tid=86980) * [https://forum.anfulai.cn/forum.php?mod=viewthread&tid=112449](https://forum.anfulai.cn/forum.php?mod=viewthread&tid=112449) 其原理是通过DMAMUX产生请求信号,触发DMA将数据从Memory搬运到IO 如GPIOX->ODR。为了保持稳定的时钟,通常借助定时器产生TIGO信号,用作DMAMUX的输入。但除了数据IO之外,还需要级联一个DMAMUX来生成时钟信号,而且要实现双缓冲的话,也是非常复杂。这个方案在调试半天未果之后,还是选择了放弃,其与硬件太过于耦合,不够灵活。 ### 意外发现:外部时钟 + 双机PSSI对接 尝试了4种方式均失败,十分令人沮丧,但仔细一想,事情似乎没有很麻烦。仔细翻看PSSI的介绍,它是支持Transmit/Receive双向通信的,只是它自己无法产生时钟。既然如此,如果将两个设备的PSSI总线对等连接,其中一个配置为发送,另一个为接收,再从外部产生一个时钟施加给双方,岂不就OK了? 于是在stm32N6的板子上验证一番,首先配置一个时钟,频率定为1Mhz,用跳线短接到PSSI_PDCK引脚上。PSSI的参数配置为: | 配置项 | 参数值 | |--------------------|---------------------| | Data Width | 8 Bits | | Control Signals | Neither DE nor RDY | | Clock Polarity | Rising Edge | | Data Enable Polarity | Active Low | | Ready Polarity | Active Low | 在main.c中: ```c void main() { ... HAL_StatusTypeDef state; uint8_t buff[1024]; for (int i = 0; i < 1024; i++) { buff[i] = 1 << (i % 8); } while (1) { state = HAL_PSSI_Transmit(&hpssi, buff, 12, 0xFFFF); if (state != HAL_OK) { ... } } } ``` 烧录后发现不能work,state的值为:```#define HAL_PSSI_ERROR_UNDER_RUN 0x00000002U /*!< FIFO Under-run error */```。猜测是在HAL_PSSI_Transmit在写寄存器的临界时间内,PSSI控制器在时钟的驱动下尝试从FIFO中获取数据,而此时尚未就绪从而导致报错。  解决办法是,在调用HAL_PSSI_Transmit函数之后,再发生时钟,这里为了便于调试,拿出了另一块板子,在按键按下之后,发送16个时钟脉冲,硬件上还是把时钟输出短接到PSSI_PDCK上。 再次尝试,呈现了完美的波形:  需要注意的是,前面Clock Polarity配置为Rising Edge,但从逻辑分析仪的波形来看,第一个上升沿时,数据并未准备就绪,但数据引脚已全部拉低,第二个上升沿时,数据线上才开始准备数据。(实测将极性改为Falling Edge,也是从第二个下降沿开始准备数据)也就是说,如果采用外部时钟驱动,每次传输时的时钟,需要多于传输次数。或者,在调用HAL_PSSI_Transmit时停止外部时钟?先在此标记,留待验证。 总结,对于都有PSSI的设备,可以对等连接后,发送方通过TIM产生时钟脉冲,输入到PDCK时钟线上,完成通信。发送方和接收方需要设置不同的时钟极性,如发送方上升沿准备数据,接收方下降沿采样。同时,需要通过DE/RDY或软件屏蔽第一个脉冲的无数据问题。