您好,欢迎进入山西润盛进出口有限公司!

咨询服务热线

15383419322

基于接近式传感器的智能接近系统设计

发布时间:2023-12-15 10:12人气:

实验任务

  • 任务:智能手机通话,手机靠近耳朵后关闭屏显,基于 STEP-MAX10M08核心板 和 STEP BaseBoard V3.0底板 完成智能接近系统设计并观察调试结果
  • 要求:驱动底板上的接近式传感器APDS-9901获得接近数据,控制核心板上LED按能量条方式点亮
  • 解析:通过FPGA编程驱动接近式传感器APDS-9901,获取接近距离信息,然后根据距离信息编码控制8个LED灯按能量条方式点亮。

实验目的

本节实验主要学习I2C总线工作原理、协议及相关知识,掌握FPGA驱动I2C设备的原理及方法,了解输入输出型端口的模型及控制实现,最终实现智能接近系统的总体设计。

  • 熟悉I2C总线工作原理及通信协议
  • 完成I2C接口接近式传感器的FPGA驱动
  • 完成智能接近系统设计实现

设计框图

根据前面的实验解析我们可以得知,该设计可以拆分成两个功能模块实现,

  • APDS9901Driver:接近式传感器APDS-9901芯片I2C总线通信驱动模块。
  • Decoder:将距离信息转换成能量条数据。

Top-Down层次设计

模块结构设计

实验原理

I2C总线介绍

I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。

I2C总线连接

主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。

I2C通信时序

字节格式

发送到SDA 线上的每个字节必须为8 位,每次传输可以发送的字节数量不受限制。每个字节后必须跟一个响应位。首先传输的是数据的最高位(MSB),如果从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节,可以使时钟线SCL 保持低电平,迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线SCL 后数据传输继续。

I2C字节格式

启动和停止

在时钟线SCL保持高电平期间,数据线SDA上的电平被拉低(即负跳变),定义为I2C总线总线的启动信号,它标志着一次数据传输的开始。启动信号是一种电平跳变时序信号,而不是一个电平信号。启动信号是由主控器主动建立的,在建立该信号之前I2C总线必须处于空闲状态。

在时钟线SCL保持高电平期间,数据线SDA被释放,使得SDA返回高电平(即正跳变),称为I2C总线的停止信号,它标志着一次数据传输的终止。停止信号也是一种电平跳变时序信号,而不是一个电平信号,停止信号也是由主控器主动建立的,建立该信号之后,I2C总线将返回空闲状态。

启动和停止格式

应答响应

数据传输必须带响应,相关的响应时钟脉冲由主机产生。在响应的时钟脉冲期间,发送器释放SDA 线(上拉电阻拉高),接收器必须将SDA 线拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平,这种情况下是应答,如果在这个时钟脉冲的高电平期间SDA线没有被拉低则表示没有应答。通常被寻址的接收器在接收到的每个字节后,必须产生一个应答。当从机接收器不应答时,主机产生一个停止或重复起始条件。

应答响应格式

通信速率

常见的I²C总线依传输速率的不同而有不同的模式:标准模式(100 Kbit/s)、低速模式(10 Kbit/s),但时钟频率可被允许下降至零,这代表可以暂停通信。而新一代的I2C总线可以和更多的节点(支持10比特长度的地址空间)以更快的速率通信:快速模式(400 Kbit/s)、高速模式(3.4 Mbit/s)。

APDS-9901模块连接

STEP BaseBoard V3.0底板上的接近光传感器APDS-9901模块电路图如下(上拉电阻未显示):

APDS-9901模块电路

上图为接近光传感器APDS-9901模块电路,与FPGA硬件接口有I2C总线(SCL、SDA)和中断信号INT,APDS-9901是博通公司的集成环境光ALS、红外光IR和接近距离传感器,具有体积小、低功耗等优点,被大量应用于手机、笔记本、相机、液晶显示器等电子产品上,环境光ALS可以根据外部环境调节设备屏幕显示亮度,接近距离传感器可以根据应用场景实现产品对应应用,例如接听电话时控制手机关闭显示等,接口采用I2C总线能够支持400KHz的I2C快速模式。

APDS-9901内部框图

双向端口设计

可综合Verilog模块设计中必须有端口存在,端口有输入input,输出output,双向inout,对于输入和输出型端口我们很好理解,我们来了解一下双向端口信号的处理。

