STM32之模拟IIC总线通信(C++)

2023-11-12

目录

前言

主要内容

头文件

辅助函数

相关信号函数

起始信号

停止信号

接收应答信号

发送应答信号

发送非应答信号

发送一个字节数据

接收一个字节数据

应用


前言

        上一篇也讲解了STM32的模拟IIC总线通信,其所使用的语言为C语言,但也有了面向对象的思想了。于是某基于该思想认为可以用C++来实现这个通信,说不定使用起来会更加方便,所以有了这一篇。另外,鄙人的C++也是临时所学,其中多少有不足指出,希望读者指教,谢谢!  

主要内容

        上一篇对程序的解说已足够详细,故此篇我尽量使其语言简短。

头文件

#include<stm32f10x.h>
#include<delay.h>

/*数据传输方向*/
#define DIRECTION_OUT	GPIO_Mode_Out_PP			//输出
#define DIRECTION_IN	GPIO_Mode_IN_FLOATING		//输入

//上拉
#define SET_SCL		(SCL_GPIO -> BSRR = SCL_Pin)
#define SET_SDA		(SDA_GPIO -> BSRR = SDA_Pin)
//下拉
#define RESET_SCL	(SCL_GPIO -> BRR = SCL_Pin)
#define RESET_SDA 	(SDA_GPIO -> BRR = SDA_Pin)

#define READ_SDA	(((SDA_GPIO -> IDR & SDA_Pin) != 0x00) ? 1 : 0)

#define IIC_DELAY delay_us(1)	//IIC延时,可根据实际情况适当调整

class IIC_Soft
{
	private:
		GPIO_TypeDef *SCL_GPIO;
		GPIO_TypeDef *SDA_GPIO;
		unsigned short int SCL_Pin;
		unsigned short int SDA_Pin;

		void IIC_SetClock();
		void IIC_DataDir(GPIOMode_TypeDef direction);
	public:
		IIC_Soft(GPIO_TypeDef *SCL_GPIOx,unsigned short int SCL_Pinx,GPIO_TypeDef *SDA_GPIOx,unsigned short int SDA_Pinx);
		void IIC_Start();
		void IIC_Stop();
		unsigned char IIC_ReceiveACK();
		void IIC_SendACK();
		void IIC_SendNACK();
		void IIC_SendByte(unsigned char dat);
		unsigned char IIC_ReadByte();
};

        想必读者已经猜到我用C++写该程序的主要用意,这里所创建的类即其核心。

辅助函数

#include<iic_soft_cpp.h>

/******************************************************************************
	本文件为IIC的基本信号集合(作为主机),就目前测试,适合在各个频率下工作,
	针对不同的频率,仅需适当调整IIC延时时间和调整一小部分内容即可
	每个信号中都以SCL低电平为结尾(停止信号除外),这样一来可防止信号发送
	之后在SDA上的电平变化引起误触发
*******************************************************************************/

static GPIO_InitTypeDef GPIO_IIC;	//引脚初始化结构体

/*********************使能相应的端口时钟*********************/
void IIC_Soft::IIC_SetClock()
{
	unsigned int RCC_APB2Periph;
	switch((unsigned int)SCL_GPIO)
	{
		case (unsigned int)GPIOA:
			RCC_APB2Periph = RCC_APB2Periph_GPIOA;
			break;
		case (unsigned int)GPIOB:
			RCC_APB2Periph = RCC_APB2Periph_GPIOB;
			break;
		case (unsigned int)GPIOC:
			RCC_APB2Periph = RCC_APB2Periph_GPIOC;
			break;
		case (unsigned int)GPIOD:
			RCC_APB2Periph = RCC_APB2Periph_GPIOD;
			break;
		case (unsigned int)GPIOE:
			RCC_APB2Periph = RCC_APB2Periph_GPIOE;
			break;
		case (unsigned int)GPIOF:
			RCC_APB2Periph = RCC_APB2Periph_GPIOF;
			break;
		case (unsigned int)GPIOG:
			RCC_APB2Periph = RCC_APB2Periph_GPIOG;
			break;
	}

	switch((unsigned int)SDA_GPIO)
	{
		case (unsigned int)GPIOA:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOA;
			break;
		case (unsigned int)GPIOB:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOB;
			break;
		case (unsigned int)GPIOC:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOC;
			break;
		case (unsigned int)GPIOD:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOD;
			break;
		case (unsigned int)GPIOE:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOE;
			break;
		case (unsigned int)GPIOF:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOF;
			break;
		case (unsigned int)GPIOG:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOG;
			break;
	}
    RCC->APB2ENR |= RCC_APB2Periph;
}

