산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Industrial programming >> VHDL

FPGA 핀에서 PWM을 사용하는 RC 서보 컨트롤러

무선 제어(RC) 모델 서보는 일반적으로 애호가 모델 비행기, 자동차 및 보트에 사용되는 작은 액추에이터입니다. 이를 통해 운전자는 무선 링크를 통해 원격으로 차량을 제어할 수 있습니다. RC 모델은 오랫동안 사용되어 왔기 때문에 사실상의 표준 인터페이스는 디지털 방식이 아니라 PWM(펄스 폭 변조)입니다.

다행히 FPGA가 출력 핀에 가할 수 있는 정확한 타이밍으로 PWM을 구현하는 것은 쉽습니다. 이 기사에서는 PWM을 사용하는 모든 RC 서보에서 작동하는 일반 서보 컨트롤러를 만들 것입니다.

RC 서보의 PWM 제어 작동 방식

이전 블로그 게시물에서 이미 PWM을 다루었지만 RC 서보를 제어하는 ​​데 해당 모듈을 사용할 수 없습니다. 문제는 RC 서보가 PWM 펄스가 그렇게 자주 도착할 것으로 예상하지 않는다는 것입니다. 전체 듀티 사이클은 고려하지 않고 하이 기간의 지속 시간만 고려합니다.

위의 그림은 PWM 신호의 타이밍이 어떻게 작동하는지 보여줍니다.

펄스 사이의 이상적인 간격은 20ms이지만 지속 시간은 덜 중요합니다. 20ms는 50Hz의 PWM 주파수로 변환됩니다. 이것은 서보가 20ms마다 새로운 위치 명령을 받는다는 것을 의미합니다.

펄스가 RC 서보에 도달하면 높은 기간의 기간을 샘플링합니다. 이 간격이 서보의 각도 위치로 직접 변환되기 때문에 타이밍이 중요합니다. 대부분의 서보는 펄스 폭이 1ms에서 2ms 사이로 변할 것으로 예상하지만 정해진 규칙은 없습니다.

VHDL 서보 컨트롤러

PWM을 사용하여 모든 RC 서보와 함께 작동하도록 구성할 수 있는 일반 VHDL 서보 컨트롤러 모듈을 만들 것입니다. 그렇게 하려면 일반 입력 값을 기반으로 몇 가지 계산을 수행해야 합니다.

RC 서보에서 사용하는 PWM 주파수는 FPGA의 메가헤르츠 스위칭 주파수에 비해 느립니다. 클록 사이클의 정수 계산은 PWM 펄스 길이의 충분한 정밀도를 제공합니다. 그러나 클럭 주파수가 펄스 주기와 완벽하게 일치하지 않으면 작은 반올림 오류가 발생합니다.

실제를 사용하여 계산을 수행합니다. (부동 소수점) 숫자이지만 결국 결과를 정수로 변환해야 합니다. 대부분의 프로그래밍 언어와 달리 VHDL은 부동 소수점을 가장 가까운 정수로 반올림하지만 절반 숫자(0.5, 1.5 등)에 대한 동작은 정의되지 않습니다. 시뮬레이터 또는 합성 도구는 어느 쪽이든 반올림하도록 선택할 수 있습니다.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.round;

플랫폼 간 일관성을 보장하기 위해 라운드를 사용합니다. math_real의 함수 라이브러리는 항상 0에서 반올림합니다. 위의 코드는 math_real을 사용하여 VHDL 모듈의 가져오기를 보여줍니다. 라이브러리가 강조 표시되었습니다.

이 프로젝트에 대한 전체 코드가 필요한 경우 아래 양식에 이메일 주소를 입력하여 다운로드할 수 있습니다. 몇 분 안에 VHDL 코드, ModelSim 프로젝트 및 iCEstick FPGA 보드용 Lattice iCEcube2 프로젝트가 포함된 Zip 파일을 받게 됩니다.

제네릭이 있는 서보 모듈 엔티티