在芯片中为了管脚复用,很多管脚都是双向的,既可以输入也可以输出。在Verilog中即为inout型端口。Inout端口的实现是使用三态门,三态门的第三个状态是高阻态Z。在实际电路中高阻态意味着响应的管脚悬空、断开。

FPGA引脚输入输出模型

当inout用作输出时,就像平常一样。当inout用作输入时,需要设为高阻态,这样其电平就可以由外部输入信号决定了(这是高阻态的特性)。

双向端口应用案例:

modulebid ( inputout_en, inputa, inoutb, outputc ); assignb=out_en?a:1'bz; assignc=b; endmodule

APDS-9901驱动设计

通过前面的了解,我们对于整个I2C总线的驱动原理有了一定的了解,接下来我们根据APDS-9901的芯片手册了解其驱动方法及参数要点。

APDS-9901时序

APDS-9901时序参数

通过APDS-9901时序参数了解,APDS-9901支持I2C通信400KHz快速模式同时兼容100KHz的标准模式,还有两种模式下时序中的各种时间参数,本例中我们就采用标准模式完成驱动设计。

首先我们分频得到400KHz的时钟,整个设计都基于该时钟完成,程序实现如下:

//使用计数器分频产生400KHz时钟信号 clk_400khzregclk_400khz; reg[9:0]cnt_400khz; always@(posedgeclkornegedgerst_n)begin if(!rst_n)begin cnt_400khz<=10'd0; clk_400khz<=1'b0; endelseif(cnt_400khz>=CNT_NUM-1)begin cnt_400khz<=10'd0; clk_400khz<=~clk_400khz; endelsebegin cnt_400khz<=cnt_400khz+1'b1; end end

I2C时序可以分解成基本单元(启动、停止、发送、接收、发应答、读应答),整个I2C通信都是由这些单元按照不同的顺序组合,我们设计一个状态机,将这些基本单元做成状态,控制状态机的跳转就能实现I2C通信时序。主机每次发送数据都要接收判断从机的响应,每次接收数据也要向从机发送响应,所以发送单元和读应答单元可以合并,接收单元和写应答单元可以合并。

启动时序状态设计程序实现如下:

START:begin//I2C通信时序中的起始START if(cnt_start>=3'd5)cnt_start<=1'b0;//对START中的子状态执行控制cnt_start elsecnt_start<=cnt_start+1'b1; case(cnt_start) 3'd0:beginsda<=1'b1; scl<=1'b1; end//将SCL和SDA拉高,保持4.7us以上 3'd1:beginsda<=1'b1; scl<=1'b1; end//每个周期2.5us,需要两个周期 3'd2:beginsda<=1'b0; end//SDA拉低到SCL拉低,保持4.0us以上 3'd3:beginsda<=1'b0; end//clk_400khz每个周期2.5us,需要两个周期 3'd4:beginscl<=1'b0; end//SCL拉低,保持4.7us以上 3'd5:beginscl<=1'b0; state<=state_back; end//每个周期2.5us,两个周期 default:state<=IDLE;//如果程序失控,进入IDLE自复位状态 endcase end

发送单元和读应答单元合并,时序状态设计程序实现如下:

WRITE:begin//I2C通信时序中的写操作WRITE和相应判断操作ACK if(cnt<=3'd6)begin//共需要发送8bit的数据,这里控制循环的次数 if(cnt_write>=3'd3)begincnt_write<=1'b0; cnt<=cnt+1'b1; end elsebegincnt_write<=cnt_write+1'b1; cnt<=cnt; end endelsebegin if(cnt_write>=3'd7)begincnt_write<=1'b0; cnt<=1'b0; end//复位变量 elsebegincnt_write<=cnt_write+1'b1; cnt<=cnt; end end case(cnt_write) //按照I2C的时序传输数据 3'd0:beginscl<=1'b0; sda<=data_wr[7-cnt]; end//SCL拉低,SDA输出 3'd1:beginscl<=1'b1; end//SCL拉高,保持4.0us以上 3'd2:beginscl<=1'b1; end//clk_400khz每个周期2.5us,需要两个周期 3'd3:beginscl<=1'b0; end//SCL拉低,准备发送下1bit的数据 //获取从设备的响应信号并判断 3'd4:beginsda<=1'bz; end//释放SDA线,准备接收从设备的响应信号 3'd5:beginscl<=1'b1; end//SCL拉高,保持4.0us以上 3'd6:beginack_flag<=i2c_sda; end//获取从设备的响应信号 3'd7:beginscl<=1'b0; if(ack_flag)state<=state; elsestate<=state_back; end//SCL拉低,如果不应答循环写 default:state<=IDLE;//如果程序失控,进入IDLE自复位状态 endcase end

