三种通信总线的前世今生对比

芯片常用协议种类详解,含多协议转换器 协议是程序沟通的语言,没有语言就是哑巴 阅读详情

                                                  人生中的第一篇偏技术的文章    

     这两年主要从事嵌入式应用层的开发,对驱动层了解的不是很透彻,第一篇文章也不知道该写点神魔,那就从几个常用的通信总线开始吧,一方面算是给自己的学习经历添点痕迹(也算是重新学习的过程),二是以后回顾知识点也有个熟悉的资料,方便查阅。以后的内容有自己的想法(可能不对,还请指正,我尽量多查看一下书籍,争取总结正确),也会引用别人的一些见解,我尽量标明出处(链接),防止我断章取义,好让同志们(也可能就我自己看而已,假设有别人浏览吧)查找内容的源头。不BB了,是骡子是马拉出来遛遛吧。

STM32中几种通信方式的区别:

1.UART(USART)

2.IIC总线

3.SPI总线

 

主要参考博客:https://blog.youkuaiyun.com/oqqHuTu12345678/article/details/65445338?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase

对博主表示感谢。

UART、SPI、I2C对比

UART、SPI、I2C对比表格
对比项         UART                     SPI            I2C
信号线数目3根,RX、TX、GND4根,SDO、SDI、SCLK、SS2根,SDA、SCLK
设备从属关系           ——存在主从设备。SPI用片选信号选择从机 存在主从设备。IIC用地址选择从机。
通信方式全双工通信全双工通信半双工通信
通信速率速度慢比I2C总线要快,速度可达到几MbpsI2C的速度比SPI慢
应用领域

1、UART常用于控制计算机与串行设备的芯片

2、就是我们经常所说的串口,基本都用于调试。

主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间I2C一般是用在同一个板子上的2个IC之间的通信 ,它可以替代标准的并行总线,连接各种集成电路和功能模块。
传输距离  I2C需要有双向IO的支持,而且使用上拉电阻,抗干扰能力较弱,一般用于同一板卡上芯片之间的通信,较少用于远距离通信
通信特征异步,一帧可以传5/6/7/8位同步,SPI允许数据一位一位的传送,甚至允许暂停。从最高位开始传。同步,电平信号,一次连续8bit。从最高位开始传
协议复杂度结构比较复杂SPI实现要比UART简单,UART需要固定的波特率,就是说两位数据的间隔要相等,而SPI则无所谓,因为它是有时钟的协议。协议比SPI复杂,但是连线比标准的SPI要少
对比 

在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。

在多个从器件的系统中,每个从器件需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。

 

USART简介:通用同步异步收发器,全双工数据交换,UART,异步通信,为常用外设,用作开发调试。

代码如下:

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 打开串口GPIO的时钟
    DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
    
    // 打开串口外设的时钟
    DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

    // 将USART Tx的GPIO配置为推挽复用模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
    
    // 配置串口的工作参数
    // 配置波特率
    USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
    // 配置 针数据字长
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    // 配置停止位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    // 配置校验位
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    // 配置硬件流控制
    USART_InitStructure.USART_HardwareFlowControl =
    USART_HardwareFlowControl_None;
    // 配置工作模式,收发一起
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    // 完成串口的初始化配置
    USART_Init(DEBUG_USARTx, &USART_InitStructure);
    
    // 串口中断优先级配置
    NVIC_Configuration();
    
    // 使能串口接收中断
    USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);    
    
    // 使能串口
    USART_Cmd(DEBUG_USARTx, ENABLE);        
}

/**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
 
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
 
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
  uint8_t ucTemp;
    if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
    {        
        buffer[num] = USART_ReceiveData(DEBUG_USARTx);
   // USART_SendData(DEBUG_USARTx,buffer[num]);
        num ++;
        
        if(num==10)
        {
            for(num=0;num<10;num++)
            {
                Delay(20);//在发送两个数据的间隙需要一定的时间。
                Usart_SendByte(DEBUG_USARTx,buffer[num]);
                Delay(20);
            }
            num = 0;
        }
    }    
}

uint16_t USART_ReceiveData(USART_TypeDef* USARTx)//串口接收数据函数,直接读取的USART的数据寄存器。
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
 
  /* Receive Data */
  return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
}

/*****************  发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
    /* 发送一个字节数据到USART */
    USART_SendData(pUSARTx,ch);
        
    /* 等待发送数据寄存器为空 */
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);    
}

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_DATA(Data));
    
  /* Transmit Data */
  USARTx->DR = (Data & (uint16_t)0x01FF);
}

串口也就这些东西,自从有了固件库,开发就变得很容易了,只要懂得原理,代码不需要写太多,只要将一些问题想全面,考虑上一些特殊的情况,软件上做一些保护尽量避免发生问题。

SPI简介:高速全双工的串行总线。

(1)四条信号线:串行时钟(SCLK)、串行数据输出(SDO)、串行数据输入(SDI)、片选线(SS)。(所谓的进出,是针对信号进出主机而言)