일반 상수를 사용하여 PWM 지원 RC 서보에서 작동하는 모듈을 만들 수 있습니다. 아래 코드는 서보 모듈의 엔티티를 보여줍니다.

첫 번째 상수는 실제 유형으로 제공된 FPGA의 클록 주파수이며 pulse_hz PWM 출력이 펄스되는 빈도를 지정하고 다음 두 상수는 최소 및 최대 위치에서 펄스 폭을 마이크로초 단위로 설정합니다. 최종 일반 상수는 끝점을 포함하여 최소 및 최대 위치 사이에 몇 단계가 있는지 정의합니다.

entity servo is
  generic (
    clk_hz : real;
    pulse_hz : real; -- PWM pulse frequency
    min_pulse_us : real; -- uS pulse width at min position
    max_pulse_us : real; -- uS pulse width at max position
    step_count : positive -- Number of steps from min to max
  );
  port (
    clk : in std_logic;
    rst : in std_logic;
    position : in integer range 0 to step_count - 1;
    pwm : out std_logic
  );
end servo;

클럭 및 리셋 외에도 포트 선언은 단일 입력 및 단일 출력 신호로 구성됩니다.

위치 신호는 서보 모듈에 대한 제어 입력입니다. 0으로 설정하면 모듈은 min_pulse_us를 생성합니다. 마이크로초 길이의 PWM 펄스. 위치일 때 값이 가장 높으면 max_pulse_us가 생성됩니다. 긴 펄스.

pwm 출력은 외부 RC 서보에 대한 인터페이스입니다. FPGA 핀을 통과해야 하며 일반적으로 노란색 또는 흰색 와이어인 서보의 "신호" 입력에 연결해야 합니다. 레벨 변환기를 사용해야 할 수도 있습니다. 대부분의 FPGA는 3.3V 로직 레벨을 사용하지만 대부분의 RC 서보는 5V에서 실행됩니다.

선언 영역

서보 모듈의 선언 영역 상단에서 몇 가지 상수를 계산하는 데 사용할 함수를 선언하고 있습니다. cycles_per_us 아래 표시된 함수는 us_count를 측정하기 위해 계산해야 하는 가장 가까운 클록 주기 수를 반환합니다. 마이크로초.

function cycles_per_us (us_count : real) return integer is
begin
  return integer(round(clk_hz / 1.0e6 * us_count));
end function;

함수 바로 아래에 도우미 상수를 선언합니다. 이 상수는 제네릭에 따라 출력 PWM의 타이밍을 만드는 데 사용할 것입니다.

먼저 최소 및 최대 마이크로초 값을 클록 주기의 절대 수로 변환합니다. min_countmax_count . 그런 다음 둘 사이의 범위를 마이크로초 단위로 계산하여 step_us를 도출합니다. , 각 선형 위치 단계 간의 지속 시간 차이. 마지막으로 마이크로초를 실수로 변환합니다. 고정된 수의 시계 주기로 값:cycles_per_step .

constant min_count : integer := cycles_per_us(min_pulse_us);
constant max_count : integer := cycles_per_us(max_pulse_us);
constant min_max_range_us : real := max_pulse_us - min_pulse_us;
constant step_us : real := min_max_range_us / real(step_count - 1);
constant cycles_per_step : positive := cycles_per_us(step_us);

다음으로 PWM 카운터를 선언합니다. 이 정수 신호는 pulse_hz를 래핑하는 자유 실행 카운터입니다. 매초마다. 이것이 제네릭에 주어진 PWM 주파수를 달성하는 방법입니다. 아래 코드는 계산해야 하는 클록 사이클 수를 계산하는 방법과 정수 범위를 선언하기 위해 상수를 사용하는 방법을 보여줍니다.

constant counter_max : integer := integer(round(clk_hz / pulse_hz)) - 1;
signal counter : integer range 0 to counter_max;

signal duty_cycle : integer range 0 to max_count;

마지막으로 duty_cycle이라는 카운터의 복사본을 선언합니다. . 이 신호는 PWM 출력의 하이 기간 길이를 결정합니다.

