주기와 주파수는 역수 관계이다
10ns를 1us로 만들기 위해선 100분주를 했는데 이것은 10ns가 100번 지나면 1000ns=1us이기 때문에 100을 곱해준것과 같이 받아들이면 됬다
주파수의 관점에서 생각해 봤을때 10ns는 1억Hz이다 이것을 임의로 원하는 주파수가 10,000Hz라고 설정했을때, 10,000Hz로 만들기 위해선 1억/10,000=10,000Hz이다, 그런데 여기서 한번에 10,000을 나누는것이 아니라 duty_step을 만들기 위해 두번에 걸처서 나눗셈을 한다 따라서 나누는값 10,000은 (duty_step x temp)인데 여기서 temp는 임의의 duty_step값에 따라 변화하는 변수이다. 이것은 duty_step의값 만큼을 한 덩어리로 묶어서 이 묶여진 덩어리들이 temp값만큼 있으면 원하는 주파수(여기선 10,000)을 만들 수 있게된다. 이제 1억Hz를 temp갯수 만큼의 나누어 각각의 덩어리로 만들고 한덩어리가 duty_step으로 나눠져있는 것을 만들었다 여기서 duty_step을 일정 갯수 만큼을 1과 0을 주면(아래 코드에서 'duty'값) 원하는 듀티폭을 만들 수 있다.
duty_step, temp로 나눈 덩어리들을 주파수에서 나누면 원하는 주파수로 줄이는 분주, 주기에 곱하면 원하는 주기로 늘려주는 분주
결국 주파수 관점에서의 분주를 구하면 아래의 temp식이 나오는 것이고 duty_step, temp둘다 분주비 이기때문에 아래 코드에서는 구해놓은 분주비를 주기의 관점에서 작성한다.

module led_pwm_top(
input clk, reset_p,
// input [6:0] duty,
output pwm, led_r, led_g, led_b);
// pwm_100step pwm_inst(.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(pwm));
reg [31:0] clk_div;
always @(posedge clk)clk_div = clk_div + 1;
pwm_100step pwm_inst(.clk(clk), .reset_p(reset_p), .duty(clk_div[27:21]), .pwm(pwm));
pwm_100step pwm_r(.clk(clk), .reset_p(reset_p), .duty(clk_div[26:20]), .pwm(led_r));
pwm_100step pwm_g(.clk(clk), .reset_p(reset_p), .duty(clk_div[25:19]), .pwm(led_g));
pwm_100step pwm_b(.clk(clk), .reset_p(reset_p), .duty(clk_div[24:18]), .pwm(led_b));
endmodule
100단계 컨트롤 모듈
module pwm_100step(
input clk, reset_p,
input [6:0] duty, //100단계 //듀티값은 시뮬레이션에서 정해주었다
output pwm);
parameter sys_clk_freq = 100_000_000;
parameter pwm_freq = 10_000;
parameter duty_step = 100;
parameter temp = sys_clk_freq / duty_step / pwm_freq;
parameter temp_half = temp / 2;
integer cnt_sysclk; //100까지 세는 카운터,7비트 필요
wire pwm_freqX100;
always @(negedge clk or posedge reset_p)begin //100분주, 10ns->1us
if(reset_p)cnt_sysclk = 0;
else begin
if(cnt_sysclk >= temp-1) cnt_sysclk = 0;
else cnt_sysclk = cnt_sysclk + 1;
end
end
assign pwm_freqX100 = (cnt_sysclk < temp_half) ? 1 : 0;
wire pwm_freqX100_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(pwm_freqX100),
.n_edge(pwm_freqX100_nedge));
reg [6:0] cnt_duty; //100까지 세는 카운터,7비트 필요
always @(negedge clk or posedge reset_p)begin
if(reset_p)cnt_duty = 0;
else if(pwm_freqX100_nedge)begin
if(cnt_duty >= 99) cnt_duty = 0; //cnt_duty가 99를넘어 다시 0되면 100us
else cnt_duty = cnt_duty + 1;
end
end
assign pwm = (cnt_duty < duty) ? 1 : 0; //duty값은 시뮬레이션에서 넣어줌
endmodule
XDC-JC 2,3,4 사용
set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports {led_r}];#Sch name = JC2
set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports {led_g}];#Sch name = JC3
set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports {led_b}];#Sch name = JC4
module pwm_Nstep_freq
#( parameter sys_clk_freq = 100_000_000,
parameter pwm_freq = 10_000,
parameter duty_step = 100,
parameter temp = sys_clk_freq / duty_step / pwm_freq,
parameter temp_half = temp / 2
)
(
input clk, reset_p,
input [31:0] duty,
output pwm);
integer cnt_sysclk; //100까지 세는 카운터,7비트 필요
wire clk_freqXstep;
always @(negedge clk or posedge reset_p)begin //100분주, 10ns->1us
if(reset_p)cnt_sysclk = 0;
else begin
if(cnt_sysclk >= temp-1) cnt_sysclk = 0;
else cnt_sysclk = cnt_sysclk + 1;
end
end
assign clk_freqXstep = (cnt_sysclk < temp_half) ? 1 : 0;
wire clk_freqXstep_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_freqXstep),
.n_edge(clk_freqXstep_nedge));
integer cnt_duty;
always @(negedge clk or posedge reset_p)begin
if(reset_p)cnt_duty = 0;
else if(clk_freqXstep_nedge)begin
if(cnt_duty >= (duty_step-1)) cnt_duty = 0;
else cnt_duty = cnt_duty + 1;
end
end
assign pwm = (cnt_duty < duty) ? 1 : 0; //duty값은 시뮬레이션에서 넣어줌
endmodule
탑모듈
module led_pwm_top(
input clk, reset_p,
// input [6:0] duty,
output pwm, led_r, led_g, led_b);
// pwm_100step pwm_inst(.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(pwm));
reg [31:0] clk_div;
always @(posedge clk)clk_div = clk_div + 1;
pwm_100step pwm_inst(.clk(clk), .reset_p(reset_p), .duty(clk_div[27:21]), .pwm(pwm));
pwm_Nstep_freq #(.duty_step(77)) pwm_r(.clk(clk), .reset_p(reset_p), .duty(clk_div[28:23]), .pwm(led_r));
pwm_Nstep_freq #(.duty_step(93)) pwm_g(.clk(clk), .reset_p(reset_p), .duty(clk_div[27:22]), .pwm(led_g));
pwm_Nstep_freq #(.duty_step(103)) pwm_b(.clk(clk), .reset_p(reset_p), .duty(clk_div[26:21]), .pwm(led_b));
endmodule
듀티스텝은 펄스폭(펄스의 두께)을 몇단계로 제어 할것이냐
led 문턱 2v
3.3v를 인가하여 led가 켜지긴하지만 50%의 듀티폭으로 인가하면 1.65v의 힘으로 킨거과 같은 밝기가 되는 것

입력에 5v인가, 출력에 넣지 않게 주의!, 전원핀은 모터의 방향차이라 Vcc,GND 방향 상관없음

모터 동작을 위한 탑모듈
module dc_motor_pwm_top(
input clk, reset_p,
output motor_pwm);
reg [31:0] clk_div;
always @(posedge clk)clk_div = clk_div + 1;
pwm_Nstep_freq #(
.duty_step(4),
.pwm_freq(100))
pwm_b(
.clk(clk),
.reset_p(reset_p),
.duty(clk_div[31:30]), //0번째 비트에서 부터 1씩 증가하며 채워오다가 30번째 비트가 1이 되면 duty_step 4단계중에 1단계
.pwm(motor_pwm)); //그러다가 (31번째 비트, 30번째 비트)가 (1,0)이 되면 duty_step 4단계 중에 2단계
//즉 duty_step 4단계를 clk_div의 상위 2비트를 잘라서 duty에 넣으므로서 duty값이 계속해서 변화하게 시킴
endmodule //시스템클락마다 증가하는 clk_div레지스터중 가장 늦게 변화하는 최상위 2비트를 사용

module dc_motor_pwm_top(
input clk, reset_p,
output motor_pwm,
output [3:0] com,
output [7:0] seg_7);
reg [31:0] clk_div;
always @(posedge clk)clk_div = clk_div + 1;
pwm_Nstep_freq #(
.duty_step(4),
.pwm_freq(100))
pwm_b(
.clk(clk),
.reset_p(reset_p),
.duty(clk_div[31:30]),
.pwm(motor_pwm));
wire [15:0] duty_bcd;
bin_to_dec bcd_humi(.bin({10'b0, clk_div[31:30]}), .bcd(duty_bcd));
fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
100단계로 나눈
module dc_motor_pwm_top(
input clk, reset_p,
output motor_pwm,
output [3:0] com,
output [7:0] seg_7);
reg [31:0] clk_div;
always @(posedge clk)clk_div = clk_div + 1;
pwm_Nstep_freq #(
.duty_step(100),
.pwm_freq(100))
pwm_b(
.clk(clk),
.reset_p(reset_p),
.duty(clk_div[31:26]),
.pwm(motor_pwm));
wire [15:0] duty_bcd;
bin_to_dec bcd_humi(.bin({6'b0, clk_div[31:26]}), .bcd(duty_bcd));
fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
module dc_motor_pwm_top(
input clk, reset_p,
output motor_pwm,
output [3:0] com,
output [7:0] seg_7);
reg [31:0] clk_div;
always @(posedge clk or posedge reset_p)begin
if(reset_p) clk_div = 0;
else clk_div = clk_div + 1;
end
wire clk_div_26_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div[26]),
.n_edge(clk_div_26_nedge));
reg [6:0] duty;
always @(posedge clk or posedge reset_p)begin
if(reset_p) duty = 20;
else if(clk_div_26_nedge)begin
if(duty >= 50) duty = 20;
else duty = duty + 1;
end
end
pwm_Nstep_freq #(
.duty_step(100),
.pwm_freq(100))
pwm_b(
.clk(clk),
.reset_p(reset_p),
.duty(duty),
.pwm(motor_pwm));
wire [15:0] duty_bcd;
bin_to_dec bcd_humi(.bin({6'b0, duty}), .bcd(duty_bcd));
fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule

전체 20ms 주기중 1ms는 5%의 펄스폭, 1.5ms는 7.5%의 펄스폭 2ms는 10%의 펄스폭을 갖는다
데이터 시트가 정확한 수치를 표시하지 않을수있어서 FND에 듀티값을 표시해 실제 어떤 듀티값에서 어떤 각도가 되는지 알아볼 필요가 있다. 저 세밀한 각도를 알기 위해서는 duty_step을 늘릴 필요가 있다
module dc_motor_pwm_top(
input clk, reset_p,
output motor_pwm,
output [3:0] com,
output [7:0] seg_7);
reg [31:0] clk_div;
always @(posedge clk or posedge reset_p)begin
if(reset_p) clk_div = 0;
else clk_div = clk_div + 1;
end
wire clk_div_26_nedge;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div[25]), //2배 빠르게 진행하기 위해 변경
.n_edge(clk_div_26_nedge));
reg [6:0] duty;
always @(posedge clk or posedge reset_p)begin
if(reset_p) duty = 0;
else if(clk_div_26_nedge)begin
if(duty >= 20) duty = 0; //듀티값 변경
else duty = duty + 1;
end
end
pwm_Nstep_freq #(
.duty_step(100), //펄스폭 100단계로 나눔
.pwm_freq(50)) //데이터시트에 표기된 서보모터가 필요한 주파수
pwm_b(
.clk(clk),
.reset_p(reset_p),
.duty(duty),
.pwm(motor_pwm));
wire [15:0] duty_bcd;
bin_to_dec bcd_humi(.bin({6'b0, duty}), .bcd(duty_bcd));
fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
module servo_pwm_top(
input clk, reset_p,
input [3:0] btn,
output motor_pwm,
output [3:0] com,
output [7:0] seg_7);
reg [31:0] clk_div;
always @(posedge clk or posedge reset_p)begin
if(reset_p) clk_div = 0;
else clk_div = clk_div + 1;
end
// wire clk_div_26_nedge;
// edge_detector_n ed(
// .clk(clk), .reset_p(reset_p), .cp(clk_div[25]),
// .n_edge(clk_div_26_nedge));
wire btn_ctr;
button_cntr btn1(.clk(clk), .reset_p(reset_p), .btn(btn[3]), .btn_pedge(btn_ctr)); //버튼 채터링 방지
wire btn_nedge;
edge_detector_n ed1(
.clk(clk), .reset_p(reset_p), .cp(btn_ctr),
.n_edge(btn_nedge));
reg [1:0] cnt_btn;
always @(negedge clk or posedge reset_p)begin
if(reset_p)begin
cnt_btn = 1;
end
else if(btn_nedge)begin
if(cnt_btn>=3)begin
cnt_btn = 1;
end
cnt_btn = cnt_btn + 1;
end
end
pwm_Nstep_freq #(
.duty_step(200), //듀티폭을 100단계로 나눔
.pwm_freq(50)) //연결한 센서에 따라다름, 데이터시트에 필요한 주파수 있음
pwm_b(
.clk(clk),
.reset_p(reset_p),
.duty(duty),
.pwm(motor_pwm));
wire [15:0] duty_bcd;
bin_to_dec bcd_humi(.bin({6'b0, duty}), .bcd(duty_bcd));
fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
module servo_pwm_top(
input clk, reset_p,
input [3:0] btn,
output motor_pwm,
output [3:0] com,
output [7:0] seg_7);
reg [31:0] clk_div;
always @(posedge clk or posedge reset_p)begin
if(reset_p) clk_div = 0;
else clk_div = clk_div + 1;
end
// wire clk_div_26_nedge;
// edge_detector_n ed(
// .clk(clk), .reset_p(reset_p), .cp(clk_div[25]),
// .n_edge(clk_div_26_nedge));
wire btn_ctr1, btn_ctr2, btn_ctr3;
button_cntr btn1(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_ctr1)); //버튼 채터링 방지
button_cntr btn2(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_ctr2)); //버튼 채터링 방지
button_cntr btn3(.clk(clk), .reset_p(reset_p), .btn(btn[3]), .btn_pedge(btn_ctr3)); //버튼 채터링 방지
wire btn_nedge1, btn_nedge2, btn_nedge3;
edge_detector_n ed1(
.clk(clk), .reset_p(reset_p), .cp(btn_ctr1),
.n_edge(btn_nedge1));
edge_detector_n ed2(
.clk(clk), .reset_p(reset_p), .cp(btn_ctr2),
.n_edge(btn_nedge2));
edge_detector_n ed3(
.clk(clk), .reset_p(reset_p), .cp(btn_ctr3),
.n_edge(btn_nedge3));
// reg [1:0] cnt_btn;
// always @(negedge clk or posedge reset_p)begin
// if(reset_p)begin
// cnt_btn = 1;
// end
// else if(btn_nedge)begin
// if(cnt_btn>=3)begin
// cnt_btn = 1;
// end
// cnt_btn = cnt_btn + 1;
// end
// end
reg [6:0] duty;
always @(posedge clk or posedge reset_p)begin
if(reset_p) duty = 1;
else if(btn_nedge1)begin
duty = 5;
end
else if(btn_nedge2)begin
duty = 14;
end
else if(btn_nedge3)begin
duty = 24;
end
end
pwm_Nstep_freq #(
.duty_step(200), //듀티폭을 100단계로 나눔
.pwm_freq(50)) //연결한 센서에 따라다름, 데이터시트에 필요한 주파수 있음
pwm_b(
.clk(clk),
.reset_p(reset_p),
.duty(duty),
.pwm(motor_pwm));
wire [15:0] duty_bcd;
bin_to_dec bcd_humi(.bin({6'b0, duty}), .bcd(duty_bcd));
fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(duty_bcd), .com(com), .seg_7(seg_7));
endmodule
module pwm_100step(
input clk, reset_p,
input [6:0] duty, //100단계 //듀티값은 시뮬레이션에서 정해주었다
output pwm);
parameter sys_clk_freq = 100_000_000;
parameter pwm_freq = 10_000;
parameter duty_step = 100;
parameter temp = sys_clk_freq / duty_step / pwm_freq;
parameter temp_half = temp / 2;
integer cnt_sysclk; //100까지 세는 카운터,7비트 필요
wire pwm_freqX100;
always @(negedge clk or posedge reset_p)begin //100분주, 10ns->1us
if(reset_p)cnt_sysclk = 0;
else begin
if(cnt_sysclk >= temp-1) cnt_sysclk = 0;
else cnt_sysclk = cnt_sysclk + 1;
end
end
assign pwm_freqX100 = (cnt_sysclk < temp_half) ? 1 : 0;
wire pwm_freqX100_nedge;
e