NFC Reader based on FPAG (6) – PPM generation

This chapter mainly implements the conversion of commands into the encoding method of 1 out of 4 in ISO1569, and realizes the PPM conversion of commands. Here we only briefly introduce the coding rules. 4 takes 1, 2bit encoding, low bit to high bit; b2b1, b4b3, b6b5, b8b7.

Based on this encoding method, 8-bit data PPM conversion is performed first. Observe the above encoding method,

00: first position;

01: The third position;

10: fifth position;

11: The seventh position.

There is a certain relationship between the position of the low level and the encoding. It is the value after adding a “1” after encoding; that is:

00: 001 = 1;

01: 011 = 3;

10: 101 = 5;

11: 111 = 7.

In addition, a start frame needs to be added before the command, and an end frame needs to be added after the command ends. Starting frame:

end frame:

In this way, PPM encoding is easy. What needs to be noted here is the duration of a low pulse (9.44us), and a cycle is the duration of 8 low pulses, that is, 8 pulses constitute a cycle. The entire process is implemented using a state machine. This module encodes in bytes. Each time one byte of data is input, it is updated when the 1byte data encoding is completed. It should be noted that the code involves two clocks, so when implemented using combinational logic, there are many additional flag bits. In fact, the code can be optimized later and one master clock can be used for counting distribution to achieve PPM encoding. The top-level module implementation will be given in the next chapter.

module nfc_tx_ppm
#(
    parameter BYTE_CNT = 4
)(
    input rstn, //¤?
    input clk_system,
    input clk_ppm, //?¨9.44us
    
    input [BYTE_CNT:0] cnt, //?°?°é
    input datavaild, //?°
    input [7:0] crc, //?CRC?bit?°

    output reg byte_shift, //-
    output reg ppm_finish, //¤?é?
    output reg ppm_out //PPM
    );

    localparam period = 8; //?§?8?

    parameter idle = 3'd0; //?é
    parameter start = 3'd1; //§?é?
    parameter start_cnt = 3'd2; //?é?SOL
    parameter shift = 3'd3; //?é°
    parameter finish = 3'd4; //§?é?§
    parameter finish_cnt = 3'd5; //?§?

    reg [2:0]state; //?
    reg [2:0]next_state; //?

    reg [7:0]crc_temp; //?-?¨é?byte?°
    reg [3:0]period_cnt; //§
    reg [2:0]pause_insert; //
    reg [BYTE_CNT:0]byte_cnt; //?-?°
    reg [BYTE_CNT:0]temp_cnt;
    reg [1:0]shift_cnt;
    reg shift_flag;
    reg flag_state;
    reg flag_start_cnt;
    reg flag_finish_cnt;

    reg data_vaild_temp;

    always @(posedge clk_system or negedge rstn) begin
        if(!rstn)
            data_vaild_temp <= 1'd0;
        else if(datavaild & amp; state == idle)
            data_vaild_temp <= 1'd1;
        else if(ppm_finish)
            data_vaild_temp <= 1'd0;
    end

    //
    always @(posedge clk_ppm or negedge rstn) begin
        if (!rstn)begin
            state <= idle;
        end
        else
            state <= next_state;
    end

    //é?°
    always @(*)begin
        case (state)
            idle :if(data_vaild_temp) next_state = start;else next_state = idle; //?°
            start :if(flag_start_cnt)next_state = start_cnt;else next_state = start; //?é?SOL?é
            start_cnt:if(flag_state) next_state = shift;else next_state = start_cnt; //?é?
            shift :if(byte_cnt == temp_cnt) next_state = finish;else next_state = shift; //?é°
            finish :if(flag_finish_cnt)next_state = finish_cnt;else next_state = finish_cnt; //?é?EOF
            finish_cnt:if(ppm_finish) next_state = idle;else next_state = finish_cnt; //¤?éé
        endcase
    end

    always @(posedge clk_ppm or negedge rstn) begin
        if(!rstn)begin
            ppm_out <= 1'd1;
            ppm_finish <= 1'd0;
            crc_temp <= 7'd0;
            period_cnt <= 4'd0;
            pause_insert <= 3'd0;
            byte_shift <= 1'd0;
            byte_cnt <= 0;
            shift_cnt <= 2'd0;
            shift_flag <= 1'd1;
            flag_state <= 1'd0;
            temp_cnt <= 0;
            flag_start_cnt <= 1'd0;
            flag_finish_cnt <= 1'd0;
        end
        else begin
            case (next_state)
                idle: begin
                    ppm_out <= 1'd1;
                    ppm_finish <= 1'd0;
                end //?é?é?
                start: begin
                    period_cnt <= 4'd0; //?′?°?é°
                    temp_cnt <= cnt;
                    crc_temp <= {2'd0,crc[7:2]}; //
                    pause_insert <= {crc[1:0],1'd1}; //
                    ppm_out <= 1'd0; //SOL
                    flag_start_cnt <= 1'd1;
                end
                start_cnt:begin
                    ppm_out <= 1'd1; //SOL
                    if(period_cnt == period/2)
                        ppm_out <= 1'd0;
                    else
                        ppm_out <= 1'd1;
                    if(period_cnt == period-1)begin //SOL
                        period_cnt <= 4'd0;
                        flag_state <= 1'd1;
                    end
                    else
                        period_cnt <= period_cnt + 1;
                end
                shift:begin
                    if(period_cnt == pause_insert-1) //
                        ppm_out <= 1'd0;
                    else
                        ppm_out <= 1'd1;
                    if(period_cnt == period-1)
                        if(shift_cnt == 3)begin
                            pause_insert <= {crc[1:0],1'd1};
                            crc_temp <= {2'd0,crc[7:2]};
                            byte_cnt <= byte_cnt + 1'd1;
                            shift_cnt <= 2'd0;
                            if(byte_cnt!=cnt-2)
                                shift_flag <= 1'd1;
                            else
                                shift_flag <= 1'd0;
                            period_cnt <= 4'd0;
                        end
                        else begin
                            shift_cnt <= shift_cnt + 1'd1;
                            period_cnt <= 4'd0;
                            pause_insert <= {crc_temp[1:0],1'd1};
                            crc_temp <= {2'd0,crc_temp[7:2]};
                        end
                    else
                        period_cnt <= period_cnt + 1'd1;
                    if (shift_flag)begin
                        byte_shift <= 1'd1;
                        shift_flag <= 1'd0;
                    end
                    else
                        byte_shift <= 1'd0;
                end
                finish:begin
                    shift_flag <= 1'd1;
                    ppm_out <= 1'd1;
                    period_cnt <= period_cnt + 1'd1;
                    flag_finish_cnt <= 1'd1;
                end
                finish_cnt:begin
                    flag_start_cnt <= 1'd0;
                    flag_state <= 1'd0;
                    byte_cnt <= 0;
                    flag_finish_cnt <= 1'd0;
                    period_cnt <= period_cnt + 1'd1;
                    if(period_cnt == 1)
                        ppm_out <= 1'd0;
                    else
                        ppm_out <= 1'd1;
                    if (period_cnt == period/2-1)
                        ppm_finish <= 1'd1;
                end
                default: ;
            endcase
        end
    end
endmodule