STM32软件模拟I2C从机的实现方法

2023-05-16

 1.1 前言

在使用I2C通信时,一般会用到软件模拟I2C。目前网络上能搜索到的软件模拟I2C一般都是模拟I2C主机,很少有模拟I2C从机的例程。由于I2C主机在进行数据收发时,有明确的可预见性,也就是主机明确知道什么时候要进行数据的收发操作,而且I2C的同步时钟信号也是由主机产生的,所以实现起来相对来说比较简单。而I2C从机的通信受制于主机,即什么时候需要进行数据的收发都是由主机发起的,数据收发的发起时机具有随机性。由于实际使用时,MCU的固件还会执行其他的操作,所以如果单纯使用软件查询的方法来判断I2C通信的起始信号不太现实。这里提供一种软件模拟I2C从机的实现方法,考虑使用GPIO中断的方法来及时接收I2C通信的起始信号,并进行数据的收发。

1.2 测试平台

这里使用的开发环境和相关硬件如下。

  •  操作系统:Ubuntu 20.04.2 LTS x86_64(使用uname -a命令查看)
  • 集成开发环境(IDE):Eclipse IDE for Embedded C/C++ Developers,Version: 2021-06 (4.20.0)
  • 编译器:arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10-2020-q4-major) 10.2.1 20201103 (release)
  • 硬件开发板:STM32F429I-DISCO
  • 本文对应的例程代码链接如下。 

        STM32软件模拟I2C从机的例程代码-单片机文档类资源-CSDN下载

1.3 软件模拟I2C从机实现方法

这里结合开发板STM32F429I-DISCO上的STM32F429ZI的单片机来演示软件模拟I2C从机的实现方法。

I2C通信的时序图如下图1所示。

​ 图1 I2C通信时序图

I2C通信的时序中关键的几个点如下。

  1. START和ReSTART信号:用于标识I2C通信的开始,时序特点是SCL为高电平的时候,SDA从高电平变成低电平。
  2. STOP信号:用于标识I2C通信的结束,时序特点是SCL为高电平的时候,SDA从低电平变成高电平。
  3. 应答信号:I2C通信每传输完8个比特的数据位后,紧接着需要传输应答标志位,当该位为0时,是ACK应答信号,该位为1时,是NACK无应答信号。应答信号在SCL的第9个时钟周期的位置。
  4. 数据采集时刻:I2C通信的数据在SCL的上升沿进行采集确认,所以在SCL的高电平期间,数据必须保持不变,防止数据采集出错。当然,START信号和STOP信号的时序在SCL高电平期间是特殊情况,具有专门的含义。
  5. 数据更新时刻:I2C通信的数据更新需要在SCL为低电平的时候进行。

由于各个关键点基本都发生在SCL或SDA的上升沿或者下降沿的地方,所以可以将用于模拟I2C通信引脚的GPIO口配置成边沿中断,这样就可以通过中断实时抓取边沿信号,并在中断中进行及时的数据处理。使用GPIO的边沿中断来模拟I2C从机的好处是可以实时获取到START和STOP信号,I2C主机发过来的数据可以通过中断得到及时处理,而且程序主流程无需关心模拟I2C从机的相关处理,可以处理其他事务。

因为是I2C从机,所以SCL引脚直接固定成输入引脚即可,而SDA信号由于是双向的,所以需要根据I2C通信中的各个状态来设置输入或输出方向。另外,由于GPIO中断只在GPIO配置成输入时才会产生,所以默认情况下,SDA必须设置成输入引脚。

