当前位置: 首页 > news >正文

I2C软件模拟与Delay寄存器延迟函数

环境

芯片:STM32F103ZET6

库:来自HAL的STM32F1XX.H

原理图

有图可知SCL和SDA两条线接到了PB10和PB11

  • Driver_I2C.h

    • #ifndef __DRIVER_I2C
      #define __DRIVER_I2C#include "stm32f1xx.h"
      #include "Com_Delay.h"
      // 定义拉高SCL引脚的宏操作
      #define SCL_HIGH (GPIOB->ODR |= GPIO_ODR_ODR10)
      // 定义拉低SCL引脚的宏操作
      #define SCL_LOW (GPIOB->ODR &= ~GPIO_ODR_ODR10)// 定义拉高SDA引脚的宏操作
      #define SDA_HIGH (GPIOB->ODR |= GPIO_ODR_ODR11)
      // 定义拉低SDA引脚的宏操作
      #define SDA_LOW (GPIOB->ODR &= ~GPIO_ODR_ODR11)// 定义读取SDA引脚状态的宏操作
      #define READ_SDA (GPIOB->IDR & GPIO_IDR_IDR11)// 定义I2C通信中的短暂延时宏
      #define I2C_DELAY Delay_us(5)// 定义ACK和NACK信号的常量
      #define ACK 0
      #define NACK 1// 初始化I2C驱动的函数声明
      void Driver_I2C_Init(void);// I2C的启动信号函数声明,发送启动信号
      void Driver_I2C_Start(void);
      // I2C的停止信号函数声明,发送停止信号
      void Driver_I2C_Stop(void);// 发送ACK信号的函数声明
      void Driver_I2C_ACK(void);
      // 发送NACK信号的函数声明
      void Driver_I2C_NACK(void);// 等待并返回ACK信号的函数声明,返回值为接收到的ACK/NACK信号
      uint8_t Driver_I2C_WaitACK(void);// 发送一个字节数据的函数声明
      void Driver_I2C_SendChar(uint8_t ch);// 读取一个字节数据的函数声明,返回值为读取到的数据字节
      uint8_t Driver_I2C_ReceiveChar(void);
      #endif
      
  • Driver_I2C.c

    • #include "Driver_I2C.h"/*** I2C驱动初始化函数* * 使用软件模拟的I2C,这意味着我们不需要利用STM32的硬件I2C外设* 而是通过GPIO的基本功能来实现I2C通信,具体为:* - 配置PB10作为SCL* - 配置PB11作为SDA* * 主要工作步骤:* 1. 使能相关时钟* 2. 配置GPIO引脚模式*/
      void Driver_I2C_Init(void)
      {// 使能GPIOB时钟,为PB10和PB11的配置做准备RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;// 配置PB10和PB11为开漏输出模式,以支持I2C通信的需要// 这里通过掩码操作清除了有关配置位,然后设置为开漏输出模式GPIOB->CRH &= ~(GPIO_CRH_CNF10_1 | GPIO_CRH_CNF11_1);GPIOB->CRH |= (GPIO_CRH_CNF10_0 | GPIO_CRH_CNF11_0 | GPIO_CRH_MODE10 | GPIO_CRH_MODE11);
      }/*** @brief I2C通信开始函数* * 本函数用于初始化I2C通信,并发送起始信号。在I2C通信的开始,* 需要确保SDA和SCL引脚都处于高电平状态,然后通过将SDA引脚拉低* 而SCL引脚保持高电平来产生起始条件。*/
      void Driver_I2C_Start(void)
      {// 保持初始状态SDA_HIGH;SCL_HIGH;I2C_DELAY;// 发送起始信号SDA_LOW;I2C_DELAY;
      }/*** Driver_I2C_Stop函数用于在I2C通信中发送停止信号。* * 该函数通过拉低SCL和SDA信号线到低电平,然后在SCL为高电平的时候将SDA线拉高,从而发送一个标准的I2C停止信号。* 这个操作确保了在I2C总线上其他设备能够识别到当前传输已经结束。*/
      void Driver_I2C_Stop(void)
      {// 保持初始状态,确保SCL和SDA均为低电平SCL_LOW;SDA_LOW;I2C_DELAY;// 发送停止信号,当SCL为高电平时,将SDA拉高SCL_HIGH;I2C_DELAY;SDA_HIGH;I2C_DELAY;
      }/*** 函数名: Driver_I2C_ACK* 功能: 在I2C通信中发送一个ACK信号* 描述:*   该函数通过控制I2C总线上的SCL(时钟)和SDA(数据)信号来发送一个ACK(Acknowledge)信号。*   ACK信号用于响应接收到的字节,表示接收器已成功接收该字节。*   函数首先设置数据线SDA为高电平,时钟线SCL为低电平,进入空闲状态。*   然后将数据线SDA拉低,开始发送ACK(0)。*   随后时钟线SCL被拉高,完成数据的发送。*   最后恢复时钟线和数据线到空闲状态。* 注意: 该函数使用了特定的宏定义SCL_LOW, SDA_HIGH, SDA_LOW, SCL_HIGH和I2C_DELAY来控制I2C总线信号。*/
      void Driver_I2C_ACK(void)
      {// 设置传输数据时的空闲状态SCL_LOW;SDA_HIGH;I2C_DELAY;// 发送ACK(0)SDA_LOW;I2C_DELAY;// 发送数据,伴随SCL上升沿SCL_HIGH;I2C_DELAY;// 恢复时钟线和数据线SCL_LOW;I2C_DELAY;SDA_HIGH;I2C_DELAY;
      }// 函数名称:Driver_I2C_NACK
      // 功能:处理I2C通信中的非应答(NACK)情况
      // 该函数通过设置I2C线路的状态来通知其他设备当前设备不响应
      void Driver_I2C_NACK(void)
      {// 保持初始状态SCL_LOW;SDA_HIGH;I2C_DELAY;// 准备数据// 发送数据SCL_HIGH;I2C_DELAY;// 恢复时钟线和数据线
      }/*** @brief 等待并检测I2C通信中的ACK信号* * 此函数用于在I2C通信中发送数据后等待并检测从设备返回的ACK(应答信号)或NACK(非应答信号)。* 它通过操纵SCL(时钟线)和SDA(数据线)来同步和检测ACK信号。如果检测到ACK,则返回ACK;* 否则,返回NACK,表示从设备未正确接收到数据或未响应。* * @return uint8_t 返回ACK表示成功接收到ACK信号,返回NACK表示未接收到ACK信号。*/
      uint8_t Driver_I2C_WaitACK(void)
      {// 将SCL线设置为低电平,准备开始发送或接收数据SCL_LOW;// 将SDA线设置为高电平,为发送ACK或NACK做准备SDA_HIGH;// 延时以确保信号稳定I2C_DELAY;   // 将SCL线设置为高电平,使能数据传输SCL_HIGH;// 延时以确保数据线稳定I2C_DELAY;// 初始化返回值为ACK,表示准备确认接收到的数据uint8_t r = ACK;// 检查SDA线的状态,决定是否发送ACK或NACKif (READ_SDA){// 如果SDA为高电平,则发送NACK,表示未收到预期的ACKr=NACK;}// 将SCL线再次设置为低电平,完成此次通信SCL_LOW;// 延时以确保信号稳定I2C_DELAY;// 返回确认状态,ACK表示成功,NACK表示失败return r;
      }/*** 通过I2C协议发送一个字符* @param ch 要发送的字符数据* * 本函数实现了在I2C总线上发送一个字符的数据过程* 它通过操纵SCL和SDA线来发送数据位*/
      void Driver_I2C_SendChar(uint8_t ch)
      {// 设置初始状态SCL_LOW;SDA_HIGH;I2C_DELAY;// 从高位到低位依次发送每一位数据for (uint8_t i = 0; i < 8; i++){// 当前位为1时,拉高SDA线if (ch & 0x80){SDA_HIGH;}else{// 当前位为0时,拉低SDA线SDA_LOW;}I2C_DELAY;// 左移数据,准备发送下一位ch <<= 1;// 发送数据,伴随SCL上升沿SCL_HIGH;I2C_DELAY;// 恢复状态,拉低SCL线SCL_LOW;I2C_DELAY;// 准备发送下一个数据位,先拉高SDA线SDA_HIGH;I2C_DELAY;}
      }
      /*** @brief I2C总线接收一个字符* * @return uint8_t 接收到的字符数据*/
      uint8_t Driver_I2C_ReceiveChar(void)
      {uint8_t r = 0;// 设置初始状态SCL_LOW;SDA_HIGH;I2C_DELAY;// 从高位到低位依次接收每一位数据for (uint8_t i = 0; i < 8; i++){// 拉高时钟线,准备接收数据SCL_HIGH;I2C_DELAY;// 接收数据if (READ_SDA){r |= 0x1;}I2C_DELAY;// 除最后一位外,接收的数据左移一位if (i < 7){r <<= 1;}// 恢复时钟线SCL_LOW;I2C_DELAY;}return r;
      }

