IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 二、STM32用HAL库写I2C底层时序 -> 正文阅读

[嵌入式]二、STM32用HAL库写I2C底层时序

一、应用场景

??????? I2C在单片机开发中应用特别广泛,比如EEPROM中AT24CXX芯片使用的就是I2C总线。在STM32中,HAL库提供了I2C底层驱动,但小编在使用中用不了,不知是哪的问题。所以还是自己写底层时序吧,这种引脚使用上也更加的灵活。

二、原理图

(1)这里以AT24C128芯片和STM32F103RET6为例。

(2)EEPROM部分原理图

上图中A0、A1和A2为地址引脚;

WP为写保护引脚;

EEPROM_SCL为时钟引脚;

EEPROM_SDA为数据引脚。

?(3)STM32F103RET6部分电路图

?WP引脚接PB5引脚;EEPROM_SCL接PB6引脚;EEPROM_SDA接PB7引脚。

三、STM32CubeMX配置

1、时钟配置。略

2、使用定时器完成延时uS

一、STM32用HAL库实现uS级延时_朱嘉鼎的博客-CSDN博客

四、KeilMDK中代码的编写

1、新建drv_i2c.c文件和drv_i2c.h文件。

2、drv_i2c.h代码

#ifndef __DRV_I2C_H__
#define __DRV_I2C_H__

/*头文件包含*/
#include "tim.h"
#include "drv_gpio.h"

/*置位与清零SCL管脚*/
#define __AT24CXX_I2C_SCL_H()       HAL_GPIO_WritePin(EP_SCL_GPIO_Port,EP_SCL_Pin,GPIO_PIN_SET)    
#define __AT24CXX_I2C_SCL_L()       HAL_GPIO_WritePin(EP_SCL_GPIO_Port,EP_SCL_Pin,GPIO_PIN_RESET)
/*置位与清零SDA管脚*/
#define __AT24CXX_I2C_SDA_H()       HAL_GPIO_WritePin(EP_SDA_GPIO_Port,EP_SDA_Pin,GPIO_PIN_SET) 
#define __AT24CXX_I2C_SDA_L()       HAL_GPIO_WritePin(EP_SDA_GPIO_Port,EP_SDA_Pin,GPIO_PIN_RESET)
/*读取SDA管脚状态*/
#define __AT24CXX_READ_I2C_SDA()    HAL_GPIO_ReadPin(EP_SDA_GPIO_Port,EP_SDA_Pin)

/*设置SDA管脚为输出模式*/
#define __AT24CXX_I2C_SDA_OUT()     SDA_OutputMode()
/*设置SDA管脚为输入模式*/
#define __AT24CXX_I2C_SDA_INPUT()   SDA_InputMode()


/*I2C通信时的应答信号*/
typedef enum
{
	ACK	 = GPIO_PIN_RESET,
	NACK = GPIO_PIN_SET
}I2C_ACK_TypeDef;


typedef enum
{
	DEVICE_AT24CXX	= 0x00U
}I2C_DeviceTypeDef;


/*函数声明*/
void I2C_Delay_us(uint16_t t);
void I2C_Start(I2C_DeviceTypeDef Device);       													 /*I2C开始*/
void I2C_Stop(I2C_DeviceTypeDef Device);        													 /*I2C停止*/
I2C_ACK_TypeDef I2C_Write_Byte(I2C_DeviceTypeDef Device, uint8_t Byte);    /*I2C写一个字节*/   
uint8_t I2C_Read_Byte(I2C_DeviceTypeDef Device, I2C_ACK_TypeDef AckValue); /*I2C读一个字节*/


#endif

3、drv_i2c.c代码

#include "drv_i2c.h"  


/*
 *功能:定时器I2C延时
 *参数:延时的us数,us 范围-0~65535us
 *注意:使用定时器2产生延时
 *返回值:无
 */
void I2C_Delay_us(uint16_t t)
{
	uint16_t counter = 0;
	__HAL_TIM_SET_AUTORELOAD(&htim2, t); 	// 设置定时器自动加载值,到该值后重新计数   
	__HAL_TIM_SET_COUNTER(&htim2, 0);     // 设置定时器初始值
	HAL_TIM_Base_Start(&htim2);           // 启动定时器
	while(counter != t)                   // 直到定时器计数从 0 计数到 us 结束循环,刚好  us
	{
		counter = __HAL_TIM_GET_COUNTER(&htim2); // 获取定时器当前计数
	}
	HAL_TIM_Base_Stop(&htim2);   // 停止定时器
}