程序的具体设计思路如下。

  1. 将SCL和SDA引脚设置成GPIO的边沿中断模式,默认为输入引脚。I2C通信状态机设置成默认的IDLE状态。SCL的中断用于处理数据的收发,SDA的中断只用于START/ReSTART/STOP这些特殊信号的判断。
  2. SDA引脚中断处理思路:发生下降沿中断,并且SCL为高电平,则收到START信号,状态机更新成START状态;发生上升沿中断,并且SCL为高电平,则收到STOP信号,紧接着I2C通信就应该处于空闲状态,所以这里直接将状态机设置成IDLE状态。
  3. SCL引脚中断处理思路:

        A. 发生下降沿中断时

            A1. 如果状态机为START状态,则I2C通信正式开始,准备开始接收设备地址,状态机更新成DATA状态。

           A2. 如果状态机为DATA状态,SCL下降沿计数小于8时,如果是主机读取数据,则更新SDA的位数据输出。SCL下降沿计数等于8时,进入应答阶段,状态机更新成ACK状态;如果是主机写入数据,并且是设备地址数据,则判断设备地址是否匹配,如果设备地址匹配,则将SDA设置成输出,并输出ACK信号,否则如果地址不匹配,则SDA保持为输入状态,不输出ACK信号;如果是主机读取数据,将SDA设置成输入,准备接收主机的应答信号。

           A3. 如果状态机为ACK状态,这时应答信号已经传输完毕,状态机更新成DATA状态,准备继续接收或发送数据。如果是主机写入数据,将SDA设置成输入,继续接收后续数据;如果是主机读取数据,将SDA设置成输出,继续发送后续数据。

           A4. 如果状态机为NACK状态,说明紧接着I2C通信将停止或重新启动,准备接收STOP或者ReSTART信号,所以需要将SDA设置成输入。此时状态机状态保持不变。

        B. 发生上升沿中断时

           B1. 如果状态机为DATA状态,I2C通信处于数据阶段,如果是主机写入数据,则采集主机通过SDA发送过来的位数据。

           B2. 如果状态机为ACK状态,I2C通信处于应答阶段,如果是主机读取数据,则采集主机的应答信号,如果主机应答信号为1,说明主机发送了NACK的应答,状态机需要更新成NACK状态,准备接收停止或重新启动信号。

1.4 软件模拟I2C从机的代码实现

根据上面的程序思路,可以开始进行程序代码的设计,步骤如下。

1)设计I2C从机通信对应的结构体,I2C通信状态定义,I2C通信相关的宏定义的声明。对应的头文件代码如下。

#ifndef __SW_SLAVE_I2C_H_
#define __SW_SLAVE_I2C_H_

#ifdef __cplusplus
extern "C" {
#endif

#include "stm32f4xx_hal.h"

#define SW_SLAVE_ADDR		0xA2

#define SW_SLAVE_SCL_CLK_EN()	__HAL_RCC_GPIOB_CLK_ENABLE()
#define SW_SLAVE_SDA_CLK_EN()	__HAL_RCC_GPIOB_CLK_ENABLE()

#define SW_SLAVE_SCL_PRT		GPIOB
#define SW_SLAVE_SCL_PIN		GPIO_PIN_6
#define SW_SLAVE_SDA_PRT		GPIOB
#define SW_SLAVE_SDA_PIN		GPIO_PIN_7

#define GPIO_MODE_MSK            0x00000003U

#define I2C_STA_IDLE			0
#define I2C_STA_START			1
#define I2C_STA_DATA			2
#define I2C_STA_ACK			3
#define I2C_STA_NACK			4
#define I2C_STA_STOP			5

#define I2C_READ				1
#define I2C_WRITE			0

#define GPIO_DIR_IN			0
#define GPIO_DIR_OUT			1

#define SET_SCL_DIR(Temp, InOut)				\
	Temp = SW_SLAVE_SCL_PRT->MODER;			\
	Temp &= ~(GPIO_MODER_MODER6);			\
	Temp |= ((InOut & GPIO_MODE_MSK) << (6 * 2U));	\
	SW_SLAVE_SCL_PRT->MODER = temp;

#define SET_SDA_DIR(Temp, InOut)				\
	Temp = SW_SLAVE_SDA_PRT->MODER;			\
	Temp &= ~(GPIO_MODER_MODER7);			\
	Temp |= ((InOut & GPIO_MODE_MSK) << (7 * 2U));	\
	SW_SLAVE_SDA_PRT->MODER = Temp;

#define CLR_SDA_PIN()			(SW_SLAVE_SDA_PRT->BSRR = SW_SLAVE_SDA_PIN << 16)
#define SET_SDA_PIN()			(SW_SLAVE_SDA_PRT->BSRR = SW_SLAVE_SDA_PIN)

typedef struct _SwSlaveI2C_t
{
    	uint8_t State;					// I2C通信状态
	uint8_t Rw;						// I2C读写标志:0-写,1-读
	uint8_t SclFallCnt;				// SCL下降沿计数
	uint8_t Flag;					// I2C状态标志,BIT0:0-地址无效,1-地址匹配
	uint32_t StartMs;				// I2C通信起始时间,单位ms,用于判断通信是否超时
	uint8_t* RxBuf;					// 指向接收缓冲区的指针
	uint8_t* TxBuf;					// 指向发送缓冲区的指针
	uint8_t RxIdx;					// 接收缓冲区数据写入索引,最大值255
	uint8_t TxIdx;					// 发送缓冲区数据读取索引,最大值255
}SwSlaveI2C_t;

extern SwSlaveI2C_t SwSlaveI2C;

void InitSwSlaveI2C(void);
void I2cGpioIsr(void);
void CheckSwSlaveI2cTimeout(void);

#ifdef __cplusplus
}
#endif

