本文章是基于轮趣科技的一款ros教育小车为运动载体。
一.前端定义
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity death_bridge is
Port(
clk :in STD_LOGIC;
reset :in STD_LOGIC;
key1:in STD_LOGIC;
LED0,LED1 : out STD_LOGIC;
L_INF1,L_INF2,L_INF3,L_INF4: in STD_LOGIC;
R_INF1,R_INF2,R_INF3,R_INF4: in STD_LOGIC;
pwm_out: out STD_LOGIC;
motor_pwmA1,motor_pwmB1: out STD_LOGIC--只用一个端口,另一个默认为0;
);
end death_bridge;
循迹模块为八路红外模块
二.主代码
1.状态切换
为了更好地控制小车启动,我们使用了一个key1按键来切换小车的停止状态与运动状态。
type StateType is(FWD,STOPPED);
signal current_state : StateType;
signal tempA : INTEGER RANGE 0 TO 1; -- 使用内部信号tempA来保存计数值
constant DELAY_TIME : integer := 10000000; -- 假设延迟时间为10个时钟周期
signal key1_pressed : std_logic := '0'; -- 按键是否已经被检测为按下的标志
signal counter1 : integer range 0 to DELAY_TIME-1 := 0; -- 假设DELAY_TIME是一个已定义的常数
state:process(clk)
begin
if rising_edge(clk) then
if tempA=0 then
current_state <=STOPPED;
elsif tempA=1 then
current_state <=FWD;
end if;
end if;
end process;
每次按下按键都会对tempA的值进行改变进而改变小车的状态。
按键具体代码如下:
key:process(reset, clk)--按键key1进行消抖操作,并切换状态
begin
if reset = '0' then
tempA <= 0; --
counter1 <= 0;
key1_pressed <= '0';
elsif rising_edge(clk) then
if key1 = '0' then
key1_pressed <= '1';
counter1 <= DELAY_TIME - 1;
elsif key1 = '1' and key1_pressed = '1' then
if counter1 = 0 then
tempA <= tempA + 1;
if tempA >= 2 then
tempA <= 0;
end if;
key1_pressed <= '0';
else
counter1 <= counter1 - 1;
end if;
end if;
end if;
end process;
在按键按下的过程中存在一个消抖过程与一个状态确定过程。
状态确定过程:key1_pressed为一个状态量,一次按键按下的过程为“下+上”,一次TMEPA的数量增加相当于这一个过程。具体请读者自己思考。
消抖过程:DELAY_TIME为延迟总量,counter1为计数量,由于信号抖动的存在,其保证了信号传递的稳定性。
2.舵机的控制与小车的速度控制
(1)前端定义
signal duty_cycle : unsigned(19 downto 0) := (others => '0');
constant PWM_PERIOD : integer := 10000000; -- 假设PWM周期是1000000个时钟周期 20ms 20*50=1000=1s
signal counter2 : unsigned(19 downto 0) := (others => '0'); -- 计数器 2^20= 1048576。26
--20ms的周期时间下0.5ms -90 1 -45 1.5ms 0 2ms 45 2.5ms 90
constant motor_pwm_period:integer := 10000000;
signal counter3: unsigned(19 downto 0) := (others => '0');
signal motor_duty_cycle: unsigned(19 downto 0) := (others => '0');
constant INF_COUNT: integer := 1000; -- 假设PWM周期是1000000个时钟周期 20ms
signal counter4: unsigned(19 downto 0) := (others => '0');
signal LEFT_INF:INTEGER RANGE 0 TO 10;--用于计算不同的转动角度
signal LEFT1,LEFT2,LEFT3,LEFT4:INTEGER RANGE 0 TO 5 ;
signal RIGHT_INF :INTEGER RANGE 0 TO 10;
signal RIGHT1,RIGHT2,RIGHT3,RIGHT4:INTEGER RANGE 0 TO 5 ;
signal caculate_total : INTEGER RANGE 0 TO 10 :=0;--用于转换成相应的PWM
signal caculate_number : INTEGER RANGE 0 TO 10 :=5;--计算的中间值
signal motor_condition: INTEGER RANGE 0 TO 10 :=5;--计算车轮的速度
在这里声明一下 caculate_total与caculate_number是用于后续的pwm控制。
(2)电机控制
motor_pwm:process(clk,reset,current_state)
begin
if reset='0' then
counter3 <= (others=>'0');
elsif rising_edge(clk) then
case current_state is
when FWD=>
LED0 <='1'; LED1 <='1';
motor_duty_cycle <= to_unsigned(8000000-100000*motor_condition,20);--计算出对应的PWM数值
counter3<= counter3+1;
if counter3 >= motor_pwm_period-1 then
counter3 <=(others=>'0') ;
else
if counter3 < motor_duty_cycle then
motor_pwmA1 <='1';
motor_pwmB1<='1';
else
motor_pwmA1 <='0';
motor_pwmB1 <='0';
end if;
end if;
when STOPPED =>
motor_pwmA1 <='0';
motor_pwmB1 <='0';
LED0 <='0'; LED1 <='0';
end case;
end if;
end process;
代码段的具体PWM实现与STM32的PWM实现底层原理相同。
即是一个周期为1000ms,在500ms为高电平,500ms为低电平,那么占空比就位50%。(只是举一个简单的例子,不适用与当前的代码)具体的FPGA单片机运行速率请查询官网。
LED此处用于确定其处于哪一种状态,前进或者停止。
(3)舵机控制
pwm_generate:process(clk,reset)--根据传感器的数据确定pwm值进而控制转向
begin
if reset='0' then
duty_cycle <= to_unsigned(75000,20);--25000-125000
counter2 <= (others=>'0');
elsif rising_edge(clk) then
duty_cycle <= to_unsigned(25000+10000*caculate_total,20);--计算出对应的PWM数值
counter2<= counter2+1;
if counter2 >= PWM_PERIOD-1 then
counter2 <=(others=>'0') ;
else
if counter2 < duty_cycle then
pwm_out <='1';
else
pwm_out <='0';
end if;
end if;
end if;
end process;
与电机控制原理相同,此处不再过多赘述。
(4)八路红外部分具体实现
INF_number:process(clk)--对传感器进行计算,进而确定占空比来决定不同的转向角
begin
if rising_edge(clk) then
if L_INF1='0' then
LEFT1 <= 1;
else
LEFT1 <= 0;
end if;
if L_INF2='1' then
LEFT2 <= 0;
else
LEFT2 <= 2;
end if;
if L_INF3='0' then
LEFT3 <= 0;
else
LEFT3 <= 3;
end if;
if L_INF4='0' then
LEFT4 <= 0;
else
LEFT4 <= 5;
end if;
if R_INF1='0' then
RIGHT1 <=1;
else
RIGHT1 <=0;
end if;
if R_INF2='1' then
RIGHT2 <=0;
else
RIGHT2 <=2;
end if;
if R_INF3='0' then
RIGHT3 <=0;
else
RIGHT3 <=3;
end if;
if R_INF4='0' then
RIGHT4 <=0;
else
RIGHT4 <=5;
end if;
LEFT_INF <=LEFT1+LEFT2+LEFT3+LEFT4;
RIGHT_INF <=RIGHT1+RIGHT2+RIGHT3+RIGHT4;
caculate_total <= caculate_number-RIGHT_INF+LEFT_INF;
end if;
end process;
多路红外分为左路有右路,当小车偏离黑线越大时,转角越大,速度越慢。
三.尾声
上述代码只做部分演示,实际操作请读者稍加更改。