延迟函数

  • Com_Delay.h

    • //
      // Created by seven on 2024/8/20.
      //#ifndef __DELAY_H
      #define __DELAY_H#include "stm32f1xx.h"void Delay_us(uint32_t nus);
      void Delay_ms(uint32_t nms);#endif
  • Com_Delay.c

    • #include "Com_Delay.h"/// @brief nus延时
      /// @param nus 延时的nus数
      void Delay_us(uint32_t nus)
      {uint32_t temp;SysTick->LOAD=nus*168-1; // 计数值加载SysTick->VAL=0x00; // 清空计数器SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; // 开始计数do{temp=SysTick->CTRL; // 读取控制寄存器状态}while((temp&0x01)&&!(temp&(1<<16))); // temp&0x01:定时器使能,!(temp&(1<<16)):定时器计数值不为0SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; // 关闭计数SysTick->VAL=0x00;// 清空计数器
      }/// @brief nms延时
      /// @param nus 延时的ms数
      void Delay_ms(uint32_t nms)
      {uint32_t repeat=nms/50;uint32_t remain=nms%50;while(repeat){Delay_us(50*1000); // 延时 50 msrepeat--;}if(remain){Delay_us(remain*1000); // 延时remain ms}
      }


http://www.mrgr.cn/news/6293.html

相关文章:

  • 2024河南萌新联赛第五场 C小美想收集(并查集拓展域,2-sat)
  • Python爬虫案例二:获取虎牙主播图片(动态网站)
  • Spring Boot实战:通过Spring Batch处理批量订单数据
  • UDP+TCP
  • 类别特征编码 ———特征工程
  • Unity 编辑器-UGUI拓展Button,一个和原Button一样按钮⭐
  • AI大模型日报#0820:DeepMind创始人访谈、阿里多模态mPLUG-Owl3、抱抱脸SOTA小模型
  • P1167 刷题
  • GIS空间数据库,基本概念
  • docker相关
  • 蛋托清洗机的优势特点以及维护和保养:
  • TCP的连接建立及报文段首部格式
  • Android CCodec Codec2 (三)C2Param - Ⅰ
  • C# Dictionary->ConcurrentDictionary和哈希表
  • 【速览】计算机网络(更新中)
  • 【Spring Boot】全局异常处理
  • 微信小程序:浮动按钮
  • jenkins 修改访问路径
  • node.js express创建本地服务以及使用pm2启动服务
  • 25届网安秋招面试之后台信息泄露