#endif /* __SW_TIMER_H_ */

2)I2C通信引脚SCL/SDA对应的GPIO的初始化。这里使用PB6/PB7引脚。代码如下。

void InitSwSlaveI2C(void)
{
  GPIO_InitTypeDef   GPIO_InitStructure;
​
  /* Enable I2C GPIO clock */
  SW_SLAVE_SCL_CLK_EN();
  SW_SLAVE_SDA_CLK_EN();
​
  /* Configure SCL GPIO pin */
  GPIO_InitStructure.Pin       = SW_SLAVE_SCL_PIN;
  GPIO_InitStructure.Mode      = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStructure.Pull      = GPIO_PULLUP;
  GPIO_InitStructure.Speed     = GPIO_SPEED_FAST;
  HAL_GPIO_Init(SW_SLAVE_SCL_PRT, &GPIO_InitStructure);
​
  /* Configure SDA GPIO pin */
  GPIO_InitStructure.Pin       = SW_SLAVE_SDA_PIN;
  HAL_GPIO_Init(SW_SLAVE_SDA_PRT, &GPIO_InitStructure);
​
  /* Configure SCL GPIO pin as input interruption with pull up */
  GPIO_InitStructure.Pin       = SW_SLAVE_SCL_PIN;
  GPIO_InitStructure.Mode      = GPIO_MODE_IT_RISING_FALLING;
  HAL_GPIO_Init(SW_SLAVE_SCL_PRT, &GPIO_InitStructure);
​
  /* Configure SDA GPIO pin as input interruption with pull up */
  GPIO_InitStructure.Pin       = SW_SLAVE_SDA_PIN;
  HAL_GPIO_Init(SW_SLAVE_SDA_PRT, &GPIO_InitStructure);
​
  /* Enable and set EXTI Line9_5 Interrupt to the highest priority */
  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}

3)由于SCL/SDA引脚被设置成中断引脚,需要实现GPIO的中断处理函数。中断处理函数中已经包含了软件模拟I2C从机的所有功能。代码如下。其中EXTI9_5_IRQHandler为STM32外部line9-5中断的入口函数,在该入口函数中调用模拟I2C从机的GPIO口中断处理函数I2cGpioIsr()。