클록 사이클 계산

아래 코드는 자유 실행 카운터를 구현하는 프로세스를 보여줍니다.

COUNTER_PROC : process(clk)
begin
  if rising_edge(clk) then
    if rst = '1' then
      counter <= 0;

    else
      if counter < counter_max then
        counter <= counter + 1;
      else
        counter <= 0;
      end if;

    end if;
  end if;
end process;

서명된과 달리 및 서명되지 않음 자체 래핑 유형의 경우 카운터가 최대 값에 도달하면 명시적으로 0을 할당해야 합니다. counter_max에 이미 최대값이 정의되어 있기 때문에 상수, If-Else 구문으로 쉽게 달성할 수 있습니다.

PWM 출력 프로세스

PWM 출력이 높은 값인지 낮은 값인지 결정하기 위해 카운터를 비교합니다. 및 의무 주기 신호. 카운터가 듀티 사이클보다 작으면 출력은 높은 값입니다. 따라서 duty_cycle의 값은 신호는 PWM 펄스의 지속 시간을 제어합니다.

PWM_PROC : process(clk)
begin
  if rising_edge(clk) then
    if rst = '1' then
      pwm <= '0';

    else
      pwm <= '0';

      if counter < duty_cycle then
        pwm <= '1';
      end if;

    end if;
  end if;
end process;

듀티 사이클 계산

듀티 사이클은 min_count보다 작아서는 안 됩니다. 시계 주기는 min_pulse_us에 해당하는 값이기 때문입니다. 일반 입력. 따라서 min_count를 사용합니다. duty_cycle의 재설정 값으로 아래와 같이 신호를 보냅니다.

DUTY_CYCLE_PROC : process(clk)
begin
  if rising_edge(clk) then
    if rst = '1' then
      duty_cycle <= min_count;

    else
      duty_cycle <= position * cycles_per_step + min_count;

    end if;
  end if;
end process;

모듈이 재설정되지 않은 경우 입력 위치의 함수로 듀티 사이클을 계산합니다. 주기당_단계 상수는 가장 가까운 정수로 반올림된 근사치입니다. 따라서 이 상수에 대한 오류는 최대 0.5일 수 있습니다. 명령된 위치를 곱하면 오류가 커집니다. 그러나 FPGA 클럭이 PWM 주파수보다 훨씬 빠르기 때문에 눈에 띄지 않을 것입니다.

서보 테스트벤치

RC 서보 모듈을 테스트하기 위해 파형에서 서보 모듈의 동작을 관찰할 수 있는 수동 검사 테스트벤치를 만들었습니다. ModelSim이 컴퓨터에 설치되어 있는 경우 아래 양식에 이메일 주소를 입력하여 예제 시뮬레이션 프로젝트를 다운로드할 수 있습니다.

시뮬레이션 상수

시뮬레이션 시간을 단축하기 위해 테스트벤치에서 1MHz의 낮은 클록 주파수를 지정합니다. 또한 걸음 수를 5로 설정했는데 DUT(테스트 대상 장치)가 작동하는 것을 확인하기에 충분합니다.

아래 코드는 테스트벤치에 정의된 모든 시뮬레이션 상수를 보여줍니다.

constant clk_hz : real := 1.0e6;
constant clk_period : time := 1 sec / clk_hz;

constant pulse_hz : real := 50.0;
constant pulse_period : time := 1 sec / pulse_hz;
constant min_pulse_us : real := 1000.0;
constant max_pulse_us : real := 2000.0;
constant step_count : positive := 5;

DUT 신호

테스트벤치에 선언된 신호는 DUT의 입력 및 출력과 일치합니다. 아래 코드에서 알 수 있듯이 clk 그리고 첫 번째 초기값 '1'을 나타냅니다. 이것은 시계가 높은 위치에서 시작하고 모듈이 처음에 재설정된다는 것을 의미합니다.

