本文主要是介绍基于12C协议的AHT20温湿度采集,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
- 12C通信协议
- 硬软件I2C
- AHT20测温湿度
- 参考
12C通信协议
I2C通信协议
(1) I2C 协议简介
I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps公司开发的,由于它引脚少,硬件实 现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地 使用在系统内多个集成电路(IC)间的通讯。
在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和 片上外设;STM32 标准库则是在寄存器与用户代码之间的软件层。对于通讯协议,我们也 以分层的方式来理解,最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流。
(2) I2C 物理层
I2C通讯系统接线图如下:
(1) 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线 中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。
(2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
(3) 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之 间的访问。
(4) 总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都空 闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
(5) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用 总线。
(6) 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s,高速模式 下可达 3.4Mbit/s,但目前大多 I2C设备尚不支持高速模式。
(7) 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制。
(3) I2C 协议层
I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地 址广播等环节。
① 通讯的起始和停止信号
当 SCL 线是高电 平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始。当 SCL 是高电平时 SDA
线由低电平向高电平切换,表示通讯的停止。起始和停止信号一般由主机产生。
② 数据传输
(1) 起始信号后,总线上所有的从机开始等待主机紧接下来的从机地址广播。因为总线上每个设备的地址都是唯一的,当主机广播的地址与某个设备地址相同时,该设备就被选中,并向主机发出应答(ACK)或者非应答(NACK),主机只有在接收到应答信号后继续发送/接收数据,没选中的设备将会忽略之后的数据信号。根据I2C协议,从机地址可以是7位或者10位。
(2) I2C总线上传输的数据包括上述的地址信号,又包括真正的数据信号。在起始信号后需传送一个从机地址(7位),第8位是数据的传输方向(接收/发送),“0”表示主机发送数据,“1”表示主机接收数据。每次数据的传输总是由主机产生结束信号以结束传输,但若主机希望继续占用总线进行新的数据传输时,则可以不产生结束信号,而是再次发送起始信号对另一从机地址寻址。
(3) 若配置为写数据方向,主机开始向从机传输数据,数据包大小为8位,主机每发送完1字节数据都有等到从机的应答信号(ACK),多字节数据发送时重复此过程。传输结束后,主机向从机发送一个停止信号表不再传输数据。
(4) 若配置为读方向,从机开始向主机返回数据,数据包大小还是8位。同理,从机每发送完一字节数据都要等到主机的应答信号(ACK),重复此过程可以返回多个数据。当主机希望停止接收数据时就向返回一个非应答信号(NACK),数据传输将结束。
(5) 实际I2C通讯采用的是读写复合的格式。传输过程中主机需要发出2次起始信号:第一次传输主机通过从机地址找到从机设备,发送一段数据,这个数据是从设备内部寄存器或者存储器地址;第二次传输是对该地址进行读/写。主机要读取从机数据时,主机会释放对SDA总线的控制,由从机控制SDA总线,主机负责接收信号;主机要向从机设备写数据时,SDA由主机控制,从机负责接收信号。
硬软件I2C
硬件 I2C:对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,因而效率要远高于软件模拟的I2C;一般也较为稳定,但是程序较为繁琐。硬件(固件)I2C是直接调用内部寄存器进行配置;而软件I2C是没有寄存器这个概念的。
**软件 I2C:**一般是使用GPIO管脚,用软件控制SCL,SDA线输出高低电平,模拟i2c协议的时序。
主要对比:
硬件IIC用法比较复杂,模拟IIC的流程更清楚一些。
硬件IIC速度比模拟快,并且可以用DMA
模拟IIC可以在任何管脚上,而硬件只能在固定管脚上。
软件i2c 是使用程序控制SCL,SDA线输出高低电平,模拟i2c协议的时序。一般较硬件i2c稳定,但是程序较为繁琐,但不难。
硬件i2c 只要调用i2c的控制函数即可,不用直接的去控制SCL,SDA高低电平的输出。但是有些单片机的硬件i2c不太稳定,调试问题较多。
AHT20测温湿度
这个改编自野火自带例程串口通信4口,如下:
连接STM32开发板与AHT20的温湿度传感器引脚如下:
测温湿度函数计算代码如下:
void read_AHT20(void)
{uint8_t i;for(i=0; i<6; i++){readByte[i]=0;}//-------------I2C_Start();I2C_WriteByte(0x71);ack_status = Receive_ACK();readByte[0]= I2C_ReadByte();Send_ACK();readByte[1]= I2C_ReadByte();Send_ACK();readByte[2]= I2C_ReadByte();Send_ACK();readByte[3]= I2C_ReadByte();Send_ACK();readByte[4]= I2C_ReadByte();Send_ACK();readByte[5]= I2C_ReadByte();SendNot_Ack();//Send_ACK();I2C_Stop();//--------------if( (readByte[0] & 0x68) == 0x08 ){H1 = readByte[1];H1 = (H1<<8) | readByte[2];H1 = (H1<<8) | readByte[3];H1 = H1>>4;H1 = (H1*1000)/1024/1024;T1 = readByte[3];T1 = T1 & 0x0000000F;T1 = (T1<<8) | readByte[4];T1 = (T1<<8) | readByte[5];T1 = (T1*2000)/1024/1024 - 500;AHT20_OutData[0] = (H1>>8) & 0x000000FF;AHT20_OutData[1] = H1 & 0x000000FF;AHT20_OutData[2] = (T1>>8) & 0x000000FF;AHT20_OutData[3] = T1 & 0x000000FF;}else{AHT20_OutData[0] = 0xFF;AHT20_OutData[1] = 0xFF;AHT20_OutData[2] = 0xFF;AHT20_OutData[3] = 0xFF;printf("失败了");}printf("\r\n");printf("当前温度为: %d%d.%d",T1/100,(T1/10)%10,T1%10);printf("\r\n");printf("µ±Ç°Êª¶ÈΪ: %d%d.%d",H1/100,(H1/10)%10,H1%10);printf("\r\n");
}
具体文件需要delay.c,usart.c,i2c.c,sys.c,main.c
delay.c文件源码:
#include "delay.h"
#include "sys.h"
#if SYSTEM_SUPPORT_UCOS
#include "includes.h"
#endif
static u8 fac_us=0;
static u16 fac_ms=0;
#ifdef OS_CRITICAL_METHOD
void SysTick_Handler(void)
{ OSIntEnter(); OSTimeTick(); OSIntExit();
#endifvoid delay_init()
{#ifdef OS_CRITICAL_METHOD u32 reload;
#endifSysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_us=SystemCoreClock/8000000; #ifdef OS_CRITICAL_METHOD reload=SystemCoreClock/8000000; reload*=1000000/OS_TICKS_PER_SEC;fac_ms=1000/OS_TICKS_PER_SEC; SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;SysTick->LOAD=reload;SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;
#elsefac_ms=(u16)fac_us*1000;
#endif
} #ifdef OS_CRITICAL_METHOD
void delay_us(u32 nus)
{ u32 ticks;u32 told,tnow,tcnt=0;u32 reload=SysTick->LOAD; ticks=nus*fac_us; tcnt=0;told=SysTick->VAL; while(1){tnow=SysTick->VAL; if(tnow!=told){ if(tnow<told)tcnt+=told-tnow;else tcnt+=reload-tnow+told; told=tnow;if(tcnt>=ticks)break;} };
}void delay_ms(u16 nms)
{ if(OSRunning==TRUE) { if(nms>=fac_ms){OSTimeDly(nms/fac_ms);}nms%=fac_ms; }delay_us((u32)(nms*1000));
}
#else
void delay_us(u32 nus)
{ u32 temp; SysTick->LOAD=nus*fac_us; SysTick->VAL=0x00; SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; do{temp=SysTick->CTRL;}while(temp&0x01&&!(temp&(1<<16)));SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; SysTick->VAL =0X00;
}void delay_ms(u16 nms)
{ u32 temp; SysTick->LOAD=(u32)nms*fac_ms;SysTick->VAL =0x00; SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; do{temp=SysTick->CTRL;}while(temp&0x01&&!(temp&(1<<16))); SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; SysTick->VAL =0X00;
}
#endif
usart.c文件源码:
#include "sys.h"
#include "usart.h"#if SYSTEM_SUPPORT_UCOS
#include "includes.h"
#if 1
#pragma import(__use_no_semihosting)
struct __FILE
{ int handle; }; FILE __stdout;
void _sys_exit(int x)
{ x = x;
}
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0);//Ñ»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï USART1->DR = (u8) ch; return ch;
}
#endif #if EN_USART1_RX
u8 USART_RX_BUF[USART_REC_LEN];
u16 USART_RX_STA=0; void uart_init(u32 bound){GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);USART_InitStructure.USART_BaudRate = bound;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);USART_Cmd(USART1, ENABLE); }
void USART1_IRQHandler(void) {u8 Res;
#ifdef OS_TICKS_PER_SEC OSIntEnter();
#endifif(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {Res =USART_ReceiveData(USART1);//(USART1->DR); if((USART_RX_STA&0x8000)==0){if(USART_RX_STA&0x4000){if(Res!=0x0a)USART_RX_STA=0;else USART_RX_STA|=0x8000;}else { if(Res==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0; } }} }
#ifdef OS_TICKS_PER_SEC OSIntExit();
#endif
}
#endif
i2c.c文件源码:
#include "i2c.h"
#include "delay.h"uint8_t ack_status=0;
uint8_t readByte[6];
uint8_t AHT20_status=0;uint32_t H1=0; //Humility
uint32_t T1=0; //Temperatureuint8_t AHT20_OutData[4];
uint8_t AHT20sendOutData[10] = {0xFA, 0x06, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF};void IIC_Init(void)
{ GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //ÍÆÍìÊä³öGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);IIC_SCL=1;IIC_SDA=1;}
void IIC_Start(void)
{SDA_OUT(); IIC_SDA=1; IIC_SCL=1;delay_us(4);IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4);IIC_SCL=0;
} void IIC_Stop(void)
{SDA_OUT();IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highdelay_us(4);IIC_SCL=1; IIC_SDA=1;delay_us(4);
}
u8 IIC_Wait_Ack(void)
{u8 ucErrTime=0;SDA_IN(); IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA){ucErrTime++;if(ucErrTime>250){IIC_Stop();return 1;}}IIC_SCL=0;return 0;
}
void IIC_Ack(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=0;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}
//²»²úÉúACKÓ¦´ð
void IIC_NAck(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=1;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}
void IIC_Send_Byte(u8 txd)
{ u8 t; SDA_OUT(); IIC_SCL=0;for(t=0;t<8;t++){ IIC_SDA=(txd&0x80)>>7;txd<<=1; delay_us(2); IIC_SCL=1;delay_us(2); IIC_SCL=0; delay_us(2);}
}
u8 IIC_Read_Byte(unsigned char ack)
{unsigned char i,receive=0;SDA_IN();for(i=0;i<8;i++ ){IIC_SCL=0; delay_us(2);IIC_SCL=1;receive<<=1;if(READ_SDA)receive++; delay_us(1); } if (!ack)IIC_NAck();elseIIC_Ack(); return receive;
}
void IIC_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr)
{IIC_Start(); if(device_addr==0xA0) IIC_Send_Byte(0xA0 + ((addr/256)<<1));elseIIC_Send_Byte(device_addr); IIC_Wait_Ack(); IIC_Send_Byte(addr&0xFF); IIC_Wait_Ack(); IIC_Send_Byte(data); IIC_Wait_Ack(); IIC_Stop();if(device_addr==0xA0)delay_ms(10);elsedelay_us(2);
}
uint16_t IIC_ReadByte(uint16_t addr,uint8_t device_addr,uint8_t ByteNumToRead)
{ uint16_t data;IIC_Start(); if(device_addr==0xA0)IIC_Send_Byte(0xA0 + ((addr/256)<<1));elseIIC_Send_Byte(device_addr); IIC_Wait_Ack();IIC_Send_Byte(addr&0xFF); IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(device_addr+1);IIC_Wait_Ack();if(ByteNumToRead == 1){data=IIC_Read_Byte(0);}else{data=IIC_Read_Byte(1);data=(data<<8)+IIC_Read_Byte(0);}IIC_Stop();//²úÉúÒ»¸öÍ£Ö¹Ìõ¼þ return data;
}
void read_AHT20_once(void)
{delay_ms(10);startMeasure_AHT20();delay_ms(80);read_AHT20();delay_ms(5);
}
void startMeasure_AHT20(void)
{I2C_Start();I2C_WriteByte(0x70);ack_status = Receive_ACK();if(ack_status);else printf("7-n-");I2C_WriteByte(0xAC);ack_status = Receive_ACK();if(ack_status);else printf("8-n-");I2C_WriteByte(0x33);ack_status = Receive_ACK();if(ack_status);else printf("9-n-");I2C_WriteByte(0x00);ack_status = Receive_ACK();if(ack_status);else printf("10-n-");I2C_Stop();
}
void read_AHT20(void)
{uint8_t i;for(i=0; i<6; i++){readByte[i]=0;}I2C_Start();I2C_WriteByte(0x71);ack_status = Receive_ACK();readByte[0]= I2C_ReadByte();Send_ACK();readByte[1]= I2C_ReadByte();Send_ACK();readByte[2]= I2C_ReadByte();Send_ACK();readByte[3]= I2C_ReadByte();Send_ACK();readByte[4]= I2C_ReadByte();Send_ACK();readByte[5]= I2C_ReadByte();SendNot_Ack();//Send_ACK();I2C_Stop();if( (readByte[0] & 0x68) == 0x08 ){H1 = readByte[1];H1 = (H1<<8) | readByte[2];H1 = (H1<<8) | readByte[3];H1 = H1>>4;H1 = (H1*1000)/1024/1024;T1 = readByte[3];T1 = T1 & 0x0000000F;T1 = (T1<<8) | readByte[4];T1 = (T1<<8) | readByte[5];T1 = (T1*2000)/1024/1024 - 500;AHT20_OutData[0] = (H1>>8) & 0x000000FF;AHT20_OutData[1] = H1 & 0x000000FF;AHT20_OutData[2] = (T1>>8) & 0x000000FF;AHT20_OutData[3] = T1 & 0x000000FF;}else{AHT20_OutData[0] = 0xFF;AHT20_OutData[1] = 0xFF;AHT20_OutData[2] = 0xFF;AHT20_OutData[3] = 0xFF;printf("失败了");}printf("\r\n");printf("当前温度为: %d%d.%d",T1/100,(T1/10)%10,T1%10);printf("\r\n");printf("当前湿度为: %d%d.%d",H1/100,(H1/10)%10,H1%10);printf("\r\n");
}
uint8_t Receive_ACK(void)
{uint8_t result=0;uint8_t cnt=0;IIC_SCL = 0;SDA_IN(); delay_us(4);IIC_SCL = 1;delay_us(4);while(READ_SDA && (cnt<100)){cnt++;}IIC_SCL = 0;delay_us(4);if(cnt<100){result=1;}return result;
}void Send_ACK(void)
{SDA_OUT();IIC_SCL = 0;delay_us(4);IIC_SDA = 0;delay_us(4);IIC_SCL = 1;delay_us(4);IIC_SCL = 0;delay_us(4);SDA_IN();
}void SendNot_Ack(void)
{SDA_OUT();IIC_SCL = 0;delay_us(4);IIC_SDA = 1;delay_us(4);IIC_SCL = 1;delay_us(4);IIC_SCL = 0;delay_us(4);IIC_SDA = 0;delay_us(4);
}void I2C_WriteByte(uint8_t input)
{uint8_t i;SDA_OUT();for(i=0; i<8; i++){IIC_SCL = 0;delay_ms(5);if(input & 0x80){IIC_SDA = 1;//delaymm(10);}else{IIC_SDA = 0;//delaymm(10);}IIC_SCL = 1;delay_ms(5);input = (input<<1);}IIC_SCL = 0;delay_us(4);SDA_IN();delay_us(4);
} uint8_t I2C_ReadByte(void)
{uint8_t resultByte=0;uint8_t i=0, a=0;IIC_SCL = 0;SDA_IN();delay_ms(4);for(i=0; i<8; i++){IIC_SCL = 1;delay_ms(3);a=0;if(READ_SDA){a=1;}else{a=0;}//resultByte = resultByte | a;resultByte = (resultByte << 1) | a;IIC_SCL = 0;delay_ms(3);}SDA_IN();delay_ms(10);return resultByte;
}void set_AHT20sendOutData(void)
{AHT20sendOutData[3] = AHT20_OutData[0];AHT20sendOutData[4] = AHT20_OutData[1];AHT20sendOutData[5] = AHT20_OutData[2];AHT20sendOutData[6] = AHT20_OutData[3];
}void I2C_Start(void)
{SDA_OUT();IIC_SCL = 1;delay_ms(4);IIC_SDA = 1;delay_ms(4);IIC_SDA = 0;delay_ms(4);IIC_SCL = 0;delay_ms(4);
}void I2C_Stop(void)
{SDA_OUT();IIC_SDA = 0;delay_ms(4);IIC_SCL = 1;delay_ms(4);IIC_SDA = 1;delay_ms(4);
}
sys.c文件源码:
#include "sys.h"
void NVIC_Configuration(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
}
main.c文件源码:
#include "delay.h"
#include "usart.h"
#include "i2c.h"int main(void)
{ delay_init(); uart_init(115200); IIC_Init();while(1){printf("野火STM32指南者基于I2C协议的AHT20温湿度传感器开始测量:");read_AHT20_once();delay_ms(1500);}
}
实验结果
最后烧录进STM32后,打开串口助手,显示如下:
当朝着AHT20呼气或者握住传感器时,显然温度与湿度都上升了,说明测试正确。
参考
https://blog.csdn.net/xwmrqqq/article/details/111472031
https://blog.csdn.net/weixin_43202477/article/details/84823920
这篇关于基于12C协议的AHT20温湿度采集的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!