void EXTI9_5_IRQHandler(void)
{
  I2cGpioIsr();
}
​
void I2cGpioIsr(void)
{
  uint32_t temp;
​
  // 处理SCL的上下沿中断
  if(__HAL_GPIO_EXTI_GET_IT(SW_SLAVE_SCL_PIN) != RESET)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(SW_SLAVE_SCL_PIN);
    // 更新通信起始时间
    SwSlaveI2C.StartMs = HAL_GetTick();
    // SCL的下降沿事件处理,此时需要更新要传输的数据
    if((SW_SLAVE_SCL_PRT->IDR & SW_SLAVE_SCL_PIN) == (uint32_t)GPIO_PIN_RESET)
    {
      switch(SwSlaveI2C.State)
      {
        case I2C_STA_START:    // 起始信号的下降沿,初始化相关参数并转到接收比特数据状态
          SwSlaveI2C.SclFallCnt = 0;
          SwSlaveI2C.RxIdx = 0;
          SwSlaveI2C.TxIdx = 0;
          SwSlaveI2C.Flag = 0;  // 默认地址不匹配
          SwSlaveI2C.RxBuf[SwSlaveI2C.RxIdx] = 0;
          SwSlaveI2C.Rw = I2C_WRITE;  // 第1字节为设备地址,一定是写入
          SwSlaveI2C.State = I2C_STA_DATA;
          break;
        case I2C_STA_DATA:
          SwSlaveI2C.SclFallCnt++;
          if(8 > SwSlaveI2C.SclFallCnt)
          {
            // 如果是主机读取数据,则在SCL低电平时更新比特数据
            if(SwSlaveI2C.Rw == I2C_READ)
            {
              if(SwSlaveI2C.TxBuf[SwSlaveI2C.TxIdx] & (1 << (7 - SwSlaveI2C.SclFallCnt)))
              {
                SET_SDA_PIN();
              }
              else
              {
                CLR_SDA_PIN();
              }
            }
          }
          else if(8 == SwSlaveI2C.SclFallCnt)
          {
            if(SwSlaveI2C.Rw == I2C_WRITE)
            {
              // 从第一个地址字节中获取读写标志位,并判断地址是否匹配
              if(SwSlaveI2C.RxIdx == 0)
              {
                if((SwSlaveI2C.RxBuf[0] & 0xFE) == SW_SLAVE_ADDR)
                {
                  SwSlaveI2C.Flag = 1;  // 地址匹配
                  SwSlaveI2C.Rw = SwSlaveI2C.RxBuf[0] & 0x01;
                }
              }
              if(SwSlaveI2C.Flag)
              {
                // 如果是主机写入数据,且地址匹配,则接收完8比特数据后,需要发送ACK信号进行应答
                SET_SDA_DIR(temp, GPIO_DIR_OUT);
                CLR_SDA_PIN();
              }
            }
            else
            {
              // 如果是主机读取数据,需要将SDA设置成输入以便判断应答标志位状态
              SET_SDA_DIR(temp, GPIO_DIR_IN);
              // 如果是主机读取数据,准备发送下一个字节的数据
              SwSlaveI2C.TxIdx++;
            }
            // 接收或发送完8比特数据后,准备发送或接收应答信号
            SwSlaveI2C.State = I2C_STA_ACK;
          }
          break;
        case I2C_STA_ACK:
          SwSlaveI2C.SclFallCnt = 0;
          if(SwSlaveI2C.Rw == I2C_WRITE)
          {
            // 如果是主机写入数据,且ACK发送完毕,则SDA设置成输入,继续接收数据
            SET_SDA_DIR(temp, GPIO_DIR_IN);
            SwSlaveI2C.RxIdx++;
            SwSlaveI2C.RxBuf[SwSlaveI2C.RxIdx] = 0;
          }
          else
          {
            // 如果是主机读取数据,且ACK接收完毕,则SDA设置成输出,继续发送数据
            SET_SDA_DIR(temp, GPIO_DIR_OUT);
            if(SwSlaveI2C.TxBuf[SwSlaveI2C.TxIdx] & 0x80)
            {
              SET_SDA_PIN();
            }
            else
            {
              CLR_SDA_PIN();
            }
          }
          SwSlaveI2C.State = I2C_STA_DATA;
          break;
        case I2C_STA_NACK:    // 如果收到了NACK,则后面将是STOP或者ReSTART信号,需要将SDA设置成输入
          SwSlaveI2C.SclFallCnt = 0;
          SET_SDA_DIR(temp, GPIO_DIR_IN);
          break;
      }
    }
    // SCL的上升沿事件处理,此时需要采集数据,而且在数据阶段,SCL高电平时数据必须保持不变
    else
    {
      switch(SwSlaveI2C.State)
      {
        case I2C_STA_DATA:  // 数据阶段,如果是主机写入数据,则采集比特数据
          if((I2C_WRITE == SwSlaveI2C.Rw) && (8 > SwSlaveI2C.SclFallCnt))
          {
            if(SW_SLAVE_SDA_PRT->IDR & SW_SLAVE_SDA_PIN)
            {
              SwSlaveI2C.RxBuf[SwSlaveI2C.RxIdx] |= (1 << (7 - SwSlaveI2C.SclFallCnt));
            }
          }
          break;
        case I2C_STA_ACK:  // 应答阶段,如果是主机读取数据,则判断ACK/NACK信号,默认状态是ACK
          if((SwSlaveI2C.Rw == I2C_READ) && (SW_SLAVE_SDA_PRT->IDR & SW_SLAVE_SDA_PIN))
          {
            SwSlaveI2C.State = I2C_STA_NACK;
          }
          break;
      }
    }
  }
  else if(__HAL_GPIO_EXTI_GET_IT(SW_SLAVE_SDA_PIN) != RESET)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(SW_SLAVE_SDA_PIN);
    if((SW_SLAVE_SDA_PRT->IDR & SW_SLAVE_SDA_PIN) == (uint32_t)GPIO_PIN_RESET)
    {
      // SCL为高电平时,SDA从高变低,说明是起始信号
      if(SW_SLAVE_SCL_PRT->IDR & SW_SLAVE_SCL_PIN)
      {
        SwSlaveI2C.State = I2C_STA_START;
      }
    }
    else
    {
      // SCL为高电平时,SDA从低变高,说明是停止信号,一次I2C通信结束,直接将状态设置成空闲
      if(SW_SLAVE_SCL_PRT->IDR & SW_SLAVE_SCL_PIN)
      {
        SwSlaveI2C.State = I2C_STA_IDLE;
      }
    }
  }
}