signal clk : std_logic := '1';
signal rst : std_logic := '1';
signal position : integer range 0 to step_count - 1;
signal pwm : std_logic;

테스트벤치에서 클럭 신호를 생성하기 위해 아래에 표시된 일반 단일 라이너 프로세스를 사용합니다.

clk <= not clk after clk_period / 2;

DUT 인스턴스화

클럭 자극 라인 아래에서 DUT를 인스턴스화합니다. 이름이 일치하는 제네릭에 테스트벤치 상수를 할당합니다. 또한 DUT의 포트 신호를 테스트벤치의 로컬 신호에 매핑합니다.

DUT : entity work.servo(rtl)
generic map (
  clk_hz => clk_hz,
  pulse_hz => pulse_hz,
  min_pulse_us => min_pulse_us,
  max_pulse_us => max_pulse_us,
  step_count => step_count
)
port map (
  clk => clk,
  rst => rst,
  position => position,
  pwm => pwm
);

테스트벤치 시퀀서

DUT에 대한 자극을 제공하기 위해 아래에 표시된 시퀀서 프로세스를 사용합니다. 먼저 DUT를 재설정합니다. 그런 다음 For 루프를 사용하여 가능한 모든 입력 위치(이 경우 5개)를 반복합니다. 마지막으로 시뮬레이터 콘솔에 메시지를 출력하고 VHDL-2008 finish를 호출하여 테스트벤치를 종료합니다. 절차.

SEQUENCER : process
begin
  wait for 10 * clk_period;
  rst <= '0';

  wait for pulse_period;

  for i in 0 to step_count - 1 loop
    position <= i;
    wait for pulse_period;
  end loop;

  report "Simulation done. Check waveform.";
  finish;
end process;

서보 시뮬레이션 파형

아래 파형은 Testbench가 ModelSim에서 생성하는 파형의 일부를 보여줍니다. 테스트 벤치가 위치 입력을 주기적으로 변경하고 DUT가 PWM 펄스를 생성하여 응답하는 것을 볼 수 있습니다. PWM 출력은 가장 낮은 카운터 값에서만 높음에 유의하십시오. 이것이 PWM_PROC 프로세스의 작업입니다.

프로젝트 파일을 다운로드하면 Zip 파일에 있는 지침에 따라 시뮬레이션을 재현할 수 있습니다.

FPGA 구현의 예

다음으로 원하는 것은 FPGA에서 설계를 구현하여 실제 RC 서보인 TowerPro SG90을 제어하도록 하는 것입니다. 이를 위해 Lattice iCEstick FPGA 개발 보드를 사용할 것입니다. 제가 초급 VHDL 과정과 고급 FPGA 과정에서 사용하는 것과 같은 보드입니다.

Lattice iCEstick이 있는 경우 아래 양식을 사용하여 iCEcube2 프로젝트를 다운로드할 수 있습니다.

그러나 서보 모듈은 단독으로 작동할 수 없습니다. FPGA에서 작동하려면 몇 가지 지원 모듈이 필요합니다. 최소한 입력 위치를 변경할 무언가가 필요하고 리셋 모듈도 있어야 합니다.

서보 모션을 더 흥미롭게 만들기 위해 이전 기사에서 다룬 Sine ROM 모듈을 사용할 것입니다. 앞에서 언급한 기사의 Counter 모듈과 함께 Sine ROM은 부드러운 좌우 이동 패턴을 생성합니다.

여기에서 사인 ROM 모듈에 대해 읽어보세요.
블록 RAM에 저장된 사인파를 사용하여 숨쉬는 LED 효과를 만드는 방법

아래 데이터 흐름도는 하위 모듈과 연결 방법을 보여줍니다.

상단 모듈 엔티티

상단 모듈의 엔터티는 클럭 및 리셋 입력과 RC 서보를 제어하는 ​​PWM 출력으로 구성됩니다. pwm을 라우팅했습니다. Lattice iCE40 HX1K FPGA의 119번 핀에 신호를 보냅니다. 가장 왼쪽 헤더 랙의 가장 왼쪽 핀입니다. 시계는 iCEstick의 온보드 발진기에서 나오며 첫 번째를 연결했습니다. 내부 풀업 저항으로 구성된 핀에 신호를 보냅니다.

