STM32实战总结:HAL之I2C
创始人
2024-02-06 09:16:15

I2C基础知识参考:

嵌入式常见接口协议总结_路溪非溪的博客-CSDN博客

电路图

扩展的I2C接口,可以连接支持I2C的设备。常见于传感器等。

参考手册

目前大部分MCU都带有IIC总线接口,STM32F1也不例外。但是这里我们不使用STM32F1的硬件IIC,而是通过软件模拟。原因是ST为了规避飞利浦IIC专利问题,将STM32的硬件IIC设计的比较复杂,且稳定性不怎么好。
用软件模拟IIC,最大的好处就是方便移植, 同一个代码兼容所有MCU,任何一个单片机只要有IO口,就可以很快的移植过去,而且不需要特定的IO口。而硬件IIC,则换一款 MCU,基本上就得重新搞一次,移植是比较麻烦的,这是我们推荐使用软件模拟IIC的另外一个原因。

自行阅读参考手册,大概了解下STM32的IIC。

此处通过SHT30的使用来学习IIC协议。

电路图如下:

注意,这里的引脚连接没有连到硬件IIC上。

所以,就算想用硬件IIC也用不起来。

STN30

STN30是一款温湿度传感器芯片,关键性能如下:

Wide supply voltage range, from 2.4 to 5.5 V

I2C Interface with communication speeds up to 1MHz and two user selectable addresses Typical accuracy of 2%RH and 0.3°C

The sensor shows best performance when operated within recommended normal temperature and humidity range of 5 – 60 °C and 20 – 80 %RH

饮片引脚如下:

 

设备地址:

上面的原理图中,ADDR接了地,所以这里的地址是0x44.

为了提高准确度,该芯片采用多次测量取均值的策略。所以,当重复度越高的时候,所用的时间会越长。

更多自行阅读手册。

GPIO配置

因为是用软件I2C,所以只用配置对应的两个GPIO口,即PG11和PG12。

要注意的一点是:

我们在配置时,需要将端口配置成输出或者输入,但是,在I2C工作过程中,既需要输出,又需要输入,通过半双工来完成整个过程。

那怎么办呢?怎么能让一个IO口既是输出也是输入呢?

当引脚配置成输出,并且是开漏模式时,引脚对输入的数据也能进行读取。

不过,读取时需要先输出一个高电平,关闭内部的NMOS管,才能作为输入使用。

这就是STM32的准双向口。

关键代码

I2C.c

/* Includes ------------------------------------------------------------------*/
#include "MyApplication.h"/* Private define-------------------------------------------------------------*/
//置位与清零SCL管脚
#define	SET_SCL	HAL_GPIO_WritePin(SHT30_SCL_GPIO_Port,SHT30_SCL_Pin,GPIO_PIN_SET) 
#define	CLR_SCL	HAL_GPIO_WritePin(SHT30_SCL_GPIO_Port,SHT30_SCL_Pin,GPIO_PIN_RESET)
//置位与清零SDA管脚
#define	SET_SDA	HAL_GPIO_WritePin(SHT30_SDA_GPIO_Port,SHT30_SDA_Pin,GPIO_PIN_SET)
#define	CLR_SDA	HAL_GPIO_WritePin(SHT30_SDA_GPIO_Port,SHT30_SDA_Pin,GPIO_PIN_RESET)
//读SDA管脚状态
#define READ_SDA	HAL_GPIO_ReadPin(SHT30_SDA_GPIO_Port,SHT30_SDA_Pin)/* Private variables----------------------------------------------------------*/
void Init(void);  //I2C初始化
void Start(void); //I2C起始信号
void Stop(void);  //I2C停止信号
ACK_Value_t Write_Byte(uint8_t);      //I2C写字节
uint8_t     Read_Byte (ACK_Value_t);  //I2C读字节/* Public variables-----------------------------------------------------------*/
I2C_Soft_t I2C_Soft = 
{Init,Start,Stop,Write_Byte,Read_Byte
};/* Private function prototypes------------------------------------------------*/      
static void I2C_Delay_us(uint8_t);/** @name   Init* @brief  I2C初始化* @param  None* @retval None      
*/
static void Init(void)
{SET_SCL;SET_SDA;
}/** @name   Start* @brief  I2C起始信号* @param  None* @retval None      
*/
static void Start(void)
{//SCL为高电平,SDA的下降沿为I2C起始信号SET_SDA;SET_SCL;I2C_Delay_us(1);CLR_SDA;I2C_Delay_us(10);CLR_SCL;I2C_Delay_us(1);
}/** @name   Stop* @brief  I2C停止信号* @param  None* @retval None      
*/
static void Stop(void)
{//SCL为高电平,SDA的上升沿为I2C停止信号CLR_SDA;SET_SCL;I2C_Delay_us(1);I2C_Delay_us(10);SET_SDA;
}/** @name   Write_Byte* @brief  I2C写字节* @param  WR_Byte -> 待写入数据* @retval ACK_Value_t -> 从机应答值      
*/
static ACK_Value_t Write_Byte(uint8_t WR_Byte)
{uint8_t i;ACK_Value_t  ACK_Rspond;//SCL为低电平时,SDA准备数据,接着SCL为高电平,读取SDA数据//数据按8位传输,高位在前,利用for循环逐个接收for(i=0;i<8;i++){//SCL清零,主机SDA准备数据CLR_SCL;I2C_Delay_us(1);if((WR_Byte&BIT7) == BIT7){SET_SDA;}else{CLR_SDA;}I2C_Delay_us(1);//SCL置高,传输数据SET_SCL;I2C_Delay_us(10);//准备发送下一比特位WR_Byte <<= 1;}CLR_SCL;	//释放SDA,等待从机应答SET_SDA;I2C_Delay_us(1);SET_SCL;I2C_Delay_us(10);ACK_Rspond = (ACK_Value_t)READ_SDA;CLR_SCL;I2C_Delay_us(1);//返回从机的应答信号return ACK_Rspond;
}/** @name   Write_Byte* @brief  I2C写字节* @param  ACK_Value -> 主机回应值* @retval 从机返回值      
*/
static uint8_t Read_Byte(ACK_Value_t ACK_Value)
{uint8_t RD_Byte = 0,i;接收数据//SCL为低电平时,SDA准备数据,接着SCL为高电平,读取SDA数据//数据按8位传输,高位在前,利用for循环逐个接收for(i=0;i<8;i++){//准备接收下一比特位RD_Byte <<= 1;//SCL清零,从机SDA准备数据CLR_SCL;I2C_Delay_us(10);//SCL置高,获取数据SET_SCL;I2C_Delay_us(10);	RD_Byte |= READ_SDA;		}//SCL清零,主机准备应答信号CLR_SCL;I2C_Delay_us(1);//主机发送应答信号	if(ACK_Value == ACK){CLR_SDA;}else{SET_SDA;	}	I2C_Delay_us(1);SET_SCL; 	I2C_Delay_us(10);//Note://释放SDA数据线//SCL先清零,再释放SDA,防止连续传输数据时,从机错将SDA释放信号当成NACk信号CLR_SCL;SET_SDA; 	I2C_Delay_us(1);//返回数据return RD_Byte;
}/** @name   I2C_Delay* @brief  I2C延时* @param  None* @retval None      
*/
static void I2C_Delay_us(uint8_t us)
{uint8_t i = 0;//通过示波器测量进行校准while(us--){for(i=0;i<7;i++);}
}
/********************************************************End Of File
********************************************************/