4)为了确保模拟I2C从机通信的可靠性,额外设计了I2C通信超时处理函数。在I2C通信进行的过程中,如果通信出现了中断,则通过超时判断来重置I2C从机状态,确保出现通信异常时可以从异常状态中自动恢复。该函数需要在主流程中调用。代码如下。

void CheckSwSlaveI2cTimeout(void)
{
  uint32_t TimeMs, TimeCurMs;
​
  if(SwSlaveI2C.State != I2C_STA_IDLE)
  {
    TimeCurMs = HAL_GetTick();
    if(TimeCurMs >= SwSlaveI2C.StartMs)
    {
      TimeMs = TimeCurMs - SwSlaveI2C.StartMs;
    }
    else
    {
      TimeMs = ~(SwSlaveI2C.StartMs - TimeCurMs) + 1;
    }
    if(500 <= TimeMs)
    {
      // I2C通信超时的话,重置状态机,并把SDA设置成输入
      SwSlaveI2C.State = I2C_STA_IDLE;
      SET_SDA_DIR(TimeMs, GPIO_DIR_IN);
    }
  }
}

5)软件模拟I2C从机相关功能验证代码。这里需要借助STM32的另外一个I2C主机进行配合测试。这里将PF0/PF1对应的引脚配置成I2C主机,主机直接使用STM32的硬件I2C实现。PF0/PF1分别和PB7/PB6连接,然后验证数据收发的正确性。具体代码参见上面的工程链接。这里只展示最终的测试结果数据。如下图2和图3所示。

​ 软件模拟I2C从机状态

I2C主机发送数据

 软件模拟I2C从机接收数据

图2 软件模拟I2C从机数据接收验证结果

 软件模拟I2C从机状态

 软件模拟I2C从机发送数据

 I2C主机接收数据

图3 软件模拟I2C从机数据发送验证结果

1.5 软件模拟I2C从机的注意事项

本例程中,对于400kbps速率的I2C通信,在进行代码编译链接时,需要使用-Ofast的优化方式,以提高中断处理函数的执行速度,使程序能正确执行。如果使用默认的无优化配置,会造成程序无法正确运行。

