前言:笔记参考立创开发文档,连接放在最后
#IIC概念介绍
#IIC介绍
IIC通信协议,一种常见的串行通信协议,英文全程是 Inter-Integrated Circuit 使用这种通信方式的模块,通常有SCL(Serial Clock Line) 和SDA(Serial Date)两个引脚 ,也就是当看到这两个引脚也就知道,使用的IIC通信。
IIC通信分为硬件IIC通信 软件IIC通信,本篇介绍硬件IIC通信 ,想要了解软件IIC的可以看另一篇文章,IIC通信SDA数据线 高电平电压范围在 2.5V~5.5V 低电平范围 0V~0.3V
#软件IIC介绍
软件IIC指的是,通过GPIO引脚模拟IIC通信的波形和时序,也就是控制引脚的电平变化来传输数据还有时序信号,达到IIC通信的效果目的,软件IIC通信好处是,在MCU内部没有集成硬件IIC电路的时候,可以使用软件IIC通信,只要MCU支持GPIO就能使用软件IIC通信。
软件IIC的性能相对于硬件IIC较低,通常使用在低速通信还有简单通信的情况下去使用。
#硬件IIC介绍
硬件IIC指的是芯片内部集成的IIC硬件外设,硬件IIC效率远高于软件IIC, STMF4VET6上带有3个硬件IIC,允许与外部IIC接口进行通信,STMF4的硬件IIC使用特定的引脚复位,来进行数据和时钟信号的传输。
硬件IIC的通信时序波形,是通过硬件电路还有寄存器来实现的,这些电路还有寄存器负责生成时钟,还有数据线的电平变化,使其符合IIC通信协议的要求,这个就是IIC通信的硬件控制流程,具体可以参考下图硬件IIC的框图。
硬件IIC框图里面,可以发现硬件IIC,也支持中断 还有 DMA输出传输数据。
#配置硬件IIC相关流程
#硬件IIC发送流程
**软件初始化:**硬件IIC的通信时序,由内部的寄存器 控制器,来负责所以首先要配置IIC控制器的参数,需要设置 IIC速率 地址模式 设备地址。
**START设置:**发送起始信号到IIC总线,设置硬件IIC控制器开始位来启动发送过程,通过标志位SBSEND判断起始信号是否发送完毕,发送完毕标志位(SBSEND)会置一。
**清除SBSEND:**当起始信号发送完成,SBSEND这个标志位会被硬件置1,10位地址模式,需要清除标志位才能进行下一步,7位地址模式,该标志位不能清除。
**清除ADDSEND:**如果地址为10位模式,要发送 地址高位 和 地址低位,发送完成ADD10SEND 和 ADDSEND 会由硬件置1,这个时候需要清除 ADD10SEND 和 ADDSEND 如果地址为 7位模式则只需要发送一次地址,并等待ADDSEND硬件置1后,清除ADDSEND标志位。
**传输数据:**为了防止数据溢出,需要判断发送寄存器的数据是否为空,也就是查询TBE标志位的值,当发送数据寄存器为空的时候,TBE寄存器会被硬件置1,主机接受从机应答信号,此时发送数据成功,BTC标志位会被硬件置1。
**设置STOP:**当数据发送完成,这个时候要停止IIC通信,设置STOP也就是发送停止信号。
#硬件IIC接收流程
硬件IIC,接收数据跟发送数据过程大致相同, 设置START 清除SBSEND 清除ADDEND 读取字节数据 清除ACKEN 设置STOP 。下面只介绍不同的部分。
**在此设置START:**这里其实信号发送之前,硬件IIC必须是空闲状态才能发送,负责没有办法进行下一步。
**读取数据字节:**RBNE标志位,当接收数据寄存器中,如果有数据,会将RBNE自动硬件置1,这个时候通过读取标志位信息,停止发送信息,防止数据溢出。取出寄存器里面的数据之后,通过使能ACK应答位,硬件会自动发送,这个时候从机才会继续发送数据。
#软件IIC实验
硬件IIC通常来说不为常用,一般通过软件IIC进行通信,也就是通过控制GPIO引脚电平,模拟IIC通信时序,电平。
#宏定义 IIC引脚 调用函数
使用宏定义,去写代码,这种函数是能够提升程序的执行速度,因为宏定义是预处理指令,在程序执行前,开始执行的,如果是将函数在次封装在调用,这种程序执行速度是比不上,宏定义函数,宏定义常量的。
cpp
#define RCU_SCL RCC_AHB1Periph_GPIOB
#define PORT_SCL GPIOB
#define GPIO_SCL GPIO_Pin_6
#define RCU_SDA RCC_AHB1Periph_GPIOB
#define PORT_SDA GPIOB
#define GPIO_SDA GPIO_Pin_7
#define SDA_IN() {SHT20_MODE_SET( GPIO_Mode_IN );} //SDA输入模式
#define SDA_OUT() {SHT20_MODE_SET( GPIO_Mode_OUT );} //SDA输出模式
#define SCL(BIT) GPIO_WriteBit(PORT_SCL, GPIO_SCL, BIT)
#define SDA(BIT) GPIO_WriteBit(PORT_SDA, GPIO_SDA, BIT)
#define SDA_GET() GPIO_ReadInputDataBit(PORT_SDA, GPIO_SDA)
cpp
void SHT20_GPIO_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_SCL;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(PORT_SCL, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_SDA;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(PORT_SDA, &GPIO_InitStructure);
}
这里使用了宏定义常量 重定义了函数,同时初始化了 GPIO 引脚 用它作为 IIC通信数据线。
#配置IIC通信时序
cpp
void IIC_Start(void)
{
SDA_OUT();
SCL(0);
SDA(1);
SCL(1);
delay_us(5);
SDA(0);
delay_us(5);
SCL(0);
delay_us(5);
}
cpp
void IIC_Stop(void)
{
SDA_OUT();
SCL(0);
SDA(0);
SCL(1);
delay_us(5);
SDA(1);
delay_us(5);
}
cpp
void IIC_Send_Ack(uint8_t ack)
{
SDA_OUT();
SCL(0);
SDA(0);
delay_us(5);
if(!ack) SDA(0);
else SDA(1);
SCL(1);
delay_us(5);
SCL(0);
SDA(1);
}
cpp
uint8_t IIC_Wait_Ack(void)
{
char ack = 0;
unsigned char ack_flag = 10;
SDA_IN();
SDA(1);
delay_us(5);
SCL(1);
delay_us(5);
while( (SDA_GET()==1) && ( ack_flag ) )
{
ack_flag--;
delay_us(5);
}
if( ack_flag <= 0 )
{
IIC_Stop();
return 1;
}
else
{
SCL(0);
SDA_OUT();
}
return ack;
}
cpp
void IIC_Write(uint8_t data)
{
int i = 0;
SDA_OUT();
SCL(0);//拉低时钟开始数据传输
for( i = 0; i < 8; i++ )
{
SDA( (data & 0x80) >> 7 );
delay_us(2);
data<<=1;
delay_us(6);
SCL(1);
delay_us(4);
SCL(0);
delay_us(4);
}
}
cpp
uint8_t IIC_Read(void)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
SCL(0);
delay_us(5);
SCL(1);
delay_us(5);
receive<<=1;
if( SDA_GET() )
{
receive|=1;
}
delay_us(5);
}
return receive;
}
上面配置了IIC通信时序,接下来,根据温度湿度公式,计算实际温度还是湿度定义函数输出即可。
#配置温度湿度函数
cpp
float SHT20_Read(uint8_t regaddr)
{
unsigned char data_H = 0;
unsigned char data_L = 0;
float temp = 0;
IIC_Start();
IIC_Write(0x80|0);
if( IIC_Wait_Ack() == 1 ) printf("error -1\r\n");
IIC_Write(regaddr);
if( IIC_Wait_Ack() == 1 ) printf("error -2\r\n");
do{
delay_us(10);
IIC_Start();
IIC_Write(0x80|1);
}while( IIC_Wait_Ack() == 1 );
delay_us(20);
data_H = IIC_Read();
IIC_Send_Ack(0);
data_L = IIC_Read();
IIC_Send_Ack(1);
IIC_Stop();
if( regaddr == 0xf3 )
{
temp = ((data_H<<8)|data_L) / 65536.0 * 175.72 - 46.85;
}
if( regaddr == 0xf5 )
{
temp = ((data_H<<8)|data_L) / 65536.0 * 125.0 - 6;
}
return temp;
}
cpp
int main(void)
{
board_init();
uart1_init(115200U);
//引脚初始化
SHT20_GPIO_INIT();
//等待传感器上电初始化完成
delay_ms(20);
while(1)
{
//采集温度
printf("temp = %.2f\r\n", SHT20_Read(0xf3) );
//采集湿度
printf("humi = %.2f\r\n", SHT20_Read(0xf5) );
printf("\r\n");
delay_ms(500);
}
}
【立创·天空星STM32F407VET6】入门手册 - 飞书云文档 (feishu.cn)
欢迎指正,希望对你,有所帮助!!!