SHT30.c

/* Includes ------------------------------------------------------------------*/
#include "MyApplication.h"/* Private define-------------------------------------------------------------*//* Private variables----------------------------------------------------------*/
void Measure_Period_Mode(void);  //周期测量模式/* Public variables-----------------------------------------------------------*/
SHT30_t SHT30 = 
{0.0,0,Measure_Period_Mode
};/* Private function prototypes------------------------------------------------*/      
static uint8_t CRC_8(uint8_t *,uint8_t);/** @name   Measure_Period_Mode* @brief  周期测量模式* @param  None* @retval None      
*/
static void Measure_Period_Mode(void)
{uint8_t   temp_array[6] = {0};uint16_t  temp_uint     = 0;float     temp_float    = 0;//启动周期性测量I2C_Soft.Start();I2C_Soft.Write_Byte(SHT30_ADDR & Write_CMD);I2C_Soft.Write_Byte(0x27); //High repeat , mps = 10I2C_Soft.Write_Byte(0x37);Timer6.SHT30_Measure_Timeout = 0;//发送接收数据命令do{		if(Timer6.SHT30_Measure_Timeout >= TIMER6_2S) //2s内没获取到数据,退出等待break;I2C_Soft.Start();I2C_Soft.Write_Byte(SHT30_ADDR & Write_CMD);I2C_Soft.Write_Byte(0xE0);I2C_Soft.Write_Byte(0x00);I2C_Soft.Start();}while(I2C_Soft.Write_Byte(SHT30_ADDR | Read_CMD) ==NACK);//开始接收测量数据,并计算if(Timer6.SHT30_Measure_Timeout < TIMER6_2S){temp_array[0] = I2C_Soft.Read_Byte(ACK);temp_array[1] = I2C_Soft.Read_Byte(ACK);temp_array[2] = I2C_Soft.Read_Byte(ACK);temp_array[3] = I2C_Soft.Read_Byte(ACK);temp_array[4] = I2C_Soft.Read_Byte(ACK);temp_array[5] = I2C_Soft.Read_Byte(NACK);I2C_Soft.Stop();//计算温度,精度0.1if(CRC_8(temp_array,2) == temp_array[2]) //CRC-8 校验{temp_uint         = temp_array[0]*256+temp_array[1];temp_float        = ((float)temp_uint)*0.267032-4500;SHT30.fTemperature = temp_float*0.01;}//计算湿度,精度1%RHif(CRC_8(&temp_array[3],2) == temp_array[5]) //CRC-8 校验{temp_uint      = temp_array[3]*256+temp_array[4];temp_float     = ((float)temp_uint)*0.152590;temp_float     = temp_float*0.01;SHT30.ucHumidity = (unsigned char)temp_float;  }}
}/** @name   CRC_8* @brief  CRC-8校验* @param  Crc_ptr -> 校验数据首地址LEN     -> 校验数据长度* @retval CRC_Value -> 校验值      
*/
static uint8_t CRC_8(uint8_t *Crc_ptr,uint8_t LEN)
{uint8_t CRC_Value = 0xFF;uint8_t i = 0,j = 0;for(i=0;i

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...