(2)SPI总线可以实现多SPI设备互相连接。提供时钟的SPI设备为主设备(Master),其他设备为从设备(Slave)。SCLK信号线只由主设备控制,从设备不能控制信号线。

(3)在SPI总线上,某一时刻可以出现多个从机,但只能存在一个主机。主机通过片选线来确定要通信的从机。这就要求从机的MISO口具有三态特性,使得该口线在器件未被选通时表现为高阻抗。

(4)主从设备间可以实现全双工通信,收发独立,操作简单,数据传输速率较高,但需要占用主机较多的口线(每个从机都需要一根片选线),而且只支持单个主机,没有指定的流控制,没有应答机制确认是否接收到数据。

(5)数据输出通过SDO线,数据在时钟上沿或下沿时改变(即发送),在紧接着的下沿或上沿被读取,从而完成一位数据传输。数据输入也使用同样原理。因此,8位数据的传输,至少需要8次时钟信号的改变(上沿和下沿为一次)。

(6)普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。

(7)SPI接口在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,高位在前(先传?),低位在后,为全双工通信。

2、数据传输

(1)SPI在数据传输的时候,需要确定两件事情:

  • 其一,数据是在时钟的上升沿采集还是下降沿采集;
  • 其二,时钟的初始(空闲)状态是为高电平还是低电平。
  • 对比:I2C空闲状态时,时钟线(不是数据线?)为高电平,数据采集时,时钟线也为高电平,但SPI给出了更自由的方式。

(2)两个概念

CPOL:时钟极性,表示SPI在空闲时,时钟信号是高电平还是低电平。

CPHA:时钟相位,表示SPI设备是在在时钟的上升沿采集还是下降沿采集。

则SPI数据传输就有四种可能---按照标准的说法,SPI数据传输就有四种模式。

(3)四种模式

模式CPOLCPHA
000
101
210
311

模式0(杠cs表示片选信号)

模式1

模式2

模式3

代码如下:

/**
  * @brief  SPI_FLASH初始化
  * @param  无
  * @retval 无
  */
void SPI_FLASH_Init(void)
{
  SPI_InitTypeDef  SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
    
    /* 使能SPI时钟 */
    FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );
    
    /* 使能SPI引脚相关的时钟 */
     FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK|
                                                                    FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE );
    
  /* 配置SPI的 CS引脚,普通IO即可 */
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
    
  /* 配置SPI的 SCK引脚*/
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);

  /* 配置SPI的 MISO引脚*/
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
  GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);

  /* 配置SPI的 MOSI引脚*/
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
  GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);

  /* 停止信号 FLASH: CS引脚高电平*/
  SPI_FLASH_CS_HIGH();

  /* SPI 模式配置 */
  // FLASH芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  SPI_Init(FLASH_SPIx , &SPI_InitStructure);

  /* 使能 SPI  */
  SPI_Cmd(FLASH_SPIx , ENABLE);
    
}

/* 获取 SPI Flash ID */
    FlashID = SPI_FLASH_ReadID();   

函数主体:

 /**
  * @brief  读取FLASH ID
  * @param     无
  * @retval FLASH ID
  */

#define Dummy_Byte                0xFF

#define W25X_JedecDeviceID            0x9F
u32 SPI_FLASH_ReadID(void)
{
  u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

  /* 开始通讯:CS低电平 */
  SPI_FLASH_CS_LOW();

  /* 发送JEDEC指令,读取ID */
  SPI_FLASH_SendByte(W25X_JedecDeviceID);

  /* 读取一个字节数据 */
  Temp0 = SPI_FLASH_SendByte(Dummy_Byte);

  /* 读取一个字节数据 */
  Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

  /* 读取一个字节数据 */
  Temp2 = SPI_FLASH_SendByte(Dummy_Byte);

 /* 停止通讯:CS高电平 */
  SPI_FLASH_CS_HIGH();

  /*把数据组合起来,作为函数的返回值*/
    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

  return Temp;
}

/**
  * @brief  使用SPI发送一个字节的数据
  * @param  byte:要发送的数据
  * @retval 返回接收到的数据
  */
u8 SPI_FLASH_SendByte(u8 byte)
{
     SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待发送缓冲区为空,TXE事件 */
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET)
    {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
   }

  /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
  SPI_I2S_SendData(FLASH_SPIx , byte);

    SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待接收缓冲区非空,RXNE事件 */
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
  {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
   }

  /* 读取数据寄存器,获取接收缓冲区数据 */
  return SPI_I2S_ReceiveData(FLASH_SPIx );
}

这样就可以获取FLASH的ID了,通过ID的读取操作可以检测FLASH连接是否稳妥。

/* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 */
        // 这里擦除4K,即一个扇区,擦除的最小单位是扇区
        SPI_FLASH_SectorErase(0x00);        

 /**
  * @brief  擦除FLASH扇区
  * @param  SectorAddr:要擦除的扇区地址
  * @retval 无
  */
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
  /* 发送FLASH写使能命令 */
  SPI_FLASH_WriteEnable();
  SPI_FLASH_WaitForWriteEnd();
  /* 擦除扇区 */
  /* 选择FLASH: CS低电平 */
  SPI_FLASH_CS_LOW();
  /* 发送扇区擦除指令*/
  SPI_FLASH_SendByte(W25X_SectorErase);
  /*发送擦除扇区地址的高位*/
  SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
  /* 发送擦除扇区地址的中位 */
  SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
  /* 发送擦除扇区地址的低位 */
  SPI_FLASH_SendByte(SectorAddr & 0xFF);
  /* 停止信号 FLASH: CS 高电平 */
  SPI_FLASH_CS_HIGH();
  /* 等待擦除完毕*/
  SPI_FLASH_WaitForWriteEnd();
}

/**
  * @brief  等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
  * @param  none
  * @retval none
  */
void SPI_FLASH_WaitForWriteEnd(void)
{
  u8 FLASH_Status = 0;

  /* 选择 FLASH: CS 低 */
  SPI_FLASH_CS_LOW();

  /* 发送 读状态寄存器 命令 */
  SPI_FLASH_SendByte(W25X_ReadStatusReg);

  /* 若FLASH忙碌,则等待 */
  do
  {
        /* 读取FLASH芯片的状态寄存器 */
    FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);    
  }
  while ((FLASH_Status & WIP_Flag) == SET);  /* 正在写入标志 */

  /* 停止信号  FLASH: CS 高 */
  SPI_FLASH_CS_HIGH();
}

 /**
  * @brief  对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
  * @param    pBuffer,要写入数据的指针
  * @param WriteAddr,写入地址
  * @param  NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
  * @retval 无
  */
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  /* 发送FLASH写使能命令 */
  SPI_FLASH_WriteEnable();

  /* 选择FLASH: CS低电平 */
  SPI_FLASH_CS_LOW();
  /* 写页写指令*/
  SPI_FLASH_SendByte(W25X_PageProgram);
  /*发送写地址的高位*/
  SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  /*发送写地址的中位*/
  SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  /*发送写地址的低位*/
  SPI_FLASH_SendByte(WriteAddr & 0xFF);

  if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
  {
     NumByteToWrite = SPI_FLASH_PerWritePageSize;
     FLASH_ERROR("SPI_FLASH_PageWrite too large!");
  }

  /* 写入数据*/
  while (NumByteToWrite--)
  {
    /* 发送当前要写入的字节数据 */
    SPI_FLASH_SendByte(*pBuffer);
    /* 指向下一字节数据 */
    pBuffer++;
  }

  /* 停止信号 FLASH: CS 高电平 */
  SPI_FLASH_CS_HIGH();

  /* 等待写入完毕*/
  SPI_FLASH_WaitForWriteEnd();
}

 /**
  * @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
  * @param    pBuffer,要写入数据的指针
  * @param  WriteAddr,写入地址
  * @param  NumByteToWrite,写入数据长度
  * @retval 无
  */
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
    
    /*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/
  Addr = WriteAddr % SPI_FLASH_PageSize;
    
    /*差count个数据值,刚好可以对齐到页地址*/
  count = SPI_FLASH_PageSize - Addr;
    /*计算出要写多少整数页*/
  NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
    /*mod运算求余,计算出剩余不满一页的字节数*/
  NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
    
    /* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
  if (Addr == 0)
  {
        /* NumByteToWrite < SPI_FLASH_PageSize */
    if (NumOfPage == 0)
    {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
            /*先把整数页都写了*/
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
            /*若有多余的不满一页的数据,把它写完*/
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    }
  }
    /* 若地址与 SPI_FLASH_PageSize 不对齐  */
  else
  {
        /* NumByteToWrite < SPI_FLASH_PageSize */
    if (NumOfPage == 0)
    {
            /*当前页剩余的count个位置比NumOfSingle小,一页写不完*/
      if (NumOfSingle > count)
      {
        temp = NumOfSingle - count;
                /*先写满当前页*/
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
                
        WriteAddr +=  count;
        pBuffer += count;
                /*再写剩余的数据*/
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
      }
      else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
            /*地址不对齐多出的count分开处理,不加入这个运算*/
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
            
            /* 先写完count个数据,为的是让下一次要写的地址对齐 */
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
            
            /* 接下来就重复地址对齐的情况 */
      WriteAddr +=  count;
      pBuffer += count;
            /*把整数页都写了*/
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
            /*若有多余的不满一页的数据,把它写完*/
      if (NumOfSingle != 0)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      }
    }
  }
}

 /**
  * @brief  对FLASH读取数据,
  * @retval 无
  */

void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
  /* 选择FLASH: CS低电平 */
  SPI_FLASH_CS_LOW();

  /* 发送 读 指令 */
  SPI_FLASH_SendByte(W25X_ReadData);

  /* 发送 读 地址高位 */
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  /* 发送 读 地址中位 */
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  /* 发送 读 地址低位 */
  SPI_FLASH_SendByte(ReadAddr & 0xFF);
    
    /* 读取数据 */
  while (NumByteToRead--) /* while there is data to be read */
  {
    /* 读取一个字节*/
    *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
    /* 指向下一个字节缓冲区 */
    pBuffer++;
  }

  /* 停止信号 FLASH: CS 高电平 */
  SPI_FLASH_CS_HIGH();
}

