如何读懂时序图?

读懂时序图

前言

读时序图之前,首先要明确几个概念

  1. 引脚

    首先,时序图一般指同步时序图,异步时序例如串口(UART/SCI)不在讨论之列

    判断同步时序的方法很简单,就是看是否存在专门的Clock信号引脚,其次看I/O引脚上的边沿是否和Clock同步,一般常见的同步时序串行接口有SPI、SDIO、I2C、I2S、USART等

    本文档由W25Q64(华邦电子出品,NOR Flash,SPI/QSPI接口)中的一个简单I/O指令的执行,解释如何读懂时序图

  2. 端序

    一般存储顺序分为两种,大端和小端

    • 大端:一串数据的高字节存储在低地址,低字节存储在高地址
    • 小端:高低字节按照高低地址顺序排列,和大端相反

    尤其注意是字节的存储顺序,而非数据位的顺序

    就目前看到过的各种芯片的串行时序,多字节指令(≥2Byte)一般是大端序,而常见单片机例如ARM却是小端序,这部分尤其需要注意区分

  3. 数据顺序

    指I/O数据的存储顺序

    • 如果是存储芯片,那么无论如何,按照顺序写入和读出可以保证数据的完整性
    • 其他芯片就看时序图中给定的端序,然后整理成单片机的端序即可
  4. 单片机相关

    一般单片机的串行通信接口的硬件buffer均为一个字节,但是受限于驱动库(软件限制),多用小端序读写此buffer

    直接操作硬件的话,可以避开驱动库的限制,自由地配置为大端或者小端

W25Q64的Fast Read指令

  • W25Qxxx系列是使用SPI接口的NOR Flash,使用24位地址宽度
  • 通信格式为:指令——地址(大端序)——I/O数据
  • SPI通信开始的标志是CS/NSS被主机拉低,从设备被选中
  • SPI总线的I/O在空闲时处于High-Impedance(高阻)状态
  • 一般时序图上会标注时钟的周期号,可以据此判断时序图的先后
    如何读懂时序图?_第1张图片

对于上述的时序图,可以这样解读:

  1. CS拉低,开始通信
  2. CLK引脚上出现脉冲,在脉冲的边沿上,I/O引脚开始脱离高阻态,最多占用一个时钟周期
  3. MOSI引脚发送指令0x0Bh,MISO引脚处于高阻态,占用八个时钟周期
  4. MOSI引脚按照大端序发送24位地址,先发送高字节,MISO引脚处于高阻态共占用24个时钟周期
  5. MOSI和MISO均进入高阻态,等待八个时钟周期(也可以将该Dummy Byte读出并舍去)
  6. MOSI进入空闲(非高阻态,且无需关心),MISO上开始出现数据脉冲,在该芯片上,数据按照写入的顺序读出,读出N个字节,占用N*8个时钟周期
  7. CS拉高,MOSI/MOSI进入高阻态,CLK引脚回到空闲电平,通信结束

综上,同步时序逻辑的理解,大致分为几步:

  1. 找到通信开始和结束的标志,找到时钟脉冲
  2. 对时钟脉冲分段,找到每一段传输的数据是什么,是否有空闲时钟脉冲(这很重要)
  3. 分好数据段后,确定端序,并在程序中做出相应处理
  4. 根据上述信息,即可整理成代码,进行下一步调试

该指令对应的伪代码如下(C语言)

/* Fake Functions Declerations */
void SPI_Transmit(uint8_t* pTxData, uint16_t pTxSize);
void SPI_Receive(uint8_t* pRxData, uint16_t pRxSize);

/* Command Function */
void W25Qxx_FastRead(uint8_t* pReadData, uint16_t pReadSize, uint32_t Addr)
{
    /* Variables Definitions */
    uint8_t command_buff = 0x0Bu, dummy_buff = 0u, addr_buff[3] = {0};
    
    /* Address Processing */
    addr_buff[0] = (Addr & 0x00FF0000ul) >> 16u;
    addr_buff[1] = (Addr & 0x0000FF00ul) >> 8u;
    addr_buff[2] = Addr & 0x000000FFul;
    
    /* Instruction Transmitting */
    SPI_Transmit(&command_buff, 1u);
    
    /* Address Transmitting */
    SPI_Transmit(addr_buff, 3u);
    
    /* Dummy Reading */
    SPI_Receive(&dummy_buff, 1u);
    
    /* Data Reading */
    SPI_Receive(pReadData, pReadSize);
}

你可能感兴趣的:(C/C++,数字信号处理,Driver,Code)