2024.7.10 수업날
< PWM으로 servo_motor 제어하기 >
pwm_256step_freq 소스코드
///////////////////////////////////////////////// 2024.7.10
//////////////////////////pwm 제어하기, 256분주기(100MHz로 나눈)
// 서브모터는 50Hz
module pwm_256step_freq
#( parameter sys_clk_freq = 100_000_000, //시스템 클록, 100MHz
parameter pwm_freq = 50, //pwm 출력, 50Hz
parameter duty_step = 256, // 듀티 싸이클 256로 설정--> 많으면 많을수록 모터의 움직임이 부드러워짐
parameter temp = sys_clk_freq / pwm_freq / duty_step, // 타이머 카운트 주기, parameter 상수는 그냥 수이기 때문에 회로 만들때는 나누기 회로가 만들어지지 않음
parameter temp_half = temp / 2)
(
input clk, reset_p,
input [7:0]duty,
output reg pwm );
integer cnt; // 카운터 변수 선언
reg pwm_freqX256; // 128배 주파수를 가지는 PWM 신호를 저장하는 레지스터 선언
always @ (posedge clk or posedge reset_p) begin
if(reset_p) begin
pwm_freqX256 = 0;
cnt = 0;
end
else begin
if(cnt >= (temp - 1))cnt = 0; //78분주-->10,000/128
else cnt = cnt + 1;
//if(cnt < (pwmXduty_steps / 2)) pwm_freqX128 = 0; // 마찬가지로 상수이기 때문에 상관 없음
if(cnt < temp_half) pwm_freqX256 = 0;
else pwm_freqX256 = 1;
end
end
wire pwm_freqX256_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p),
.cp(pwm_freqX256), .n_edge(pwm_freqX256_nedge));
reg [7:0] cnt_duty; // 2^8=256분주기
always @ (posedge clk or posedge reset_p)begin
if(reset_p) begin
cnt_duty = 0;
pwm = 0;
end
else if(pwm_freqX256_nedge)begin
cnt_duty = cnt_duty + 1;
if(cnt_duty < duty)pwm = 1;
else pwm = 0;
end
end
endmodule
servo_motor_top 소스코드
//////////////////////////////////////////////////////////////////////2024.7.10
//////////////// 서브모터 pwm 제어
module servo_motor_top(
input clk, reset_p,
output servo_pwm,
output [3:0] com,
output [7:0] seg_7);
integer clk_div;
always @(posedge clk) clk_div = clk_div + 1;
wire clk_div_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div[26]), // clk_div[26]-->1.3s
.n_edge(clk_div_nedge));
integer cnt; // integer는 32비트
always @(posedge clk or posedge reset_p) begin
if(reset_p)cnt = 0;
else if(clk_div_nedge)begin
//if(cnt >= 12)cnt = 6; // 6에서 12까지 증가하는 카운터 만들기--> 90도 움직일 수 있음
if(cnt >= 16)cnt = 3; // 3부터 16까지 카운트됨-->180도 움직일 수 있음
else cnt = cnt + 1;
end
end
pwm_128step_freq #(.pwm_freq(50)) pwm_motor( // #으로 해놓으면 모듈마다 수정할 필요없음
.clk(clk), .reset_p(reset_p),
.duty(cnt),
.pwm(servo_pwm));
wire [15:0] duty_bcd;
bin_to_dec btd_duty(.bin({cnt}), .bcd(duty_bcd));
fnd_4digit_cntr FND_duty(.clk(clk), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
servo_motor_down_up_top 소스코드
//////////////////////////////////// 서브모터 pwm 제어, 최대값에 도달하면 다시 다운카운트가 되도록
module servo_motor_down_up_top(
input clk, reset_p,
output servo_pwm,
output [3:0] com,
output [7:0] seg_7);
integer clk_div;
always @(posedge clk) clk_div = clk_div + 1;
wire clk_div_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div[25]), // clk_div[21]-->0.3s
.n_edge(clk_div_nedge));
integer cnt; // integer는 32비트
reg down_up;
// 3~16카운터--> 총 12단계, 180도 움직임--> 한번에 15도씩 움직이는 것
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
cnt = 3;
down_up = 0;
end
else if(clk_div_nedge)begin
if(down_up) begin
if(cnt <= 3) begin
down_up = 0;
end
else cnt = cnt - 1;
end
else begin
if(cnt >= 16)begin
down_up = 1;
end
else cnt = cnt + 1;
end
end
end
pwm_128step_freq #(.pwm_freq(50)) pwm_motor( // #으로 해놓으면 모듈마다 수정할 필요없음
.clk(clk), .reset_p(reset_p),
.duty(cnt),
.pwm(servo_pwm));
wire [15:0] duty_bcd;
bin_to_dec btd_duty(.bin({cnt}), .bcd(duty_bcd));
fnd_4digit_cntr FND_duty(.clk(clk), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
servo_motor_down_up_256_top 소스코드
//////////////////////////////////// 서브모터 pwm 제어, 256주기, 최대값에 도달하면 다시 다운카운트가 되도록
module servo_motor_down_up_256_top(
input clk, reset_p,
output servo_pwm,
output [3:0] com,
output [7:0] seg_7);
integer clk_div;
always @(posedge clk) clk_div = clk_div + 1;
wire clk_div_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div[24]),
.n_edge(clk_div_nedge));
integer cnt; // integer는 32비트
reg down_up;
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
cnt = 6;
down_up = 0;
end
else if(clk_div_nedge)begin
if(down_up) begin
if(cnt <= 6) down_up = 0;
else cnt = cnt - 1;
end
else begin
if(cnt >= 32)down_up = 1;
else cnt = cnt + 1;
end
end
end
pwm_256step_freq #(.pwm_freq(50)) pwm_motor( // #으로 해놓으면 모듈마다 수정할 필요없음
.clk(clk), .reset_p(reset_p),
.duty(cnt),
.pwm(servo_pwm));
wire [15:0] duty_bcd;
bin_to_dec btd_duty(.bin({cnt}), .bcd(duty_bcd));
fnd_4digit_cntr FND_duty(.clk(clk), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
servo_down_up_256_comparator 소스코드
//////////////////////////////////// 서브모터 pwm 제어, 256주기, 최대값에 도달하면 다시 다운카운트가 되도록
/////////////////비교기 사용--> 부드럽게 움직이도록 함
module servo_down_up_256_comparator_top(
input clk, reset_p,
output servo_pwm,
output [3:0] com,
output [7:0] seg_7);
integer clk_div;
always @(posedge clk) clk_div = clk_div + 1;
wire clk_div_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div[21]),
.n_edge(clk_div_nedge));
integer cnt; // integer는 32비트
reg down_up;
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
cnt = 6;
down_up = 0;
end
else if(clk_div_nedge)begin
if(down_up) begin
if(cnt <= 6) down_up = 0;
else cnt = cnt - 1;
end
else begin
if(cnt >= 32)down_up = 1;
else cnt = cnt + 1;
end
end
end
pwm_256step_comparator_freq #(.pwm_freq(50)) pwm_motor( // #으로 해놓으면 모듈마다 수정할 필요없음
.clk(clk), .reset_p(reset_p),
.duty(cnt),
.pwm(servo_pwm));
wire [15:0] duty_bcd;
bin_to_dec btd_duty(.bin({cnt}), .bcd(duty_bcd));
fnd_4digit_cntr FND_duty(.clk(clk), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
servo_down_up_256_btn_top 소스코드
//////////////////////////////////// 서브모터 pwm 제어, 256주기, 최대값에 도달하면 다시 다운카운트가 되도록
/////////////////비교기 사용--> 부드럽게 움직이도록 함, 버튼을 누르면 반대로 토글되게 제어하기
module servo_down_up_256_btn_top(
input clk, reset_p,
input btn,
output servo_pwm,
output [3:0] com,
output [7:0] seg_7);
wire btn_nedge;
button_cntr_reg_n_p btn_t(
.clk(clk), .reset_p(reset_p),
.btn(btn), .btn_nedge(btn_nedge));
integer clk_div;
always @(posedge clk) clk_div = clk_div + 1;
wire clk_div_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div[21]),
.n_edge(clk_div_nedge));
integer cnt; // integer는 32비트
reg down_up;
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
cnt = 6;
down_up = 0;
end
else if(clk_div_nedge)begin
if(down_up) begin
if(cnt <= 6) down_up = 0;
else cnt = cnt - 1;
end
else begin
if(cnt >= 32)down_up = 1;
else cnt = cnt + 1;
end
end
else if(btn_nedge)down_up = ~down_up;
end
pwm_256step_comparator_freq #(.pwm_freq(50)) pwm_motor( // #으로 해놓으면 모듈마다 수정할 필요없음
.clk(clk), .reset_p(reset_p),
.duty(cnt),
.pwm(servo_pwm));
wire [15:0] duty_bcd;
bin_to_dec btd_duty(.bin({cnt}), .bcd(duty_bcd));
fnd_4digit_cntr FND_duty(.clk(clk), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
< ADC , 아날로그 디지털 변환 회로(Analog-to-digital converter) >
이제 아날로그 입력을 받아서 디지털로 출력하는 adc에 대해서 알아보자
아래와 같이 fpga에는 아날로그 입력을 받을 수 있도록 adc가 있다.
아래와 같은 순서로 진행하면 된다.
XADC Wizard를 더블클릭하면 이렇게 새로운 창이 뜨게 된다.
OK를 누른 후 생기는 다음 창
그러면 xadc_wiz_0 소스코드 가 자동으로 추가되는데, 아래와 같이 수정해준다.
xadc_wiz_0 소스코드
// file: xadc_wiz_0.v
// (c) Copyright 2009 - 2013 Xilinx, Inc. All rights reserved.
//
// This file contains confidential and proprietary information
// of Xilinx, Inc. and is protected under U.S. and
// international copyright and other intellectual property
// laws.
//
// DISCLAIMER
// This disclaimer is not a license and does not grant any
// rights to the materials distributed herewith. Except as
// otherwise provided in a valid license issued to you by
// Xilinx, and to the maximum extent permitted by applicable
// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
// (2) Xilinx shall not be liable (whether in contract or tort,
// including negligence, or under any other theory of
// liability) for any loss or damage of any kind or nature
// related to, arising under or in connection with these
// materials, including for any direct, or any indirect,
// special, incidental, or consequential loss or damage
// (including loss of data, profits, goodwill, or any type of
// loss or damage suffered as a result of any action brought
// by a third party) even if such damage or loss was
// reasonably foreseeable or Xilinx had been advised of the
// possibility of the same.
//
// CRITICAL APPLICATIONS
// Xilinx products are not designed or intended to be fail-
// safe, or for use in any application requiring fail-safe
// performance, such as life-support or safety devices or
// systems, Class III medical devices, nuclear facilities,
// applications related to the deployment of airbags, or any
// other applications that could lead to death, personal
// injury, or severe property or environmental damage
// (individually and collectively, "Critical
// Applications"). Customer assumes the sole risk and
// liability of any use of Xilinx products in Critical
// Applications, subject only to applicable laws and
// regulations governing limitations on product liability.
//
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
// PART OF THIS FILE AT ALL TIMES.
`timescale 1ns / 1 ps
(* CORE_GENERATION_INFO = "xadc_wiz_0,xadc_wiz_v3_3_7,{component_name=xadc_wiz_0,enable_axi=false,enable_axi4stream=false,dclk_frequency=100,enable_busy=true,enable_convst=false,enable_convstclk=false,enable_dclk=true,enable_drp=true,enable_eoc=true,enable_eos=true,enable_vbram_alaram=false,enable_vccddro_alaram=false,enable_Vccint_Alaram=false,enable_Vccaux_alaram=false,enable_vccpaux_alaram=false,enable_vccpint_alaram=false,ot_alaram=false,user_temp_alaram=false,timing_mode=continuous,channel_averaging=256,sequencer_mode=off,startup_channel_selection=single_channel}" *)
module xadc_wiz_0
(
daddr_in, // Address bus for the dynamic reconfiguration port
dclk_in, // Clock input for the dynamic reconfiguration port
den_in, // Enable Signal for the dynamic reconfiguration port
di_in, // Input data bus for the dynamic reconfiguration port
dwe_in, // Write Enable for the dynamic reconfiguration port
reset_in, // Reset signal for the System Monitor control logic
vauxp6, // Auxiliary channel 6
vauxn6,
busy_out, // ADC Busy signal
channel_out, // Channel Selection Outputs
do_out, // Output data bus for dynamic reconfiguration port
drdy_out, // Data ready signal for the dynamic reconfiguration port
eoc_out, // End of Conversion Signal
eos_out, // End of Sequence Signal
alarm_out, // OR'ed output of all the Alarms
vp_in, // Dedicated Analog Input Pair
vn_in);
input [6:0] daddr_in;
input dclk_in;
input den_in;
input [15:0] di_in;
input dwe_in;
input reset_in;
input vauxp6;
input vauxn6;
input vp_in;
input vn_in;
output busy_out;
output [4:0] channel_out;
output [15:0] do_out;
output drdy_out;
output eoc_out;
output eos_out;
output alarm_out;
wire FLOAT_VCCAUX;
wire FLOAT_VCCINT;
wire FLOAT_TEMP;
wire GND_BIT;
wire [2:0] GND_BUS3;
assign GND_BIT = 0;
assign GND_BUS3 = 3'b000;
wire [15:0] aux_channel_p;
wire [15:0] aux_channel_n;
wire [7:0] alm_int;
assign alarm_out = alm_int[7];
assign aux_channel_p[0] = 1'b0;
assign aux_channel_n[0] = 1'b0;
assign aux_channel_p[1] = 1'b0;
assign aux_channel_n[1] = 1'b0;
assign aux_channel_p[2] = 1'b0;
assign aux_channel_n[2] = 1'b0;
assign aux_channel_p[3] = 1'b0;
assign aux_channel_n[3] = 1'b0;
assign aux_channel_p[4] = 1'b0;
assign aux_channel_n[4] = 1'b0;
assign aux_channel_p[5] = 1'b0;
assign aux_channel_n[5] = 1'b0;
assign aux_channel_p[6] = vauxp6;
assign aux_channel_n[6] = vauxn6;
assign aux_channel_p[7] = 1'b0;
assign aux_channel_n[7] = 1'b0;
assign aux_channel_p[8] = 1'b0;
assign aux_channel_n[8] = 1'b0;
assign aux_channel_p[9] = 1'b0;
assign aux_channel_n[9] = 1'b0;
assign aux_channel_p[10] = 1'b0;
assign aux_channel_n[10] = 1'b0;
assign aux_channel_p[11] = 1'b0;
assign aux_channel_n[11] = 1'b0;
assign aux_channel_p[12] = 1'b0;
assign aux_channel_n[12] = 1'b0;
assign aux_channel_p[13] = 1'b0;
assign aux_channel_n[13] = 1'b0;
assign aux_channel_p[14] = 1'b0;
assign aux_channel_n[14] = 1'b0;
assign aux_channel_p[15] = 1'b0;
assign aux_channel_n[15] = 1'b0;
XADC #(
.INIT_40(16'h3016), // config reg 0
.INIT_41(16'h310F), // config reg 1
.INIT_42(16'h0400), // config reg 2
.INIT_48(16'h0100), // Sequencer channel selection
.INIT_49(16'h0000), // Sequencer channel selection
.INIT_4A(16'h0000), // Sequencer Average selection
.INIT_4B(16'h0000), // Sequencer Average selection
.INIT_4C(16'h0000), // Sequencer Bipolar selection
.INIT_4D(16'h0000), // Sequencer Bipolar selection
.INIT_4E(16'h0000), // Sequencer Acq time selection
.INIT_4F(16'h0000), // Sequencer Acq time selection
.INIT_50(16'hB5ED), // Temp alarm trigger
.INIT_51(16'h57E4), // Vccint upper alarm limit
.INIT_52(16'hA147), // Vccaux upper alarm limit
.INIT_53(16'hCA33), // Temp alarm OT upper
.INIT_54(16'hA93A), // Temp alarm reset
.INIT_55(16'h52C6), // Vccint lower alarm limit
.INIT_56(16'h9555), // Vccaux lower alarm limit
.INIT_57(16'hAE4E), // Temp alarm OT reset
.INIT_58(16'h5999), // VCCBRAM upper alarm limit
.INIT_5C(16'h5111), // VCCBRAM lower alarm limit
.SIM_DEVICE("7SERIES"),
.SIM_MONITOR_FILE("design.txt")
)
inst (
.CONVST(GND_BIT),
.CONVSTCLK(GND_BIT),
.DADDR(daddr_in[6:0]),
.DCLK(dclk_in),
.DEN(den_in),
.DI(di_in[15:0]),
.DWE(dwe_in),
.RESET(reset_in),
.VAUXN(aux_channel_n[15:0]),
.VAUXP(aux_channel_p[15:0]),
.ALM(alm_int),
.BUSY(busy_out),
.CHANNEL(channel_out[4:0]),
.DO(do_out[15:0]),
.DRDY(drdy_out),
.EOC(eoc_out),
.EOS(eos_out),
.JTAGBUSY(),
.JTAGLOCKED(),
.JTAGMODIFIED(),
.OT(),
.MUXADDR(),
.VP(vp_in),
.VN(vn_in)
);
endmodule
그리고 test_top에 adc_top 소스코드를 아래와 같이 추가한다.
adc_top 소스코드
//////////////////////////////////////////////////////// adc
module adc_top(
input clk, reset_p,
input vauxp6, vauxn6,
output [3:0] com,
output [7:0] seg_7);
wire [4:0] channel_out;
wire eoc_out;
wire [15:0] do_out;
xadc_wiz_0 adc_ch6
(
.daddr_in({2'b0, channel_out}), // Address bus for the dynamic reconfiguration port
.dclk_in(clk), // Clock input for the dynamic reconfiguration port
.den_in(eoc_out), // Enable Signal for the dynamic reconfiguration port
.reset_in(reset_p), // Reset signal for the System Monitor control logic
.vauxp6(vauxp6), // Auxiliary channel 6
.vauxn6(vauxn6),
.channel_out(channel_out), // Channel Selection Outputs
.do_out(do_out), // Output data bus for dynamic reconfiguration port
.eoc_out(eoc_out) // End of Conversion Signal
);
wire eoc_out_pedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(eoc_out),
.n_edge(eoc_out_pedge));
reg [11:0] adc_value; // 2^12=4096, 비트수가 많을수록 정밀도가 높다
always @(posedge clk or posedge reset_p)begin
if(reset_p)adc_value = 0;
else if(eoc_out_pedge)begin
adc_value = do_out[15:4]; // [15:0] do_out에서 상위 12비트만 측정값으로 한다고 정했기 때문
end
end
wire [15:0] adc_value_bcd;
bin_to_dec btd_duty(.bin({adc_value}), .bcd(adc_value_bcd));
fnd_4digit_cntr FND_duty(.clk(clk), .value(adc_value_bcd), .com(com), .seg_7(seg_7));
endmodule
PWM으로 servo 모터 제어하기, ADC 끝!
'Harman 세미콘 아카데미 > Verilog' 카테고리의 다른 글
2024.8.12 [Verilog]18 - 주방타이머 수정, 조이스틱 ADC (0) | 2024.08.12 |
---|---|
2024.7.11 [Verilog] 다기능 선풍기 만들기 팀 프로젝트 (0) | 2024.07.11 |
2024.7.9 [Verilog]16 - PWM으로 모터 제어하기 (0) | 2024.07.09 |
2024.7.8 [Verilog]15 - PWM으로 led 제어하기 (0) | 2024.07.08 |
2024.7.5 [Verilog]14 - 다기능 시계 만들기 개인프로젝트 (0) | 2024.07.05 |