目前网上大部分DHT20/AHT20温湿度传感器的读取都是基于stm32等单片机的,本篇文章讲一讲如何借助vivado和vitis在FPGA上进行DHT20温湿度传感器数据的读取。

Vivado部分

在Vivado中需完成软核的搭建,软核有很多种,自行搜索一个自己板子支持的就好。我的板子是NexysA7,搭建的软核是MicroBlaze。

 

MicroBlaze软核的搭建我是直接跟着B站上视频来的:

第一期 简介与搭建 基于ARTY A7的MicroBlaze系统搭建与应用_哔哩哔哩_bilibili

注意:视频中的部分步骤需要根据自己的板子实际情况来设置;更改波特率那部分需要自己进入设备管理器查看自己电脑上USB端口的波特率;另外,视频比较老了,里面提到的SDK现在是Vitis。

下一步需要导入AXI IIC的ip核,如下图所示,这是实现iic通信协议的关键。

为iic ip核的 sci 和sda分配FPGA上的引脚,两个pmod端口

 之后生成比特流,导出.xsa硬件平台文件,再在Vitis导入生成的.xsa文件。

Vitis部分

vitis内部对iic模块封装好了对应的通信函数,我主要使用了xiic_l.h中声明的XIic_Send()和XIic_Recv()进行数据通信,使用这两个函数前需要导入xiic_l.h这个头文件。

XIic_Send()四个参数分别为: 

  • BaseAddress : 所用 iic接口的基地址

在vitis中可以在这里查看iic的基地址,我这里为0x4080 0000

 

  • Address : iic从机的地址,DHT20从机地址固定为0x38
  • BufferPtr : 向从机发送的数据指针
  • ByteCount : 发送的数据字节数
  • Option : 发送完数据后释放总线还是保持(有两个选择—— XIIC_STOP = 以STOP结束,即释放总线 ;XIIC_REPEATED_START = 不以STOP结束,保持总线)

XIic_Recv()函数的参数同理,注意使用这两个函数内部包含了iic协议开始通信的信号,在使用这两个函数前不需要额外发送开始通信信号,函数调用结束后是否释放数据总线需要自行设定Option参数进行设置。

DHT20官方手册:DHT20_(广州奥松)DHT20中文资料_价格_PDF手册-立创电子商城 (szlcsc.com)

在上面可以了解到DHT20数据读取的流程:

 

 

依照此流程编写代码:

1.上电后等待不少于100ms,发送0x71,之后需要获取状态字,故使用XIic_Send()函数发送1字节数据,然后保持总线不释放,接着使用XIic_Recv()函数接受1字节数据,接受完后释放总线。

u8 init = 0x71;
u8 data;

XIic_Send(Iic.BaseAddress,0x38,&init,1,XIIC_REPEATED_START);
XIic_Recv(Iic.BaseAddress,0x38,&data,1,XIIC_STOP);

 函数的第三个参数要求数据指针,于是我提前定义了两个变量init和data分别保存要发送的数据和接受的数据。

2.判断接收到的数据是否等于0x18,若不等则进行寄存器初始化,初始化的流程较复杂这里没给出解释,我后面对着官方的例程代码复现了一遍,这里先不讨论,就假装他等于0x18继续进行。

3.等待10ms发送0xAC命令出发测量,接着再发送两个命令参数0x33和0x00。看上面第二张图片,在发送第二张图片前需要先发送一字节IIC address+write数据,此处为0x70,为什么呢?

这里iic的地址位为7位,前面说过DHT20的从机地址为0x38七位地址的前三位表示“3”的低三位即0011中取011,后四位为“8”即1000,同时“0”表示“write”,“1”表示“读”,所以七位从机地址在加上一位写组合起来就是 01110000,换成十六进制就是0x70。

综上所述,这一步要发送四个字节的数据,依次为0x70,0xAC, 0x33,0x00

先定义一个数组

u8 start_measure_signal[4] = {0x70,0xAC,0x33,0x00};

之后使用XIic_Send()函数一次性发送四字节的数据,发送完毕后释放总线:

XIic_Send(Iic.BaseAddress,0x38,start_measure_signal,4,XIIC_STOP);

C语言中, 数组名表示的就是首元素地址,所以这里不需要加取地址符&

4.接着发送xiic address+read表示开始读取,与上面同理这里要发送的是0x71,接着可直接读取七字节的数据,理论上说应该先读取第一个字节的数据判断一下是否测量完成的,我这里嫌麻烦就没判断。

u8 rec_data[7];//0-1 地址和状态;2、3、4.5湿度;4.5,5,6温度;7:CRC校验
u8 read_signal = 0x71;

XIic_Send(Iic.BaseAddress,0x38,&read_signal,1,XIIC_REPEATED_START);
XIic_Recv(Iic.BaseAddress,0x38,rec_data,7,XIIC_STOP);

 5.接着就可根据以下公式进行数据处理,得到温度和湿度的十进制表示:

 

//数据处理

humid = (humid|rec_data[1])<<8;
humid = (humid|rec_data[2])<<8;
humid = (humid|rec_data[3]);
humid =humid >>4;
temper = (temper|rec_data[3])<<8;
temper = (temper|rec_data[4])<<8;
temper = (temper|rec_data[5]);
temper = temper&0xfffff;

int temp;int hum;
temp = temper*200*10/1024/1024 -500;
hum = humid*100*10/1024/1024;

在读取到温湿度后,便可进行串口输入至电脑,或者编写数码管展示模块:

 

其他:

  • 上面提到的延时我适用vitis中封装的sleep函数实现,需要导入sleep.h头文件。

sleep()以秒为单位延时,usleep以微妙为单位延时,想延时80毫秒用usleep(80000)即可

  • 下面是当第一步状态字不等于0x18时初始化寄存器的代码
    //判断状态字
    if((data&0x18)!=0x18)
    {
    	AHT20_Start_Init(Iic);
    	//usleep(10000);
    }
    
    
    //需要提前定义的函数
    void JH_Reset_REG(u8 addr,XIic Iic)
    {
    	u8 data = 0x71;
    	u8 message[4] = {0x70,0x00,0x00,0x00};
    	message[1] = addr;
    	u8 recv[3];
    	XIic_Send(Iic.BaseAddress,0x38,message,4,XIIC_STOP);
    
    	usleep(5000);
    
    	XIic_Send(Iic.BaseAddress,0x38,&data,1,XIIC_REPEATED_START);
    	XIic_Recv(Iic.BaseAddress,0x38,recv,3,XIIC_STOP);
    
    	usleep(10000);
    
    	message[0] = 0x70;message[1] = (0xB0|addr);
    	message[2] = recv[1];message[3] = recv[2];
    	XIic_Send(Iic.BaseAddress,0x38,message,4,XIIC_STOP);
    
    	recv[1] = 0x00;
    	recv[2] = 0x00;
    
    }
    
    void AHT20_Start_Init(XIic Iic)
    {
    	JH_Reset_REG(0x1b,Iic);
    	JH_Reset_REG(0x1c,Iic);
    	JH_Reset_REG(0x1e,Iic);
    }

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