接收单元和写应答单元合并,时序状态设计程序实现如下:

READ:begin//I2C通信时序中的读操作READ和返回ACK的操作 if(cnt<=3'd6)begin//共需要接收8bit的数据,这里控制循环的次数 if(cnt_read>=3'd3)begincnt_read<=1'b0; cnt<=cnt+1'b1; end elsebegincnt_read<=cnt_read+1'b1; cnt<=cnt; end endelsebegin if(cnt_read>=3'd7)begincnt_read<=1'b0; cnt<=1'b0; end//复位变量值 elsebegincnt_read<=cnt_read+1'b1; cnt<=cnt; end end case(cnt_read) //按照I2C的时序接收数据 3'd0:beginscl<=1'b0; sda<=1'bz; end//SCL拉低,释放SDA线 3'd1:beginscl<=1'b1; end//SCL拉高,保持4.0us以上 3'd2:begindata_r[7-cnt]<=i2c_sda; end//读取从设备返回的数据 3'd3:beginscl<=1'b0; end//SCL拉低,准备接收下1bit的数据 //向从设备发送响应信号 3'd4:beginsda<=ack; end//发送响应信号,将前面接收的数据锁存 3'd5:beginscl<=1'b1; end//SCL拉高,保持4.0us以上 3'd6:beginscl<=1'b1; end//SCL拉高,保持4.0us以上 3'd7:beginscl<=1'b0; state<=state_back; end//SCL拉低 default:state<=IDLE;//如果程序失控,进入IDLE自复位状态 endcase end

停止时序状态设计程序实现如下:

STOP:begin//I2C通信时序中的结束STOP if(cnt_stop>=3'd5)cnt_stop<=1'b0;//对STOP中的子状态执行控制cnt_stop elsecnt_stop<=cnt_stop+1'b1; case(cnt_stop) 3'd0:beginsda<=1'b0; end//SDA拉低,准备STOP 3'd1:beginsda<=1'b0; end//SDA拉低,准备STOP 3'd2:beginscl<=1'b1; end//SCL提前SDA拉高4.0us 3'd3:beginscl<=1'b1; end//SCL提前SDA拉高4.0us 3'd4:beginsda<=1'b1; end//SDA拉高 3'd5:beginsda<=1'b1; state<=state_back; end//完成STOP操作 default:state<=IDLE;//如果程序失控,进入IDLE自复位状态 endcase end

基本单元都有了,接下来我们需要了解APDS-9901驱动的流程,手册上看到APDS-9901芯片有很多寄存器,有的配置工作模式,有的配置功能使能,有的返回结果数据,这个需要大家自己查看芯片手册,这里不作讲解。

APDS-9901寄存器

手册给用户提供了基本功能的C实例代码,流程如下:

WriteRegData(0,0);//DisableandPowerdown WriteRegData(1,0xff);//2.7ms–minimumALSintegrationtime WriteRegData(2,0xff);//2.7ms–minimumProxintegrationtime WriteRegData(3,0xff);//2.7ms–minimumWaittime WriteRegData(0xe,1);//Minimumproxpulsecount WriteRegData(0xf,0x20);//CH1Diode WriteRegData(0,0x0f);//EnableWENPENAENPON Wait(12);//Waitfor12ms CH0_data=Read_Word(0x14); CH1_data=Read_Word(0x16); Prox_data=Read_Word(0x18); WriteRegData(uint8reg,uint8data) { m_I2CBus.WriteI2C(0x39,0x80|reg,1,&data); } uint16Read_Word(uint8reg) { uint8barr[2]; m_I2CBus.ReadI2C(0x39,0xA0|reg,2,refbarr); return(uint16)(barr[0]+256*barr[1]); }

根据手册提供的软件操作流程,我们首先有7次向寄存器写入数据的操作,按照时序

