C++例程:使用I/O模拟IIC接口(6)
完整的STM32F405代码工程I2C驱动源代码跟踪
一)myiic.c
#include "myiic.h"
#include "delay.h"
#include "stm32f4xx_rcc.h"
//初始化IIC
void IIC_Init(void)
{ GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟//SCL_1->GPIOA0,SDA_1->GPIOA1GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化SCL_1=1;SDA_1=1;
}//产生IIC1起始信号
void IIC1_Start(void)
{SDA1_OUT(); //sda线输出SDA_1=1; SCL_1=1;delay_us(4);delay_us(4);SDA_1=0;//START:when CLK is high,DATA change form high to low delay_us(4);delay_us(4);SCL_1=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC1_Stop(void)
{SDA1_OUT();//sda线输出SCL_1=0;SDA_1=0;//STOP:when CLK is high DATA change form low to highdelay_us(4);delay_us(4);SCL_1=1; delay_us(4);delay_us(4);SDA_1=1;//发送I2C总线结束信号delay_us(4);delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC1_Wait_Ack(void)
{u8 ucErrTime=0;SDA1_IN(); //SDA设置为输入 SDA_1=1;delay_us(1); SCL_1=1;delay_us(1); while(READ_SDA1){ucErrTime++;if(ucErrTime>250){IIC1_Stop();return 1;}}SCL_1=0;//时钟输出0 return 0;
} //产生ACK应答
void IIC1_Ack(void)
{SCL_1=0;SDA1_OUT();SDA_1=0;delay_us(2);delay_us(2);SCL_1=1;delay_us(2);delay_us(2);SCL_1=0;
}//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC1_Send_Byte(u8 txd)
{ u8 t; SDA1_OUT(); SCL_1=0;//拉低时钟开始数据传输for(t=0;t<8;t++){ SDA_1=(txd&0x80)>>7;txd<<=1; delay_us(2); //对TEA5767这三个延时都是必须的delay_us(2);SCL_1=1;delay_us(2); delay_us(2);SCL_1=0; delay_us(2);delay_us(2);}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC1_Read_Byte(unsigned char ack)
{unsigned char i,receive=0;SDA1_IN();//SDA设置为输入for(i=0;i<8;i++ ){SCL_1=0; delay_us(2);delay_us(2);SCL_1=1;receive<<=1;if(READ_SDA1)receive++; delay_us(1); delay_us(1);} if (!ack)IIC1_NAck();//发送nACKelseIIC1_Ack(); //发送ACK return receive;
}
二) myiic.h
#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
//
//PA1输入模式 输出模式
#define SDA1_IN() {GPIOA->MODER&=~(3<<(1*2));GPIOA->MODER|=0<<1*2;}
#define SDA1_OUT() {GPIOA->MODER&=~(3<<(1*2));GPIOA->MODER|=1<<1*2;}
//IO操作函数
#define SCL_1 PAout(0) //SCL
#define SDA_1 PAout(1) //SDA
#define READ_SDA1 PAin(1) //输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC1_Start(void); //发送IIC开始信号
void IIC1_Stop(void); //发送IIC停止信号
void IIC1_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC1_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC1_Wait_Ack(void); //IIC等待ACK信号
void IIC1_Ack(void); //IIC发送ACK信号
void IIC1_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
三) sys.h
#ifndef __SYS_H
#define __SYS_H
#include "stm32f4xx.h"
//
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+20) //0x40020014
#define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#endif
四)stm32f4xx.h
#ifndef __STM32F4xx_H
#define __STM32F4xx_H#ifdef __cplusplusextern "C" {
#endif /* __cplusplus */typedef struct
{/*!< GPIO port mode register, Address offset: 0x00 */__IO uint32_t MODER; /*!< GPIO port output type register, Address offset: 0x04 */__IO uint32_t OTYPER; /*!< GPIO port output speed register, Address offset: 0x08 */ __IO uint32_t OSPEEDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */__IO uint32_t PUPDR; /*!< GPIO port input data register, Address offset: 0x10 */__IO uint32_t IDR; /*!< GPIO port output data register, Address offset: 0x14 */ __IO uint32_t ODR; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */ __IO uint16_t BSRRL; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */ __IO uint16_t BSRRH; /*!< GPIO port configuration lock register, Address offset: 0x1C */ __IO uint32_t LCKR; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */__IO uint32_t AFR[2];
} GPIO_TypeDef;
/*!< Peripheral base address in the alias region */
#define PERIPH_BASE ((uint32_t)0x40000000)
/*!< Peripheral memory map */
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000)
/*!< AHB1 peripherals */
#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000)#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)#ifdef __cplusplus
}
#endif /* __cplusplus */#endif /* __STM32F4xx_H */
五)stm32f4xx_rcc.h
#ifndef __STM32F4xx_RCC_H
#define __STM32F4xx_RCC_H#ifdef __cplusplusextern "C" {
#endif#define RCC_AHB1Periph_GPIOD ((uint32_t)0x00000008)void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);#ifdef __cplusplus
}
#endif#endif /* __STM32F4xx_RCC_H */
六)stm32f4xx_rcc.c
#include "stm32f4xx_rcc.h"
/*** @brief Enables or disables the AHB1 peripheral clock.* @note After reset, the peripheral clock (used for registers read/write access)* is disabled and the application software has to enable this clock before * using it. * @param RCC_AHBPeriph: specifies the AHB1 peripheral to gates its clock.* This parameter can be any combination of the following values:* @arg RCC_AHB1Periph_GPIOA: GPIOA clock* @arg RCC_AHB1Periph_GPIOB: GPIOB clock * @arg RCC_AHB1Periph_GPIOC: GPIOC clock* @arg RCC_AHB1Periph_GPIOD: GPIOD clock* @arg RCC_AHB1Periph_GPIOE: GPIOE clock* @arg RCC_AHB1Periph_GPIOF: GPIOF clock* @arg RCC_AHB1Periph_GPIOG: GPIOG clock* @arg RCC_AHB1Periph_GPIOG: GPIOG clock* @arg RCC_AHB1Periph_GPIOI: GPIOI clock* @arg RCC_AHB1Periph_GPIOJ: GPIOJ clock (STM32F42xxx/43xxx devices) * @arg RCC_AHB1Periph_GPIOK: GPIOK clock (STM32F42xxx/43xxx devices) * @arg RCC_AHB1Periph_CRC: CRC clock* @arg RCC_AHB1Periph_BKPSRAM: BKPSRAM interface clock* @arg RCC_AHB1Periph_CCMDATARAMEN CCM data RAM interface clock* @arg RCC_AHB1Periph_DMA1: DMA1 clock* @arg RCC_AHB1Periph_DMA2: DMA2 clock* @arg RCC_AHB1Periph_DMA2D: DMA2D clock (STM32F429xx/439xx devices) * @arg RCC_AHB1Periph_ETH_MAC: Ethernet MAC clock* @arg RCC_AHB1Periph_ETH_MAC_Tx: Ethernet Transmission clock* @arg RCC_AHB1Periph_ETH_MAC_Rx: Ethernet Reception clock* @arg RCC_AHB1Periph_ETH_MAC_PTP: Ethernet PTP clock* @arg RCC_AHB1Periph_OTG_HS: USB OTG HS clock* @arg RCC_AHB1Periph_OTG_HS_ULPI: USB OTG HS ULPI clock* @param NewState: new state of the specified peripheral clock.* This parameter can be: ENABLE or DISABLE.* @retval None*/
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState)
{/* Check the parameters */assert_param(IS_RCC_AHB1_CLOCK_PERIPH(RCC_AHB1Periph));assert_param(IS_FUNCTIONAL_STATE(NewState));if (NewState != DISABLE){RCC->AHB1ENR |= RCC_AHB1Periph;}else{RCC->AHB1ENR &= ~RCC_AHB1Periph;}
}
七)delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include <sys.h>
void delay_init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);#endif
相关文章:
C++例程:使用I/O模拟IIC接口(6)
完整的STM32F405代码工程I2C驱动源代码跟踪 一)myiic.c #include "myiic.h" #include "delay.h" #include "stm32f4xx_rcc.h" //初始化IIC void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphCl…...
58.在 Vue 3 中使用 OpenLayers 绘制点、线、圆、多边形
前言 在现代 Web 开发中,地图功能已经成为许多应用的重要组成部分。OpenLayers 是一个强大的开源地图库,支持多种地图源和地图操作。结合 Vue 3 的响应式特性,我们可以轻松实现地图的交互功能。本文将详细介绍如何在 Vue 3 中使用 OpenLayer…...
如何快速上手一个鸿蒙工程
作为一名鸿蒙程序猿,当你换了一家公司,或者被交接了一个已有的业务。前辈在找你之前十分钟写了一个他都看不懂的交接文档,然后把一个鸿蒙工程交接给你了,说以后就是你负责了。之后几天你的状态大概就是下边这样的,一堆…...
c++入门之 命名空间与输入输出
1、命名空间 1.1使用命名空间的原因 先看一个例子: #include <iostream>int round 0;int main() {printf("%d", round);return 0; }请问,这个程序能跑起来吗? 答案是否定的 原因是,当我们想创建一个全局变量 …...
GRE技术的详细解释
GRE(Generic Routing Encapsulation,通用路由封装)是一种隧道协议,主要用于在不同网络之间封装和传输其他网络层协议的数据包。它最常用于在IP网络上建立虚拟点到点的隧道连接,是实现VPN的一项关键技术。 下面从原理、…...
Mysql--基础篇--多表查询(JOIN,笛卡尔积)
在MySQL中,多表查询(也称为联表查询或JOIN操作)是数据库操作中非常常见的需求。通过多表查询,你可以从多个表中获取相关数据,并根据一定的条件将它们组合在一起。MySQL支持多种类型的JOIN操作,每种JOIN都有…...
Java 泛型的用法
1. 泛型类 泛型类是指在类定义时使用类型参数来指定类的类型。这样可以在类的内部使用这些类型参数来定义字段、方法的返回类型和参数类型。 public class Box<T> {private T t;public void set(T t) {this.t t;}public T get() {return t;} }在这个例子中,…...
人工智能与物联网:智慧城市的未来
引言 清晨6点,智能闹钟根据你的睡眠状态和天气情况,自动调整叫醒时间;窗帘缓缓打开,阳光洒满房间;厨房里的咖啡机已经为你准备好热饮,而无人驾驶公交车正按时抵达楼下站点。这不是科幻电影的场景ÿ…...
Python标准库之SQLite3
包含了连接数据库、处理数据、控制数据、自定义输出格式及处理异常的各种方法。 官方文档:sqlite3 --- SQLite 数据库的 DB-API 2.0 接口 — Python 3.13.1 文档 官方文档SQLite对应版本:3.13.1 SQLite主页:SQLite Home Page SQL语法教程&a…...
力扣 二叉树的最大深度
树的遍历,dfs与bfs基础。 题目 注意这种题要看根节点的深度是0还是1。 深度优先遍历dfs,通过递归分别计算左子树和右子树的深度,然后返回左右子树深度的最大值再加上 1。递归会一直向下遍历树,直到达到叶子节点或空节点。在回溯…...
Linux_进程间通信_共享内存
什么是共享内存? 对于两个进程,通过在内存开辟一块空间(操作系统开辟的),进程的虚拟地址通过页表映射到对应的共享内存空间中,进而实现通信;物理内存中的这块空间,就叫做共享内存。…...
ubuntu 下生成 core dump
在Ubuntu下,发现程序崩溃后不生成core dump文件, 即使设置了ulimit -c unlimited后仍然无效。 1.ulimit -c unlimited 输出的的含义是核心转储文件的大小限制,单位是blocks,默认是0,表示不生成core dump文件。 2. 重设core_pattern ulimit -c unlimited后,核心转储文件…...
学习HLS.js
前言 HTTP 实时流(也称为HLS(.m3u8))是一种基于HTTP的自适应比特率流通信协议。HLS.js依靠HTML5视频和MediaSource Extensions进行播放,其特点:视频点播和直播播放列表、碎片化的 MP4 容器、加密媒体扩展 …...
2025年华为OD上机考试真题(Java)——判断输入考勤信息能否获得出勤奖
题目: 公司用一个字符串来表示员工的出勤信息: absent:缺勤late:迟到leaveearly:早退present:正常上班 现需根据员工出勤信息,判断本次是否能获得出勤奖,能获得出勤奖的条件如下&am…...
空对象模式
在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。 在空对象模式中,我…...
开启Excel导航仪,跨表跳转不迷路-Excel易用宝
都2025年了,汽车都有导航了,你的表格还没有导航仪吗?那也太OUT了。 面对着一个工作簿中有N多个工作表,工作表中又有超级表,数据透视表,图表等元素,如何快速的切换跳转到需要查看的数据呢&#…...
年度技术突破奖|中兴微电子引领汽车芯片新变革
随着以中央计算区域控制为代表的新一代整车电子架构逐步成为行业主流,车企在电动化与智能化之后,正迎来以架构创新为核心的新一轮技术竞争。中央计算SoC,作为支撑智驾和智舱高算力需求的核心组件,已成为汽车电子市场的重要新增量。…...
Ubuntu 如何查看盘是机械盘还是固态盘
在 Ubuntu 系统中,您可以通过以下方法来确定硬盘是机械硬盘(HDD)还是固态硬盘(SSD): 使用 lsblk 命令: 打开终端,输入以下命令: lsblk -d -o name,rota该命令将列出所…...
计算机网络(三)——局域网和广域网
一、局域网 特点:覆盖较小的地理范围;具有较低的时延和误码率;使用双绞线、同轴电缆、光纤传输,传输效率高;局域网内各节点之间采用以帧为单位的数据传输;支持单播、广播和多播(单播指点对点通信…...
STM32F4分别驱动SN65HVD230和TJA1050进行CAN通信
目录 一、CAN、SN65HVD230DR二、TJA10501、TJA1050 特性2、TJA1050 引脚说明 三、硬件设计1、接线说明2、TJA1050 模块3、SN65HVD230 模块 四、程序设计1、CAN_Init:CAN 外设初始化函数2、CAN_Send_Msg、CAN_Receive_Msg 五、功能展示1、接线图2、CAN 数据收发测试 …...
openclaw 配置教程:本地安装、网关接入与模型 API 配置完整说明
如果你在折腾 openclaw 配置,通常会发现真正影响使用体验的,不是把程序装上去,而是后面的模型来源怎么接、网关怎么起、控制面板怎么进,以及默认模型如何切换。只要这些环节没有理顺,就算安装完成,后续也很…...
手机相册端侧文本搜图方案调研
手机相册端侧文本搜图方案调研 调研日期:2026-04-02(UTC) 目标场景:手机相册中存在大量图片,需要支持基于自然语言的本地搜图;希望模型与系统架构可在骁龙平台端侧执行,并具备后续接入 tag/caption 与 rerank 的可扩展性。 一、结论摘要 已有现成开源例子,最接近目标场…...
Phi-3-Mini-128K快速原型开发:微信小程序集成AI对话功能
Phi-3-Mini-128K快速原型开发:微信小程序集成AI对话功能 最近在捣鼓一些AI小应用,发现很多开发者都想给自己的小程序加个“智能大脑”,让用户能聊聊天、问问问题。但一提到集成大模型,很多人就觉得门槛高、流程复杂,光…...
Windows驱动存储深度管理:DriverStore Explorer全方位解决方案
Windows驱动存储深度管理:DriverStore Explorer全方位解决方案 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 一、驱动管理困境与突破路径 1.1 系统驱动管理的核心挑战 W…...
Linux 五大 I/O 模型深度解析
在构建高并发、高性能的后端系统时(如各种中间件、Web 服务器),我们不可避免地会接触到 I/O(Input/Output)模型。很多开发者对 BIO、NIO、AIO 以及多路复用等概念感到混淆。要真正从底层掌握这些模型,我们需…...
libssh2非阻塞模式实战:单线程管理多个SSH连接的高效技巧
libssh2非阻塞模式实战:单线程管理多个SSH连接的高效技巧 在当今分布式系统和自动化运维的浪潮中,SSH协议作为远程管理的黄金标准,其性能瓶颈往往出现在需要同时管理大量连接时。传统多线程方案不仅资源消耗大,还面临线程同步的复…...
Zstats高级版教程(3):如何进行数据整理(下),分类变量如何设置对照组?设置值标签?
本篇是风暴统计平台教程系列的第三章,将详细说明如何使用数据整理模块,节省后续分析的时间。因为涉及内容比较多,分为上中下三篇,此为下篇。前两篇数据整理教程分别向大家详细介绍了数据整理模块的定量数据转分类、计算新变量、变…...
别再手动算Offset了!Vector DaVinci里这样配置AUTOSAR OS Alarm,让任务调度更丝滑
Vector DaVinci实战:AUTOSAR OS Alarm智能配置与任务调度优化 在汽车电子系统开发中,任务调度就像交响乐团的指挥,需要精确协调各个执行单元的时间节奏。传统手动计算Alarm Offset的方式,不仅效率低下,还容易引入人为错…...
OFA-VQA镜像可解释性增强:Grad-CAM热力图可视化答案依据区域
OFA-VQA镜像可解释性增强:Grad-CAM热力图可视化答案依据区域 1. 引言:为什么需要可视化VQA模型的决策依据? 当我们使用视觉问答(VQA)模型时,经常会遇到一个关键问题:模型给出的答案真的可靠吗…...
【2024大厂AI基础设施面试压轴题】:手写Cuvil自定义Op注册+自动融合Pass(附可运行验证代码)
第一章:Cuvil 编译器在 Python AI 推理中的应用 面试题汇总Cuvil 是一款面向 AI 推理场景的轻量级领域专用编译器(DSL Compiler),专为优化 Python 中基于 PyTorch/TensorFlow 模型的部署而设计。它通过静态图重写、算子融合与硬件…...
