Semiconductor/FPGA

FPGA - SPI 통신을 이용한 DC Motor 제어하기

잇(IT) 2024. 7. 15. 21:59

https://insoobaik.tistory.com/648

 

SPI - 신호 검증 (ILA) & (Cadence) Synthesis ~ POST SIM

보호되어 있는 글입니다. 내용을 보시려면 비밀번호를 입력하세요.

insoobaik.tistory.com

이전에 SPI를 ILA를 통해 전달되는 신호를 확인하고 Synthesis부터 Post Layout Simulation을 통해 코드가 정상적으로 실행되는 것을 확인하였다.

https://insoobaik.tistory.com/630

 

Verilog- FPGA를 이용한 DC 모터 구동

보호되어 있는 글입니다. 내용을 보시려면 비밀번호를 입력하세요.

insoobaik.tistory.com

 

위에서 SPI 통신을 통해 전달 받은 Data를 이전에 실습해본 Dc Motor를 제어해 볼 것이다.


 

위 그림은 SPI와 DC_Motor 모듈이 각각의 Top 모듈 안에서의 상호작용을 나타낸 것이다.

 

아두이노(SPI_Master)를 통해 spi_slave에게 Data를 전달하면 Data 중  마지막 4bit를 Duty Cycle로 사용하게 된다.

Duty Cycle은 4bit를 16등분 하여 전달되는 Data 따라 정해진다.

(예를 들어 1111이 들어오면 16중에 16이 1이기 때문에 Duty Cycle은 100%가 되고,  1000이 들어오게 되면 16중에 8이 1이기 때문에 Duty Cycle은 50%가 된다.

*추가로 평균 전압은 DC_Moter를 동작 시키기 위한 최소 전압 이상이 되어야 한다.)

 

Output Pin의 pwm_out이 Duty Cycle을 정하게 된다. (nsleep의 경우 Data Sheet를 보게되면 1을 전달해야 DC Motor가 동작하게 되어있다.)

 


Schematic

 

spi_slave

 

motor


 

 

f값 전달

아두이노를 통해 f(1111) 신호를 전달하게 마지막 4비트에 1111이 전달되고, 1111이 전달되면 Duty Cycle이 100%가 되기 때문에 위와 같이 PWM이 1의 신호를 계속해서 유지하는 것을 확인할 수 있다.

 

8값 전달

아두이노를 통해 f(1000) 신호를 전달하게 마지막 4비트에 1000이 전달되고, 1000이 전달되면 Duty Cycle이 50%가 되기 때문에 위와 같이 PWM이 1의 신호를 계속해서 유지하는 것을 확인할 수 있다.


실행 영상

 


전체 코드

 

spi_slave.v

