Author Topic: ADC with Altera Cyclone FPGA - internal and external (I2C) implementation  (Read 7419 times)

0 Members and 1 Guest are viewing this topic.

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7733
  • Country: ca
Next step, the 1 dimensional sequencer.

First, you will need to add:
reg - sequence_counter[].
reg - sequence_busy
localparam sequence_size  (for now, make it 20)

Update the code to reset these & when the reset is released, wait for a Conv.

Once the Conv is received, run the period counter sequence for the sequence_size, meaning you should see only 10 cycles of the ADC_rdy and then it should stop until Conv is pulsed again for at least 1 clock cycle.  Basically, only take in the Conv when 'sequence_busy' is false, then make 'sequence_busy' active and run the period counter and oscillator until the sequence_counter[] reaches it's end, then clear the sequence busy.

(I hope you have a 'MCP3021', around 2 posts from now, it should already work...)
« Last Edit: May 18, 2020, 04:12:37 am by BrianHG »
 

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1324
  • Country: ca
Next step, the 1 dimensional sequencer.

First, you will need to add:
reg - sequence_counter[].
reg - sequence_busy
localparam sequence_size  (for now, make it 20)

Update the code to reset these & when the reset is released, wait for a Conv.

Once the Conv is received, run the period counter sequence for the sequence_size, meaning you should see only 10 cycles of the ADC_rdy and then it should stop until Conv is pulsed again for at least 1 clock cycle.  Basically, only take in the Conv when 'sequence_busy' is false, then make 'sequence_busy' active and run the period counter and oscillator until the sequence_counter[] reaches it's end, then clear the sequence busy.

This

Code: [Select]
module MCP3021_Reader (
Clk, // System clock
Rst_N, // General reset active low
SCL_in, // I2C serial cloc input. Is this necessary for single master?
SDA_in, // I2C serial data input
Conv, // Start acquisition

SCL_out, // Serial clock output
SDA_out, // Serial data output
SCL_oe, // Output enable for SCL
SDA_oe, // Output enable for SDA
ADC_rdy, // ADC_rdy output - strobes high when data is available
ADC_val // 10 bit ADC register. ADC data is available in this register when ADC_Rdy
);

input Clk;
input Rst_N;
input SCL_in;
input SDA_in;
input Conv;

output reg SCL_out;
output reg SDA_out;
output reg SCL_oe;
output reg SDA_oe;
output reg ADC_rdy;
output reg [9:0] ADC_val;

reg[23:0] I2C_Period = 24'd0; // I2C clock divider

reg[3:0] Sequence_counter;
reg  Sequence_busy;

parameter   CLK_IN_HZ =  16;    //25'd24000000; // System clock frequency
parameter   I2C_SCL_HZ = 4; // I2C SCL frequency (= 120 for 100KHz or 30 for 400KHz)
localparam  I2C_PERIOD_SIZE = (CLK_IN_HZ/I2C_SCL_HZ)-1 ;
localparam Sequence_size = 10;

always @ (posedge Clk)
begin
if (!Rst_N)
begin
I2C_Period <= I2C_PERIOD_SIZE ;
ADC_rdy    <= 0;

SCL_out    <= 1;  // I2C default state is all HIGH
SCL_oe     <= 0;  // make I2C tristate by default.
SDA_out    <= 1;  // I2C default state is all HIGH
SDA_oe     <= 0;  // make I2C tristate by default.

ADC_rdy    <= 0;
Sequence_busy <= 0;
Sequence_counter <= 0;
end

if (!Conv && !Sequence_busy)
begin
Sequence_busy <= 1;
Sequence_counter <= Sequence_size;
end

else

if(Sequence_busy)
begin
if (I2C_Period == 0)
begin

SCL_out    <= 1;  // I2C default state is all HIGH
SCL_oe     <= 1;  // make I2C tristate by default.
SDA_out    <= 1;  // I2C default state is all HIGH
SDA_oe     <= 1;  // make I2C tristate by default.

ADC_rdy <= ~ADC_rdy;
I2C_Period <= I2C_PERIOD_SIZE;

if (ADC_rdy)
begin
Sequence_counter <= Sequence_counter -1;
end

if (Sequence_counter == 0)
begin
Sequence_busy <= 0;
ADC_rdy <= 0;
end
end

else
begin
I2C_Period <= I2C_Period - 1;
end
end

end // posedge clk

endmodule

(I hope you have a 'MCP3021', around 2 posts from now, it should already work...)

Nope, not yet. I want to simulate using an EEPROM 24LC08 with known content for now.

Datasheet here: http://ww1.microchip.com/downloads/en/devicedoc/21710k.pdf
« Last Edit: May 18, 2020, 10:55:46 pm by Miti »
Fear does not stop death, it stops life.
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7733
  • Country: ca

(I hope you have a 'MCP3021', around 2 posts from now, it should already work...)

Nope, not yet. I want to simulate using an EEPROM 24LC08 with known content for now.

Datasheet here: http://ww1.microchip.com/downloads/en/devicedoc/21710k.pdf

Well, we will still start with it since it is far simpler than the eeprom which needs the ee slaves master address, then subaddress, then with a read, another I2C_start & that master address again, in read mode, then read the data...

For the 'MCP3021', you present it's address in read mode, then receive 2 bytes, every time.
Don't worry, we will make progress very fast.

Ok, for your code, the ' (!Conv && !Sequence_busy) '.  I know how with TTL logic, and a lot of input button pullups, you can easily make the mistake of wanting negative logic on the input of your modules.

Remember, this module will eventually be buried in logic and controlled by other logic.  By default, for example, if you use our 'ADC_rdy' output to feed and Altera fifo or ram module's 'wren' input to accumulate a read cache, if we made it negative logic, you would need to invert that signal's 'WIRE' between modules.

Personally, even for reset I prefer positive logic.  Here is how I deal with negative logic inputs:



Now, if you prefer to keep your module's 'negative' logic inputs, let me know know.  We will change the Rst input in the module back to Rst_n, and change the Conv input to a Conv_n so when you wire this module into a larger design, when feeding it's input ports, you know that these controls are negative.

After simulation your design, here is what I get:

994422-1

Step #1 patches: (please update your code so we match)
Code: [Select]
module MCP3021_Reader (
Clk, // System clock
Rst, // General reset active low
SCL_in, // I2C serial cloc input. Is this necessary for single master?
SDA_in, // I2C serial data input
Conv, // Start acquisition

SCL_out, // Serial clock output
SDA_out, // Serial data output
SCL_oe, // Output enable for SCL
SDA_oe, // Output enable for SDA
ADC_rdy, // ADC_rdy output - strobes high when data is available
ADC_val // 10 bit ADC register. ADC data is available in this register when ADC_Rdy
);

input Clk;
input Rst;
input SCL_in;
input SDA_in;
input Conv;

output reg SCL_out;
output reg SDA_out;
output reg SCL_oe;
output reg SDA_oe;
output reg ADC_rdy;
output reg [9:0] ADC_val;

reg[15:0] I2C_Period = 16'd0; // I2C clock divider

reg[3:0] Sequence_counter;
reg  Sequence_busy;

parameter   CLK_IN_HZ =  16;    //25'd24000000; // System clock frequency
parameter   I2C_SCL_HZ = 4; // I2C SCL frequency (= 120 for 100KHz or 30 for 400KHz)
localparam  I2C_PERIOD_SIZE = (CLK_IN_HZ/I2C_SCL_HZ)-1 ;
localparam Sequence_size = 10;

always @ (posedge Clk)
begin
if (Rst)
begin
//I2C_Period <= I2C_PERIOD_SIZE[15:0] ; // This would add a delay after Conv has been asserted before the I2C transaction begins
I2C_Period <= 1'd0 ;                       // This would begin the I2C transaction immediately
ADC_rdy    <= 1'd0 ;

SCL_out    <= 1'd1;  // I2C default state is all HIGH
SCL_oe     <= 1'd0;  // make I2C tristate by default.
SDA_out    <= 1'd1;  // I2C default state is all HIGH
SDA_oe     <= 1'd0;  // make I2C tristate by default.

ADC_rdy          <= 1'd0;
Sequence_busy    <= 1'd0;
Sequence_counter <= 1'd0;
end

if (Conv && !Sequence_busy)
begin
Sequence_busy    <= 1'd1;
Sequence_counter <= Sequence_size[3:0];
end

else