/*
 *功能:设置SDA管脚为输出模式
 *参数:无
 *返回值:无
 */
void SDA_OutputMode(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	GPIO_InitStruct.Pin = EP_SDA_Pin;                    
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;    /*输出模式*/
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}


/*
 *功能:设置SDA管脚为输入模式
 *参数:无
 *返回值:无
 */
void SDA_InputMode(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	GPIO_InitStruct.Pin = EP_SDA_Pin;                    
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;      /*输入模式*/
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}


/*
 *功能:SDA和SCL引脚电平初始化
 *参数:无
 *返回值:无
 *测试:OK
 */
static void AT24CXX_I2C_Init(void)
{
	__AT24CXX_I2C_SCL_H();
	__AT24CXX_I2C_SDA_H();
}


/*
 *功能:I2C总线起始信号时序
 *参数:无
 *返回值:无
 *测试:OK
 */
static void AT24CXX_I2C_Start(void)
{
	__AT24CXX_I2C_SDA_OUT();     /*SDA配置为输出模式*/

	/*SCL为高电平,SDA的下降沿为I2C起始信号*/
	__AT24CXX_I2C_SDA_H();
	__AT24CXX_I2C_SCL_H();
	I2C_Delay_us(1);
	
	__AT24CXX_I2C_SDA_L();
	I2C_Delay_us(10);
	
	__AT24CXX_I2C_SCL_L();
	I2C_Delay_us(1);
}


/*
 *功能:I2C总线停止信号时序
 *参数:无
 *返回值:无
 *测试:OK
 */
static void AT24CXX_I2C_Stop(void)
{
  __AT24CXX_I2C_SDA_OUT();

	/*SCL为高电平,SDA的上升沿为I2C停止信号*/
	__AT24CXX_I2C_SDA_L();
	__AT24CXX_I2C_SCL_H();
	I2C_Delay_us(1);
		
	I2C_Delay_us(10);
	__AT24CXX_I2C_SDA_H();
}


/*
 *功能:I2C总线写字节时序
 *参数:Byte:写入的内容
 *返回值:I2C_ACK_TypeDef:应答信号是高电平还是低电平
 *测试:OK
 */
static I2C_ACK_TypeDef AT24CXX_I2C_Write_Byte(uint8_t Byte)
{
	I2C_ACK_TypeDef ack_value;
	__AT24CXX_I2C_SDA_OUT();     /*SDA配置为输出模式*/
	
	/*SCL为低电平时,SDA准备数据,接着SCL为高电平,读取SDA数据*/
	for (uint8_t i = 0; i < 8; i++)
	{
		/*SCL清零,主机SDA准备数据*/
		__AT24CXX_I2C_SCL_L();
		I2C_Delay_us(1);
		if((Byte & (1 << 7)) == (1 << 7))
		{
			__AT24CXX_I2C_SDA_H();
		}
		else
		{
			__AT24CXX_I2C_SDA_L();
		}
		I2C_Delay_us(1);
		/*SCL置高,传输数据*/
		__AT24CXX_I2C_SCL_H();
		I2C_Delay_us(10);
		/*准备发送下一比特位*/
		Byte <<= 1;
	}
	__AT24CXX_I2C_SCL_L();	

	__AT24CXX_I2C_SDA_INPUT();

	/*释放SDA,等待从机应答*/
	__AT24CXX_I2C_SDA_H();
	I2C_Delay_us(1);

	__AT24CXX_I2C_SCL_H();
	I2C_Delay_us(10);

	ack_value = (I2C_ACK_TypeDef)__AT24CXX_READ_I2C_SDA();
 
	__AT24CXX_I2C_SCL_L();
	I2C_Delay_us(1);
	/*返回从机的应答信号*/
	return ack_value;
}


/*
 *功能:I2C总线读字节时序
 *参数:
 *返回值:读取到的数据
 *测试:OK
 */