读写FLASH就完成了,要是看着乱,那就仔细梳理吧,鼓捣累了,我就休息休息了,IIC明天继续。--------------------------------------

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------2020.06.08 23:23--------------------------------------------------------------------------------------

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------以下内容编辑于20.06.09 18:40----------------------------------------------------------------------

IIC简介:

1.总线配置:

(1)SCL(serial clock):时钟线,传输CLK信号,一般是I2C主设备向从设备提供时钟的通道。

(2)SDA(serial data):数据线,通信数据(命令、地址、数据)都通过SDA线传输。

2.通信特征:串行、同步、电平、低速率

(1)串行通信。所有的数据以bit为单位在SDA线上串行传输(但每次传输8bit)。

(2)同步通信。即通信双方工作在同一个时钟。A方通过一根CLK信号线传输A的时钟给B,B工作在A传输的时钟下。

(3)非差分。因为I2C通信速率不高,而且通信双方距离很近,所以使用电平信号通信。

(4)低速率。I2C一般用在同一个板子上的2个IC之间,传输的数据量不大,因此本身通信速率很低(一般几百KHz,不同的I2C芯片的通信速率可能不同,具体在编程的时候要看设备允许的I2C通信最高速率)。

特征:

(1)主设备+从设备

    分主设备和从设备。通信由主设备发起和主导,从设备只是按照I2C时序协议被动的接受主设备的通信。
    谁是主从设备,由通信双方来决定(I2C协议并无规定)。

(2)可以多个设备挂在一条总线上(从设备地址)

    主设备负责调度总线,决定某一时间和哪个从设备通信。
    同一时刻,只能有一个从设备和主设备通信,其他从设备处于“冬眠”状态。
    每个I2C从设备在通信中都有一个I2C从设备地址,共7个bit,广播地址全0。
    它是从设备本身固有的属性。通信时主设备需要知道从设备的地址,然后在通信中通过地址来甄别是不是自己要找的那个从设备。这个地址是一个电路板上唯一的,不是全球唯一的。
    系统中可能有多个同种芯片,为此addr分为固定部分和可编程部份,细节视芯片而定,看datasheet。
    理论上7位有128位地址,然而除去保留几个保留地址如广播地址0x00等,数量少于128个,且标准协议里预见了地址的局限性,扩充了10位地址的概念。

电平示意图:

3、数据在总线上的传输协议

 

主设备首先会发送7bit位的slave device地址,和1bit位的rean或者write命令。(这里的读写,讲的是主机对总线的操作。比如写,主机写数据到总线,那么从机是读的;比如读,主机读总线的数据,那么从机是写的。)

(1)write命令

如果为write命令,则主设备释放总线(If the I2C-bus is free, both SDA and SCL lines should be both at High level),即SDA为高位;然后从设备拉低SDA,表示ACK主设备;然后主设备再发送8bit数据,从设备再ACK(A),通信结束(P)。

写寄存器的标准流程:

1.    Master发起START

2.    Master发送I2C addr(7bit)和w操作0(1bit),等待ACK

3.    Slave发送ACK

4.    Master发送reg addr(8bit),等待ACK

5.    Slave发送ACK

6.   Master发送data(8bit),即要写入寄存器中的数据,等待ACK

7.    Slave发送ACK

8.    第6步和第7步可以重复多次,即顺序写多个寄存器

9.    Master发起STOP

(2)read命令

如果为read命令,则从设备先拉低SDA表示ACK主设备,然后再发送8bit数据。主设备拉低SDA表示ACK从设备(我已经读取8bit的数据了),之后结束。

读寄存器的标准流程

1、Master发送I2Caddr(7bit)和 W操作1(1bit),等待ACK

2.    Slave发送ACK

3.    Master发送reg addr(8bit),等待ACK

4.    Slave发送ACK

5.   Master发起START

6.    Master发送I2C addr(7bit)和 R操作1(1bit),等待ACK

7.    Slave发送ACK

8.   Slave发送data(8bit),即寄存器里的值

9.   Master发送ACK

10.    第8步和第9步可以重复多次,即顺序读多个寄存器
 

应用实例:

IIC常运用在读写EEPROM、与传感器进行通信。就拿读写EEPROM为例,整点源码放在这。据说硬件IIC是有问题的,一般都用电平的高低来模拟硬件IIC,达到通信的效果。

