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

news2024/9/23 16:13:55

环境

芯片: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或NACK
          if (READ_SDA)
          {
          // 如果SDA为高电平,则发送NACK,表示未收到预期的ACK
          r=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)):定时器计数值不为0
          SysTick->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 ms
              repeat--;
          }
          if(remain)
          {
              Delay_us(remain*1000); // 延时remain ms
          }
      }
      
      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2068120.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【电子数据取证】应用程序提取及固定

文章关键词&#xff1a;电子数据取证、手机取证、计算机取证、计算机仿真、云取证 一、前言 在数字化时代&#xff0c;电子数据已成为现代社会不可或缺的一部分&#xff0c;它不仅记录着个人的日常生活轨迹&#xff0c;也承载着企业运营的核心信息&#xff0c;更在司法体系中…

Nginx知识详解(理论+实战更易懂)

目录 一、Nginx架构和安装 1.1 Nginx 概述 1.1.1 nginx介绍 1.1.2 Nginx 功能介绍 1.1.3 基础特性 1.1.4 Web 服务相关的功能 1.2 Nginx 架构和进程 1.2.1 Nginx 进程结构 1.2.2 Nginx 进程间通信 1.2.3 Nginx 启动和 HTTP 连接建立 1.2.4 HTTP 处理过程 1.3 Nginx …

pdf转换成excel在线转换?这3款别错过

作为一名财务人员&#xff0c;我每天的工作都离不开处理大量的文件和数据。其中&#xff0c;将PDF格式的报表转换成Excel表格是一项经常要做的工作。在众多的PDF转换工具中&#xff0c;我试过了三款PDF转换工具&#xff0c;现在就来分享一下我的使用体验。 一、福昕PDF转换大师…

普元EOS-低开页面下拉选择控件加载列表数据

1 前言 普元EOS进行低代码开发页面可以高效提高开发效率&#xff0c;并且减少代码的出错机会。 在低代码开发页面的时候&#xff0c;表单页面中可以使用大量的常用控件。 本文将讲解下拉选择组件的使用。 2 下拉选择使用EOS内置字典作为数据源 下拉选择可从字典作为数据源&a…

25届网安秋招面试之后台信息泄露

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330#rd 《网安面试指南》http://mp.weixin.qq.com/s…

C++ TinyWebServer项目总结(9. I/O 复用)

I/O 复用使得程序能够同时监听多个文件描述符&#xff0c;从而提高程序的性能。I/O 复用本身是阻塞的。Linux 下实现 I/O 复用的系统调用主要有 select、poll 和 epoll。 select 系统调用 select API select系统调用&#xff1a;在一段指定时间内&#xff0c;监听用户感兴趣…