static uint8_t AT24CXX_I2C_Read_Byte(I2C_ACK_TypeDef AckValue)
{
	uint8_t byte = 0;
	
  __AT24CXX_I2C_SDA_INPUT();

	/*SCL为低电平时,SDA准备数据,接着SCL为高电平,读取SDA数据*/
	for (uint8_t i = 0; i < 8; i++)
	{
		/*准备接收下一比特位*/
		byte <<= 1;
		/*SCL清零,从机SDA准备数据*/
		__AT24CXX_I2C_SCL_L();
		I2C_Delay_us(10);
		/*SCL置高,获取数据*/
		__AT24CXX_I2C_SCL_H();
		I2C_Delay_us(10);	
		byte |= __AT24CXX_READ_I2C_SDA();	
	}
	/*SCL清零,主机准备应答信号*/
	__AT24CXX_I2C_SCL_L();
	I2C_Delay_us(1);

  __AT24CXX_I2C_SDA_OUT();

	/*主机发送应答信号*/	
	if(AckValue == ACK)
	{
		__AT24CXX_I2C_SDA_L();
	}
	else
	{
		__AT24CXX_I2C_SDA_H();	
	}
	I2C_Delay_us(1);
	__AT24CXX_I2C_SCL_H(); 	
	I2C_Delay_us(10);
	/*SCL先清零,再释放SDA,防止连续传输数据时,从机错将SDA释放信号当成NACk信号*/
	__AT24CXX_I2C_SCL_L();
	__AT24CXX_I2C_SDA_H(); 	
	I2C_Delay_us(1);
	
	/*返回数据*/
	return byte;
}


/*
 *功能:I2C初始化
 *测试:OK
 */
void I2C_Init(I2C_DeviceTypeDef Device)
{
	switch(Device)
	{
		case DEVICE_AT24CXX: AT24CXX_I2C_Init(); break;
		default : break;
	}
}


/*
 *功能:I2C开始信号,针对不同型号设备的封装
 *测试:OK
 */
void I2C_Start(I2C_DeviceTypeDef Device)
{
	switch(Device)
	{
		case DEVICE_AT24CXX: AT24CXX_I2C_Start(); break;
		default : break;
	}
}


/*
 *功能:I2C停止信号,针对不同型号设备的封装
 *测试:OK
 */
void I2C_Stop(I2C_DeviceTypeDef Device)
{
	switch(Device)
	{
		case DEVICE_AT24CXX: AT24CXX_I2C_Stop(); break;
		default : break;
	}
}


/*
 *功能:I2C写一个字节
 *参数:Device:写入的设备
 *      Byte: 需要写入的内容
 *返回值:应答信号,正确应答信号是将SDA拉低
 */
I2C_ACK_TypeDef I2C_Write_Byte(I2C_DeviceTypeDef Device, uint8_t Byte)
{
	switch(Device)
	{
		case DEVICE_AT24CXX: return AT24CXX_I2C_Write_Byte(Byte); break;
		default : return NACK;    break;
	}
}


/*
 *功能:I2C读一个字节
 *参数:Device:读出的设备
 *      AckValue: 主机发送的应答信号
 *返回值:读到的内容
 *测试:OK
 */
uint8_t I2C_Read_Byte(I2C_DeviceTypeDef Device, I2C_ACK_TypeDef AckValue)
{
	switch(Device)
	{
		case DEVICE_AT24CXX: return AT24CXX_I2C_Read_Byte(AckValue);
		default : return 0;
	}
}





3、mai.c文件中测试代码

(1)包含头文件

#include "drv_uart.h"
#include "drv_at24cxx.h"

(2)main函数中while中代码

/*Test*/
printf("Hello World! \r\n");
HAL_Delay(500);
__EEPROM_UNLOCK();
AT24CXX_WriteOneByte(0X00, 88);
__EEPROM_LOCK();	
HAL_Delay(500);
printf("AT24CXX Test: %d\r\n", AT24CXX_ReadOneByte(0X00));

(3)下载程序后,串口打印信息如下

---------------------------------------------------------------------------------------------------------------------------------如有不足,请忽略。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 01:27:45  更:2022-09-04 01:29:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:44:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码