I2C写操作

向regaddr地址寄存器中写入数据regdata,程序实现如下

4'd0:beginstate<=START; end//I2C通信时序中的START 4'd1:begindata_wr<=dev_addr<<1; state<=WRITE; end//设备地址 4'd2:begindata_wr<=reg_addr; state<=WRITE;end//寄存器地址 4'd3:begindata_wr<=reg_data; state<=WRITE; end//写入数据 4'd4:beginstate<=STOP; end//I2C通信时序中的STOP

7次向寄存器写入数据的操作需要7段上面的代码,罗列起来程序不易读,干脆我们将1次写操作做成状态机的一个状态,这样7次向寄存器写入数据的操作只需要在这个状态上循环执行7次就好了,单词写操作状态程序实现如下:

MODE1:begin//单次写操作 if(cnt_mode1>=4'd5)cnt_mode1<=1'b0;//对START中的子状态执行控制cnt_start elsecnt_mode1<=cnt_mode1+1'b1; state_back<=MODE1; case(cnt_mode1) 4'd0:beginstate<=START;end//I2C通信时序中的START 4'd1:begindata_wr<=dev_addr<<1;state<=WRITE;end//设备地址 4'd2:begindata_wr<=reg_addr;state<=WRITE;end//寄存器地址 4'd3:begindata_wr<=reg_data;state<=WRITE;end//写入数据 4'd4:beginstate<=STOP;end//I2C通信时序中的STOP 4'd5:beginstate<=MAIN;end//返回MAIN default:state<=IDLE;//如果程序失控,进入IDLE自复位状态 endcase end

同理两字节数据连读的操作也做成一个状态

I2C读字操作

程序实现如下:

MODE2:begin//两次读操作 if(cnt_mode2>=4'd10)cnt_mode2<=1'b0;//对START中的子状态执行控制cnt_start elsecnt_mode2<=cnt_mode2+1'b1; state_back<=MODE2; case(cnt_mode2) 4'd0:beginstate<=START;end//I2C通信时序中的START 4'd1:begindata_wr<=dev_addr<<1;state<=WRITE;end//设备地址 4'd2:begindata_wr<=reg_addr;state<=WRITE;end//寄存器地址 4'd3:beginstate<=START;end//I2C通信时序中的START 4'd4:begindata_wr<=(dev_addr<<1)|8'h01;state<=WRITE;end//设备地址 4'd5:beginack<=ACK;state<=READ;end//读寄存器数据 4'd6:begindat_l<=data_r;end 4'd7:beginack<=NACK;state<=READ;end//读寄存器数据 4'd8:begindat_h<=data_r;end 4'd9:beginstate<=STOP;end//I2C通信时序中的STOP 4'd10:beginstate<=MAIN;end//返回MAIN default:state<=IDLE;//如果程序失控,进入IDLE自复位状态 endcase end

因为用到延时,也设计成一个状态,程序实现如下:

DELAY:begin//延时模块 if(cnt_delay>=num_delay)begin cnt_delay<=1'b0; state<=MAIN; endelsecnt_delay<=cnt_delay+1'b1; end

最后我们编程控制状态机按照驱动例程代码中流程运行,程序实现如下:

4'd0:begindev_addr<=7'h39;reg_addr<=8'h80|8'h00;reg_data<=8'h00;state<=MODE1;end 4'd1:begindev_addr<=7'h39;reg_addr<=8'h80|8'h01;reg_data<=8'hff;state<=MODE1;end 4'd2:begindev_addr<=7'h39;reg_addr<=8'h80|8'h02;reg_data<=8'hff;state<=MODE1;end 4'd3:begindev_addr<=7'h39;reg_addr<=8'h80|8'h03;reg_data<=8'hff;state<=MODE1;end 4'd4:begindev_addr<=7'h39;reg_addr<=8'h80|8'h0e;reg_data<=8'h01;state<=MODE1;end 4'd5:begindev_addr<=7'h39;reg_addr<=8'h80|8'h0f;reg_data<=8'h20;state<=MODE1;end 4'd6:begindev_addr<=7'h39;reg_addr<=8'h80|8'h00;reg_data<=8'h0f;state<=MODE1;end 4'd7:beginstate<=DELAY;dat_valid<=1'b0;end//12ms延时 4'd8:begindev_addr<=7'h39;reg_addr<=8'ha0|8'h14;state<=MODE2;end 4'd9:beginch0_dat<={dat_h,dat_l};end//读取数据 4'd10:begindev_addr<=7'h39;reg_addr<=8'ha0|8'h16;state<=MODE2;end 4'd11:beginch1_dat<={dat_h,dat_l};end//读取数据 4'd12:begindev_addr<=7'h39;reg_addr<=8'ha0|8'h18;state<=MODE2;end 4'd13:beginprox_dat<={dat_h,dat_l};end//读取数据 4'd14:begindat_valid<=1'b1;end//读取数据