/******************************设置数据线(SDA)的传输方向******************************/
void IIC_Soft::IIC_DataDir(GPIOMode_TypeDef direction)
{
	GPIO_IIC.GPIO_Pin   = SDA_Pin;
	GPIO_IIC.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_IIC.GPIO_Mode  = direction;			//数据传输方向
	GPIO_Init(SDA_GPIO,&GPIO_IIC);
}

/*******************IIC初始化*******************/
IIC_Soft::IIC_Soft(GPIO_TypeDef *SCL_GPIOx,unsigned short int SCL_Pinx,GPIO_TypeDef *SDA_GPIOx,unsigned short int SDA_Pinx)
{
	SCL_GPIO = SCL_GPIOx;
	SCL_Pin  = SCL_Pinx;
	SDA_GPIO = SDA_GPIOx;
	SDA_Pin  = SDA_Pinx;

	IIC_SetClock();
	
	GPIO_IIC.GPIO_Pin = SCL_Pin;
	GPIO_IIC.GPIO_Mode  = GPIO_Mode_Out_PP;
	GPIO_IIC.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(SCL_GPIO,&GPIO_IIC);		//初始化SCL引脚

	IIC_DataDir(DIRECTION_OUT);			//初始化SDA引脚(输出模式)

	RESET_SCL;
	SET_SDA;
	SET_SCL;
}

相关信号函数

起始信号

/****IIC起始信号(SCL高电平下SDA的一个下降沿,后以SCL变为低电平收尾)****/
/*	     				   ____
						 /     \4.7us
					___/        \___									 */
void IIC_Soft::IIC_Start()
{
	IIC_DataDir(DIRECTION_OUT);	//SDA为输出模式

	SET_SDA;
	SET_SCL;
	RESET_SDA;
	__nop();
	__nop();	//必须延时
	RESET_SCL;
}

停止信号

/****IIC停止信号(SCL高电平下SDA一个上升沿,后以SCL变为低电平收尾)****/
/*							  _____________
					SCL ____/					
							 4us_____
					SDA ______/	     \____						   */
void IIC_Soft::IIC_Stop()
{
	IIC_DataDir(DIRECTION_OUT);	//SDA为输出模式

	RESET_SDA;
	SET_SCL;
	SET_SDA;
//	RESET_SCL;
}

接收应答信号

/*接收应答信号,若接收到非应答信号,则发送停止信号(SDA低电平表示应答,高电平表示非应答)*/
unsigned char IIC_Soft::IIC_ReceiveACK()
{
	unsigned char state;
	IIC_DataDir(DIRECTION_IN);	//SDA为输入模式
	
	SET_SDA;										//释放SDA
	SET_SCL;
	state = READ_SDA;
	RESET_SCL;
	if(state)										//若接收到非应答信号,发送停止信号
	{
		IIC_Stop();
	}
	return state;
}

发送应答信号

/****应答(SDA保持为低电平)****/
/*							 	 ____
					SCL _______/	 \_____				
						_____
					SDA	   	 \____________						   */
void IIC_Soft::IIC_SendACK()
{
	IIC_DataDir(DIRECTION_OUT);	//方向为输出

	RESET_SDA;
	SET_SCL;
	RESET_SCL;
}

发送非应答信号

/****非应答(SDA保持为高电平)****/
/*							 	 ____
					SCL _______/	 \_____				
							  _____________
					SDA	____/		 	 							*/
void IIC_Soft::IIC_SendNACK()
{
	IIC_DataDir(DIRECTION_OUT);	//方向为输出

	SET_SDA;
	SET_SCL;
	RESET_SCL;
}

发送一个字节数据

/****IIC发送一个字节数据(每发送一个字节接收一次应答)****/
void IIC_Soft::IIC_SendByte(unsigned char dat)
{
	unsigned char i;
	IIC_DataDir(DIRECTION_OUT);	//方向为输出
	for(i = 0;i < 8;i++)
	{
		if((dat >> 7) & 0x01)
		{
			SET_SDA;
		}
		else
		{
			RESET_SDA;
		}

		__nop();
		__nop();
		__nop();
		__nop();	//此处必须延时,但用微秒级的延时显得过慢,故此处用nop延时
		SET_SCL;
		RESET_SCL;
		dat <<= 1;
	}
}

接收一个字节数据

/****IIC接收一个字节(每接收一个字节发送一次应答/非应答)****/
unsigned char IIC_Soft::IIC_ReadByte()
{
	unsigned char i,receive;
	IIC_DataDir(DIRECTION_IN);	//方向为输入
	SET_SDA;										//释放数据线

	for(i = 0;i < 8;i++)
	{
		SET_SCL;
		receive <<= 1;
		receive |= (READ_SDA & 0x01);
		RESET_SCL;
		IIC_DELAY;		//必须延时
	}
	return receive;
}

应用

        以上便是主要的函数,下面我同样用OLED来举例子。

IIC_Soft OLED_IIC(GPIOE,GPIO_Pin_13,GPIOE,GPIO_Pin_14);		//OLED引脚初始化结构体

/**********************往OLED中发送一个字节(数据/命令)**********************/
/*
	mode:字节性质
		命令(OLED_CMD)
		数据(OLED_DATA)
	data:发送内容
*/
static void OLED_IIC_SendByte(unsigned char mode,unsigned char data)
{
	OLED_IIC.IIC_Start();
	OLED_IIC.IIC_SendByte((OLED_ADDRESS << 1) & 0xfe);		//direction is write
	OLED_IIC.IIC_ReceiveACK();
	OLED_IIC.IIC_SendByte(mode);
	OLED_IIC.IIC_ReceiveACK();
	OLED_IIC.IIC_SendByte(data);
	OLED_IIC.IIC_ReceiveACK();
	OLED_IIC.IIC_Stop();
}

       此处即向OLED中发送一个字节的函数,对于其他IIC外设的使用方法相似。至于其他疑问,可参考我上一篇用C语言所写的文章,谢谢!

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

STM32之模拟IIC总线通信(C++) 的相关文章

