이 웹 페이지는 Smart Zynq 보드의 기능을 살펴보는 소규모 프로젝트 모음에 속하는 문서입니다.
소개
시중에는 SG90, MG90S, MG995, MG996R 등과 같이 간단하고 저렴한 DC 서보 모터가 여러 가지 있습니다. 이 모터들은 취미용 프로젝트, 특히 간단한 로봇을 만드는 데 사용됩니다. 이러한 모터는 단일 PWM 신호로 제어됩니다.
일부 모터는 일반적으로 180도 범위 내에서 제한적으로 회전합니다. 이러한 모터의 경우 PWM 신호가 모터의 각도 위치를 제어합니다. 다른 모터(흔히 "360도 모터"라고도 함)는 연속 회전이 가능합니다. 이러한 모터에서는 PWM 신호가 회전 속도와 방향을 제어합니다. 모터 모델에 따라 제한 각도 버전과 연속 회전 버전의 두 가지 변형이 있는 경우가 많다는 점에 유의하세요. 이런 종류의 모터를 구매할 때는 이 차이에 주의하는 것이 중요합니다.
이 튜토리얼에서는 Smart Zynq 보드에 서보 모터를 최대 8개까지 연결하고, 간단한 Linux 명령으로 이 모터들을 제어하는 방법을 보여줍니다. 또한 FPGA 내부에 레지스터 인터페이스를 구현하기 위해 Xillybus의 시크 가능 스트림(seekable stream)을 사용하는 방법도 함께 소개합니다.
설명과 사진을 단순하게 유지하기 위해 이 튜토리얼에서 보여주는 전기 배선은 모터 하나에만 적용됩니다. 모터를 추가로 연결하려면 여기에 보이는 것과 같은 부품에 와이어만 추가로 연결하면 됩니다.
모터의 전기 인터페이스
이러한 종류의 서보 모터 연결은 세 가닥의 와이어로 구성됩니다.
- 갈색 와이어: 접지(GND)에 연결.
- 빨간색 와이어: +5V 전원 공급 장치에 연결.
- 주황색 와이어: 제어 신호(PWM)에 연결.
PWM 신호의 주기는 20ms(50Hz)여야 합니다. 펄스가 high인 시간이 모터의 위치 또는 각속도를 제어합니다.
대부분의 데이터시트에 따르면 펄스 지속 시간은 1ms에서 2ms 사이여야 합니다. 일부 모터의 경우 이 정보는 정확하지 않습니다. 예를 들어, Tower Pro의 SG90(180도 회전 제한 모델)의 올바른 범위는 대략 500μs에서 2450μs입니다. 이 범위는 180도 회전에 해당합니다. 최대 2560μs의 펄스를 사용하면 모터를 약간 더 회전시킬 수 있습니다. 이 모터는 이보다 긴 펄스는 무시합니다.
따라서 모터가 반응하는 펄스 지속 시간의 범위를 탐색하려면 데이터시트에 의존하기보다는 모터로 직접 실험해 보는 것이 좋습니다. 이 모터들은 취미용으로 만들어졌기 때문에 사양 데이터가 정확하지 않을 수 있습니다.
전기 연결
우선, 서보 모터의 기계적 모터에 에너지를 공급하기 위해 별도의 전원 공급 장치가 필요합니다. 이 전원 공급 장치의 전압은 +5V입니다. Smart Zynq 보드에 공급되는 전원을 같이 사용하는 것은 권장하지 않습니다. 모터는 전압 레벨에 갑작스러운 변화를 일으키는 경우가 많기 때문입니다. 공급 전압이 불안정하면 Smart Zynq 보드가 불안정하게 동작할 수 있습니다.
주황색 와이어의 PWM 신호는 high일 때 전원 공급 장치와 거의 같은 전압을 가져야 합니다. 즉, 주황색 와이어와 접지 사이의 전압은 0이거나 약 +5V여야 합니다.
하지만 Smart Zynq 보드의 출력 전압은 high일 때 3.3V에 불과합니다. 이러한 출력을 모터의 주황색 와이어에 직접 연결하면 모터가 올바르게 반응할 가능성이 높습니다. 5V 전원을 기반으로 하는 디지털 회로는 일반적으로 2.5V 이상의 전압을 논리 '1'로 간주하기 때문입니다.
그렇다고 해서 PWM 신호의 전압이 3.3V일 때 모터가 안정적으로 동작할지는 확실하지 않습니다. 권장되는 해결책은 Smart Zynq 보드의 출력을 0V 또는 5V로 변환해 주는 전압 레벨 시프터(voltage level shifter)를 사용하는 것입니다. 이 튜토리얼에서는 TXS0108E 칩이 장착된 소형 보드를 사용합니다. 이 보드는 칩의 핀을 그대로 노출시켜 일반 와이어를 칩에 연결할 수 있게 해줍니다.
이 사진은 서보 모터 하나를 제어하기 위한 전기 연결 구성을 보여줍니다. 사진 아래쪽의 Smart Zynq 보드는 듀폰(Dupont) 와이어를 통해 TXS0108E 보드에 연결되어 있습니다. TXS0108E 보드의 반대쪽에는 서보 모터의 PWM 입력과 모터용 별도 전원 공급 장치(표준 전원 플러그용 어댑터를 통해)가 연결됩니다. 모터의 빨간색 및 갈색 와이어도 이 보드에 납땜되어 모터에 전원 공급 전압을 인가합니다.
이 사진은 TXS0108E 보드와 그 연결부를 더 가까이서 본 모습입니다.
OE와 VA 사이의 빨간색 와이어에 주목하세요. 이 와이어는 칩의 출력 활성화(output enable)가 high가 되도록 보장합니다.
Smart Zynq 핀 헤더에 대한 연결은 다음 사진에 나와 있습니다. 여기서 모터는 J6/1로 제어됩니다.
이 표는 관련된 네 부분 사이의 연결을 요약한 것입니다.
| TXS0108E 보드 핀 | Smart Zynq 핀 | 모터 와이어 | +5V 전원 공급 장치 | 설명 |
|---|---|---|---|---|
| VA | J6/37(3.3V) | -- | -- | OE에도 연결됨 |
| A4 | J6/1 | -- | -- | |
| OE | -- | -- | -- | VA에 연결됨 |
| GND | J6/35(GND) | 갈색 와이어 | GND | |
| VB | -- | 빨간색 와이어 | +5V | |
| B4 | -- | 주황색 와이어 | -- |
TXS0108E 칩은 A4 입력에서 B4 출력으로 전압 레벨을 시프트합니다. 칩에는 추가로 7쌍의 핀이 더 있습니다. 이 튜토리얼에서는 A4/B4 쌍을 임의로 선택했습니다. 8개의 모터를 제어하려면 이 칩의 8개 핀 쌍을 모두 사용할 수 있습니다. 다만 여기서는 8개 모터 제어에 필요한 와이어 수 때문에 사진을 이해하기 어려워지므로 이 구성을 보여주지 않습니다.
J6 핀 헤더를 찾으려면 Smart Zynq 보드 뒷면에서 "Bank 33 VCCIO Vadj"라고 적힌 부분을 찾으세요. 이 표시 근처에 있는 핀 열이 이 튜토리얼에서 사용하는 핀 헤더입니다. 따라서 J6/1은 HDMI 커넥터에 가장 가까운 핀입니다.
Smart Zynq 보드는 USB 포트 중 하나를 통해 별도의 전원 공급 장치에 연결된다는 점에 유의하세요. 또한 J6 핀 헤더의 마지막 핀은 5V이므로 이 핀에는 와이어를 연결하지 마세요.
Vivado 프로젝트 준비
데모 번들(demo bundle)의 zip 파일(부트 파티션 키트)에서 새 Vivado 프로젝트를 만드세요. 텍스트 편집기로 verilog/src/xillydemo.v를 여세요. 코드에서 "PART 4"라고 표시된 부분을 삭제하고, 그 자리에 아래 코드를 삽입하세요.
assign user_r_mem_8_empty = 0;
assign user_r_mem_8_eof = 0;
assign user_w_mem_8_full = 0;
reg [7:0] reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7;
always @(posedge bus_clk)
if (user_w_mem_8_wren)
case (user_mem_8_addr[2:0])
0: reg0 <= user_w_mem_8_data;
1: reg1 <= user_w_mem_8_data;
2: reg2 <= user_w_mem_8_data;
3: reg3 <= user_w_mem_8_data;
4: reg4 <= user_w_mem_8_data;
5: reg5 <= user_w_mem_8_data;
6: reg6 <= user_w_mem_8_data;
7: reg7 <= user_w_mem_8_data;
endcase
reg [7:0] mem_8_data_reg;
assign user_r_mem_8_data = mem_8_data_reg;
always @(posedge bus_clk)
if (user_r_mem_8_rden)
case (user_mem_8_addr[2:0])
0: mem_8_data_reg <= reg0;
1: mem_8_data_reg <= reg1;
2: mem_8_data_reg <= reg2;
3: mem_8_data_reg <= reg3;
4: mem_8_data_reg <= reg4;
5: mem_8_data_reg <= reg5;
6: mem_8_data_reg <= reg6;
7: mem_8_data_reg <= reg7;
endcase
servo_pwm servo_pwm_i [7:0]
(
.clk(bus_clk),
.rst(quiesce),
.pwm_signal(J6[7:0]),
.pwm_width( { reg7, reg6, reg5, reg4, reg3, reg2, reg1, reg0 } )
);
업데이트된 xillydemo.v는 이 링크에서 내려받을 수도 있습니다.
프로젝트의 verilog/src/ 디렉터리에 servo_pwm.v라는 새 파일을 만들고 다음 내용을 넣으세요(복사-붙여넣기 또는 이 링크에서 내려받기).
module servo_pwm
(
input clk,
input rst,
output reg pwm_signal,
input [7:0] pwm_width
);
reg [9:0] enable_count;
reg enable;
reg [10:0] pwm_count;
reg [8:0] threshold;
always @(posedge clk)
begin
if (enable_count == 999)
begin
enable_count <= 0;
enable <= 1;
end
else
begin
enable_count <= enable_count + 1;
enable <= 0;
end
threshold <= pwm_width + 50; // Add 0.5 ms to PWM width
if (enable)
begin
if (pwm_count == 1999)
pwm_count <= 0;
else
pwm_count <= pwm_count + 1;
pwm_signal <= (pwm_count < threshold);
end
if (rst)
begin
enable_count <= 0;
pwm_count <= 0;
pwm_signal <= 0;
end
end
endmodule
그런 다음 이 파일을 Vivado 프로젝트에 추가합니다. File > Add Sources…를 클릭하고 "Add or create design sources"를 선택합니다. 그런 다음 Next를 클릭하세요. "Add Files" 버튼을 클릭하고 verilog/src/ 디렉터리에서 "servo_pwm.v" 파일을 선택합니다. 그런 다음 "Finish" 버튼을 클릭하세요.
업데이트된 프로젝트에서 데모 번들용 비트스트림(bitstream) 파일을 만들 때와 같은 방법으로 비트스트림 파일을 만드세요. 그리고 비트스트림 파일을 같은 방법으로 TF 카드에 복사하세요(기존 xillydemo.bit 파일을 이 프로젝트에서 만든 파일로 덮어쓰세요).
서보 모터 제어
이 섹션에 나오는 단계들은 Smart Zynq 보드의 shell 프롬프트에서 실행해야 합니다.
먼저 디렉터리를 변경하고 Xillybus용 데모 애플리케이션을 컴파일하세요.
# cd ~/xillybus/demoapps/ # make gcc -g -Wall -O3 memwrite.c -o memwrite gcc -g -Wall -O3 memread.c -o memread gcc -g -Wall -O3 streamread.c -o streamread gcc -g -Wall -O3 streamwrite.c -o streamwrite gcc -g -Wall -O3 -pthread fifo.c -o fifo
FPGA에는 서보 모터를 제어하기 위한 8개의 레지스터가 있습니다. 처음에는 그 값이 모두 0입니다. 다음 명령으로 이 값을 10진수 형식으로 출력할 수 있습니다.
# hexdump -v -n 8 -e '8/1 "%u " "\n" ' /dev/xillybus_mem_8 0 0 0 0 0 0 0 0
더 간단한 hexdump 명령으로 같은 값을 16진수 형식으로 출력할 수도 있습니다.
# hexdump -v -n 8 -C /dev/xillybus_mem_8 00000000 00 00 00 00 00 00 00 00 |........| 00000008
memwrite 프로그램(xillybus/demoapps/ 디렉터리 안에 있음)으로 모터를 제어할 수 있습니다. 예를 들어, 다음 명령은 레지스터 0의 값을 120으로 변경합니다.
# ./memwrite /dev/xillybus_mem_8 0 120
이 명령은 J6/1에 연결된 모터(위 사진에 나온 것처럼)의 회전 위치 또는 속도를 변경합니다.
PWM 펄스의 폭은 이 레지스터의 값에 따라 다음 공식으로 결정됩니다.
t = 500 + (x * 10)
이 공식에서 t는 마이크로초 단위이고, x는 해당 레지스터의 값입니다. 따라서 펄스의 기본 폭은 500μs입니다(x=0일 때). 위 명령은 레지스터 값을 120으로 변경했으므로, 펄스 폭은 1700μs로 바뀌었습니다.
각 레지스터는 1바이트로 구성되므로 값의 범위는 0부터 255까지입니다. 따라서 각 모터의 펄스 폭은 500μs에서 3050μs 사이로 변경할 수 있습니다. 이 범위 중 상당 부분은 대부분의 모터에서 허용되지 않는 값이라는 점에 유의하세요. 각 모터로 여러 값을 시험해 보고 어떻게 반응하는지 확인하는 것이 가장 좋습니다.
다른 핀에 연결된 모터를 제어하려면 다른 레지스터에 값을 쓰면 됩니다. 예를 들어 모터가 Smart Zynq의 J6/4에 연결되어 있다면 다음과 같은 명령을 사용할 수 있습니다.
# ./memwrite /dev/xillybus_mem_8 3 50
이 명령은 펄스 폭을 1000μs(즉, 1ms)로 변경합니다.
이 두 명령을 실행한 후 값을 다시 읽어 변경 사항을 확인할 수 있습니다.
# hexdump -v -n 8 -e '8/1 "%u " "\n" ' /dev/xillybus_mem_8 120 0 0 50 0 0 0 0
이 출력은 값을 10진수 형식으로 보여줍니다. 16진수 형식은 다음과 같습니다.
# hexdump -v -n 8 -C /dev/xillybus_mem_8 00000000 78 00 00 32 00 00 00 00 |x..2....| 00000008
memwrite의 동작 방식
memwrite의 소스 코드는 ~/xillybus/demoapps/ 디렉터리의 memwrite.c 파일입니다. 이 프로그램은 Xillybus의 시크 가능 스트림(seekable stream)을 이용해 레지스터에 접근하는 방법을 보여줍니다. 이 프로그램의 동작 방식을 이해하고 싶다면 소스 코드를 살펴보는 것이 좋습니다. 여기서는 중요한 두 부분만 살펴보겠습니다.
프로그램은 첫 번째 인수로 주어진 이름의 파일을 엽니다. 이 파일의 파일 디스크립터는 @fd 변수에 저장됩니다.
그런 다음 프로그램은 다음과 같이 lseek() 함수를 호출합니다.
if (lseek(fd, address, SEEK_SET) < 0) {
perror("Failed to seek");
exit(1);
}
@address 변수에는 프로그램의 두 번째 인수가 들어 있습니다. 위에서 memwrite를 사용한 첫 번째 예제에서 이것은 0이었습니다. 두 번째 예제에서는 3이었습니다. 일반적으로 lseek()는 파일 내부의 특정 위치로 이동하는 데 사용됩니다. 이 경우 파일의 위치는 접근하려는 레지스터의 번호와 같습니다.
다음으로 프로그램은 allwrite() 함수를 호출합니다.
allwrite(fd, &data, 1);
이 호출은 파일에 1바이트를 씁니다. allwrite()는 memwrite.c에 정의되어 있으며, 잘 알려진 write() 함수와 비슷합니다. 따라서 allwrite() 대신 다음과 같이 써도 거의 같습니다.
write(fd, &data, 1);
차이점은 write()는 데이터가 실제로 쓰였는지 보장하지 않지만, allwrite()는 데이터가 파일에 쓰이는 것을 보장한다는 것입니다.
이 경우 allwrite() 함수 호출은 프로그램의 세 번째 인수 값을 씁니다. 즉, 이것이 레지스터의 새 값입니다.
호스트 측 Xillybus API에 대한 자세한 내용은 이 주제에 관한 문서, 특히 섹션 6.1을 참조하세요.
Verilog 코드 설명
먼저 xillydemo.v의 Verilog 코드가 FPGA 내부에 레지스터를 어떻게 구현하는지 보여드리겠습니다. 그다음 PWM 펄스의 구현을 설명하겠습니다.
하드웨어 레지스터 구현을 위한 Xillybus API는 Xillybus FPGA 설계 가이드에 자세히 설명되어 있습니다.
레지스터와 관련된 와이어들은 원래 xillydemo.v 파일에 이미 정의되어 있었습니다.
// Wires related to /dev/xillybus_mem_8
wire user_r_mem_8_rden;
wire user_r_mem_8_empty;
wire [7:0] user_r_mem_8_data;
wire user_r_mem_8_eof;
wire user_r_mem_8_open;
wire user_w_mem_8_wren;
wire user_w_mem_8_full;
wire [7:0] user_w_mem_8_data;
wire user_w_mem_8_open;
wire [4:0] user_mem_8_addr;
wire user_mem_8_addr_update;
이 와이어들은 Xillybus IP 코어(IP core)의 인스턴스화(instantiation) 일부로 연결됩니다.
xillybus xillybus_ins (
// Ports related to /dev/xillybus_mem_8
// FPGA to CPU signals:
.user_r_mem_8_rden(user_r_mem_8_rden),
.user_r_mem_8_empty(user_r_mem_8_empty),
.user_r_mem_8_data(user_r_mem_8_data),
.user_r_mem_8_eof(user_r_mem_8_eof),
.user_r_mem_8_open(user_r_mem_8_open),
// CPU to FPGA signals:
.user_w_mem_8_wren(user_w_mem_8_wren),
.user_w_mem_8_full(user_w_mem_8_full),
.user_w_mem_8_data(user_w_mem_8_data),
.user_w_mem_8_open(user_w_mem_8_open),
// Address signals:
.user_mem_8_addr(user_mem_8_addr),
.user_mem_8_addr_update(user_mem_8_addr_update),
[ ... ]
.quiesce(quiesce)
);
다음으로, 이 프로젝트를 위해 특별히 추가된 Verilog 코드를 살펴보겠습니다. 먼저 다음 부분이 있는데, 이 부분은 호스트와의 데이터 교환에 흐름 제어(flow control)가 적용되지 않도록 보장합니다.
assign user_r_mem_8_empty = 0;
assign user_r_mem_8_eof = 0;
assign user_w_mem_8_full = 0;
그다음 8개의 레지스터가 선언됩니다.
reg [7:0] reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7;
다음 부분은 레지스터 쓰기 동작을 구현합니다. @user_w_mem_8_wren이 high이면 레지스터 중 하나에 새 값이 쓰입니다. @user_mem_8_addr은 어떤 레지스터가 영향을 받는지 선택합니다. @user_w_mem_8_wren과 @user_mem_8_addr은 모두 Xillybus IP 코어의 출력입니다.
always @(posedge bus_clk)
if (user_w_mem_8_wren)
case (user_mem_8_addr[2:0])
0: reg0 <= user_w_mem_8_data;
1: reg1 <= user_w_mem_8_data;
2: reg2 <= user_w_mem_8_data;
3: reg3 <= user_w_mem_8_data;
4: reg4 <= user_w_mem_8_data;
5: reg5 <= user_w_mem_8_data;
6: reg6 <= user_w_mem_8_data;
7: reg7 <= user_w_mem_8_data;
endcase
그다음에는 레지스터에서 값을 읽을 수 있는 기능이 구현됩니다. @user_r_mem_8_rden이 high이면 @user_r_mem_8_data가 업데이트되어 레지스터 중 하나의 값을 담게 됩니다. @user_mem_8_addr은 어떤 레지스터의 값을 읽을지 선택합니다.
reg [7:0] mem_8_data_reg;
assign user_r_mem_8_data = mem_8_data_reg;
always @(posedge bus_clk)
if (user_r_mem_8_rden)
case (user_mem_8_addr[2:0])
0: mem_8_data_reg <= reg0;
1: mem_8_data_reg <= reg1;
2: mem_8_data_reg <= reg2;
3: mem_8_data_reg <= reg3;
4: mem_8_data_reg <= reg4;
5: mem_8_data_reg <= reg5;
6: mem_8_data_reg <= reg6;
7: mem_8_data_reg <= reg7;
endcase
마지막으로 servo_pwm 모듈의 인스턴스화(instantiation)가 이루어집니다.
servo_pwm servo_pwm_i [7:0]
(
.clk(bus_clk),
.rst(quiesce),
.pwm_signal(J6[7:0]),
.pwm_width( { reg7, reg6, reg5, reg4, reg3, reg2, reg1, reg0 } )
);
이 인스턴스화는 servo_pwm의 복사본 8개를 만든다는 점에 유의하세요. 첫 번째 복사본은 reg0과 J6[0]에 연결되고, 두 번째 복사본은 reg1과 J6[1]에 연결되는 식입니다.
이제 servo_pwm 모듈을 살펴보겠습니다. 모듈은 포트와 레지스터 선언으로 시작합니다.
module servo_pwm
(
input clk,
input rst,
output reg pwm_signal,
input [7:0] pwm_width
);
reg [9:0] enable_count;
reg enable;
reg [10:0] pwm_count;
reg [8:0] threshold;
앞서 @pwm_width는 호스트의 memwrite 명령으로 업데이트되는 레지스터 값을 받는다는 것을 기억하세요. @pwm_signal은 Smart Zynq 보드의 핀에 연결되므로, 이 신호가 주황색 와이어를 통해 모터에 도달합니다.
이 모듈의 다음 부분은 스트로브(strobe) 신호를 구현합니다. @enable은 1000 클록 사이클마다 한 번 high가 됩니다. @clk의 주파수는 100MHz입니다. 따라서 @enable은 10μs마다 한 번 high가 됩니다.
always @(posedge clk)
begin
if (enable_count == 999)
begin
enable_count <= 0;
enable <= 1;
end
else
begin
enable_count <= enable_count + 1;
enable <= 0;
end
앞서 언급했듯이, 0부터 255까지의 레지스터 값은 500μs부터 3050μs까지의 PWM 펄스 폭에 해당합니다(펄스 폭을 마이크로초 단위로 나타내는 수식은 500 + x * 10이었습니다).
따라서 @threshold의 수식은 다음과 같습니다.
threshold <= pwm_width + 50; // Add 0.5 ms to PWM width
이 레지스터는 PWM 펄스가 high인 동안(즉, PWM 신호가 high인 동안) @enable이 high가 되는 횟수를 담고 있습니다. 이 수식은 레지스터 값이 PWM 펄스 길이를 10μs 단위로 나타낸 것임을 반영합니다. 레지스터 값에 50을 더하는 것은 최소 펄스 폭인 500μs에 해당합니다.
이제 @enable은 클록 활성화(clock enable) 신호로 사용됩니다.
if (enable)
begin
if (pwm_count == 1999)
pwm_count <= 0;
else
pwm_count <= pwm_count + 1;
pwm_signal <= (pwm_count < threshold);
end
@pwm_count는 0부터 1999까지 센 다음 다시 0부터 시작합니다. 이 카운터는 10μs마다 한 번씩만 값이 바뀌므로, 한 바퀴를 도는 데 20000μs = 20ms가 걸립니다. 즉, PWM 펄스의 반복 주기는 요구사항대로 20ms입니다.
@pwm_signal은 @pwm_count가 @threshold보다 작은 동안 high입니다. 이렇게 해서 레지스터가 펄스 폭을 제어합니다.
이 모듈의 마지막 부분은 일부 레지스터를 리셋하는 역할을 합니다.
if (rst)
begin
enable_count <= 0;
pwm_count <= 0;
pwm_signal <= 0;
end
end
endmodule
@rst가 high이면 이 부분이 위에서 작성된 모든 내용을 무시하고 덮어씁니다. 따라서 @rst는 리셋 신호의 기능을 합니다.
Verilog 코드와 실제 핀의 관계
위의 Verilog 코드는 J6이라는 inout 포트를 사용합니다. 그런데 이 포트의 연결이 어떻게 핀 헤더에 도달할까요? 답은 xillydemo.xdc에 있습니다. 이 파일은 비트스트림(bitstream)을 생성하는 Vivado 프로젝트의 일부입니다("vivado-essentials" 디렉터리).
xillydemo.xdc에는 FPGA가 전자 부품으로서 올바르게 동작하기 위해 필요한 다양한 정보가 들어 있습니다. 그중에서 이 파일에는 다음과 같은 행이 포함됩니다.
[ ... ]
## J6 on board (BANK33 VADJ)
set_property PACKAGE_PIN U22 [get_ports {J6[0]}]; #J6/1 = IO_B33_LN2
set_property PACKAGE_PIN T22 [get_ports {J6[1]}]; #J6/2 = IO_B33_LP2
set_property PACKAGE_PIN W22 [get_ports {J6[2]}]; #J6/3 = IO_B33_LN3
set_property PACKAGE_PIN V22 [get_ports {J6[3]}]; #J6/4 = IO_B33_LP3
set_property PACKAGE_PIN Y21 [get_ports {J6[4]}]; #J6/5 = IO_B33_LN9
set_property PACKAGE_PIN Y20 [get_ports {J6[5]}]; #J6/6 = IO_B33_LP9
set_property PACKAGE_PIN AB22 [get_ports {J6[6]}]; #J6/7 = IO_B33_LN7
set_property PACKAGE_PIN AA22 [get_ports {J6[7]}]; #J6/8 = IO_B33_LP7
[ ... ]
첫 번째 행은 J6[0] 포트를 U22에 연결해야 한다고 말합니다. U22는 FPGA 물리적 패키지의 위치입니다. Smart Zynq의 회로도에 따르면 이 FPGA 핀은 핀 헤더의 첫 번째 핀에 연결됩니다. 다른 포트들의 위치도 같은 방식으로 정의됩니다.
요약
이 튜토리얼에서는 Smart Zynq 보드와 Xillybus의 레지스터 API를 사용하여 서보 모터를 제어하는 방법을 보여드렸습니다. 이 응용을 위한 호스트 프로그램과 Verilog 코드를 모두 살펴보았습니다.
이 튜토리얼은 레지스터를 이용해 하드웨어를 제어해야 하는 다른 응용 프로젝트의 기반으로도 사용할 수 있습니다.