对于主频比较低的MCU,使用这里提供的软件模拟I2C从机进行I2C通信时,建议使用100kpbs以下的通信速率,并且注意使用可以提高代码执行速度的代码优化配置。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32软件模拟I2C从机的实现方法 的相关文章

  • 133-基于stm32单片机停车场车位管理系统Proteus仿真+源程序

    资料编号 133 一 功能介绍 1 采用stm32单片机 4位数码管 独立按键 制作一个基于stm32单片机停车场车位管理系统Proteus仿真 2 通过按键进行模拟车辆进出 并且通过程序计算出当前的剩余车位数量 3 将剩余的车位数量显示到
  • STM32F4 通过软复位跳转到引导加载程序,无需 BOOT0 和 BOOT1 引脚

    我问这个问题是因为可以在这里找到类似问题的答案 通过应用程序跳转到 STM32 中的引导加载程序 即从用户闪存在引导模式下使用引导 0 和引导 1 引脚 用户 JF002 JF002回答 当我想跳转到引导加载程序时 我在其中一个备份寄存器中
  • 如何为连接到 I2C gpio 扩展器的虚拟 mdio-gpio 设备配置 ACPI *.asl

    我正在使用 Q7 模块 x86 并尝试在 Linux 上使用 ACPI SSDT Overlay 配置我们的外设 但我对此很挣扎 我想我误解了ACPI的一些核心概念 Problem CPU gt I2C gt PCA9575 GPIO Ex
  • HAL 锁定和解锁函数如何使用以及为什么?

    我试图理解另一位程序员编写的代码 它使用了I C http en wikipedia org wiki I C2 B2C通信以将数据写入 STM32 微控制器的 EEPROM 一般来说 我理解他的代码是如何工作的 但我不明白他为什么使用HA
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 在 Atollic TrueStudio、STM32CubeMX 中导入 C 库

    我目前正在开发 STM32F767ZI Nucleo 板和一个小安全芯片 microchip atecc508a 通过 i2c 连接进行连接 该芯片有一个可用的库加密验证库 https github com MicrochipTech cr
  • 跟着野火学FreeRTOS:第一段(任务定义,切换以及临界段)

    在裸机系统中 系统的主体就是 C P U CPU CP U 按照预先设定的程序逻辑在 m a i n
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • STM32 暂停调试器时冻结外设

    当到达断点或用户暂停代码执行时 调试器可以停止 Cortex 中代码的执行 但是 当皮质停止在暂停状态下执行代码时 调试器是否会冻结其他外设 例如 DMA UART 和定时器 您只能保留时间 r 取决于外围设备 我在进入主函数时调用以下代码
  • 如何将 I2C 地址传递给 Adafruit CircuitPython 代码? (运行 ADS1115)

    我正在尝试使用两个 I2C 地址 0x48 0x49 在一个 Raspberry Pi 上运行两个 Adafruit ADS1115 每个设备的地址可以通过将 ADDR 引脚连接为高电平 0x49 或使其悬空 默认为 0x48 来设置 我已
  • for循环延时时间计算

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 之前做led点亮的实验 好像是被delay函数影响了 因为delay参数设置的不对
  • 特殊寄存器

    特殊寄存器 文章目录 前言 一 背景 二 2 1 2 2 总结 前言 前期疑问 STM32特殊寄存器到底是什么 特殊寄存器怎么查看和调试代码 本文目标 记录和理解特殊寄存器 一 背景 最近在看ucosIII文章是 里面提到特殊寄存器 这就进
  • 核心耦合内存在 STM32F4xx 上可执行吗?

    尝试从 STM32F429s CCM 运行代码 但每当我命中 CCM 中的第一条指令时 我总是会遇到硬故障 并且 IBUSERR 标志被设置 该指令有效且一致 STM32F4xx 是否可能不允许从 CCM 执行 数据访问效果良好 alios
  • 通过JTAG恢复STM32 MCU磨掉的标记

    我有一块可能带有 STM32 MCU 的板 我想为该板制作定制固件 因为库存板有很多问题 不幸的是 电路板制造商很友善地磨掉了所有标记 有没有办法通过 jtag 获取设备 系列 ID 并将其交叉引用到型号 我能找到的一切都是关于获取芯片的唯
  • STM32 上的 ADC 单次转换

    我正在研究 STM32 F103x 上的 ADC 编程 并从最简单的情况 单次转换开始 测量内部温度传感器 连接到 ADC1 的值 并使用 USART 将其发送到 COM 端口 目标似乎很明确 但是当我尝试将源代码下载到闪存时 它不会向 C
  • ESP8266 I2C从机不确认数据

    我有一个 TM4C123 处理器作为 I2C 主处理器 一个 ESP8266 作为从处理器 对于 ESP 我使用的是 Arduino IDE 并在 2 5 2 版安装了 ESP8266 支持 它应该支持 I2C 从模式 但是 我无法让它工作
  • STM32 传输结束时,循环 DMA 外设到存储器的行为如何?

    我想问一下 在以下情况下 STM32 中的 DMA SPI rx 会如何表现 我有一个指定的 例如 96 字节数组 名为 A 用于存储从 SPI 接收到的数据 我打开循环 SPI DMA 它对每个字节进行操作 配置为 96 字节 是否有可能
  • Linux、ARM:为什么仅当启动时存在 I2C GPIO 扩展器时才创建 gpiochip

    在 imx6sx 硬件平台 NXP 嵌入式 ARM 上使用 Linux 3 14 52 问题是设备树中指定的 PCF8575 I2C GPIO 扩展器不会实例化为 sys class gpio 结构中的设备 除非它们在内核启动期间存在 这些