/*
*********************************************************************************************************
*    函 数 名: i2c_Start
*    功能说明: CPU发起I2C总线启动信号
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start(void)
{
    /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
    EEPROM_I2C_SDA_1();
    EEPROM_I2C_SCL_1();
    i2c_Delay();
    EEPROM_I2C_SDA_0();
    i2c_Delay();
    EEPROM_I2C_SCL_0();
    i2c_Delay();
}

/*
*********************************************************************************************************
*    函 数 名: i2c_Start
*    功能说明: CPU发起I2C总线停止信号
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{
    /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
    EEPROM_I2C_SDA_0();
    EEPROM_I2C_SCL_1();
    i2c_Delay();
    EEPROM_I2C_SDA_1();
}

/*
*********************************************************************************************************
*    函 数 名: ee_CheckOk
*    功能说明: 判断串行EERPOM是否正常
*    形    参:无
*    返 回 值: 1 表示正常, 0 表示不正常
*********************************************************************************************************
*/
uint8_t ee_CheckOk(void)
{
    if (i2c_CheckDevice(EEPROM_DEV_ADDR) == 0)
    {
        return 1;
    }
    else
    {
        /* 失败后,切记发送I2C总线停止信号 */
        i2c_Stop();        
        return 0;
    }
}

上电先检查一下EEPROM工作是否正常,确认无误后再进行通信。

/*
*********************************************************************************************************
*    函 数 名: i2c_CheckDevice
*    功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
*    形    参:_Address:设备的I2C总线地址
*    返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
    uint8_t ucAck;

    i2c_CfgGpio();        /* 配置GPIO */
    i2c_Start();        /* 发送启动信号 */

    /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
    i2c_SendByte(_Address | EEPROM_I2C_WR);
    ucAck = i2c_WaitAck();    /* 检测设备的ACK应答 */

    i2c_Stop();            /* 发送停止信号 */

    return ucAck;
}

/*
*********************************************************************************************************
*    函 数 名: i2c_CfgGpio
*    功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
static void i2c_CfgGpio(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(EEPROM_RCC_I2C_PORT, ENABLE);    /* 打开GPIO时钟 */

    GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;      /* 开漏输出 */
    GPIO_Init(EEPROM_GPIO_PORT_I2C, &GPIO_InitStructure);

    /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
    i2c_Stop();
}

/*
*********************************************************************************************************
*    函 数 名: i2c_SendByte
*    功能说明: CPU向I2C总线设备发送8bit数据
*    形    参:_ucByte : 等待发送的字节
*    返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{
    uint8_t i;

    /* 先发送字节的高位bit7 */
    for (i = 0; i < 8; i++)
    {        
        if (_ucByte & 0x80)
        {
            EEPROM_I2C_SDA_1();
        }
        else
        {
            EEPROM_I2C_SDA_0();
        }
        i2c_Delay();
        EEPROM_I2C_SCL_1();
        i2c_Delay();    
        EEPROM_I2C_SCL_0();
        if (i == 7)
        {
             EEPROM_I2C_SDA_1(); // 释放总线
        }
        _ucByte <<= 1;    /* 左移一个bit */
        i2c_Delay();
    }
}

/*
*********************************************************************************************************
*    函 数 名: ee_WriteBytes
*    功能说明: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
*    形    参:_usAddress : 起始地址
*             _usSize : 数据长度,单位为字节
*             _pWriteBuf : 存放读到的数据的缓冲区指针
*    返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize)
{
    uint16_t i,m;
    uint16_t usAddr;
    
    /*
        写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。
        对于24xx02,page size = 8
        简单的处理方法为:按字节写操作模式,没写1个字节,都发送地址
        为了提高连续写的效率: 本函数采用page wirte操作。
    */

    usAddr = _usAddress;    
    for (i = 0; i < _usSize; i++)
    {
        /* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */
        if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0)
        {
            /* 第0步:发停止信号,启动内部写操作 */
            i2c_Stop();
            
            /* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms             
                CLK频率为200KHz时,查询次数为30次左右
            */
            for (m = 0; m < 1000; m++)
            {                
                /* 第1步:发起I2C总线启动信号 */
                i2c_Start();
                
                /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
                i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);    /* 此处是写指令 */
                
                /* 第3步:发送一个时钟,判断器件是否正确应答 */
                if (i2c_WaitAck() == 0)
                {
                    break;
                }
            }
            if (m  == 1000)
            {
                goto cmd_fail;    /* EEPROM器件写超时 */
            }
        
            /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
            i2c_SendByte((uint8_t)usAddr);
            
            /* 第5步:等待ACK */
            if (i2c_WaitAck() != 0)
            {
                goto cmd_fail;    /* EEPROM器件无应答 */
            }
        }
    
        /* 第6步:开始写入数据 */
        i2c_SendByte(_pWriteBuf[i]);
    
        /* 第7步:发送ACK */
        if (i2c_WaitAck() != 0)
        {
            goto cmd_fail;    /* EEPROM器件无应答 */
        }

        usAddr++;    /* 地址增1 */        
    }
    
    /* 命令执行成功,发送I2C总线停止信号 */
    i2c_Stop();
    return 1;

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 0;
}

/*
*********************************************************************************************************
*    函 数 名: i2c_ReadByte
*    功能说明: CPU从I2C总线设备读取8bit数据
*    形    参:无
*    返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(void)
{
    uint8_t i;
    uint8_t value;

    /* 读到第1个bit为数据的bit7 */
    value = 0;
    for (i = 0; i < 8; i++)
    {
        value <<= 1;
        EEPROM_I2C_SCL_1();
        i2c_Delay();
        if (EEPROM_I2C_SDA_READ())
        {
            value++;
        }
        EEPROM_I2C_SCL_0();
        i2c_Delay();
    }
    return value;
}