系统总体实现

程序中我们做了一个简单的滤波处理,为了保证数据的有效,将瞬间变化太大的采样数据舍弃,程序实现如下:

reg[15:0]prox_dat0,prox_dat1,prox_dat2; always@(posedgedat_valid)begin prox_dat0<=prox_dat; prox_dat1<=prox_dat0; if(((prox_dat1-prox_dat0)>=16'h200)||((prox_dat1-prox_dat0)>=16'h200))prox_dat2<=prox_dat2; elseprox_dat2<=prox_dat0; end

我们从传感器读取的距离信息为16位数据,有效范围Full Scale ADC Counts为0~1023,对应0到16‘h3ff,可以设置一个阈值,当采样回来的数据与阈值比较控制手机屏幕的显示与否,本实验要求用能量条的方式显示距离的远近,我们设计一个编码器将0到16‘h3ff的范围控制8个led灯的控制,程序实现如下:

always@(prox_dat2[9:7]) case(prox_dat2[9:7]) 3'b000:Y_out=8'b11111110; 3'b001:Y_out=8'b11111100; 3'b010:Y_out=8'b11111000; 3'b011:Y_out=8'b11110000; 3'b100:Y_out=8'b11100000; 3'b101:Y_out=8'b11000000; 3'b110:Y_out=8'b10000000; 3'b111:Y_out=8'b00000000; default:Y_out=8'b11111111; endcase

在顶层设计中例化两个模块,将信号连接,程序实现如下:

wiredat_valid; wire[15:0]ch0_dat,ch1_dat,prox_dat; APDS_9901_Driveru1( .clk(clk),//系统时钟 .rst_n(rst_n),//系统复位,低有效 .i2c_scl(i2c_scl),//I2C总线SCL .i2c_sda(i2c_sda),//I2C总线SDA .dat_valid(dat_valid),//数据有效脉冲 .ch0_dat(ch0_dat),//ALS数据 .ch1_dat(ch1_dat),//IR数据 .prox_dat(prox_dat)//Prox数据 ); Decoderu2(.dat_valid(dat_valid), .prox_dat(prox_dat), .Y_out(led) );

综合后的设计框图如下:

RTL设计框图

实验步骤

  1. 双击打开Quartus Prime工具软件;
  2. 新建工程:File → New Project Wizard(工程命名,工程目录选择,设备型号选择,EDA工具选择);
  3. 新建文件:File → New → Verilog HDL File,键入设计代码并保存;
  4. 设计综合:双击Tasks窗口页面下的Analysis & Synthesis对代码进行综合;
  5. 管脚约束:Assignments → Assignment Editor,根据项目需求分配管脚;
  6. 设计编译:双击Tasks窗口页面下的Compile Design对设计进行整体编译并生成配置文件;
  7. 程序烧录:点击Tools → Programmer打开配置工具,Program进行下载;
  8. 观察设计运行结果。

实验现象

将设计加载到FPGA,手指在接近光传感器上下移动,观察核心板上8个LED灯的状态,APDS-9901还是环境光传感器,有兴趣的同学可以尝试一下其他应用。

实验现象示意

  • 联系方式
  • 传 真:
  • 手 机:15383419322
  • 电 话:15383419322
  • 地 址:山西太原市杏花岭区解放路 175 号万达中心 A 座 33 楼 3301 室
友情链接
plc控制器
自动化设备
自动化设备
伺服驱动器
在线咨询

咨询电话:

15383419322

  • 微信扫码 关注我们

Copyright © 2022-2024 山西润盛进出口有限公司 版权所有 晋ICP备2021008479号-14

晋公网安备 14010702070906号

扫一扫咨询微信客服
15383419322