随机推荐

  • freertos应用程序常见错误排查

    freertos系统应用程序常见问题 对一些比较常见的问题 xff0c 下面简要的以 FAQ 问答 的形式给出可能的原因和解决方法 问题现象 xff1a 在一个 Demo 应用程序中增加了一个简单的任务 xff0c 导致应用程序崩溃 任务创
  • keil5编译工程常见问题汇总

    简介 我们在编译keil工程的时候总是遇到很多问题 xff0c 我把一些常见的问题和解决方案汇总下来 xff0c 仅供大家参考 问题汇总 问题1 问题描述 选择arm v6版本编译器 xff0c 编译keil5工程 xff0c 报错 xff
  • mdk arm debug配置

    简述 本文简要讲述启动调试之前如何配置debug 点击魔术棒 xff0c 进入debug选项界面 xff0c 如下图 xff1a 我们可以选择软件仿真 xff0c 也可以选择硬件仿真 xff08 软件仿真不需要接开发板和仿真器 xff09
  • stm32高级定时器实现pwm互补输出

    简介 stm32设备一般都有很多类型的定时器 xff0c 常见的有systick timer 基本定时器 通用定时器 高级定时器 看门狗定时器 RTC等等 xff0c 本文简单介绍高级定时器是如何实现pwm互补输出 详细 我这里使用的dev
  • shell脚本基础知识(入门)

    简介 本文会全面介绍shell脚本的基础知识 脚本格式 要把shell命令放到一个 脚本 当中 xff0c 有一个要求 xff1a 脚本的第一行必须写成类似这样的格式 xff1a bin bash bash是一个shell解释器 xff0c
  • 记ADB shell for循环踩坑

    abd 里面的shell的电脑Linux的shell有点不太一样 以下这些案例均不能执行 xff1a for i 61 1 i lt 61 100 i 43 43 do echo i done for i in 1 100 do echo
  • linux线程调度策略简述

    简述 linux系统调度执行的最小单位是线程 xff0c 线程的调度策略有以下三种 xff1a xff08 1 xff09 SCHED FIFO 其静态优先级必须设置为1 99 xff0c 这将意味着一旦线程处于就绪态 xff0c 他就能立
  • stm32串口发送接口

    简介 本文记录一下stm32标准库实现串口发送功能和接收功能的接口函数 轮询方式发送串口数据 1 标准库实现 span class token comment 61 61 61 61 61 61 61 61 61 61 61 61 61 6
  • linux系统线程池

    简述 一个进程中的线程就好比是一家公司里的员工 xff0c 员工的数目应该根据公司的业务多少来定 xff0c 太少了忙不过来 xff0c 但是太多了也浪费资源 最理想的情况是让进程有一些初始数目的线程 xff08 线程池 xff09 xff
  • STM32串口环形缓冲区

    目录 1 xff1a 概述 2 xff1a 代码 1 xff1a 概述 1 1 xff1a 本篇实现串口驱动 xff0c 实现printf函数的重定向 xff0c 实现串口的中断接受和发送 xff0c 效仿modbus协议中的3 5T超时机
  • 天地飞6通道遥控器解码

    在做四轴 xff0c 想到要改造一下遥控器 我用的是天地飞6通道2 4G版 改造的思路是这样的 xff1a 用M8单片机读取PPM信号 xff0c 用液晶显示出6个通道的解码 xff0c 当然这个解码还需要根据飞控板进行一下 校准 xff0
  • 如何计算网络协议校验和以及为什么这么计算

    一 校验和是如何计算的 xff1f 这个问题牵扯到计算机最底层最神秘的部分 二进制运算 说实话 xff0c 从学计组计统起 xff0c 我就比较讨厌思考二进制的相关运算 但毕竟是学这个的 xff0c 只好硬着头皮想了 首先发送方校验和的计算
  • JAVA又臭又长,是一门垃圾语言,早晚会被淘汰

    1 JAVA又臭又长 xff0c 是一门垃圾语言 xff0c 早晚会被淘汰 2 JAVA能做的 xff0c python 等上层解释类语言大部分都可以坐到 3 JAVA为了面向对象而面向对象 xff0c 导致程序冗长 xff0c 效率低下
  • 封装OkHttp

    懒汉 安全 加同步 私有的静态成员变量 只声明不创建 私有的构造方法 提供返回实例的静态方法 private static OkHttpClient okHttpClient 61 null private OkHttp3Util publ
  • WXP380 电容式MEMS高性能数字气压传感器 电容式MEMS压力传感器快速代DPS310 BMP380 SLP06 HP303B “电容式”噪声超低的高精度MEMS气压传感器;

    午芯高科WXP380气压传感器 稳定供应 填补国内空白 2 WXP380气压传感器的 电容式 MEMS芯片填补了国内技术空白 xff01 高性能 xff1a 电容式 噪声超低的高精度MEMS气压传感器 xff1b 高度差测量精确度小至2cm
  • 解决ros的rosdep update报错的问题。

    参见以下文章 xff1a https blog csdn net leida wt article details 115120940 如果 xff1a https raw githubusercontent com ros rosdist
  • Docker报错Error spawning command line “dbus-launch --autolaunch=xxx --binary-syntax --close-stderr”

    环境 xff1a ubuntu 18 报错描述 xff1a Error spawning command line dbus launch autolaunch 61 a3d9197b765643568af09eb2bd3e5ce7 bin
  • 一种测量STM32程序执行时间的方法

    同名微信公众号 固件工人 同步发布的文章 xff0c 欢迎同时关注 xff0c 及时获取最新文章 1 1 背景 在单片机的固件开发过程中 xff0c 有的时候需要评估固件代码的执行性能 xff0c 会对部分关键程序代码的执行时间进行测量 通
  • STM32使用内核System Tick定时器实现微秒延时

    同名微信公众号 固件工人 同步发布的文章 xff0c 欢迎同时关注 xff0c 及时获取最新文章 在单片机的程序开发中 xff0c 延时一般都会用到 xff0c 在对延时精度要求不高的场合 xff0c 一般使用软件延时实现 xff0c 就是
  • STM32软件模拟I2C从机的实现方法

    1 1 前言 在使用I2C通信时 xff0c 一般会用到软件模拟I2C 目前网络上能搜索到的软件模拟I2C一般都是模拟I2C主机 xff0c 很少有模拟I2C从机的例程 由于I2C主机在进行数据收发时 xff0c 有明确的可预见性 xff0