前言
N32L40X系列采用32 bit Arm® Cortex®-M4F内核,最高工作主频64MHz,支持浮点运算和DSP指令,集成高达 128KB嵌入式Flash,24KB SRAM,集成丰富的高性能模拟器件,内置1个12bit 4.5Msps ADC,2路独立轨到轨运 算放大器,2个高速比较器,1个1Msps 12bit DAC,集成U(S)ART、LPUART、I2C、SPI、USB、CAN等数字通信接口, Segment LCD驱动接口,内置多种密码算法硬件加速引擎。
由于AHT10是基于IIC协议,IIC协议我们就不做多余介绍。重要的就是IIC从机地址与AHT10的几个重要的寄存器。
引脚上不用过多关注,就看ADR怎么接,如果是接地。那从机地址就是0x38<<1,写地址0x38<<1,读地址(0x38<<1 )| 1
建议是2s测量一次。
最主要的三条命令(三个寄存器):初始化(0xE1),测量(0xAC),软复位(0xBA)
状态位在我们进行测量后,读取数据的时候有用,那时可以决定是否转换完毕,是留下数据还是舍去数据再重新转换。
大概AHT10的内容就那么多。
一、基于N32库函数的IIC通用软件接口
立创天地星卡发板驱动:AHT10温湿度传感器 | 立创开发板技术文档中心
与一个GitCode上的老哥的基于stm32F103C8t6代码包GitCode - 全球开发者的开源社区,开源代码托管平台
#include "iic_basen32lib.h"
/**
* @brief Inserts a delay time.
* @param count specifies the delay time length.
*/
void delay_us(uint32_t count)
{
count=(count*4);
for (; count > 0; count--)
{
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
}
}
void delay_ms(uint32_t count)
{
for (; count > 0; count--)
{
delay_us(1006);
}
}
/**
* @brief SDA线输入模式配置
* @param bus IIC总线控制器
* @retval None
*/
static void SDA_Input_Mode(s_iic_bus_t* bus)
{
GPIO_InitType GPIO_InitStructure ;
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = bus->IIC_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Input;
GPIO_InitStructure.GPIO_Pull = GPIO_No_Pull;
GPIO_InitPeripheral(bus->IIC_SDA_GPIO, &GPIO_InitStructure);
}
/**
* @brief SDA线输出模式配置
* @param bus IIC总线控制器
* @retval None
*/
static void SDA_Output_Mode(s_iic_bus_t *bus)
{
GPIO_InitType GPIO_InitStructure;
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = bus->IIC_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pull = GPIO_No_Pull;
GPIO_InitPeripheral(bus->IIC_SDA_GPIO, &GPIO_InitStructure);
}
/**
* @brief SDA线输出一个位
* @param bus IIC总线控制器 val 输出的数据 1/0
* @retval None
*/
static void SDA_Output(s_iic_bus_t *bus, uint8_t val)
{
if(val)GPIO_SetBits(bus->IIC_SDA_GPIO,bus->IIC_SDA_PIN);
else GPIO_ResetBits(bus->IIC_SDA_GPIO,bus->IIC_SDA_PIN);
}
/**
* @brief SCL线输出一个位
* @param bus IIC总线控制器 val 输出的数据 1/0
* @retval None
*/
static void SCL_Output(s_iic_bus_t *bus, uint8_t val)
{
if(val)GPIO_SetBits(bus->IIC_SCL_GPIO,bus->IIC_SCL_PIN);
else GPIO_ResetBits(bus->IIC_SCL_GPIO,bus->IIC_SCL_PIN);
}
/**
* @brief SDA输入一位
* @param bus IIC总线控制器
* @return 读到的一位数据
* @retval GPIO读入一位
*/
uint8_t SDA_Input(s_iic_bus_t *bus)
{
return GPIO_ReadInputDataBit(bus->IIC_SDA_GPIO,bus->IIC_SDA_PIN);
}
// _____
// SDA: |_____
// _______
// SCL: |___
/**
* @brief IIC起始信号
* @param bus IIC总线控制器
* @retval None
*/
void IICStart(s_iic_bus_t *bus)
{
SDA_Output_Mode(bus);
SDA_Output(bus,1);
SCL_Output(bus,1);
delay_us(4);
SDA_Output(bus,0);
delay_us(4);
SCL_Output(bus,0);
}
// _____
// SDA: _____|
// _______
// SCL: ___|
/**
* @brief IIC结束信号
* @param bus IIC总线控制器
* @retval None
*/
void IICStop(s_iic_bus_t *bus)
{
SDA_Output_Mode(bus);
SCL_Output(bus,0);
SDA_Output(bus,0);
delay_us(4);
SCL_Output(bus,1);
delay_us(4);
SDA_Output(bus,1);
delay_us(4);
}
// 应答___ 非应答 __________
// SDA: |_______ SDA:
// _____ _____
// SCL:___| | SCL: ___| |
/**
* @brief IIC主机等待从机应答
* @param bus IIC总线控制器
* @retval None
*/
uint8_t IICWaitAck(s_iic_bus_t *bus)
{
uint8_t ucErrTime=0;
SDA_Input_Mode(bus);
SDA_Output(bus,1);
delay_us(1);
SCL_Output(bus,1);
delay_us(1);
while((SDA_Input(bus)==1))//应答超时
{
ucErrTime++;
if(ucErrTime >=250)
{
IICStop(bus);
return 1;
}
}
SCL_Output(bus,0);
return 0;
}
/**
* @brief IIC发送应答信号(主机)
* @param bus IIC总线控制器
* @retval None
*/
void IICSendAck(s_iic_bus_t *bus)
{
SCL_Output(bus,0);
SDA_Output_Mode(bus);
SDA_Output(bus,0);
delay_us(2);
SCL_Output(bus,1);
delay_us(2);
SCL_Output(bus,0);
}
/**
* @brief IIC发送非应答信号(主机)
* @param bus IIC总线控制器
* @retval None
*/
void IICSendNotAck(s_iic_bus_t *bus)
{
SCL_Output(bus,0);
SDA_Output_Mode(bus);
SDA_Output(bus,1);
delay_us(2);
SCL_Output(bus,1);
delay_us(2);
SCL_Output(bus,0);
}
/**
* @brief IIC发送一个字节
* @param byte 需要发送的字节
* @retval None
*/
void IICSendByte(s_iic_bus_t *bus,uint8_t cSendByte)
{
uint8_t i = 0;
SDA_Output_Mode(bus);
SCL_Output(bus,0);
for(i=0;i<8;i++)
{
if((cSendByte&0x80)>>7)SDA_Output(bus,1);
else SDA_Output(bus,0);
cSendByte <<=1;
delay_us(2);
SCL_Output(bus,1);
delay_us(2);
SCL_Output(bus,0);
delay_us(2);
}
}
/**
* @brief IIC接收一个字节
* @param None 0 nack 1 ack
* @retval 接收到的字节
*/
uint8_t IICReceiveByte(s_iic_bus_t *bus, uint8_t ack)
{
uint8_t i = 0;
uint8_t cReceiveByte = 0;
SDA_Input_Mode(bus);
for(i=0;i<8;i++)
{
SCL_Output(bus,0);
delay_us(2);
SCL_Output(bus,1);
cReceiveByte <<=1;
if(SDA_Input(bus))
{
cReceiveByte++;
}
delay_us(1);
}
if(!ack ) IICSendNotAck(bus);
else IICSendAck(bus);
return cReceiveByte;
}
void IICInit(s_iic_bus_t *bus)
{
GPIO_InitType GPIO_InitStructure ; /*初始化GPIO结构体*/
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = bus->IIC_SCL_PIN ;
GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
GPIO_InitStructure.GPIO_Pull = GPIO_No_Pull;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;//以高速50M速率为例 翻转电平仅需20ns
GPIO_InitPeripheral(bus->IIC_SCL_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.Pin = bus->IIC_SDA_PIN ;
GPIO_InitPeripheral(bus->IIC_SDA_GPIO, &GPIO_InitStructure);
GPIO_SetBits(bus->IIC_SDA_GPIO,bus->IIC_SDA_PIN);/*拉高总线*/
GPIO_SetBits(bus->IIC_SCL_GPIO,bus->IIC_SCL_PIN);
}
头文件
#ifndef __IIC_SOFT_H
#define __IIC_SOFT_H
#include "n32l40x.h"
typedef struct
{
GPIO_Module* IIC_SDA_GPIO;
GPIO_Module* IIC_SCL_GPIO;
uint16_t IIC_SDA_PIN;
uint16_t IIC_SCL_PIN;
}s_iic_bus_t;/*IIC总线控制器*/
void delay_us(uint32_t count);
void delay_ms(uint32_t count);
void IICStart(s_iic_bus_t *bus);
void IICStop(s_iic_bus_t *bus);
uint8_t IICWaitAck(s_iic_bus_t *bus);
void IICSendAck(s_iic_bus_t *bus);
void IICSendNotAck(s_iic_bus_t *bus);
void IICSendByte(s_iic_bus_t *bus, uint8_t cSendByte);
uint8_t IICReceiveByte(s_iic_bus_t *bus, uint8_t ack);
void IICInit(s_iic_bus_t *bus);
#endif
延时函数要自己解决(推荐是移植正点原子关于滴答定时器的)
二、基于IIC接口实例化的AHT10驱动
AHT10驱动
#include "iic_basen32lib.h"
#include "AHT10.h"
#include <stdio.h>
s_iic_bus_t AHT_bus =
{
.IIC_SDA_GPIO = AHT10_SDA_GPIO,
.IIC_SCL_GPIO = AHT10_SCL_GPIO,
.IIC_SDA_PIN = AHT10_SDA_PIN,
.IIC_SCL_PIN = AHT10_SCL_PIN,
};
/**
* @brief aht10的SCL与SDA时钟使能
* @param None
* @retval None
*/
static void aht10_clk_en(void)
{
aht10_scl_clk_en();
aht10_sda_clk_en();
}
/**
brief AHT10初始化函数
param NONE
return NONE
*/
void AHT10Init()
{
u8 ack=0;
aht10_clk_en();
IICInit(&AHT_bus);
delay_ms(100);
IICStart(&AHT_bus);
IICSendByte(&AHT_bus, AHT10_ADDRESS);
ack=IICWaitAck(&AHT_bus);//printf("ack is %d\r\n",ack);
IICSendByte(&AHT_bus,0xe1);
ack=IICWaitAck(&AHT_bus);//printf("ack is %d\r\n",ack);
IICSendByte(&AHT_bus,0x08);
ack=IICWaitAck(&AHT_bus);//printf("ack is %d\r\n",ack);
IICSendByte(&AHT_bus,0x00);
IICStop(&AHT_bus);
delay_ms(40);//延时40ms让传感器稳定
}
/**
brief 检查AHT10是否存在
param NONE
return 0存在 1不存在
*/
u8 AHT10Check(void)
{
u8 ack=0;
IICStart(&AHT_bus);
IICSendByte(&AHT_bus, AHT10_ADDRESS );
ack=IICWaitAck(&AHT_bus);
IICStop(&AHT_bus);
return ack;
}
/**
brief AHT10软复位
param NONE
return NONE
*/
void AHT10Reset(void)
{
IICStart(&AHT_bus);
IICSendByte(&AHT_bus, AHT10_WRITE);
IICWaitAck(&AHT_bus);
IICSendByte(&AHT_bus, 0xba);
IICWaitAck(&AHT_bus);
IICStop(&AHT_bus);
}
/**
brief 检查AHT10读温湿度数据
param *temperature:需要读出的温度数据,float指针类型,精度范围+-0.3C
param *humidity:需要读出的湿度数据,u8指针类型,精度范围+-2RH
return 0 读数据正常 1读数据失败
*/
u8 AHT10ReadData(float *temperature,u8 *humidity)
{
u8 ack;
u32 SRH=0,ST=0;
u8 databuff[6];
IICStart(&AHT_bus);
IICSendByte(&AHT_bus,AHT10_WRITE);
ack = IICWaitAck(&AHT_bus);//printf("ack is %d\r\n",ack);
IICSendByte(&AHT_bus, 0xac);
ack = IICWaitAck(&AHT_bus);//printf("ack is %d\r\n",ack);
IICSendByte(&AHT_bus,0x33);
ack = IICWaitAck(&AHT_bus);//printf("ack is %d\r\n",ack);
IICSendByte(&AHT_bus,0x00);
ack = IICWaitAck(&AHT_bus);//printf("ack is %d\r\n",ack);
IICStop(&AHT_bus);
delay_ms(80);//延时一会等待数据读出
IICStart(&AHT_bus);
IICSendByte(&AHT_bus,AHT10_READ);
ack = IICWaitAck(&AHT_bus);//printf("ack is %d\r\n",ack);
ack=IICReceiveByte(&AHT_bus,1);//printf("ack is %d\r\n",ack);
if((ack&0x40)==0)
{
databuff[0]=IICReceiveByte(&AHT_bus,1);
databuff[1]=IICReceiveByte(&AHT_bus,1);
databuff[2]=IICReceiveByte(&AHT_bus,1);
databuff[3]=IICReceiveByte(&AHT_bus,1);
databuff[4]=IICReceiveByte(&AHT_bus,0);
//printf("buff is %d %d %d %d %d\r\n",databuff[0],databuff[1],databuff[2],databuff[3],databuff[4]);
IICStop(&AHT_bus);
SRH=(databuff[0]<<12)+(databuff[1]<<4)+(databuff[2]>>4);
ST=((databuff[2]&0X0f)<<16)+(databuff[3]<<8)+(databuff[4]);
*humidity=(int)(SRH*100.0/1024/1024+0.5);
*temperature=((int)(ST*2000.0/1024/1024+0.5))/10.0-50;
return 0;
}
IICStop(&AHT_bus);
return 1;
}
头文件
#ifndef _AHT10_H__
#define _AHT10_H__
#include "n32l40x.h"
#define aht10_scl_clk_en() RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE)
#define aht10_sda_clk_en() RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE)
#define AHT10_SCL_PIN GPIO_PIN_12
#define AHT10_SDA_PIN GPIO_PIN_11
#define AHT10_SCL_GPIO GPIOA
#define AHT10_SDA_GPIO GPIOA
#define AHT10_ADDRESS 0x70
#define AHT10_WRITE 0x70
#define AHT10_READ 0x71
/*****************函数声明******************/
extern void AHT10Init(void);
extern uint8_t AHT10Check(void);
extern void AHT10Reset(void);
extern uint8_t AHT10ReadData(float *temperature,uint8_t *humidity);
#endif
在main函数里初始化,隔1s读一下数据就行
三、时序截图
初始化
读
四、串口查看
五、可能出现的问题
IIC没有收到应答:如果接的是杜邦线,看看插紧了没。
自己焊的元器件虚汗了没,推荐买个小模块玩,才几块钱。
时序波特率接近400kbps属于正常速度。