`define ESTA_READY  2'b00
`define ESTA_READ   2'b01
`define ESTA_WRITE  2'b10
                
module spi_slave(sclk, cs, id, mosi, miso, reset, duty);              
    input sclk, cs, mosi, reset;
    input [1:0] id;
    output reg miso;
    output [3:0] duty;
    
    //received_data
    reg [31:0] received_data;
    //bitcnt
    reg [5:0]  bitcnt;
    
    always @(negedge sclk or posedge cs) begin
        if(cs) begin
            received_data[31:0] <= 0;
        end
        else begin
            if(bitcnt < 33) begin
                received_data[31:0] <= {received_data[30:0], mosi};
            end 
            else begin
                received_data[31:0]<= received_data[31:0];
            end
        end
    end
    
    always @(negedge sclk or posedge cs) begin
        if (cs) begin
            bitcnt <= 0;
        end
        else begin
            if(bitcnt < 33) begin
                bitcnt <= bitcnt + 1;
            end
            else begin
                bitcnt <= 0;
            end
        end
    end
 
    //id_check
    reg id_check;

    always @(posedge sclk or posedge cs) begin
        if(cs) begin
            id_check <= 0;
        end
        else begin
            if(bitcnt == 2 && id[1:0] == received_data[1:0])
                id_check <= 1;
            else if(bitcnt == 2 && id[1:0] != received_data[1:0])
                id_check <= 0;
            else
                //                 ?                        ?      ?  
                id_check <= id_check;
        end
    end

    //current_state
    reg [1:0] current_state;
    //next_state
    reg [1:0] next_state;
    
    always @(negedge sclk or posedge cs) begin
        if(cs) begin
            current_state <= 0;
        end
        else
            current_state <= next_state;
    end
    

    always @(posedge sclk or posedge cs) begin
        if(cs) begin
            next_state <= 0;
        end
        else begin
            case(current_state)
                `ESTA_READY : begin
                    if(bitcnt == 3 && received_data[0] == 1'b1 && id_check == 1)
                        next_state <= `ESTA_READ;
                    else if(bitcnt == 3 && received_data[0] == 1'b0 && id_check == 1)
                        next_state <= `ESTA_WRITE;
                    else
                        next_state <= next_state;
                end
                `ESTA_READ : begin
                    if(bitcnt == 0)
                        next_state <= `ESTA_READY;
                    else
                        next_state <= next_state;
                    end
                `ESTA_WRITE : begin
                    if(bitcnt == 0)
                        next_state <= `ESTA_READY;
                    else
                        next_state <= next_state;
                    end
            endcase
        end
    end

    //address    
    reg [1:0] address;

    always @(posedge sclk or posedge cs) begin
        if(cs) begin
            address <= 0;
        end
        else begin
            if(bitcnt == 4 && received_data[0] == 0) begin
                address <= 2'b01;
            end
            else if(bitcnt == 4 && received_data[0] == 1) begin
                address <= 2'b10;
            end
            else
                address <= address;
        end
    end

    //register D0, D1 
    reg [15:0] REG_D0;
    reg [15:0] REG_D1;
    
    always @(posedge sclk or posedge reset) begin
        if(reset) begin
            REG_D0[15:0] <= 0;
        end
        else begin
            if(current_state == `ESTA_WRITE && address == 2'b01 && bitcnt == 32)
                REG_D0[15:0] <= received_data[15:0];
            else begin
                REG_D0[15:0] <= REG_D0[15:0];
            end    
        end
    end

    always @(posedge sclk or posedge reset) begin
        if(reset) begin
            REG_D1[15:0] <= 0;
        end
        else begin
            if(current_state == `ESTA_WRITE && address == 2'b10 && bitcnt ==32)
                REG_D1[15:0] <= received_data[15:0];
            else begin
                REG_D1[15:0] <= REG_D1[15:0];
            end    
        end
    end

    //miso
    always @(posedge sclk or posedge cs) begin
        if(cs)
            miso <= 0;
        else if(current_state == `ESTA_READ && address == 2'b01) begin
            case(bitcnt)
                5'd16 : miso <= REG_D0[15];
                5'd17 : miso <= REG_D0[14];
                5'd18 : miso <= REG_D0[13];
                5'd19 : miso <= REG_D0[12];
                5'd20 : miso <= REG_D0[11];
                5'd21 : miso <= REG_D0[10];
                5'd22 : miso <= REG_D0[9];
                5'd23 : miso <= REG_D0[8];
                5'd24 : miso <= REG_D0[7];
                5'd25 : miso <= REG_D0[6];
                5'd26 : miso <= REG_D0[5];
                5'd27 : miso <= REG_D0[4];
                5'd28 : miso <= REG_D0[3];
                5'd29 : miso <= REG_D0[2];
                5'd30 : miso <= REG_D0[1];
                5'd31 : miso <= REG_D0[0];
                default : miso <= 0;
            endcase
        end
        else if(current_state == `ESTA_READ && address == 2'b10)begin
            case(bitcnt)
                5'd16 : miso <= REG_D1[15];
                5'd17 : miso <= REG_D1[14];
                5'd18 : miso <= REG_D1[13];
                5'd19 : miso <= REG_D1[12];
                5'd20 : miso <= REG_D1[11];
                5'd21 : miso <= REG_D1[10];
                5'd22 : miso <= REG_D1[9];
                5'd23 : miso <= REG_D1[8];
                5'd24 : miso <= REG_D1[7];
                5'd25 : miso <= REG_D1[6];
                5'd26 : miso <= REG_D1[5];
                5'd27 : miso <= REG_D1[4];
                5'd28 : miso <= REG_D1[3];
                5'd29 : miso <= REG_D1[2];
                5'd30 : miso <= REG_D1[1];
                5'd31 : miso <= REG_D1[0];
                default : miso <= 0;
            endcase
        end
        else
            miso <= 0;
    end
    
    assign duty[3:0] = REG_D0[3:0];
    
    //ila
//    ila_0 U0(
//        .clk(clk),
//        .probe0(cs),
//        .probe1(sclk),
//        .probe2(mosi),
//        .probe3(miso),
//        .probe4(bitcnt),
//        .probe5(address),
//        .probe6(current_state),
//        .probe7(REG_D0)
//    );

endmodule

 

motor.v

module motor(
    input [3:0] duty,
    input clk,
    input reset,
    output reg pwm_out,
    output wire nsleep
    );

    reg [25:0] counter;
    reg [3:0] pwm_counter;

    assign nsleep = 1;

    always @(posedge clk or posedge reset) begin
        if(reset) begin
            pwm_out <= 0;
            counter <= 0;
            pwm_counter <= 0;
        end 
        else begin
            if(counter >= 49999) begin
                counter <= 0;
                pwm_counter <= pwm_counter +1;

                if(pwm_counter >= 15) begin
                    pwm_counter <= 0;
                end
            end 
            else begin
                counter <= counter+1;
            end
        if(pwm_counter < duty)
            pwm_out <= 1;
        else
            pwm_out <= 0;
        end
    end
    
endmodule
728x90