// Humidity & Temperature Sensor data read & write, FSM
module dht11_cntr (
    input clk, reset_p,
    inout dht11_data,                           // Input + Output, reg Declaration Not Possible
    output reg [7:0] humidity, temperature,     // Output Measurement
    output [15:0] led                           // for Debugging
    );

    // Change State Using Shift
    localparam S_IDLE       = 6'b00_0001;       // Standby State
    localparam S_LOW_18MS   = 6'b00_0010;       // MCU Sends Out Start Signal
    localparam S_HIGH_20US  = 6'b00_0100;       // MCU Pull Up & Wait for Sensor Response
    localparam S_LOW_80US   = 6'b00_1000;       // DHT Sends Out Response Signal
    localparam S_HIGH_80US  = 6'b01_0000;       // DHT Pull Up & Get Ready Data
    localparam S_READ_DATA  = 6'b10_0000;       // DHT Data Read

    localparam S_WAIT_PEDGE = 2'b01;            // Start to Transmit 1-bit Data
    localparam S_WAIT_NEDGE = 2'b10;            // Voltage Length Measurement

    // Clock Divide 100, 10ns x 100 = 1us
    wire clk_usec_nedge;                        // Divide Clock 1us
    clock_div_100 us_clk (.clk(clk), .reset_p(reset_p), .nedge_div_100(clk_usec_nedge));

    // us Unit Count
    reg [21:0] cnt_usec;                        // us Count
    reg cnt_usec_e;                             // us Count Enable
    always @(negedge clk, posedge reset_p) begin
        if (reset_p) cnt_usec = 0;              // Count Clear
        else if (clk_usec_nedge && cnt_usec_e) begin    // Count Start when Enable & us Negative Edge
            cnt_usec = cnt_usec + 1;            // Count During Enable
        end
        else if (!cnt_usec_e) cnt_usec = 0;     // Count Clear when Disable
    end

    // Edge Detection of DHT Signal
    wire dht_nedge, dht_pedge;
    edge_detector_pos btn_ed (.clk(clk), .reset_p(reset_p),
        .cp(dht11_data), .p_edge(dht_pedge), .n_edge(dht_nedge));

    // Input Cannot be Declared as reg, Use Buffer
    reg dht11_buffer;                           // Buffer
    reg dht11_data_out_e;                       // Write Mode Enable Output, Disable Input
    assign dht11_data = dht11_data_out_e ? dht11_buffer : 'bz; // Output dout, Input Impedance Value

    reg [5:0] state, next_state;                // Current & Next State
    assign led [5:0] = state;                   // State LED Output
    reg [1:0] read_state;                       // Data Read State
    // Change State in Negative Edge
    always @(negedge clk, posedge reset_p) begin
        if (reset_p) state = S_IDLE;            // Basic Standby State
        else state = next_state;                // Change State in Negative Edge
    end

    reg [39:0] temp_data;                       // DHT Output Data 40-bit
    reg [5:0] data_cnt;                         // Counting to 40
    assign led [11:6] = data_cnt;
    // Set Next State in Positive Edge
    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            next_state = S_IDLE;                // Basic Stanby State
            temp_data = 0;                      // DHT Data Reset
            data_cnt = 0;                       // DHT Data Count Reset
            dht11_data_out_e = 0;               // DHT Write Disable, Input
            read_state = S_WAIT_PEDGE;          // Data Read Pull-Up High
        end
        else begin
            case (state)
                S_IDLE      : begin             // Standby State
                    if (cnt_usec < 22'd3_000_000) begin     // Real 3_000_000, Test 3_000
                        cnt_usec_e = 1;         // us Count Enable
                        dht11_data_out_e = 0;   // DHT Input Mode
                    end
                    else begin
                        cnt_usec_e = 0;         // Count Clear
                        next_state = S_LOW_18MS;// Change State S_LOW_18MS
                    end
                end
                S_LOW_18MS  : begin             // MCU Sends Out Start Signal
                    if (cnt_usec < 22'd18_000) begin    // Real 18_000, Test 18
                        cnt_usec_e = 1;         // us Count Enable
                        dht11_data_out_e = 1;   // DHT Output Mode
                        dht11_buffer = 0;       // Buffer Reset
                    end
                    else begin
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        next_state = S_HIGH_20US;   // Change State S_HIGH_20US
                        dht11_data_out_e = 0;   // DHT Input Mode
                    end
                end
                // It is Supposed to Respond after 20us, but in Reality, Response Occurs before that.
                // Remove 20us Waiting Part
                S_HIGH_20US : begin             // MCU Pull Up & Wait for Sensor Response
                    cnt_usec_e = 1;             // Count for Checking Response Time
                    if (cnt_usec > 22'd100_000) begin   // No Response 100ms, Comunication Error, Etc..
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        next_state = S_IDLE;    // Change State S_IDLE
                    end
                    if (dht_nedge) begin        // DHT Response, No Count
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        next_state = S_LOW_80US;    // Change State S_LOW_80US
                    end
                end
                S_LOW_80US  : begin             // DHT Sends Out Response Signal
                    cnt_usec_e = 1;             // Count for Checking Response Time
                    if (cnt_usec > 22'd100_000) begin   // No Response 100ms, Comunication Error, Etc..
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        next_state = S_IDLE;    // Change State S_IDLE
                    end
                    if (dht_pedge) begin
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        next_state = S_HIGH_80US;    // No Need to Count, Change State
                    end
                end
                S_HIGH_80US : begin             // DHT Pull Up & Get Ready Data
                    cnt_usec_e = 1;             // Count for Checking Response Time
                    if (cnt_usec > 22'd100_000) begin   // No Response 100ms, Comunication Error, Etc..
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        next_state = S_IDLE;    // Change State S_IDLE
                    end
                    if (dht_nedge) begin
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        next_state = S_READ_DATA;    // No Need to Count, Change State
                    end
                end
                S_READ_DATA : begin             // DHT Data Read
                    case (read_state)
                        S_WAIT_PEDGE : begin    // Current Low, Wait High
                            // High Signal Generation
                            if (dht_pedge) read_state = S_WAIT_NEDGE;   // Change State
                            cnt_usec_e = 0;     // us Count Disable, Clear
                        end
                        S_WAIT_NEDGE : begin    // Current High, Wait Low
                            // Low Signal Generation
                            if (dht_nedge) begin
                                read_state = S_WAIT_PEDGE;              // Change State
                                data_cnt = data_cnt + 1;                // Counting to 40
                            if (cnt_usec < 50) begin        // Distinguish by Signal Length
                                    temp_data = {temp_data[38:0], 1'b0};    // Shift Left & Enter Value in LSM
                                end
                                else begin
                                    temp_data = {temp_data[38:0], 1'b1};
                                end
                            end
                            else begin
                                cnt_usec_e = 1;     // us Count Enable
                                if (cnt_usec > 22'd100_000) begin   // No Response 100ms, Comunication Error, Etc..
                                    cnt_usec_e = 0;         // us Count Disable, Clear
                                    next_state = S_IDLE;
                                    data_cnt = 0;           // DHT Data Count Reset
                                    read_state = S_WAIT_PEDGE;
                                end
                            end
                        end
                    endcase

                    if (data_cnt >= 40) begin   // 40-bit Read Completed
                        next_state = S_IDLE;    // Basic Stanby State
                        data_cnt = 0;           // DHT Data Count Reset
    // DHT11 Data = Humidity Integral 8-bit + Decimal 8-bit + Temperature Integral 8-bit + Decimal 8-bit + Check Sum 8-bit
                        // Compare Upper 32-bits of Sum with Checksum
                        if ((temp_data[39:32] + temp_data[31:24] + temp_data[23:16] + temp_data[15:8]) == temp_data[7:0]) begin
                            humidity = temp_data[39:32];        // Use only Integer Values
                            temperature = temp_data[23:16];
                        end
                    end
                end
                default : next_state = S_IDLE;  // Basic Stanby State
            endcase
        end
    end
endmodule