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

关于学习炸鸡佬智能手表 应用硬件IIC1来取代原来软件模拟的IIC

一、用完软件模拟IIC,虽然实现了一个通用接口,只要是IO口都可以用,但是既然引脚用到了PB6和PB7,这俩都是自带硬件外设IIC的,不用白不用。这里我也给出硬件IIC的实现,不得不说HAL库封装的真好

1.硬件IIC

这里我加入一个编译宏,在main.h中,实现可以切换软硬件IIC

硬件IIC速度不要太快,我开始跳到400000的时候触摸是无效的。

其他的配置都在CubeMx生成的iic文件中。这里利用句柄的形式也可以切换不同的iic。

因为我只使用了IIC1 这里也可以再添加一个参数指明使用的是IIC几。

#include "iic_hal_hard.h"
#include "main.h"
#if IICSOFT  == 0


/* I2C1 init function */
void MX_I2C1_Init(I2C_HandleTypeDef *hi2cx)
{

  hi2cx->Instance = I2C1;
  hi2cx->Init.ClockSpeed = 100000;
  hi2cx->Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2cx->Init.OwnAddress1 = 0;
  hi2cx->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2cx->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2cx->Init.OwnAddress2 = 0;
  hi2cx->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2cx->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(hi2cx) != HAL_OK)
  {
    Error_Handler();
  }

}
void IICInit(I2C_HandleTypeDef *hi2cx)
{
		MX_I2C1_Init(hi2cx);
}

uint8_t IIC_Write_One_Byte(I2C_HandleTypeDef *hi2cx, uint8_t daddr,uint8_t reg,uint8_t data)
{
	HAL_StatusTypeDef status;
	status = HAL_I2C_Mem_Write(hi2cx, daddr << 1, reg, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
	if (status == HAL_OK)
	{
			return SUCCESS;
	}else
	{
		  return ERROR;
	}
}
uint8_t IIC_Write_Multi_Byte(I2C_HandleTypeDef *hi2cx, uint8_t daddr,uint8_t reg,uint8_t length,uint8_t buff[])
{
	HAL_StatusTypeDef status;

	status = HAL_I2C_Mem_Write(hi2cx, daddr << 1, reg, I2C_MEMADD_SIZE_8BIT, buff, length, 1000);
	if (status == HAL_OK)
	{
			return SUCCESS;// 数据写入寄存器成功
	}
	else
	{
			 return ERROR;// 数据写入失败,可能是从机无应答、通信错误等原因
	}
}
uint8_t IIC_Read_One_Byte(I2C_HandleTypeDef *hi2cx, uint8_t daddr,uint8_t reg)
{
	HAL_StatusTypeDef status;
	uint8_t RxBuff;
	status = HAL_I2C_Mem_Read(hi2cx, daddr << 1, reg, I2C_MEMADD_SIZE_8BIT, &RxBuff, 1, 1000);
	if (status == HAL_OK)
	{
			return RxBuff;
	}else
	{
		return ERROR;
	}
	
}
uint8_t IIC_Read_Multi_Byte(I2C_HandleTypeDef *hi2cx, uint8_t daddr, uint8_t reg, uint8_t length, uint8_t buff[])
{
	HAL_StatusTypeDef status;
	
	status = HAL_I2C_Mem_Read(hi2cx, daddr << 1, reg, I2C_MEMADD_SIZE_8BIT, buff, length, 1000);
	if (status == HAL_OK)
	{
			return SUCCESS;
	}else
	{
		return ERROR;
	}
}

#endif

HAL库封装的,我觉得没有这个中间IIC接口层也可以实现触摸IC的驱动编写。

2 硬件IIC头文件

#ifndef __IIC_HAL_HARD_H
#define __IIC_HAL_HARD_H

#include "stm32f4xx_hal.h"
#include "main.h"

//PB6 PB7 IIC1外设
#if IICSOFT == 0 

extern I2C_HandleTypeDef hi2c1;

void IICInit(I2C_HandleTypeDef *hi2cx);

uint8_t IIC_Write_One_Byte(I2C_HandleTypeDef *hi2cx, uint8_t daddr,uint8_t reg,uint8_t data);
uint8_t IIC_Write_Multi_Byte(I2C_HandleTypeDef *hi2cx, uint8_t daddr,uint8_t reg,uint8_t length,uint8_t buff[]);
uint8_t IIC_Read_One_Byte(I2C_HandleTypeDef *hi2cx, uint8_t daddr,uint8_t reg);
uint8_t IIC_Read_Multi_Byte(I2C_HandleTypeDef *hi2cx, uint8_t daddr, uint8_t reg, uint8_t length, uint8_t buff[]);
#endif

#endif

3 修改后的软件IIC

#include "iic_hal.h"
#include "delay.h"
#if IICSOFT == 1

//SCL 低电平期间 SDA可以任意改变电位 高电平不允许改动 因为要采样
// SDA输入模式
void SDA_Input_Mode(iic_bus_t* bus)
{
	 GPIO_InitTypeDef GPIO_InitStructure = {0};
	 
	 GPIO_InitStructure.Pin = bus->IIC_SDA_PIN;
	 GPIO_InitStructure.Pull = GPIO_MODE_INPUT;
	 GPIO_InitStructure.Pull = GPIO_PULLUP;
	 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
	 HAL_GPIO_Init(bus->IIC_SDA_PORT,&GPIO_InitStructure);
}
// SDA输出模式
void SDA_Output_Mode(iic_bus_t* bus)
{
	 GPIO_InitTypeDef GPIO_InitStructure = {0};
	 
	 GPIO_InitStructure.Pin = bus->IIC_SDA_PIN;
	 GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;//下次不要这样搞,还是用MX来配置后复制,不然容易错
	 GPIO_InitStructure.Pull = GPIO_NOPULL;
	 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
	 HAL_GPIO_Init(bus->IIC_SDA_PORT,&GPIO_InitStructure);
}

//SDA输出一个位
void SDA_Output(iic_bus_t *bus, uint8_t val)
{
	if(val)
	HAL_GPIO_WritePin(bus->IIC_SDA_PORT,bus->IIC_SDA_PIN,GPIO_PIN_SET);
	else
	HAL_GPIO_WritePin(bus->IIC_SDA_PORT,bus->IIC_SDA_PIN,GPIO_PIN_RESET);
	
}

//SCL输出一个位
void SCL_Output(iic_bus_t *bus, uint8_t val)
{
	if(val)
	HAL_GPIO_WritePin(bus->IIC_SCL_PORT,bus->IIC_SCL_PIN,GPIO_PIN_SET);
	else
	HAL_GPIO_WritePin(bus->IIC_SCL_PORT,bus->IIC_SCL_PIN,GPIO_PIN_RESET);
}

//SDA输入一位 读一位
uint8_t SDA_Input(iic_bus_t *bus)
{
	//我觉得这里可以直接返回读到得值 不用判断了
	return (uint8_t)HAL_GPIO_ReadPin(bus->IIC_SDA_PORT,bus->IIC_SDA_PIN) ;

}

//从这个严格定义的角度来说,最后将 SCL 拉低这一步不属于起始信号本身的部分
//将 SCL 拉低是为了按照 I2C 通信的正确时序,
//把总线状态调整到适合后续数据传输的起始位置
void IICStart(iic_bus_t *bus)
{
	//当 SCL 为高电平时,SDA 从高电平向低电平跳变
	SDA_Output(bus,1);
	delay_us(2);
	SCL_Output(bus,1);
	delay_us(1);
	SDA_Output(bus,0);
	delay_us(1);
	SCL_Output(bus,0);
	delay_us(1); //
	
}
//严格来说,I2C 协议中定义的停止信号是当 SCL 为高电平时,
//SDA 从低电平向高电平跳变
//为了确保正确的时序过渡:在 I2C 通信中,
//数据的传输和各种信号的产生都是按照严格的时序进行的
void IICStop(iic_bus_t *bus)
{
	//当 SCL 为高电平时,SDA 从低电平向高电平跳变
	SCL_Output(bus,0);
	delay_us(2);
	SDA_Output(bus,0);
	delay_us(1);
	SCL_Output(bus,1);
	delay_us(1); //释放总线
	SDA_Output(bus,1);
	delay_us(1);
	
}
uint8_t IICWaitAck(iic_bus_t *bus)
{	
	uint8_t cErrTime = 5;
	SDA_Input_Mode(bus);//将数据线变成输入模式 接受从机的应答
	SCL_Output(bus,1);// 拉高时钟线
	while(SDA_Input(bus))//读到的数一直是1的话就是没有应答
	{
		cErrTime--;//读5次都没有结果就是没有应答 表示接受结束
		delay_us(1);
		if(0 == cErrTime)
		{
			SDA_Output_Mode(bus);
			IICStop(bus);
			return ERROR;
		}
	}
	//如果给了应答 就是读到了0
	SDA_Output_Mode(bus);//转换成输出模式
	SCL_Output(bus,0);//拉低时钟线 为下一个时序做准备
	delay_us(2);
	return SUCCESS;
}
void IICSendAck(iic_bus_t *bus)
{
	  SDA_Output(bus,0);//我记得是低电平放上数据
		delay_us(1);
    SCL_Output(bus,1);
		delay_us(1);
    SCL_Output(bus,0);
		delay_us(2);
}
void IICSendNotAck(iic_bus_t *bus)
{
	//我记得是低电平放上数据等时钟线高电平稳定采样 且此时不允许修改数据

		SDA_Output(bus,1);
		delay_us(1);
    SCL_Output(bus,1);
		delay_us(1);
    SCL_Output(bus,0);
		delay_us(2);
}


void IICSendByte(iic_bus_t *bus, uint8_t cSendByte)
{
	  uint8_t i;
		for(i=0;i<8;i++)
		{
			SCL_Output(bus,0);
			delay_us(2);
			SDA_Output(bus,((cSendByte >> (7 - i)) & 0x01));
			delay_us(1);
			SCL_Output(bus,1);
			delay_us(1);
		}
		SCL_Output(bus,0);
		delay_us(2);
}


uint8_t IICReceiveByte(iic_bus_t *bus)
{
	uint8_t i, cR_Byte=0;
	uint8_t bit=0;
	SDA_Input_Mode(bus);//接受字节 改成输入模式
	for(i=0;i<8;i++)
	{
		SCL_Output(bus,0); //拉低时钟线 等待从机放数据
		delay_us(2);
		SCL_Output(bus,1);//拉高时钟线 可以采样了
		delay_us(1);
		bit = SDA_Input(bus);
		cR_Byte |= bit << (7-i);

	}
	SCL_Output(bus,0);//拉低时钟线
	SDA_Output_Mode(bus);//改变为输出模式 只有读数据才是输入模式
	return cR_Byte;
}
void IICInit(iic_bus_t *bus)
{
		 GPIO_InitTypeDef GPIO_InitStructure = {0};

		//bus->CLK_ENABLE();
		
    GPIO_InitStructure.Pin = bus->IIC_SDA_PIN ;
		//上面给的是开漏 这里变成推挽搞不懂 ,推挽就不用变输入模式了
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(bus->IIC_SDA_PORT, &GPIO_InitStructure);
		
		GPIO_InitStructure.Pin = bus->IIC_SCL_PIN ;
    HAL_GPIO_Init(bus->IIC_SCL_PORT, &GPIO_InitStructure);
}
//返回1表示失败
uint8_t IIC_Write_One_Byte(iic_bus_t *bus, uint8_t daddr,uint8_t reg,uint8_t data)
{
		IICStart(bus);//开始信号
		IICSendByte(bus,(daddr << 1));//写从机地址
		if(IICWaitAck(bus)) //无应答停止传输
		{
			IICStop(bus);
			//printf("%d\r\n",IICWaitAck(bus)); 第一次初始化器件的时候会非应答
			return 1;
		}
		IICSendByte(bus,reg);
		IICWaitAck(bus);
		IICSendByte(bus,data);
		IICWaitAck(bus);
		IICStop(bus);
		delay_us(1);
		return 0;
	  
}

uint8_t IIC_Write_Multi_Byte(iic_bus_t *bus, uint8_t daddr,uint8_t reg,uint8_t length,uint8_t buff[])
{
		uint8_t i;
		uint8_t* dataPtr = buff;
		IICStart(bus);//开始信号
		IICSendByte(bus,(daddr << 1));//写从机地址
		if(IICWaitAck(bus)) //无应答停止传输
		{
			IICStop(bus);
			return 1;
		}
		IICSendByte(bus,reg);
		IICWaitAck(bus);
		for(i=0;i<length;i++)
		{
			//IICSendByte(bus,buff[i]);     	
			IICSendByte(bus,*dataPtr++);//利用指针指向缓存区 先解引用给首个元素赋值 再指向下一个字节
			IICWaitAck(bus);
		}
		IICStop(bus);
		delay_us(1);
		return 0;
}
uint8_t IIC_Read_One_Byte(iic_bus_t *bus, uint8_t daddr,uint8_t reg)
{
		uint8_t cR_Byte=0;
	  IICStart(bus);//开始信号
		IICSendByte(bus,(daddr << 1));//写从机地址
		IICWaitAck(bus);
		IICSendByte(bus,reg);
		IICWaitAck(bus);
	
		IICStart(bus);//开始信号
		IICSendByte(bus,(daddr << 1)| 1);//读操作
		IICWaitAck(bus);
		cR_Byte = IICReceiveByte(bus);
		IICSendNotAck(bus);
		IICStop(bus);
		return cR_Byte;
}
uint8_t IIC_Read_Multi_Byte(iic_bus_t *bus, uint8_t daddr, uint8_t reg, uint8_t length, uint8_t buff[])
{
		uint8_t i;
		uint8_t* dataPtr = buff;
		IICStart(bus);//开始信号
		IICSendByte(bus,(daddr << 1)&0xFE );//写从机地址
		IICWaitAck(bus);
		IICSendByte(bus,reg);
		IICWaitAck(bus);
	
	
		IICStart(bus);//开始信号
		IICSendByte(bus,(daddr << 1)| 1);//读操作
		IICWaitAck(bus);
		for(i=0;i<length;i++)
		{
			//buff[i] = IICReceiveByte(bus);
			*dataPtr++ = IICReceiveByte(bus);//右++ 先操作后自加
			if(i<length-1)
				IICSendAck(bus);
			else
				IICSendNotAck(bus);
		}
		
		IICStop(bus);
		return 0;
}
#endif

4 修改后的软件IIC头文件 (就是加了个宏编译)

#ifndef __IIC_HAL_H
#define __IIC_HAL_H

#include "stm32f4xx_hal.h"
#include "main.h"

#if IICSOFT == 1

typedef struct
{
	GPIO_TypeDef* IIC_SDA_PORT;
	GPIO_TypeDef* IIC_SCL_PORT;
	uint32_t IIC_SDA_PIN;
	uint32_t IIC_SCL_PIN;
	// void (*CLK_ENABLE)(void);
}iic_bus_t;//IIC控制器


void IICStart(iic_bus_t *bus);
void IICStop(iic_bus_t *bus);
uint8_t IICWaitAck(iic_bus_t *bus);
void IICSendAck(iic_bus_t *bus);
void IICSendNotAck(iic_bus_t *bus);
void IICSendByte(iic_bus_t *bus, uint8_t cSendByte);
uint8_t IICReceiveByte(iic_bus_t *bus);
void IICInit(iic_bus_t *bus);

uint8_t IIC_Write_One_Byte(iic_bus_t *bus, uint8_t daddr,uint8_t reg,uint8_t data);
uint8_t IIC_Write_Multi_Byte(iic_bus_t *bus, uint8_t daddr,uint8_t reg,uint8_t length,uint8_t buff[]);
uint8_t IIC_Read_One_Byte(iic_bus_t *bus, uint8_t daddr,uint8_t reg);
uint8_t IIC_Read_Multi_Byte(iic_bus_t *bus, uint8_t daddr, uint8_t reg, uint8_t length, uint8_t buff[]);
#endif

#endif


http://www.kler.cn/a/393715.html

相关文章:

  • 在 Service Worker 中caches.put() 和 caches.add()/caches.addAll() 方法他们之间的区别
  • Web安全之SQL注入---基础
  • 拦截器实现http请求访问本地图片
  • vue2或vue3的name属性有什么作用?
  • 【网络工程】计算机硬件概述
  • 想租用显卡训练自己的网络?AutoDL保姆级使用教程(PyCharm版)
  • Java编程优化:深入理解多态和接口中的方法实现
  • 支持向量机 SVM
  • 密码学在网络安全中的应用
  • 基于ABNF语义定义的HTTP消息格式
  • 基于微信小程序的电商平台+LW示例参考
  • html文本元素
  • 第三次作业
  • 浅谈:基于三维场景的视频融合方法
  • 丹摩征文活动 | 丹摩智算平台:服务器虚拟化的璀璨明珠与实战秘籍
  • C++设计模式和编程框架两种设计元素的比较与相互关系
  • Jenkins常见问题
  • 计算机网络(5)
  • Java final关键字
  • ios swift开发--ios远程推送通知配置
  • leetcode83. Remove Duplicates from Sorted List
  • 域名绑定服务器小白教程
  • LeetCode 热题100之技巧关卡
  • Leetcode:118. 杨辉三角——Java数学法求解
  • 飞牛云fnOS本地部署WordPress个人网站并一键发布公网远程访问
  • MaxKB