entity top is
  port (
    clk : in std_logic;
    rst_n : in std_logic; -- Pullup
    pwm : out std_logic
  );
end top; 

신호 및 상수

최상위 모듈의 선언 영역에서 Lattice iCEstick 및 TowerPro SG90 서보와 일치하는 상수를 정의했습니다.

실험을 통해 0.5ms에서 2.5ms가 내가 원하는 180도의 움직임을 제공한다는 것을 발견했습니다. 인터넷의 다양한 출처는 다른 값을 제안하지만 이것이 저에게 효과적이었습니다. 합법적인 TowerPro SG90 서보를 사용하고 있는지 확실하지 않습니다. 위조품일 수 있습니다.

그렇다면 인터넷 판매자에게 구매한거라 의도치 않았지만 펄스 주기 값의 차이를 설명할 수 있습니다. 내 오실로스코프로 지속 시간을 확인했습니다. 아래에 표시된 코드에 작성된 내용입니다.

constant clk_hz : real := 12.0e6; -- Lattice iCEstick clock
constant pulse_hz : real := 50.0;
constant min_pulse_us : real := 500.0; -- TowerPro SG90 values
constant max_pulse_us : real := 2500.0; -- TowerPro SG90 values
constant step_bits : positive := 8; -- 0 to 255
constant step_count : positive := 2**step_bits;

cnt를 설정했습니다. 자유 실행 카운터의 신호는 25비트 너비로, iCEstick의 12MHz 클록에서 실행할 때 약 2.8초 안에 래핑됩니다.

constant cnt_bits : integer := 25;
signal cnt : unsigned(cnt_bits - 1 downto 0);

마지막으로 앞서 제시한 데이터 흐름도에 따라 최상위 모듈을 연결할 신호를 선언합니다. 이 기사의 뒷부분에서 아래 신호가 어떻게 상호 작용하는지 보여 드리겠습니다.

signal rst : std_logic;
signal position : integer range 0 to step_count - 1;
signal rom_addr : unsigned(step_bits - 1 downto 0);
signal rom_data : unsigned(step_bits - 1 downto 0);

서보 모듈 인스턴스화

서보 모듈 인스턴스화는 테스트벤치에서 수행한 방식과 유사합니다. 상수는 일반, 로컬 신호는 포트 신호입니다.

SERVO : entity work.servo(rtl)
generic map (
  clk_hz => clk_hz,
  pulse_hz => pulse_hz,
  min_pulse_us => min_pulse_us,
  max_pulse_us => max_pulse_us,
  step_count => step_count
)
port map (
  clk => clk,
  rst => rst,
  position => position,
  pwm => pwm
);

자체 래핑 카운터 인스턴스화

이전 기사에서 자체 포장 카운터 모듈을 사용했습니다. counter_bits로 계산되는 무료 실행 카운터입니다. , 그리고 다시 0이 됩니다. 별로 할 말은 없지만 살펴보고 싶으시면 예제 프로젝트를 다운받으시면 됩니다.

COUNTER : entity work.counter(rtl)
generic map (
  counter_bits => cnt_bits
)
port map (
  clk => clk,
  rst => rst,
  count_enable => '1',
  counter => cnt
);

사인 ROM 인스턴스화

이전 기사에서 Sine ROM 모듈에 대해 자세히 설명했습니다. 간단히 말해서 선형 숫자 값을 최소/최대 진폭이 동일한 전체 사인파로 변환합니다. 입력은 addr입니다. 신호 및 사인 값은 데이터에 나타납니다. 출력.

SINE_ROM : entity work.sine_rom(rtl)
generic map (
  data_bits => step_bits,
  addr_bits => step_bits
)
port map (
  clk => clk,
  addr => rom_addr,
  data => rom_data
);