/*
*********************************************************************************************************
*    函 数 名: ee_ReadBytes
*    功能说明: 从串行EEPROM指定地址处开始读取若干数据
*    形    参:_usAddress : 起始地址
*             _usSize : 数据长度,单位为字节
*             _pReadBuf : 存放读到的数据的缓冲区指针
*    返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
    uint16_t i;
    
    /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
    
    /* 第1步:发起I2C总线启动信号 */
    i2c_Start();
    
    /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);    /* 此处是写指令 */
    
    /* 第3步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }

    /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
    i2c_SendByte((uint8_t)_usAddress);
    
    /* 第5步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
    /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
    i2c_Start();
    
    /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD);    /* 此处是读指令 */
    
    /* 第8步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }    
    
    /* 第9步:循环读取数据 */
    for (i = 0; i < _usSize; i++)
    {
        _pReadBuf[i] = i2c_ReadByte();    /* 读1个字节 */
        
        /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
        if (i != _usSize - 1)
        {
            i2c_Ack();    /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
        }
        else
        {
            i2c_NAck();    /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
        }
    }
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 1;    /* 执行成功 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 0;
}

这样就是一个完整的读写EEPROM的过程了,看懂了整个过程,拿来用就行了,本篇文章到此结束。敬礼!

 

 

 

 

 

 

 

 

常见的接口 TTL信号是TFT-LCD能识别的标准信号,就算是以后用到的LVDS TMDS 都是在它的基础上编码得来的。TTL信号线一共有22根(最少的,没有算地和电源的)分别为R G B 三基色信号,两个HS VS 行场同步信号,一个数据使能信号DE 一个时钟信号CLK,其中R G B三基色中的每一基色又根据屏的位数不同,而有不同的数据线数(6位,和8位之分)6位屏和8位屏三基色分别有R0--R5(R7) G0--G5(G7) B0--B5(B7)三基色信号是颜色信号,接错会使屏显示的颜色错乱。 阅读详情

相关推荐

IIC总线、SPI总线、串口232通信、422通信、485通信、TCP/IP协议

最近在学习一些通信协议,查找资料,借鉴别人的论述,总结于下,仅供参考。相互学习,在此感谢几位博主写的技术贴! IIC总线协议:http://blog.youkuaiyun.com/zailushangha/article/details/8233448 SPI总线协议:http://blog.youkuaiyun.com/ce123_zhouwei/article/details/6897293

Charles' home 1万+

SPI 总线3-line、4-line的定义

网上搜了下,发现各种说法都有,找一个spi器件的datasheet看了下,明确了3-line、4-line真正的定义。 第一种:3-line是指CSX(Chip selection signal)、DCX(Clock signal)、SDA(Serial input/output data),其中CSX也写着CS片选,DCX也写着SCL时钟信号;4-line在3-line的基础上多了WRX(data/ command flag),也叫着D/CX。这种情况下是半双工,数据只能往一个方向传输,例如SPI的屏只

其实你就差那么一点点! 9541

Autosar的前世今生:E2E通信校验

E2E保护概念的核心是针对安全相关的数据交换,需要在运行时进行保护,以消除通信链路中可能的失效带来的影响。时有各自的机制、参数、数据格式,具有非常强的灵活性,用户可以根据实际需要选择配置。规范里规定的一种用于保证数据传输正确的概念,属于功能安全范畴。细节处可查询文档,此处不做概述。通信来举例,两个节点通过。(配置)每种配置在计算。

遇事不决,问春风 5346

SPI 协议简介

SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)。 (1)SDO – 主设备数据输出,从设备数据输入 (2)SDI – 主设备数据输入,从设备数据输出 (3)SCLK – 时钟信号,由主设备产生 (4

guduwuzhushui的专栏 1289

lin总线可以控制几个节点_汽车 LIN 总线的前世今生一文带你走向专业纵深

昨天和大家分享了《汽车CAN总线的前世今生》,引起了很多朋友们的兴趣,纷纷留言,希望能够分享汽车用LIN总线的一些相关知识。于是把这篇《汽车LIN总线的前世今生》也分享给大家,文章的标题是我自己修改的。根据大家的理解习惯,对文章内容中部分表述做了修改调整,这篇笔记原创作者为Xiaomin。作为从业多年的我而言,再度学习这些本以为烂熟于心的内容,仍旧会有新的认知和感悟,感觉有必要为热爱学习...

weixin_42139302的博客 1268

WhatsApp网页版扫码分析

扫码登录本质上是WhatsApp的多设备登录,设备之间可以独立运行,那么当我们首次通过WhatsApp扫码登录二维码时,到底发生了那些事情?交换了那些数据?

SocialMarketing的博客 5231

SPI、I2C和UART三种串行总线协议的区别

I2C(Inter-Integrated Circuit:内置集成电路)总线的概念:         I2C通讯只需要2条双向总线——一条数据线SDA(serial data:串行数据线),一条时钟线SCL(serial clock:串行时钟线)。SDA线用于传输数据,SCL线用于同步数据收发。SDA线传输数据是大端传输(字节高位先传),每次传输8bit,即1字节。支持多主控(multi-mas...

qq_15063463的博客 1018

PaddleOCR在医疗票据识别中的实战:从部署到业务字段提取

本文详细介绍了PaddleOCR在医疗票据识别中的实战应用,重点阐述了其离线部署方案与PP-OCRv3模型选型优势。针对医疗票据敏感数据不出本地的合规要求,文章提供了从环境搭建、模型配置到核心字段提取策略的完整流程,并分享了性能优化与系统集成的实践经验,有效解决了医疗票据自动化录入的难题。

weixin_29271053的博客 237

lin通信ldf文件_汽车 LIN 总线的前世今生一文带你走向专业纵深

昨天和大家分享了《汽车CAN总线的前世今生》,引起了很多朋友们的兴趣,纷纷留言,希望能够分享汽车用LIN总线的一些相关知识。于是把这篇《汽车LIN总线的前世今生》也分享给大家,文章的标题是我自己修改的。根据大家的理解习惯,对文章内容中部分表述做了修改调整,这篇笔记原创作者为Xiaomin。作为从业多年的我而言,再度学习这些本以为烂熟于心的内容,仍旧会有新的认知和感悟,感觉有必要为热爱学习的朋友再一...

weixin_42528296的博客 1861

无人机通信电台--XBee-PRO 900HP (S3B)

XBee PRO S3B也称为XBee-900HP无线模块,它是一款工作在频段900~928MHz之间,基于FHSS跳频技术的远距离无线数传电台核心模块。常用型号如下: 类别 型号 开发套件 XKB9-DMT-UHP XBee-PRO 900HP (S3B) DigiMesh模块,200Kbps,软天线 XBP9B-DMWT-002 XBee-PRO 900HP (S3B)...

low米的博客 4505

手把手教你用Apple Developer App完成苹果开发者账号注册(含证件验证失败解决方案)

本文详细指导如何通过Apple Developer App完成苹果开发者账号注册,特别针对证件验证失败问题提供解决方案。从准备工作到注册流程,再到验证失败的处理方法,帮助开发者顺利完成注册,避免常见错误。

moss5的博客 1160

对比UART、IIC、SPI 三种总线的使用

UART:适用于简单的点对点通信场景,如串口调试和低速模块间数据传输。其低成本、易于实现的特点,使其在嵌入式开发中广泛使用。IIC:适用于需要挂载多个从设备且通信速率要求不高的场景,如传感器网络和小型显示设备。其双线设计和多设备支持优势,使其在物联网设备中备受青睐。SPI:适用于对速度和实时性要求较高的场合,如高速存储器和显示屏控制。其高性能的特点,使其在工业自动化和高端嵌入式系统中占有重要地位。实际应用中,应根据项目需求选择合适的通信总线。例如,在需要多设备通信的环境中,优先考虑 IIC;

weixin_65095004的博客 1479

人形机器人硬件开发:骨架系统设计全攻略

本文以人形机器人骨架系统为核心研究对象,系统解析其从理论建模到工程实现的全流程设计方法。通过融合刚体力学、材料科学与机器人控制理论,构建"概念基础-理论框架-架构设计-实现机制-实际应用-高级考量-综合拓展"的七层分析体系。重点覆盖运动学/动力学建模、轻量化结构设计、关节模块集成、多物理场耦合优化等关键技术,结合波士顿动力Atlas、本田ASIMO等经典案例,提供从原型设计到量产验证的工程实践指南。

AI天才研究院 1878

从 Vue3 回望 Vue2:事件总线的前世今生

Vue3 的出现代表着 Vue 框架架构思想的一次深刻转变:从基于选项的声明式组件模型,转向组合式、函数化、更可维护的逻辑表达方式。但 Vue3 的前身 ——Vue2,至今依然活跃于大量老项目中。正在接手一个 Vue2 项目在做 Vue2 向 Vue3 的迁移想深入理解 Vue3 各种通信方式的“由来”事件总线(Event Bus)。事件总线是 Vue2 中一种基于 Vue 实例事件机制的跨组件通信方式,本质上是一个全局的事件调度中心。一个组件触发事件另一个组件监听事件对比维度。

Wannaer的博客 1388

TMC5130/TMC5160步进电机驱动芯片深度解析与工程实践

步进电机驱动芯片是精密运动控制系统的物理执行核心,其性能直接决定定位精度、运行噪声与系统鲁棒性。基于电流闭环控制与电压前馈调制的混合驱动原理,现代智能驱动芯片如TMC51X0系列实现了微步插补、堵转检测和片上运动规划等关键技术突破。这类芯片通过SPI寄存器级配置实现StealthChop静音驱动与SpreadCycle高动态响应模式的无缝切换,在3D打印、CNC、机器人关节等场景中显著提升能效比与控制带宽。本文围绕TMC5130与TMC5160的硬件架构差异、寄存器操作时序、驱动模式物理机制及FreeRTO

weixin_35797963的博客 410

DBC到ARXML:AUTOSAR CAN通信栈自动化配置完整指南

在AUTOSAR架构下,DBC(CAN数据库文件)与ARXML(AUTOSAR XML)分别扮演着描述总线通信和ECU内部配置的关键角色。DBC定义了网络上的报文与信号,而ARXML则是AUTOSAR工具链交换配置数据的标准格式。在实际的ECU开发中,如何将DBC中的通信信息高效、准确地转换为ARXML,并自动生成可编译的CAN通信栈BSW配置,是许多工程师面临的痛点。借助DaVinci Configurator等工具,我们可以通过自动化导入链路消除手工映射带来的字节序错误、信号起始位偏差等高风险问题,大幅

weixin_34183910的博客 539

通信方式、通信接口、通信总线、通信协议的关系

通信方式、通信接口、通信总线、通信协议的关系

Rocherrr的博客 3万+

Python自动化:3分钟搞定微信收藏链接批量导出到TXT(附完整代码)

本文介绍如何使用Python自动化工具在3分钟内将微信收藏链接批量导出到TXT文件。通过模拟人类操作和OCR技术,脚本高效处理大量链接,显著提升工作效率,并附有完整代码和实战避坑指南。

weixin_29062671的博客 630

SPI、I2C、UART(即串口)三种串行总线详解

以下内容均来源于网络资源的学习与整理,如有侵权请告知删除。参考博客几个串口协议学习整理 UART IIC SPI_mainn的博客-优快云博客SPI、I2C、UART三种串行总线的原理、区别及应用_嵌入式Linux,的博客-优快云博客RS-232 和 UART 之间有什么区别? - 知乎UART和RS232/RS485的关系是什么?串口、COM口、UART口, TTL、RS-232、RS-485区别详解_flyingju的博客-优快云博客_串口类型详细剖析I2C和SPI通信协议的区别_隋边边的博客-C

主要记录嵌入式学习与开发过程。 5万+

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

LIN总线增强型校验_汽车 LIN 总线的前世今生一文带你走向专业纵深

昨天和大家分享了《汽车CAN总线的前世今生》,引起了很多朋友们的兴趣,纷纷留言,希望能够分享汽车用LIN总线的一些相关知识。于是把这篇《汽车LIN总线的前世今生》也分享给大家,文章的标题是我自己修改的。根据大家的理解习惯,对文章内容中部分表述做了修改调整,这篇笔记原创作者为Xiaomin。作为从业多年的我而言,再度学习这些本以为烂熟于心的内容,仍旧会有新的认知和感悟,感觉有必要为热爱学习...

weixin_39705193的博客 1066

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

CAN总线35周年,回顾它的前世今生

关注+星标公众号,不错过精彩内容来源 |安富莱电子编排 |strongerHuang之前给大家分享过很多关于CAN的文章:CANOpen系列教程CAN-FD协议你了解多少?第三代CAN...

strongerHuang 2469

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

SPI、I2C、UART(即串口)三种串行总线对比

以下内容转载自:https://blog.youkuaiyun.com/oqqHuTu12345678/article/details/65445338 以下内容均来源于网络资源的学习整理,如有侵权,请告知删除。 参考博客 https://blog.youkuaiyun.com/xiaodingqq/article/details/80342459 https://blog.youkuaiyun.com/weiqifa0/article/details/8845281 https://www.

Harrison509的博客 1万+

CAN总线的前世今生

CAN总线35周年之际,花了一晚上时间整理翻译了下,通过这个帖子大家会对当前市场上各种CAN名词有的全面系统的认识. 1986年2月,Robert Bosch GmbH在汽车工程师协会(SAE)大会上推出了控制器局域网(CAN)串行总线系统。这是有史以来最成功的网络协议之一诞生的时刻。如今,欧洲几乎所有汽车都配备了至少一个CAN网络。CAN还用于其他类型的车辆,从火车到轮船,以及工业控制中,CAN是最主要的总线协议之一,甚至可能是全球领先的串行总线系统。 从构思到第一个芯片 ...

一口Linux的专栏 1376

常用通信总线协议

主要总结整理了UART、IIC、SPI总线协议相关内容。

superfaith2019的博客 4965

SPI、I2C、UART 三种串行总线对比介绍

1

skitan的博客 8900

计算机组成原理——总线控制(总线判优控制、总线通信控制)

总线控制 一、总线判优控制 1.基本概念: 总线判优控制的集中式方式有三种:链式查询、计数器定时查询、独立请求方式 1.链式查询方式 注:在查询链中离总线控制器最近的部件具有最高优先权,离总线控制器越远,优先权越低。        链式查询通过接口的优先权排队电路实现。        优点:结构简单,主要运用在简单的嵌入式系统中        缺点:速度慢,需要一直向下查询,对...

qq_39368007的博客 3万+
下一篇: 程序内存的各种变量存储区域
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值