随机推荐

  • AI日报|哈佛“AI教授”即将上线;首个生成式AI技能专业证书来了;电话推销员很烦?AI帮你“制裁”他

    今日值得关注的人工智能新动态 将GPT 4用在课程设计中 哈佛大学 AI教授 即将上线 微软推出首个生成式AI技能专业证书 纽约州议会 伤害或羞辱他人的deepfake是非法的 阿诺德 施瓦辛格 终结者 中的AI已成现实 AI诊断 老年痴呆
  • 函数粘合云服务提供端到端解决方案

    作者 西流 阿里云技术专家 导读 阿里云Serverless 产品函数计算可以作为粘合剂 串联其他云服务提供端到端解决方案 从而简化编程模型 快速实现最上层的业务目标 传统单体应用的拆解 首先我们来看下单体应用里面常见的两个编程模型 如上图
  • 【华为OD统一考试B卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • Hive SQL 2023必考常用窗口函数及面试题

    一 窗口函数概述 窗口函数也称为OLAP函数 OLAP 是OnLine Analytical Processing 的简称 意思是对数据库数据进行实时分析处理 例如 市场分析 创建财务报表 创建计划等日常性商务工作 窗口函数就是为了实现OL
  • 使用TensorFlow、Keras和深度学习实现像素无序超分辨率

    使用TensorFlow Keras和深度学习实现像素无序超分辨率 pixel shuffle Super Resolution 像素无序 洗牌超分辨率 Deep Learning Super Sampling DLSS 深度学习超级采样
  • flask_sqlalchemy中的db.session.query()和model.query()的区别

    先申明一下 我喜欢从问题入手 问题描述 使用db session commit 提交数据后 使用model query 查不到新增加的数据 问题原因分析 db session commit 是提交了数据到数据库 但是没有刷新模型映射中的数据
  • 使用Fabric8 kubernetes-client java client 操作kuberntes 自定义资源CR

    kubernetes client fabric8 to interact with kubernetes custom resources kubernetes中的一切东西都叫做 resource k8s 默认的提供了很多 resourc
  • 【Python】初识Python

    目录 执行Python的文件有三种方式 注释的细节 算术运算符 变量 输入与输出函数 input函数 print函数 强制类型转换函数 Python程序的特点 Python源程序本质上是一个特殊格式的文本文件 只不过它的后缀扩展名是 py
  • python将秒数转化为时间格式

    1 转化成时间格式 seconds 35400 m s divmod seconds 60 h m divmod m 60 print d 02d 02d h m s 结果 9 50 00 2 转化成日期时间格式 import time t
  • 服务service设置

    2017 03 10 一点简单的讲解 http cwtea blog 51cto com 4500217 842002 service命令只有很简单的几个参数 start restart reload status stop 不能利用这个命
  • 自学Python能学会吗?

    这是一个非常好的问题 作为一名IT从业者 同时也是一名教育工作者 我来回答一下 首先 随着当前Python语言的应用越来越普遍 很多职场人和大学生都希望能够通过掌握Python语言来提升职场价值和岗位竞争力 由于Python语言本身比较简单
  • FreeRTOS-信号量详解

    作者简介 嵌入式入坑者 与大家一起加油 希望文章能够帮助各位 个人主页 rivencode的个人主页 系列专栏 玩转FreeRTOS 保持学习 保持热爱 认真分享 一起进步 目录 前言 一 信号量的简介 二 FreeRTOS信号量 1 二值
  • 牛客题:Java静态块、构造块执行顺序

    public class Test public static Test t1 new Test 静态变量 构造块 System out println AAA 静态块 static System out println BBB publi
  • 【C++】模拟实现二叉搜索树(附源码、测试用例)

    二叉搜索树 一 前言 二 模拟实现 1 构建树的单个节点 2 二叉搜索树的概念 3 构造函数与析构函数 4 赋值与拷贝构造 5 实现插入 6 实现删除 7 实现查找 8 实现遍历 三 源码及部分测试用例 一 前言 二叉搜索树 和普通的二叉树
  • 立创梁山派学习笔记——GPIO输入检测

    按键检测 前言 按键的硬件电路 BOOT选择 复位按键 唤醒按键 GPIO输入框图 软件配置 寄存器简介 1 端口控制寄存器 GPIOx CTL x A I 2 端口上拉 下拉寄存器 GPIOx PUD x A I 3 端口输入状态寄存器
  • 上传代码到gitee:常用命令详解

    目录 一 创建仓库 二 首次上传 三 添加代码后在上传 四 创建分支 创建dev分支 五 合并分支 六 删除分支 看个人需求 一 创建仓库 下面是创建好的仓库 无任何代码上传的状态 二 首次上传 第一步 git config global
  • 2022-3-26 Leetcode 09.字符串轮转

    class Solution public bool isFlipedString string s1 string s2 if s1 size s2 size return false string s s2 s2 return s fi
  • Bridging ConvNeXt and U-Net for medical image segmentation

    最近在收集论文时发现一篇比较有趣的论文 当提到ConvNeXt时 大家应该都知道 比较这个网络跟Transformer 一较高低的网络 在前段时间transformer 很多的时候 涌现了许多将transformer和U Net 相结合的网
  • 轻量应用服务器性能如何?CPU带宽流量系统盘测评

    轻量应用服务器性能如何 腾讯云轻量应用服务器是一种轻量级搭建小型网站和应用的服务器 相对于其他更高性能配置的服务器CVM 性价比更高 虽然其性能不如高性能云服务器CVM 但对于小型网站和应用来说 能够提供基本的计算和存储资源 可以满足基础的
  • STM32之模拟IIC总线通信(C++)

    目录 前言 主要内容 头文件 辅助函数 相关信号函数 起始信号 停止信号 接收应答信号 发送应答信号 发送非应答信号 发送一个字节数据 接收一个字节数据 应用 前言 上一篇也讲解了STM32的模拟IIC总线通信 其所使用的语言为C语言 但也