if(Sequence_busy)
begin
if (I2C_Period == 16'd0)
begin

SCL_out    <= 1'd1;  // I2C default state is all HIGH
SCL_oe     <= 1'd1;  // make I2C tristate by default.
SDA_out    <= 1'd1;  // I2C default state is all HIGH
SDA_oe     <= 1'd1;  // make I2C tristate by default.

ADC_rdy    <= ~ADC_rdy;
I2C_Period <= I2C_PERIOD_SIZE[15:0];

if (ADC_rdy)
begin
Sequence_counter <= Sequence_counter - 1'd1;
end

if (Sequence_counter == 4'd0)
begin
Sequence_busy <= 1'd0;
ADC_rdy       <= 1'd0;
end
end

else
begin
I2C_Period <= I2C_Period - 1'd1;
end
end

end // posedge clk

endmodule

#1, I made the module's control inputs positive logic.
#2, I shrank the I2C period counter to 16 bits.
#3, To get rid of compiler warnings, I added these -> I2C_Period <= I2C_PERIOD_SIZE[15:0];
                                           and everywhere here -> ADC_rdy       <= 1'd0;
                                           as well as                   -> if (Sequence_counter == 4'd0)

All throughout the code.  It is good to avoid pesky compiler warnings all over the place...

Now, you made the inner and outer portions of your loop's behavior backwards.  This is fine since I didn't specify how we would be using the sequencer, though the hint was that I said a sequence count of '20' would cycle the ADC_rdy only 10 times.  In I2C, we want to specify actions at the I2C SCL_out's rising and falling edge, so we need the specify a sequence counter which has events at each time.  In fact, we may want 4x the period events.  IE, perform an action at the rise and another action at the fall of the SCL_out output, as well as another action right in the middle on the SCL_out being low and in the middle of it being high as well.

Next post in around an hour with a solution...

Here is a hint:
For the 2x approach, SCL_out <=  Sequence_counter[0];
For the 4x approach, SCL_out <=  Sequence_counter[1];
Within the main looping sequencer of your code.

When defining the size of the sequences, the Sequence_counter = (sequence size *2 or *4) and the preiod counter will be divided by 2 or 4 so that the SCL_out will have the correct speed.

Let me check the I2C data sheets.  The x2 method should be fine, however, I need to double check how clock stretching works since I've only used it once so long ago.  We might just need to go with the x4 method unless you want the x4 just so you have an additional timing slice you may take advantage of if you ever need it.
« Last Edit: May 19, 2020, 12:40:58 am by BrianHG »
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7733
  • Country: ca
Ok, next step, the dummy sequencer...

Add this as the new top of your code:
Code: [Select]
parameter   CLK_IN_HZ =  16;    // 25000000; // System clock frequency
parameter   I2C_SCL_HZ = 4; // 100000;          // I2C SCL frequency (100000 for 100KHz or 400000 for 400KHz)
localparam  I2C_PERIOD_SIZE = (CLK_IN_HZ/I2C_SCL_HZ/4)-1 ;
reg[15:0]   I2C_Period = 16'd0; // I2C clock divider

// *** We are calling these patterns I2C_start as 1 single long pattern for now.  During the next step, these patterns
// *** will shrink down to just the I2C_start and we will add Pat_I2C_*****_sda/scl/size for the I2C stop, I2C tx/rx byte.
// *** then during the I2C transaction, we will run a higher level sequencer which will choose and fill the correct values
// *** for the internal I2C pattern sequencer which will seam everything together.

// Sequence function                      STA s7  s6  s5  s4  s3  s2  s1  R/W ACK r7  r6  r5  r4  r3  r2  r1  r0  ACK r7  r6  r5  r4  r3  r2  r1  r0  NAK STO
// Sequence position                      111100000000009999999999888888888877777777776666666666555555555544444444443333333333222222222211111111110000000000
//         ***=SDA&SCL high               321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210
// SCL_out                             ***110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011
localparam   Pat_I2C_start_sda_out = 114'b000111100000000111111110000111111110000000000000000000000000000000000000000000000000000000000000000000000000000001;
localparam   Pat_I2C_start_sda_oe  = 114'b111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000111;
localparam   Pat_I2C_start_scl_oe  = 114'b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111;
localparam   Pat_I2C_start_size    = 29*4-3; // 29 I2C clocks for MCP3021 running in a single sequence stream
localparam   Pat_I2C_data_rdy1     = 10*4+2; // 11 Clocks from the end
localparam   Pat_I2C_data_rdy2     = 1*4+2; // This one is temporary for MCP3021 running in a single sequence stream
localparam   Pat_I2C_ack1          = 76 ; // This one is temporary for MCP3021 running in a single sequence stream
localparam   Pat_I2C_ack2          = 40 ; // This one is temporary for MCP3021 running in a single sequence stream
localparam   Pat_I2C_phase_rxd     = 2 ;  // The is the sample phase position when capturing data bits from the sda_in
localparam   Pat_I2C_phase_txd     = 2 ;  // The is the shift data out phase position when sending data bits to the sda_out

reg [113:0] seq_sda_out, seq_sda_oe, seq_scl_oe;
reg [7:0]   Sequence_counter;
reg         Sequence_busy;
// localparam Sequence_size = 10;  replace with "Pat_I2C_start_size"

Next, build you sequencer and simulate.  Hint, this should be your converter 'IF':

Code: [Select]
if (Conv && !Sequence_busy)
begin
Sequence_busy    <= 1'd1;
Sequence_counter <= Pat_I2C_start_size[7:0];
seq_sda_out      <= Pat_I2C_start_sda_out[113:0];
seq_sda_oe       <= Pat_I2C_start_sda_oe[113:0];
seq_scl_oe       <= Pat_I2C_start_scl_oe[113:0];
end

Get rid of the cycling of the ADC_rdy.
Lets see your new code and new simulation.
« Last Edit: May 19, 2020, 08:34:36 am by BrianHG »
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7733
  • Country: ca
Note there was a mistake in my sequence above.  I just edited the post and corrected it.
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7733
  • Country: ca
@Miti > I will most likely no longer be available to help this June.
 

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1324
  • Country: ca
Thanks Brian,

I’m swamped at work so I don’t know when I can finalize this. On top of that, some good weather seems to be in the forecast and I missed it so much that I can’t wait to take my bike out a bit in the weekend(s).
Fear does not stop death, it stops life.
 

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1324
  • Country: ca

Code: [Select]
// Sequence position                      111100000000009999999999888888888877777777776666666666555555555544444444443333333333222222222211111111110000000000
//         ***=SDA&SCL high               321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210


I'll admit I don't understand this part. Can someone explain what this part mean?
Fear does not stop death, it stops life.
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7733
  • Country: ca
What you are looking at is the 'Position number' in a 1 bit serial stream.  Reading vertically, from right side to left, I have:
00,01,02,03,04,05,06,07,08,09,10,11,12,...  It's actually counting from 113 at the left down to 0 on the right.

I just wrote that counter position there for your convenience.  Also, once we pass 99, I just didn't add the 100 vertically, I basically truncated the '1' in the '1xx'

The text above these 2 lines of numbers just denotes my homemade I2C labels of the transaction.

Example :
STA = start bit pattern
s7..s0 = slave address transmition bits 6..0, then the R/W bit.


Maybe it would have read better:
Code: [Select]
// Sequence function                      STA s7  s6  s5  s4  s3  s2  s1  R/W ACK r7  r6  r5  r4  r3  r2  r1  r0  ACK r7  r6  r5  r4  r3  r2  r1  r0  NAK STO
// Sequence position                      11111111111111
// Sequence position                      11110000000000999999999988888888887777777777666666666655555555554444444444333333333322222222221111111111
//         ***=SDA&SCL high               321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210

// SCL_out                             ***110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011

As for the SCL_out, I am just illustrating what level the SCL output pin should be at during that counter position, 0 -through- 113, all 114 internal clock cycles.  It is shown this way to illustrate that there are 4 internal sequence positions for every single low to high cycle of the output SCL pin.

In other words, during sequence position 113 and 112, the SCL pin should be high, during position 111 and 110, the SCL pin should be low, during position 109 and 108, the SCL pin should be once again high.

In other words, the sequence position is just a 7 bit counter counting down from 113 to 0, or up, depending on how you code.
« Last Edit: June 10, 2020, 02:59:14 am by BrianHG »
 
The following users thanked this post: Miti

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1324
  • Country: ca
Got it now, thanks!

The key words were “reading vertically “.
Fear does not stop death, it stops life.
 

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1324
  • Country: ca
I can't put this together to save my life. I blame it on being very tired, these few weeks were very hard at work. However, since I wanted a Cyclone 4 development board, I ordered this one from Ali:
https://www.aliexpress.com/item/32858413588.html

There's a link to some demo code there and I downloaded it. What I found there is demo code for reading a temperature sensor, read/write I2C and IR remote code.
All the comments are in Chinese but Google does wonders translating Chinese. Last weekend, while waiting for the board,  I looked through the code and in few hours, I was able to make all three modules that I mentioned before, working on my Terasic DE1. The code seems very clean to a noob like me.
I still don't have an I2C ADC, but reading current address from an EEPROM is very similar with reading from the DAC, except for the device address.
I've attached the code, someone may find it useful. There was a small mistake with the ACK in one of the module.
You can download the entire code from here:  fpga.redliquid.pl

Code: [Select]
module I2C_READ(
       clk,
rst_n,
scl,sda,data
              );
 
 input clk;//总线时钟 50MHz 
input rst_n;//异步复位,低电平有效 
 
output scl;//SCL 时钟 
inout  sda;// SDA 数据总线 
output [15:0] data;//温度数据 
 
reg [15:0]data_r; // Temperature (or other data) data register
reg scl; // SCL 总线寄存器 
reg sda_r; // SDA 总线寄存器 
reg sda_link; //SDA bus data direction sign: 
reg [8:0]scl_cnt; //SCL clock generation counter
reg [2:0]cnt; //Used to mark SCL clock counter
reg [25:0]timer_cnt; //定时器,每隔2s 读取一次温度数据 
reg [3:0]data_cnt; //数据串并转换寄存器 
reg [7:0]address_reg;//器件地址寄存器 
reg [8:0]state;//状态寄存器 
////////////////////////////////////////////////////////////////////////////////// 
// Process 1, 2, 3: Generate SCL bus clock
always@(posedge clk or negedge rst_n) 
    begin 
        if(!rst_n) 
            scl_cnt <= 9'd0; 
        else if(scl_cnt == 9'd199) 
            scl_cnt <= 9'd0; 
        else 
            scl_cnt <= scl_cnt + 1'b1; 
    end 
always@(posedge clk or negedge rst_n) 
    begin 
        if(!rst_n) 
            cnt <= 3'd5; 
        else   
            case(scl_cnt) 
                9'd49: cnt <= 3'd1;//高电平中间 
                9'd99: cnt <= 3'd2;//下降沿 
                9'd149:cnt <= 3'd3;//低电平中间 
                9'd199:cnt <= 3'd0;//上升沿 
               default: cnt <= 3'd5; 
            endcase 
    end 
`define SCL_HIG (cnt == 3'd1) 
`define SCL_NEG (cnt == 3'd2) 
`define SCL_LOW (cnt == 3'd3) 
`define SCL_POS (cnt == 3'd0) 
always@(posedge clk or negedge rst_n) 
    begin 
        if(!rst_n) 
            scl <= 1'b0; 
        else if(`SCL_POS) 
            scl <= 1'b1; 
        else if(`SCL_NEG) 
            scl <= 1'b0; 
    end 
////////////////////////////////////////////////////////////////////////////////// 
// Process 4: Timer, read temperature data every 1s
always@(posedge clk or negedge rst_n) 
    begin 
        if(!rst_n) 
            timer_cnt <= 26'd0; 
        else if(timer_cnt == 26'd6249999) 
            timer_cnt <= 26'd0; 
        else   
            timer_cnt <= timer_cnt + 1'b1; 
    end 
////////////////////////////////////////////////////////////////////////////////// 
//状态机定义 
parameter IDLE  = 9'b0_0000_0000, 
             START  = 9'b0_0000_0010, 
             ADDRESS    = 9'b0_0000_0100, 
             ACK1       = 9'b0_0000_1000, 
             READ1  = 9'b0_0001_0000, 
             ACK2       = 9'b0_0010_0000, 
             READ2  = 9'b0_0100_0000, 
             NACK       = 9'b0_1000_0000, 
             STOP       = 9'b1_0000_0000; 
//`define DEVICE_ADDRESS 8'b1001_0001//器件地址,读操作
`define DEVICE_ADDRESS 8'b1010_0001//器件地址,读操作   
////////////////////////////////////////////////////////////////////////////////// 
// Process 5: State machine description
always@(posedge clk or negedge rst_n) 
    begin 
        if(!rst_n) 
            begin 
                data_r  <= 16'd0; 
                sda_r       <= 1'b1; 
                sda_link    <= 1'b1; 
                state       <= IDLE; 
                address_reg <= 15'd0; 
                data_cnt    <= 4'd0; 
            end 
        else   
            case(state) 
                IDLE: 
                    begin 
                        sda_r   <= 1'b1; 
                        sda_link <= 1'b1; 
                        if(timer_cnt == 26'd6249999) 
                            state <= START; 
                        else 
                            state <= IDLE; 
                    end 
                START://产生起始信号 
                    begin 
                        if(`SCL_HIG) 
                            begin 
                                sda_r       <= 1'b0; 
                                sda_link    <= 1'b1; 
                                address_reg <= `DEVICE_ADDRESS; 
                                state           <= ADDRESS; 
                                data_cnt        <= 4'd0; 
                            end 
                        else 
                            state <= START; 
                    end 
                ADDRESS://主机对器件进行寻址 
                    begin 
                        if(`SCL_LOW) 
                            begin 
                                if(data_cnt == 4'd8)//寻址完成,SDA改变方向,器件准备输出应答讯号 
                                    begin 
                                        state   <= ACK1; 
                                        data_cnt <=  4'd0; 
                                        sda_r       <= 1'b1; 
                                        sda_link    <= 1'b0; 
                                    end 
                                else//寻址过程中,SDA对器件作为输入 
                                    begin 
                                        state   <= ADDRESS; 
                                        data_cnt <= data_cnt + 1'b1; 
                                        case(data_cnt)   
                                            4'd0: sda_r <= address_reg[7]; 
                                            4'd1: sda_r <= address_reg[6]; 
                                            4'd2: sda_r <= address_reg[5]; 
                                            4'd3: sda_r <= address_reg[4]; 
                                            4'd4: sda_r <= address_reg[3]; 
                                            4'd5: sda_r <= address_reg[2]; 
                                            4'd6: sda_r <= address_reg[1]; 
                                            4'd7: sda_r <= address_reg[0]; 
                                            default: ; 
                                        endcase 
                                    end 
                            end 
                        else 
                            state <= ADDRESS; 
                    end 
                ACK1://器件输出应答信号 
                    begin 
                        if(!sda && (`SCL_HIG)) 
                            state <= READ1; 
                        else if(`SCL_NEG) 
                            state <= READ1; 
                        else 
                            state <= ACK1; 
                    end 
                READ1://读器件数据,高字节 
                    begin 
                        if((`SCL_LOW) && (data_cnt == 4'd8))// After reading the high byte data, the SDA changes direction, and the host is ready to output the response signal 
                            begin 
                                state   <= ACK2; 
                                data_cnt <= 4'd0; 
                                sda_r       <= 1'b0; 
                                sda_link    <= 1'b1; 
                            end 
                        else if(`SCL_HIG)//读数据过程中,器件作为输出 
                            begin 
                                data_cnt <= data_cnt + 1'b1; 
                                case(data_cnt) 
                                    4'd0: data_r[15] <= sda; 
                                    4'd1: data_r[14] <= sda; 
                                    4'd2: data_r[13] <= sda; 
                                    4'd3: data_r[12] <= sda; 
                                    4'd4: data_r[11] <= sda; 
                                    4'd5: data_r[10] <= sda; 
                                    4'd6: data_r[9]  <= sda; 
                                    4'd7: data_r[8]  <= sda; 
                                    default: ; 
                                endcase 
                            end 
                        else 
                            state <= READ1; 
                    end 
                ACK2:// Host output response signal 
                    begin     
                        if(`SCL_LOW) 
                            sda_r <= 1'b0;
                        else if(`SCL_NEG) 
                            begin 
                                sda_r   <= 1'b1; 
                                sda_link    <= 1'b0; 
                                state       <= READ2; 
                            end 
                        else 
                            state <= ACK2; 
                    end 
                READ2://读低字节数据 
                    begin 
                        if((`SCL_LOW) && (data_cnt == 4'd8)) 
                            begin 
                                state   <= NACK; 
                                data_cnt <= 4'd0; 
                                sda_r       <= 1'b1; 
                                sda_link    <= 1'b1; 
                            end 
                        else if(`SCL_HIG) 
                            begin 
                                data_cnt <= data_cnt + 1'b1; 
                                case(data_cnt) 
                                    4'd0: data_r[7] <= sda; 
                                    4'd1: data_r[6] <= sda; 
                                    4'd2: data_r[5] <= sda; 
                                    4'd3: data_r[4] <= sda; 
                                    4'd4: data_r[3] <= sda; 
                                    4'd5: data_r[2] <= sda; 
                                    4'd6: data_r[1]  <= sda; 
                                    4'd7: data_r[0]  <= sda; 
                                    default: ; 
                                endcase 
                            end 
                        else 
                            state <= READ2; 
                    end 
                NACK://主机非应答 
                    begin 
                        if(`SCL_LOW) 
                            begin 
                                state <= STOP; 
                                sda_r   <= 1'b0; 
                            end 
                        else 
                            state <= NACK; 
                    end 
                STOP: 
                    begin 
                        if(`SCL_HIG) 
                            begin 
                                state <= IDLE; 
                                sda_r <= 1'b1; 
                            end 
                        else 
                            state <= STOP; 
                    end 
                default: state <= IDLE; 
            endcase 
    end 

assign sda   = sda_link ? sda_r: 1'bz; 
assign data  = data_r; 
endmodule


Code: [Select]
module I2C_Comm(
clk,rst_n,
sw1,sw2,
scl,sda,
dis_data,
wr,rd
);

input clk; // 50MHz
input rst_n; //Reset signal, active low
input sw1,sw2; //Buttons 1, 2, (1 press to perform write operation, 2 press to perform read operation)
output scl; // 24C02 clock port
inout sda; // 24C02 data port
output[7:0] dis_data; //  Nixie tube display data
output wr, rd;

//Key detection
reg sw1_r,sw2_r; //Key value latch register, detect key value every 20ms
reg[20:0] cnt_20ms; //20ms count register

reg[23:0] Delay_cnt;
/*
always @ (posedge clk or negedge rst_n)
if(!rst_n) cnt_20ms <= 20'd0;
else cnt_20ms <= cnt_20ms+1'b1; //Keep counting

always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
sw1_r <= 1'b1; //The key value register is reset, the key value is 1 when no keyboard is pressed
sw2_r <= 1'b1;
end
else if(cnt_20ms == 21'h1fffff) begin
sw1_r <= sw1; //Button 1 value latch
sw2_r <= sw2; //Button 2 value latch
end
*/
always @ (posedge clk or negedge rst_n)
if(!rst_n) Delay_cnt <= 24'd0;
else if(Delay_cnt || !sw1 || !sw2) Delay_cnt <= Delay_cnt+1'b1; //Keep counting

always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
sw1_r <= 1'b1; //The key value register is reset, the key value is 1 when no keyboard is pressed
sw2_r <= 1'b1;
end
else if(Delay_cnt == 24'd1) begin
sw1_r <= sw1; //Button 1 value latch
sw2_r <= sw2; //Button 2 value latch
end
else if(Delay_cnt == 24'd20000) begin
sw1_r <= 1'b1; //Button 1 value latch
sw2_r <= 1'b1; //Button 2 value latch
end
assign wr = sw1_r;
assign rd = sw2_r;
//---------------------------------------------
//Crossover
reg[2:0] cnt; // cnt=0: scl rising edge, cnt=1: middle of scl high level, cnt=2: falling edge of scl, cnt=3: middle of scl low level
reg[8:0] cnt_delay; //500 cycle counts to generate the clock required by iic
reg scl_r; //Clock pulse register

always @ (posedge clk or negedge rst_n)
if(!rst_n) cnt_delay <= 9'd0;
else if(cnt_delay == 9'd499) cnt_delay <= 9'd0; //Counting to 10us is the period of scl, which is 100KHz
else cnt_delay <= cnt_delay+1'b1; //Clock count

always @ (posedge clk or negedge rst_n) begin
if(!rst_n) cnt <= 3'd5;
else begin
case (cnt_delay)
9'd124: cnt <= 3'd1; //cnt=1: middle of scl high level, used for data sampling
9'd249: cnt <= 3'd2; //cnt=2:scl Falling edge
9'd374: cnt <= 3'd3; //cnt=3:scl Low level middle, used for data change
9'd499: cnt <= 3'd0; //cnt=0:scl Rising edge

default: cnt <= 3'd5;
endcase
end
end


`define SCL_POS (cnt==3'd0) //cnt=0:scl rising edge
`define SCL_HIG (cnt==3'd1) //cnt=1:scl high level middle, used for data sampling
`define SCL_NEG (cnt==3'd2) //cnt=2:scl falling edge
`define SCL_LOW (cnt==3'd3) //cnt=3:scl low level middle, used for data change

always @ (posedge clk or negedge rst_n)
if(!rst_n) scl_r <= 1'b0;
else if(cnt==3'd0) scl_r <= 1'b1; //scl signal rising edge
   else if(cnt==3'd2) scl_r <= 1'b0; //scl signal falling edge

assign scl = scl_r; //Generate the clock needed for iic
//---------------------------------------------
//Need to write the address and data of 24C02

`define DEVICE_READ 8'b1010_0111 //Address of the addressed device (read operation)
`define DEVICE_WRITE 8'b1010_0110 //Address of the addressed device (write operation)
//`define WRITE_DATA 8'b1010_0101 //Data written to EEPROM
`define WRITE_DATA 8'hA5 //Data written to EEPROM
`define BYTE_ADDR 8'b0000_1011 //Write/read address register of EEPROM
reg[7:0] db_r; //Data registers transferred on IIC
reg[7:0] read_data; //Read the data register of EEPROM

//---------------------------------------------
//Read and write timing
parameter IDLE = 4'd0;
parameter START1 = 4'd1;
parameter ADD1 = 4'd2;
parameter ACK1 = 4'd3;
parameter ADD2 = 4'd4;
parameter ACK2 = 4'd5;
parameter START2 = 4'd6;
parameter ADD3 = 4'd7;
parameter ACK3 = 4'd8;
parameter DATA = 4'd9;
parameter ACK4 = 4'd10;
parameter STOP1 = 4'd11;
parameter STOP2 = 4'd12;

reg[3:0] cstate; //Status register
reg sda_r; //Output data register
reg sda_link; //Output data sda signal inout direction control bit
reg[3:0] num; //


always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cstate <= IDLE;
sda_r <= 1'b1;
sda_link <= 1'b0;
num <= 4'd0;
read_data <= 8'b0000_0000;
end
else  
case (cstate)
IDLE: begin
sda_link <= 1'b1; //Data line sda ​​is output
sda_r <= 1'b1;
if(!sw1_r || !sw2_r) begin //One of the SW1 and SW2 keys was pressed
db_r <= `DEVICE_WRITE; //Send device address (write operation)
cstate <= START1;
end
else cstate <= IDLE; //No key is pressed
end
START1: begin
if(`SCL_HIG) begin //scl is high
sda_link <= 1'b1; //Data line sda ​​is output
sda_r <= 1'b0; //Pull down the data line sda ​​to generate the start bit signal
cstate <= ADD1;
num <= 4'd0; //num count is cleared
end
else cstate <= START1; //Wait for the middle position of scl high level to arrive
end
ADD1: begin
if(`SCL_LOW) begin
if(num == 4'd8) begin
num <= 4'd0; //num count is cleared
sda_r <= 1'b1;
sda_link <= 1'b0; //sda set to high impedance state (input)
cstate <= ACK1;
end
else begin
cstate <= ADD1;
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];
4'd1: sda_r <= db_r[6];
4'd2: sda_r <= db_r[5];
4'd3: sda_r <= db_r[4];
4'd4: sda_r <= db_r[3];
4'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
default: ;
endcase
// sda_r <= db_r[4'd7-num]; //Send device address, starting from high bit
end
end
// else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //The device address is shifted left by 1 bit
else cstate <= ADD1;
end
ACK1: begin
if(/*!sda*/`SCL_NEG) begin //Note: 24C01/02/04/08/16 device can ignore the acknowledge bit
cstate <= ADD2; //Slave response signal
db_r <= `BYTE_ADDR; // 1 address
end
else cstate <= ACK1; //Wait for slave response
end
ADD2: begin
if(`SCL_LOW) begin
if(num==4'd8) begin
num <= 4'd0; //num count is cleared
sda_r <= 1'b1;
sda_link <= 1'b0; //sda set to high impedance state (input)
cstate <= ACK2;
end
else begin
sda_link <= 1'b1; //sda as output
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];
4'd1: sda_r <= db_r[6];
4'd2: sda_r <= db_r[5];
4'd3: sda_r <= db_r[4];
4'd4: sda_r <= db_r[3];
4'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
default: ;
endcase
// sda_r <= db_r[4'd7-num]; //Send EEPROM address (start with high bit)
cstate <= ADD2;
end
end
// else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //The device address is shifted left by 1 bit
else cstate <= ADD2;
end
ACK2: begin
if(/*!sda*/`SCL_NEG) begin //Slave response signal
if(!sw1_r) begin
cstate <= DATA; //Write operation
db_r <= `WRITE_DATA; //Written data
end
else if(!sw2_r) begin
db_r <= `DEVICE_READ;//Send device address (read operation), read the specific address need to perform the following steps
cstate <= START2; //Read operation
end
end
else cstate <= ACK2; //Wait for slave response
end
START2: begin //Read operation start bit
if(`SCL_LOW) begin
sda_link <= 1'b1; //sda as output
sda_r <= 1'b1; //Pull up the data line sda
cstate <= START2;
end
else if(`SCL_HIG) begin //scl is high level middle
sda_r <= 1'b0; //Pull down the data line sda ​​to generate the start bit signal
cstate <= ADD3;
end
else cstate <= START2;
end
ADD3: begin //Send operation address
if(`SCL_LOW) begin
if(num==4'd8) begin
num <= 4'd0; //num count is cleared
sda_r <= 1'b1;
sda_link <= 1'b0; //sda set to high impedance state (input)
cstate <= ACK3;
end
else begin
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];
4'd1: sda_r <= db_r[6];
4'd2: sda_r <= db_r[5];
4'd3: sda_r <= db_r[4];
4'd4: sda_r <= db_r[3];
4'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
default: ;
endcase
// sda_r <= db_r[4'd7-num]; //Send EEPROM address (start with high bit)
cstate <= ADD3;
end
end
// else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //The device address is shifted left by 1 bit
else cstate <= ADD3;
end
ACK3: begin
if(/*!sda*/`SCL_NEG) begin
cstate <= DATA; //Slave response signal

sda_link <= 1'b0;
end
else cstate <= ACK3; //Wait for slave response
end
DATA: begin
if(!sw2_r) begin //Read operation
if(num<=4'd7) begin
cstate <= DATA;
if(`SCL_HIG) begin
num <= num+1'b1;
case (num)
4'd0: read_data[7] <= sda;
4'd1: read_data[6] <= sda; 
4'd2: read_data[5] <= sda;
4'd3: read_data[4] <= sda;
4'd4: read_data[3] <= sda;
4'd5: read_data[2] <= sda;
4'd6: read_data[1] <= sda;
4'd7: read_data[0] <= sda;
default: ;
endcase
// read_data[4'd7-num] <= sda; //Read data (start with high bit)
end
// else if(`SCL_NEG) read_data <= {read_data[6:0],read_data[7]}; //Data cycle right
end
else if((`SCL_LOW) && (num==4'd8)) begin
num <= 4'd0; //num count is cleared
cstate <= ACK4;
end
else cstate <= DATA;
end
else if(!sw1_r) begin //Write operation
sda_link <= 1'b1;
if(num<=4'd7) begin
cstate <= DATA;
if(`SCL_LOW) begin
sda_link <= 1'b1; //Data line sda ​​as output
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];
4'd1: sda_r <= db_r[6];
4'd2: sda_r <= db_r[5];
4'd3: sda_r <= db_r[4];
4'd4: sda_r <= db_r[3];
4'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
default: ;
endcase
// sda_r <= db_r[4'd7-num]; //Write data (start with high bit)
end
// else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //Write data to the left by 1 bit
end
else if((`SCL_LOW) && (num==4'd8)) begin
num <= 4'd0;
sda_r <= 1'b1;
sda_link <= 1'b0; //sda set to high impedance
cstate <= ACK4;
end
else cstate <= DATA;
end
end
ACK4: begin
if(/*!sda*/`SCL_NEG) begin
// sda_r <= 1'b1;
cstate <= STOP1;
end
else cstate <= ACK4;
end
STOP1: begin
if(`SCL_LOW) begin
sda_link <= 1'b1;
sda_r <= 1'b0;
cstate <= STOP1;
end
else if(`SCL_HIG) begin
sda_r <= 1'b1; //When scl is high, sda generates a rising edge (end signal)
cstate <= STOP2;
end
else cstate <= STOP1;
end
STOP2: begin
if(`SCL_LOW) sda_r <= 1'b1;
//else if(cnt_20ms==20'hffff0) cstate <= IDLE;
else if(Delay_cnt==24'hFFFFFF) cstate <= IDLE;
else cstate <= STOP2;
end
default: cstate <= IDLE;
endcase
end

assign sda = sda_link ? sda_r:1'bz;
assign dis_data = read_data;

endmodule


Code: [Select]
//Display the pressed numeric keys on the digital tube

module IR(clk,rst_n,IR,led_cs,led_db);

  input   clk;
  input   rst_n;
  input   IR;
  output [3:0] led_cs;
  output [7:0] led_db;
 
  reg [3:0] led_cs;
  reg [7:0] led_db;
 
  reg [7:0] led1,led2,led3,led4;
  reg [15:0] irda_data;    // save irda data,than send to 7 segment led
  reg [31:0] get_data;     // use for saving 32 bytes irda data
  reg [5:0]  data_cnt;     // 32 bytes irda data counter
  reg [2:0]  cs,ns;
  reg error_flag;          // 32 bytes data during, the data error flag

  //----------------------------------------------------------------------------
  reg irda_reg0;       //To avoid metastability and avoid driving multiple registers, this one is not used.
  reg irda_reg1;       //This can be used, the status of irda in the following program
  reg irda_reg2;       //In order to determine the edge of irda, hit the register again. The following program represents the previous state of irda
  wire irda_neg_pulse; //Determine the falling edge of irda
  wire irda_pos_pulse; //Determine the rising edge of irda
  wire irda_chang;     //Indeed ╥rda's transition edge
 
  reg[15:0] cnt_scan;//Scanning frequency counter
   
  always @ (posedge clk) //Follow register
    if(!rst_n)
      begin
        irda_reg0 <= 1'b0;
        irda_reg1 <= 1'b0;
        irda_reg2 <= 1'b0;
      end
    else
      begin
        led_cs <= 4'b0000; //The bit selection of the nixie tube is on
        irda_reg0 <= IR;
        irda_reg1 <= irda_reg0;
        irda_reg2 <= irda_reg1;
      end
     
  assign irda_chang = irda_neg_pulse | irda_pos_pulse;  //IR received signal changes, rising or falling
  assign irda_neg_pulse = irda_reg2 & (~irda_reg1);  //IR receive signal irda falling edge
  assign irda_pos_pulse = (~irda_reg2) & irda_reg1;      //IR receive signal irda rising edge


  reg [10:0] counter;  //Frequency division 1750 times
  reg [8:0]  counter2; //Count the points after frequency division
  wire check_9ms;  // check leader 9ms time
  wire check_4ms;  // check leader 4.5ms time
  wire low;        // check  data="0" time
  wire high;       // check  data="1" time
 
  //----------------------------------------------------------------------------
  //1750 counts
  always @ (posedge clk)
    if (!rst_n)
      counter <= 11'd0;
    else if (irda_chang)  //irda level jumps, it starts counting again
      counter <= 11'd0;
    else if (counter == 11'd1750)
      counter <= 11'd0;
    else
      counter <= counter + 1'b1;
 
  //----------------------------------------------------------------------------
  always @ (posedge clk)
    if (!rst_n)
      counter2 <= 9'd0;
    else if (irda_chang)  //irda level jumps, counting starts again
      counter2 <= 9'd0;
    else if (counter == 11'd1750)
      counter2 <= counter2 +1'b1;
 

  assign check_9ms = ((217 < counter2) & (counter2 < 297));
  //257  In order to increase stability, take a certain range
  assign check_4ms = ((88 < counter2) & (counter2 < 168));  //128
  assign low  = ((6 < counter2) & (counter2 < 26));         // 16
  assign high = ((38 < counter2) & (counter2 < 58));        // 48

  //----------------------------------------------------------------------------
  // generate statemachine  state machine
    parameter IDLE       = 3'b000, //Initial state
              LEADER_9   = 3'b001, //9ms
              LEADER_4   = 3'b010, //4ms
              DATA_STATE = 3'b100; //transfer data
 
  always @ (posedge clk)
    if (!rst_n)
      cs <= IDLE;
    else
      cs <= ns; //Status bit
     
  always @ ( * )
    case (cs)
      IDLE:
        if (~irda_reg1)
          ns = LEADER_9;
        else
          ns = IDLE;
   
      LEADER_9:
        if (irda_pos_pulse)   //leader 9ms check
          begin
            if (check_9ms)
              ns = LEADER_4;
            else
              ns = IDLE;
          end
        else  //complete if---else--- ;Prevent generation latch
          ns =LEADER_9;
   
      LEADER_4:
        if (irda_neg_pulse)  // leader 4.5ms check
          begin
            if (check_4ms)
              ns = DATA_STATE;
            else
              ns = IDLE;
          end
        else
          ns = LEADER_4;
   
      DATA_STATE:
        if ((data_cnt == 6'd32) & irda_reg2 & irda_reg1)
          ns = IDLE;
        else if (error_flag)
          ns = IDLE;
        else
          ns = DATA_STATE;
      default:
        ns = IDLE;
    endcase

  //The output in the state machine is described by a sequential circuit
  always @ (posedge clk)
    if (!rst_n)
      begin
        data_cnt <= 6'd0;
        get_data <= 32'd0;
        error_flag <= 1'b0;
      end
 
    else if (cs == IDLE)
      begin
        data_cnt <= 6'd0;
        get_data <= 32'd0;
        error_flag <= 1'b0;
      end
 
    else if (cs == DATA_STATE)
      begin
        if (irda_pos_pulse)  // low 0.56ms check
          begin
            if (!low)  //error
              error_flag <= 1'b1;
          end
        else if (irda_neg_pulse)  //check 0.56ms/1.68ms data 0/1
          begin
            if (low)
              get_data[0] <= 1'b0;
            else if (high)
              get_data[0] <= 1'b1;
            else
              error_flag <= 1'b1;
             
            get_data[31:1] <= get_data[30:0];
            data_cnt <= data_cnt + 1'b1;
          end
      end

  always @ (posedge clk)
    if (!rst_n)
begin
      irda_data <= 16'd0;
led2 <= 8'd0;
end
    else if ((data_cnt ==6'd32) & irda_reg1)
  begin
   led1 <= get_data[7:0];  //Data inverse
   led2 <= get_data[15:8]; //Data code
   led3 <= get_data[23:16];//User code
   led4 <= get_data[31:24];
  end
 
//Display the keys pressed by the remote control on the digital tube
always@(led2)
begin
case(led2)

                     //Display 0 to 9 on the digital tube
        8'b01101000: //Code value of remote control board 0
led_db=8'b0000_0000;  //Show 0

8'b00110000: //Code value of remote control board 1
led_db=8'b0000_0001;  //Show 1

8'b00011000: //遥控板2的码值
led_db=8'b0000_00010;  //Show 2

8'b01111010: //遥控板3的码值
led_db=8'b0000_0011;  //Show 3

8'b00010000: //遥控板4的码值
led_db=8'b0000_0100;  //Show 4

8'b00111000: //遥控板5的码值
led_db=8'b0000_0101;  //Show 5

8'b01011010: //遥控板6的码值
led_db=8'b0000_0110;  //Show 6

8'b01000010: //遥控板7的码值
led_db=8'b0000_0111;  //Show 7

8'b01001010: //遥控板8的码值
led_db=8'b0000_1000;  //Show 8

8'b01010010: //遥控板9的码值
led_db=8'b0000_1001;  //Show 9

8'b10011000: //Code value of remote control board 100+
led_db=8'b0001_0000;  //Show 10

8'b10110000: //Code value of remote control board 200+
led_db=8'b0010_0000;  //Show 20

8'b11100000: //Code value of remote control board Vol-
led_db=8'hDD;  //Show 10

8'b10101000: //Code value of remote control board Vol+
led_db=8'h11;  //Show 10

8'b10010000: //Code value of remote control board EQ
led_db=8'hE0;  //Show E0

  //When the key is not pressed, F is displayed
   default: led_db=8'b1110_1110;

endcase
end

endmodule
Fear does not stop death, it stops life.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf