显示页面 讨论 修订记录 反向链接 本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。 ======基于STEP FPGA的8色VGA功能驱动====== 本节将和大家一起使用FPGA驱动底板上的8色VGA接口实现8色彩条显示功能。 ====硬件说明==== ------- VGA(video graphics array)即视频图形阵列,是IBM在1987年随PS/2一起推出的使用模拟信号的一种视频传输标准。VGA接口分公口和母口,如下图: \\ {{ :vga接口.jpg?400 |}} \\ VGA接口引脚定义如下: \\ {{ :vga接口定义.jpg?800 |}} \\ 对于VGA的接口模拟电压,为0~0.714V,0代表无色,0.714代表满色,FPGA输出3.3V,所以还必须要经过DAC的转换。现今有两种比较成熟的方法:电阻分压方式和DAC转换方式。 \\ 我们的底板上就是采用的电阻分压的方式 \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ 待更新 ====Verilog代码==== ------ <code verilog> // -------------------------------------------------------------------- // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // -------------------------------------------------------------------- // Module: ADC_I2C // // Author: Step // // Description: ADC_I2C // // Web: www.stepfpga.com // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.1 |2016/10/30 |Initial ver // -------------------------------------------------------------------- module ADC_I2C ( input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 output scl_out, //I2C总线SCL inout sda_out, //I2C总线SDA output reg adc_done, //ADC采样完成标志 output reg [7:0] adc_data //ADC采样数据 ); parameter CNT_NUM = 15; localparam IDLE = 3'd0; localparam MAIN = 3'd1; localparam START = 3'd2; localparam WRITE = 3'd3; localparam READ = 3'd4; localparam STOP = 3'd5; //根据PCF8591的datasheet,I2C的频率最高为100KHz, //我们准备使用4个节拍完成1bit数据的传输,所以需要400KHz的时钟触发完成该设计 //使用计数器分频产生400KHz时钟信号clk_400khz reg clk_400khz; reg [9:0] cnt_400khz; always@(posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) begin cnt_400khz <= 10'd0; clk_400khz <= 1'b0; end else if(cnt_400khz >= CNT_NUM-1) begin cnt_400khz <= 10'd0; clk_400khz <= ~clk_400khz; end else begin cnt_400khz <= cnt_400khz + 1'b1; end end reg [7:0] adc_data_r; reg scl_out_r; reg sda_out_r; reg [2:0] cnt; reg [3:0] cnt_main; reg [7:0] data_wr; reg [2:0] cnt_start; reg [2:0] cnt_write; reg [4:0] cnt_read; reg [2:0] cnt_stop; reg [2:0] state; always@(posedge clk_400khz or negedge rst_n_in) begin if(!rst_n_in) begin //如果按键复位,将相关数据初始化 scl_out_r <= 1'd1; sda_out_r <= 1'd1; cnt <= 1'b0; cnt_main <= 4'd0; cnt_start <= 3'd0; cnt_write <= 3'd0; cnt_read <= 5'd0; cnt_stop <= 1'd0; adc_done <= 1'b0; adc_data <= 1'b0; state <= IDLE; end else begin case(state) IDLE:begin //软件自复位,主要用于程序跑飞后的处理 scl_out_r <= 1'd1; sda_out_r <= 1'd1; cnt <= 1'b0; cnt_main <= 4'd0; cnt_start <= 3'd0; cnt_write <= 3'd0; cnt_read <= 5'd0; cnt_stop <= 1'd0; adc_done <= 1'b0; state <= MAIN; end MAIN:begin if(cnt_main >= 4'd6) cnt_main <= 4'd6; //对MAIN中的子状态执行控制cnt_main else cnt_main <= cnt_main + 1'b1; case(cnt_main) 4'd0: begin state <= START; end //I2C通信时序中的START 4'd1: begin data_wr <= 8'h90; state <= WRITE; end //A0,A1,A2都接了GND,写地址为8'h90 4'd2: begin data_wr <= 8'h00; state <= WRITE; end //control byte为8'h00,采用4通道ADC中的通道0 4'd3: begin state <= STOP; end //I2C通信时序中的START 4'd4: begin state <= START; end //I2C通信时序中的STOP 4'd5: begin data_wr <= 8'h91; state <= WRITE; end //A0 A1 A2都接了GND,读地址为8'h91 4'd6: begin state <= READ; adc_done <= 1'b0; end //读取ADC的采样数据 4'd7: begin state <= STOP; adc_done <= 1'b1; end //I2C通信时序中的STOP,读取完成标志 4'd8: begin state <= MAIN; end //预留状态,不执行 default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 endcase end START:begin //I2C通信时序中的起始START if(cnt_start >= 3'd5) cnt_start <= 1'b0; //对START中的子状态执行控制cnt_start else cnt_start <= cnt_start + 1'b1; case(cnt_start) 3'd0: begin sda_out_r <= 1'b1; scl_out_r <= 1'b1; end //将SCL和SDA拉高,保持4.7us以上 3'd1: begin sda_out_r <= 1'b1; scl_out_r <= 1'b1; end //clk_400khz每个周期2.5us,需要两个周期 3'd2: begin sda_out_r <= 1'b0; end //SDA拉低到SCL拉低,保持4.0us以上 3'd3: begin sda_out_r <= 1'b0; end //clk_400khz每个周期2.5us,需要两个周期 3'd4: begin scl_out_r <= 1'b0; end //SCL拉低,保持4.7us以上 3'd5: begin scl_out_r <= 1'b0; state <= MAIN; end //clk_400khz每个周期2.5us,需要两个周期,返回MAIN default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 endcase end WRITE:begin //I2C通信时序中的写操作WRITE和相应判断操作ACK if(cnt <= 3'd6) begin //共需要发送8bit的数据,这里控制循环的次数 if(cnt_write >= 3'd3) begin cnt_write <= 1'b0; cnt <= cnt + 1'b1; end else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end end else begin if(cnt_write >= 3'd7) begin cnt_write <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值 else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end end case(cnt_write) //按照I2C的时序传输数据 3'd0: begin scl_out_r <= 1'b0; sda_out_r <= data_wr[7-cnt]; end //SCL拉低,并控制SDA输出对应的位 3'd1: begin scl_out_r <= 1'b1; end //SCL拉高,保持4.0us以上 3'd2: begin scl_out_r <= 1'b1; end //clk_400khz每个周期2.5us,需要两个周期 3'd3: begin scl_out_r <= 1'b0; end //SCL拉低,准备发送下1bit的数据 //获取从设备的响应信号并判断 3'd4: begin sda_out_r <= 1'bz; end //释放SDA线,准备接收从设备的响应信号 3'd5: begin scl_out_r <= 1'b1; end //SCL拉高,保持4.0us以上 3'd6: begin if(sda_out) state <= IDLE; else state <= state; end //获取从设备的响应信号并判断 3'd7: begin scl_out_r <= 1'b0; state <= MAIN; end //SCL拉低,返回MAIN状态 default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 endcase end READ:begin //I2C通信时序中的读操作READ和返回ACK的操作 if(cnt <= 3'd6) begin //共需要接收8bit的数据,这里控制循环的次数 if(cnt_read >= 3'd3) begin cnt_read <= 1'b0; cnt <= cnt + 1'b1; end else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end end else begin if(cnt_read >= 3'd7) begin cnt_read <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值 else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end end case(cnt_read) //按照I2C的时序接收数据 3'd0: begin scl_out_r <= 1'b0; sda_out_r <= 1'bz; end //SCL拉低,释放SDA线,准备接收从设备数据 3'd1: begin scl_out_r <= 1'b1; end //SCL拉高,保持4.0us以上 3'd2: begin adc_data_r[7-cnt] <= sda_out; end //读取从设备返回的数据 3'd3: begin scl_out_r <= 1'b0; end //SCL拉低,准备接收下1bit的数据 //向从设备发送响应信号 3'd4: begin sda_out_r <= 1'b0; adc_done <= 1'b1; adc_data <= adc_data_r; end //发送响应信号,将前面接收的数据锁存 3'd5: begin scl_out_r <= 1'b1; end //SCL拉高,保持4.0us以上 3'd6: begin scl_out_r <= 1'b1; adc_done <= 1'b0; end //SCL拉高,保持4.0us以上 3'd7: begin scl_out_r <= 1'b0; state <= MAIN; end //SCL拉低,返回MAIN状态 default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 endcase end STOP:begin //I2C通信时序中的结束STOP if(cnt_stop >= 3'd5) cnt_stop <= 1'b0; //对STOP中的子状态执行控制cnt_stop else cnt_stop <= cnt_stop + 1'b1; case(cnt_stop) 3'd0: begin sda_out_r <= 1'b0; end //SDA拉低,准备STOP 3'd1: begin sda_out_r <= 1'b0; end //SDA拉低,准备STOP 3'd2: begin scl_out_r <= 1'b1; end //SCL提前SDA拉高4.0us 3'd3: begin scl_out_r <= 1'b1; end //SCL提前SDA拉高4.0us 3'd4: begin sda_out_r <= 1'b1; end //SDA拉高 3'd5: begin sda_out_r <= 1'b1; state <= MAIN; end //完成STOP操作,返回MAIN状态 default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 endcase end default:; endcase end end assign scl_out = scl_out_r; //对SCL端口赋值 assign sda_out = sda_out_r; //对SDA端口赋值 endmodule </code> ====小结==== ------ 本节主要为大家讲解了使用I2C驱动PCF8591的ADC功能的原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。 \\ 如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。 ====相关资料==== ------ \\ 使用[[STEP-MXO2第二代]]的PCF8591的ADC驱动程序: 后续会有下载连接 待更新 \\ 使用[[STEP-MAX10]]的PCF8591的ADC驱动程序: 后续会有下载连接 待更新 \\