Java语言程序设计——篇十七(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

每日OJ_牛客_美国节日(日期模拟)

目录 牛客_美国节日&#xff08;日期模拟&#xff09; 解析代码 牛客_美国节日&#xff08;日期模拟&#xff09; 美国节日__牛客网 解析代码 题目表述很明白&#xff0c;难点在于要求一个月第N个星期W。那么面对这个问题&#xff0c;拆解的思路是&#xff0c;首先&#xff…

Navicat for MySQL:卓越的跨平台数据库管理开发工具

Navicat for MySQL是一款专为数据库管理员和开发人员设计的强大数据库管理开发工具&#xff0c;支持Mac和Windows操作系统&#xff0c;为用户提供了高效、便捷的数据库操作体验。无论是管理MySQL还是MariaDB数据库&#xff0c;Navicat for MySQL都能轻松胜任。 一、直观易用的…

DB-GPT开源项目文档入门

DB-GPT开源项目文档入门 (qq.com) 场景&#xff1a;服务中小金融机构、服务业小微商家 DB-GPT项目集成了多模型管理、多数据源管理、Text2SQL、增强检索RAG、生成式BI、多智能体&#xff0c;一个大而全的开源框架 项目基本信息 简介&#xff1a;一个原生数据应用开发框架 …

<数据集>流水线纸箱识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1395张 标注数量(xml文件个数)&#xff1a;1395 标注数量(txt文件个数)&#xff1a;1395 标注类别数&#xff1a;2 标注类别名称&#xff1a;[GreenCarton,RedCarton] 序号类别名称图片数框数1GreenBox131728482R…

《计算机操作系统》(第4版)第9章 操作系统接口 复习笔记

第9章 操作系统接口 一、用户接口 1. 字符显示式联机用户接口 (1)命令行方式 该方式是以行为单位&#xff0c;输入和显示不同的命令。每行长度一般不超过256个字符&#xff0c;一般情况下&#xff0c;以回车符作 为一个命令的结束标记。通常&#xff0c;命令的执行采用的是间断…

抖音收回“作品代发布”能力?短视频矩阵工具未来何去何从?

相信不少朋友都看到过抖音开放平台的公示&#xff0c;从2024年7月20日开始&#xff0c;官方将要收回“代替用户发布内容到抖音”能力&#xff0c;如果你还在用原有抖音开放平台接口的矩阵系统&#xff0c;那可要注意及时调整了。 那未来的短视频矩阵工具还能用吗&#xff1f;矩…

数学建模~~描述性分析---RFM用户分层模型聚类

目录 1.RFM用户分层模型介绍 2.获取数据&#xff0c;标准化处理 2.1获取数据 2.2时间类型转换 2.3计算时间间隔 3.对于R,F,M的描述性分析 3.1代码分析 3.2分析结果说明 3.3对于F的描述性分析 3.4对于M的描述性分析 4.数据分箱--等级划分 4.1分箱概念 4.3分箱特点 …

区间合并+并查集

前言&#xff1a;写完这个题目的时候没意识到这个和区间合并是等价的&#xff0c;现在看起来确实是一个区间合并的题目 &#xff08;注意这个多米诺骨牌是可以连续推倒的&#xff09; #include<bits/stdc.h> using namespace std;#define int long long int t; int n,m; …

转发和重定向的区别

转发和重定向的区别 转发是一次请求。因此浏览器地址栏上的地址不会发生变化。 重定向是两次请求。因此浏览器地址栏上的地址会发生变化 转发的代码实现&#xff1a;request.getRequestDispatcher(“/index”).forward(request,response); 重定向的代码实现&#xff1a;resp…

学习 Java 和数据库:从前端到全栈的进阶之路

作为一名前端开发者&#xff0c;掌握 Java 和数据库知识不仅能提升你的技术水平&#xff0c;还能让你在全栈开发的道路上走得更远&#xff08;主要是涨薪&#xff09;。本文将为你提供一个详尽的学习大纲&#xff0c;帮助你从零开始学习 Java 和数据库&#xff0c;并解释为什么…

Windows离线安装openSSH服务实现远程访问

1、问题概述? 在企业的实际的开发环境中,我们的计算机可能是没有网络的,这种时候我们安装openSSH就需要通过离线的方式安装。 1.1、下载openSSH离线包 离线下载地址:https://github.com/PowerShell/Win32-OpenSSH/releases msi格式可以在windows中直接安装 下载之后直接…

机器人拾取系统关节机械臂通过NY-PN-EIPZ进行命令控制

关节机械臂是一种精密的机器&#xff0c;旨在模拟人类手臂在订单拣选操作中的运动。这些多功能机器人由多个关节组成&#xff0c;通常有 4 到 7 个轴&#xff0c;使它们能够高度自由地移动&#xff0c;并在仓库内以各种方向和位置接触物品。 制造工厂智能仓库系统中的关节机械臂…

centos7.9系统安装cloudpods(一)

1. 简介&#xff1a; Cloudpods 是一款简单、可靠的企业IaaS资源管理软件。帮助未云化企业全面云化IDC物理资源&#xff0c;提升企业IT管理效率。 Cloudpods 帮助客户在一个地方管理所有云计算资源。统一管理异构IT基础设施资源&#xff0c;极大简化多云架构复杂度和难度&…