아래 표시된 동시 할당을 사용하여 카운터 모듈, 사인 ROM 모듈 및 서보 모듈을 연결합니다.

position <= to_integer(rom_data);
rom_addr <= cnt(cnt'left downto cnt'left - step_bits + 1);

Servo 모듈의 위치 입력은 Sine ROM 출력의 복사본이지만 unsigned 값은 유형이 다르기 때문에 정수로 변환해야 합니다. ROM 주소 입력을 위해 우리는 자유 실행 카운터의 상위 비트를 사용합니다. 이렇게 하면 cnt 신호 랩, 2.8초 후.

Lattice iCEstick에서 테스트

아래 스케치와 같이 전체 회로를 브레드보드에 연결했습니다. FPGA는 3.3V를 사용하고 서보는 5V에서 실행하기 때문에 외부 5V 전원 공급 장치와 브레드보드 가능한 레벨 시프터를 사용했습니다. 레벨 컨버터를 고려하지 않고 FPGA 핀의 PWM 출력은 TowerPro SG90 서보의 "신호" 와이어로 직접 연결됩니다.

전원 스위치를 누른 후 서보는 부드러운 180도 동작으로 앞뒤로 움직여야 하며 극단적인 위치에서 약간 멈춰야 합니다. 아래 비디오는 오실로스코프에 시각화된 PWM 신호를 사용한 설정을 보여줍니다.

최종 생각

항상 그렇듯이 VHDL 모듈을 구현하는 방법에는 여러 가지가 있습니다. 하지만 정수 유형을 카운터로 사용하는 이 기사에서 설명하는 접근 방식을 선호합니다. 모든 무거운 계산은 컴파일 시간에 발생하며 결과 논리는 카운터, 레지스터 및 멀티플렉서뿐입니다.

VHDL에서 32비트 정수를 처리할 때 가장 심각한 위험은 계산에서 자동으로 오버플로된다는 것입니다. 예상 입력 범위의 값에 대해 하위 표현식이 오버플로되지 않는지 확인해야 합니다. 우리의 서보 모듈은 모든 실제 클록 주파수 및 서보 설정에서 작동합니다.

이러한 종류의 PWM은 RC 서보 이외의 대부분의 애플리케이션에는 적합하지 않습니다. 아날로그 전력 제어의 경우 스위칭 주파수보다 듀티 사이클이 더 중요합니다.

여기에서 PWM을 사용한 아날로그 전력 제어에 대해 읽어보세요.
VHDL에서 PWM 컨트롤러를 만드는 방법

예제를 직접 시도해보고 싶다면 제가 준비한 Zip 파일을 다운로드하여 빠르게 시작할 수 있습니다. 아래 양식에 이메일 주소를 입력하면 몇 분 안에 시작하는 데 필요한 모든 것을 받게 됩니다! 패키지에는 완전한 VHDL 코드, 실행 스크립트가 있는 ModelSim 프로젝트, Lattice iCEcube2 프로젝트 및 Lattice Diamond 프로그래머 구성 파일이 포함되어 있습니다.

의견 섹션에서 어떻게 생각하는지 알려주세요!


VHDL

  1. PWM 전원 컨트롤러
  2. VHDL에서 PWM 컨트롤러를 만드는 방법
  3. TEXTIO를 사용하여 파일에서 RAM을 초기화하는 방법
  4. InitialState를 사용하여 ppDAQC Pi 플레이트에서 센서 데이터 스트리밍
  5. Raspberry Pi를 사용하여 집 온도 모니터링
  6. 단계별:IIoT를 사용하여 PLC에서 데이터를 가져오는 방법은 무엇입니까?
  7. 보안 FPGA SoC에서 TEE를 사용하여 기내 AI를 보호하는 예
  8. 서보 컨트롤러를 수리할 수 있습니까?
  9. 공장의 실제 생활:C 축 드라이브가 올바르지 않음 서보 드라이브 오류
  10. Arduino Uno를 사용한 25kHz 4핀 PWM 팬 제어