基于FPGA与红外模块实现小车的循迹功能

本文章是基于轮趣科技的一款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;

多路红外分为左路有右路,当小车偏离黑线越大时,转角越大,速度越慢。

三.尾声

上述代码只做部分演示,实际操作请读者稍加更改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值