Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Sterownik rolet na CPLD/FPGA

Witam wszystkich,

czy mógłbym prosić o sprawdzenie poprawności całego projektu (pewnie skończy się tym, iż odpowie mi Jakub). Wrzucam ten projekt w całości bo może się przyda w przyszłości komuś, kto będzie próbował zbudować coś podobnego (czyli sterowanie silnikiem DC do np. otwierania rolet).

Oto główny plik - projekt (opis sygnałów najlepiej zobaczyć w pliku DASM.vhd:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Project is
Port ( CLK : in STD_LOGIC;
RST : in STD_LOGIC;
b1 : in STD_LOGIC;--button zamykaj
b2 : in STD_LOGIC;--button otwieraj
krancEND : in STD_LOGIC;--wyłącznik krancow
krancPOC : in STD_LOGIC;--wyłącznik krancow
OVVERIDE : in STD_LOGIC;--z komparatora (za duży prąd płynie przez silnik)
DIRPIN : inout STD_LOGIC;
SLPPIN : out STD_LOGIC;
SAWARIA : out STD_LOGIC;
LEDOTWARTE : out STD_LOGIC;
LEDZAMKNIETE : out STD_LOGIC;
LEDPRACA : out STD_LOGIC;
LEDAWARIA : out STD_LOGIC;
pwm : out STD_LOGIC_VECTOR (0 downto 0));--wyjscie pwm na mostek MOSFET
end Project;

architecture Behavioral of Project is

component divider is
Port ( clk : in STD_LOGIC;
clk_out : inout STD_LOGIC := '0');--clk_out 1MHZ
end component;

component debounce1 is
port ( clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
end component;
component debounce2 is
port ( clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
end component;
component DMASM is
Port ( CLK : in STD_LOGIC;
RST : in STD_LOGIC;
bt1 : in STD_LOGIC;--btn zamykanie
bt2 : in STD_LOGIC;--btn otwieranie
krancEND : in STD_LOGIC;--active 0
krancPOC : in STD_LOGIC;--active 0
OVERRIDE : in STD_LOGIC;--active 0 (przeciazenie silnika)
DIRPIN : out STD_LOGIC;--Direction Motor ( 0 - FORWARD)
SLPPIN : out STD_LOGIC;--SLEEP PIN Mostek (0 - mostek zablokowany)
pb1_Zam : out STD_LOGIC;--gdy 0 zamykanie
pb2_Otw : out STD_LOGIC;--gdy 0 otwieranie
AWARIA : out STD_LOGIC;--gdy 0 awaria ukladu
LEDZAMKNIETE : out STD_LOGIC;--gdy 1 LED zamkniete
LEDOTWARTE : out STD_LOGIC;--gdy 1 LED otwarte
LEDPRACA : out STD_LOGIC;--gdy 1 LED Zamykanie/otwieranie
LEDAWARIA : out STD_LOGIC);--gdy 1 LED awaria;
end component;
component WEKTORY1_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end component;
component WEKTORY2_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end component;

component pwm1 is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));
end component;

component pwm2 is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));
end component;

component multiplexer1 is
Port ( SEL : in STD_LOGIC;--tu sygnal DIRPIN
pwm1 : in STD_LOGIC_VECTOR (0 downto 0);
pwm2 : in STD_LOGIC_VECTOR (0 downto 0);
pwm : out STD_LOGIC_VECTOR (0 downto 0));
end component;


signal data1_signal :STD_LOGIC_VECTOR (7 downto 0);
signal data2_signal :STD_LOGIC_VECTOR (7 downto 0);
signal b1_DB :STD_LOGIC;
signal b2_DB :STD_LOGIC;
signal clk_1MHz :STD_LOGIC;
signal pwm1_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm2_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm1_n_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm2_n_out :STD_LOGIC_VECTOR (0 downto 0);
--signal pwm :STD_LOGIC_VECTOR (0 downto 0);
signal OVERRIDE :STD_LOGIC;
signal pb1_Zam :STD_LOGIC;
signal pb2_Otw :STD_LOGIC;
signal AWARIA :STD_LOGIC;
begin
C1: divider port map (CLK, clk_1MHz);
C2: debounce1 port map (clk_1MHz, b1, b1_DB);
C3: debounce2 port map (clk_1MHz, b2, b2_DB);
C4: DMASM port map (clk_1MHz,
RST,
b1_DB,
b2_DB,
krancEND,
krancPOC,
OVERRIDE,
DIRPIN,
SLPPIN,
pb1_Zam,
pb2_Otw,
AWARIA,
LEDZAMKNIETE,
LEDOTWARTE,
LEDPRACA,
LEDAWARIA);
C5: WEKTORY1_WYPELNIEN port map (clk_1MHz, pb1_Zam, data1_signal);
C6: WEKTORY2_WYPELNIEN port map (clk_1MHz, pb2_Otw, data2_signal);
C7: pwm1 port map (clk_1MHz, RST, '1', data1_signal, pwm1_out, pwm1_n_out);
C8: pwm2 port map (clk_1MHz, RST, '1', data2_signal, pwm2_out, pwm2_n_out);
C9: multiplexer1 port map (DIRPIN, pwm1_out, pwm2_out, pwm);end Behavioral;



Plik divider.vhd:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;

entity divider is
generic (
NBit : natural := 3;
Div : natural := 6
);
Port ( clk : in STD_LOGIC;
clk_out : inout STD_LOGIC := '0');
end divider;

architecture divider_arch of divider is

signal cnt: std_logic_vector(Nbit -1 downto 0) := (others=>'0');

begin

process(clk)
begin
if (clk'event and clk='1') then
if cnt < Div then
cnt <= cnt+1;
else
cnt <= (others=>'0');
clk_out <= not clk_out;
end if;
end if;
end process; end divider_arch;


Maszyna stanów DMASM.vhd:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity DMASM is
Port ( CLK : in STD_LOGIC;--Zegar
RST : in STD_LOGIC;--Reset
bt1 : in STD_LOGIC;--btn zamykanie
bt2 : in STD_LOGIC;--btn otwieranie
krancEND : in STD_LOGIC;--active 0
krancPOC : in STD_LOGIC;--active 0
OVERRIDE : in STD_LOGIC;--active 0 (przeciazenie silnika)
DIRPIN : out STD_LOGIC;--Direction Motor ( 0 - FORWARD)
SLPPIN : out STD_LOGIC;--SLEEP PIN Mostek (0 - mostek zablokowany)
pb1_Zam : out STD_LOGIC;--gdy 0 zamykanie
pb2_Otw : out STD_LOGIC;--gdy 0 otwieranie
AWARIA : out STD_LOGIC;--gdy 0 awaria ukladu
LEDZAMKNIETE : out STD_LOGIC;--gdy 1 LED zamkniete
LEDOTWARTE : out STD_LOGIC;--gdy 1 LED otwarte
LEDPRACA : out STD_LOGIC;--gdy 1 LED Zamykanie/otwieranie
LEDAWARIA : out STD_LOGIC);--gdy 1 LED awaria;
end DMASM;

architecture BEHAVE of DMASM is
type STATE is (S0Otwarte, S1Zamykanie, S2Zamkniete, S3Otwieranie, S4Awaria);
signal CURRENT_STATE, NEXT_STATE: STATE;
begin
SEQ: process (RST, CLK)
begin
if (RST = '0') then
CURRENT_STATE <= S0Otwarte;
elsif (CLK' event and CLK = '1' ) then
CURRENT_STATE <= NEXT_STATE;
end if;
end process;

COMB: process (CURRENT_STATE, RST, bt1, bt2, krancEND, KrancPOC, OVERRIDE)
begin
DIRPin <= '0'; --Kierunek: FORWARD
SLPPin <= '0'; --Blokada mostka
pb1_Zam <= '1'; --klawisz nieaktywny
pb2_Otw <= '1'; --klawisz nieaktywny
AWARIA <= '1'; --klawisz nieaktywny
LEDZAMKNIETE <= '0';
LEDOTWARTE <= '1';
LEDPRACA <= '0';
LEDAWARIA <= '0';
NEXT_STATE <= S0Otwarte;
case CURRENT_STATE is
when S0Otwarte => DIRPin <= '0';
SLPPin <= '0';--mostek zablokowany
pb1_Zam <= '1';
pb2_Otw <= '1';--blokada ALL
AWARIA <= '1'; --klawisz nieaktywny
LEDZAMKNIETE <= '0';
LEDOTWARTE <= '1';
LEDPRACA <= '0';
LEDAWARIA <= '0';
if ((bt1 = '0') and (krancPOC = '0')) then
NEXT_STATE <= S1Zamykanie;
else
NEXT_STATE <= S0Otwarte;
end if;
when S1Zamykanie => DIRPin <= '0';
SLPPin <= '1';--FORWARD,MOSTEK OTWARTY
pb1_Zam <= '0';
pb2_Otw <= '1';
AWARIA <= '1'; --klawisz nieaktywny
LEDZAMKNIETE <= '0';
LEDOTWARTE <= '1';
LEDPRACA <= '1';
LEDAWARIA <= '0';
if (OVERRIDE = '0') then -- gdy jest awaria
NEXT_STATE <= S4Awaria;
else -- gdy nie ma awarii
if ((krancEND = '0') and (krancPOC = '1')) then
NEXT_STATE <= S2Zamkniete;
else
NEXT_STATE <= S1Zamykanie;
end if;
end if;
when S2Zamkniete => DIRPin <= '1';
SLPPin <= '0';
pb1_Zam <= '1';
pb2_Otw <= '1';--blokada ALL
AWARIA <= '1'; --klawisz nieaktywny
LEDZAMKNIETE <= '1';
LEDOTWARTE <= '0';
LEDPRACA <= '0';
LEDAWARIA <= '0';
if ((bt2 = '0') and (krancEND = '0'))then
NEXT_STATE <= S3Otwieranie;
else
NEXT_STATE <= S2Zamkniete;
end if;
when S3Otwieranie => DIRPin <= '1';
SLPPin <= '1';
pb1_Zam <= '1';
pb2_Otw <= '0';--BACKWARD,MOSTEK OTWARTY
AWARIA <= '1'; --klawisz nieaktywny
LEDZAMKNIETE <= '1';
LEDOTWARTE <= '0';
LEDPRACA <= '1';
LEDAWARIA <= '0'; if (OVERRIDE = '0') then -- gdy jest awaria
NEXT_STATE <= S4Awaria;
else -- gdy nie ma awarii
if ((krancPOC = '0') and (krancEND = '1')) then
NEXT_STATE <= S0Otwarte;
else
NEXT_STATE <= S3Otwieranie;
end if;
end if;
when S4Awaria => DIRPin <= '1';--stan S4Awaria
SLPPin <= '0';--blokada mostka
pb1_Zam <= '1';
pb2_Otw <= '1';--klawisze nieaktywne
AWARIA <= '0'; --sygnal aktywny
LEDZAMKNIETE <= '0';
LEDOTWARTE <= '0';
LEDPRACA <= '0';
LEDAWARIA <= '1';
when others => DIRPin <= '1';--stan S4Awaria
SLPPin <= '0';--blokada mostka
pb1_Zam <= '1';
pb2_Otw <= '1';--klawisze nieaktywne
AWARIA <= '0'; --sygnal aktywny
LEDZAMKNIETE <= '0';
LEDOTWARTE <= '0';
LEDPRACA <= '0';
LEDAWARIA <= '1'; end case;
end process;
end BEHAVE;



debounce1:


LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY debounce1 IS
GENERIC(
counter_size : INTEGER := 19); --counter size (19 bits gives 10.5ms with 50MHz clock)
PORT(
clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
END debounce1;

ARCHITECTURE logic OF debounce1 IS
SIGNAL flipflops : STD_LOGIC_VECTOR(1 DOWNTO 0); --input flip flops
SIGNAL counter_set : STD_LOGIC; --sync reset to zero
SIGNAL counter_out : STD_LOGIC_VECTOR(counter_size DOWNTO 0) := (OTHERS => '0'); --counter output
BEGIN

counter_set <= flipflops(0) xor flipflops(1); --determine when to start/reset counter

PROCESS(clk)
BEGIN
IF(clk'EVENT and clk = '1') THEN
flipflops(0) <= button;
flipflops(1) <= flipflops(0);
If(counter_set = '1') THEN --reset counter because input is changing
counter_out <= (OTHERS => '0');
ELSIF(counter_out(counter_size) = '0') THEN --stable input time is not yet met
counter_out <= counter_out + 1;
ELSE --stable input time is met
result <= flipflops(1);
END IF;
END IF;
END PROCESS;
END logic;



debounce2.vhd:


LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY debounce2 IS
GENERIC(
counter_size : INTEGER := 19); --counter size (19 bits gives 10.5ms with 50MHz clock)
PORT(
clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
END debounce2;

ARCHITECTURE logic OF debounce2 IS
SIGNAL flipflops : STD_LOGIC_VECTOR(1 DOWNTO 0); --input flip flops
SIGNAL counter_set : STD_LOGIC; --sync reset to zero
SIGNAL counter_out : STD_LOGIC_VECTOR(counter_size DOWNTO 0) := (OTHERS => '0'); --counter output
BEGIN

counter_set <= flipflops(0) xor flipflops(1); --determine when to start/reset counter

PROCESS(clk)
BEGIN
IF(clk'EVENT and clk = '1') THEN
flipflops(0) <= button;
flipflops(1) <= flipflops(0);
If(counter_set = '1') THEN --reset counter because input is changing
counter_out <= (OTHERS => '0');
ELSIF(counter_out(counter_size) = '0') THEN --stable input time is not yet met
counter_out <= counter_out + 1;
ELSE --stable input time is met
result <= flipflops(1);
END IF;
END IF;
END PROCESS;
END logic;



WEKTORY1_WYPELNIEN.vhd:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity WEKTORY1_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end WEKTORY1_WYPELNIEN;

architecture BEHAVE of WEKTORY1_WYPELNIEN is

signal ADDRESS :integer range 0 to 32 :=0;
signal TEMP :integer range 0 to 10000000 :=0; -- liczba impulsów
-- jaka ma minąć zanim dojdzie do zmiany adresu

signal GENERACJA_SERII_WEKTOROW : std_logic :='0';
-- okreslanie kiedy ma sie zaczac generacja serii wektorow

signal KONIEC_GENERACJI_WEKTOROW : std_logic :='0';
-- okreslenie kiedy koniec generacji serii wektorow

signal OPOZNIENIE_BUTTONA :integer range 0 to 1000000 :=0;
type mem is array ( 0 to 2**5 - 1) of std_logic_vector(7 downto 0);
constant my_Rom : mem := (
0 => "00000000",
1 => "00000111",
2 => "00001110",
3 => "00001111",
4 => "00010101",
5 => "00011100",
6 => "00100011",
7 => "00010000",
8 => "00101010",
9 => "00110001",
10 => "00111000",
11 => "00111111",
12 => "01000110",
13 => "01001101",
14 => "01010100",
15 => "01011011",
16 => "01100010",
17 => "01101001",
18 => "01110000",
19 => "01110111",
20 => "01111110",
21 => "10000101",
22 => "10001100",
23 => "10010011",
24 => "10011010",
25 => "10100001",
26 => "10101000",
27 => "10010111",
28 => "10110110",
29 => "11011001",
30 => "11100000",
31 => "11111111"
);

begin

-- ZWIEKSZANIE ADRESU KOLEJNYCH WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then

-- SPRAWDZENIE CZY PRZYCISK JEST NACISNIETY CZY NIE
if (pushB = '0') then -- lub '0' gdy po nacisnieciu
-- przycisku generowane jest przerwanie
if (OPOZNIENIE_BUTTONA < 10000) then
-- opoznienie dzialania przycisku, aby za szybko
-- nie szla generacja po nacisnieciu
OPOZNIENIE_BUTTONA <= OPOZNIENIE_BUTTONA + 1;
else
if (KONIEC_GENERACJI_WEKTOROW = '0') then
-- jesli jest koniec to nie wznawiaj generacji
GENERACJA_SERII_WEKTOROW <= '1';
end if;
end if;
else
KONIEC_GENERACJI_WEKTOROW <= '0';
OPOZNIENIE_BUTTONA <= 0;
-- zeruj koniec, gdy przycisk jest puszczony
-- zeruj opoznienie przycisku
end if;

-- GENERACJA SERII WEKTOROW PO NACISNIECIU PRZYCISKU
-- ORAZ OKRESLENIE JEJ KONCA PO JEJ WYKONANIU
if (GENERACJA_SERII_WEKTOROW = '1') then
if (TEMP < 1000000) then
TEMP <= TEMP + 1;
else
TEMP <= 0;
if (ADDRESS < 31) then
ADDRESS <= ADDRESS + 1;
else
KONIEC_GENERACJI_WEKTOROW <= '1';
GENERACJA_SERII_WEKTOROW <= '0';
ADDRESS <= 0;
end if;
end if;
else
TEMP <= 0;
end if;
end if;
end process;


-- GENEROWANIE WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
if (GENERACJA_SERII_WEKTOROW = '1') then-- gdy trwa generacja -- wektorow
DATA <= my_rom(ADDRESS);
else -- gdy generacja nie trwa
DATA <= "00000000";
end if;
end if;
end process;
end BEHAVE;



WEKTORY2_WYPELNIEN.vhd:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity WEKTORY2_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end WEKTORY2_WYPELNIEN;

architecture BEHAVE of WEKTORY2_WYPELNIEN is

signal ADDRESS :integer range 0 to 32 :=0;
signal TEMP :integer range 0 to 10000000 :=0; -- liczba impulsów
-- jaka ma minąć zanim dojdzie do zmiany adresu

signal GENERACJA_SERII_WEKTOROW : std_logic :='0';
-- okreslanie kiedy ma sie zaczac generacja serii wektorow

signal KONIEC_GENERACJI_WEKTOROW : std_logic :='0';
-- okreslenie kiedy koniec generacji serii wektorow

signal OPOZNIENIE_BUTTONA :integer range 0 to 1000000 :=0;
type mem is array ( 0 to 2**5 - 1) of std_logic_vector(7 downto 0);
constant my_Rom : mem := (
0 => "00000000",
1 => "00000111",
2 => "00001110",
3 => "00001111",
4 => "00010101",
5 => "00011100",
6 => "00100011",
7 => "00010000",
8 => "00101010",
9 => "00110001",
10 => "00111000",
11 => "00111111",
12 => "01000110",
13 => "01001101",
14 => "01010100",
15 => "01011011",
16 => "01100010",
17 => "01101001",
18 => "01110000",
19 => "01110111",
20 => "01111110",
21 => "10000101",
22 => "10001100",
23 => "10010011",
24 => "10011010",
25 => "10100001",
26 => "10101000",
27 => "10010111",
28 => "10110110",
29 => "11011001",
30 => "11100000",
31 => "11111111"
);

begin

-- ZWIEKSZANIE ADRESU KOLEJNYCH WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then

-- SPRAWDZENIE CZY PRZYCISK JEST NACISNIETY CZY NIE
if (pushB = '0') then -- lub '0' gdy po nacisnieciu
-- przycisku generowane jest przerwanie
if (OPOZNIENIE_BUTTONA < 10000) then
-- opoznienie dzialania przycisku, aby za szybko
-- nie szla generacja po nacisnieciu
OPOZNIENIE_BUTTONA <= OPOZNIENIE_BUTTONA + 1;
else
if (KONIEC_GENERACJI_WEKTOROW = '0') then
-- jesli jest koniec to nie wznawiaj generacji
GENERACJA_SERII_WEKTOROW <= '1';
end if;
end if;
else
KONIEC_GENERACJI_WEKTOROW <= '0';
OPOZNIENIE_BUTTONA <= 0;
-- zeruj koniec, gdy przycisk jest puszczony
-- zeruj opoznienie przycisku
end if;

-- GENERACJA SERII WEKTOROW PO NACISNIECIU PRZYCISKU
-- ORAZ OKRESLENIE JEJ KONCA PO JEJ WYKONANIU
if (GENERACJA_SERII_WEKTOROW = '1') then
if (TEMP < 1000000) then
TEMP <= TEMP + 1;
else
TEMP <= 0;
if (ADDRESS < 31) then
ADDRESS <= ADDRESS + 1;
else
KONIEC_GENERACJI_WEKTOROW <= '1';
GENERACJA_SERII_WEKTOROW <= '0';
ADDRESS <= 0;
end if;
end if;
else
TEMP <= 0;
end if;
end if;
end process;


-- GENEROWANIE WEKTOROW WYPELNIEN
process (CLK)
begin
if rising_edge(CLK) then
if (GENERACJA_SERII_WEKTOROW = '1') then-- gdy trwa generacja -- wektorow
DATA <= my_rom(ADDRESS);
else -- gdy generacja nie trwa
DATA <= "00000000";
end if;
end if;
end process;
end BEHAVE;


pwm1.vhd:


LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY pwm1 IS
GENERIC(
sys_clk : INTEGER := 1_000_000; --system clock frequency in Hz
pwm_freq : INTEGER := 60_000; --PWM switching frequency in Hz
bits_resolution : INTEGER := 8; --bits of resolution setting the duty cycle
phases : INTEGER := 1); --number of output pwms and phases
PORT(
clk : IN STD_LOGIC; --system clock
reset_n : IN STD_LOGIC; --asynchronous reset
ena : IN STD_LOGIC; --latches in new duty cycle
duty : IN STD_LOGIC_VECTOR(bits_resolution-1 DOWNTO 0); --duty cycle
pwm_out : OUT STD_LOGIC_VECTOR(phases-1 DOWNTO 0); --pwm outputs
pwm_n_out : OUT STD_LOGIC_VECTOR(phases-1 DOWNTO 0)); --pwm inverse outputs
END pwm1;

ARCHITECTURE logic OF pwm1 IS
CONSTANT period : INTEGER := sys_clk/pwm_freq; --number of clocks in one pwm period
TYPE counters IS ARRAY (0 TO phases-1) OF INTEGER RANGE 0 TO period - 1; --data type for array of period counters
SIGNAL count : counters := (OTHERS => 0); --array of period counters
SIGNAL half_duty_new : INTEGER RANGE 0 TO period/2 := 0; --number of clocks in 1/2 duty cycle
TYPE half_duties IS ARRAY (0 TO phases-1) OF INTEGER RANGE 0 TO period/2; --data type for array of half duty values
SIGNAL half_duty : half_duties := (OTHERS => 0); --array of half duty values (for each phase)
BEGIN
PROCESS(clk, reset_n)
BEGIN
IF(reset_n = '0') THEN --asynchronous reset
count <= (OTHERS => 0); --clear counter
pwm_out <= (OTHERS => '0'); --clear pwm outputs
pwm_n_out <= (OTHERS => '0'); --clear pwm inverse outputs
ELSIF(clk'EVENT AND clk = '1') THEN --rising system clock edge
IF(ena = '1') THEN --latch in new duty cycle
half_duty_new <= conv_integer(duty)*period/(2**bits_resolution)/2; --determine clocks in 1/2 duty cycle
END IF;
FOR i IN 0 to phases-1 LOOP --create a counter for each phase
IF(count(0) = period - 1 - i*period/phases) THEN --end of period reached
count(i) <= 0; --reset counter
half_duty(i) <= half_duty_new; --set most recent duty cycle value
ELSE --end of period not reached
count(i) <= count(i) + 1; --increment counter
END IF;
END LOOP;
FOR i IN 0 to phases-1 LOOP --control outputs for each phase
IF(count(i) = half_duty(i)) THEN --phase's falling edge reached
pwm_out(i) <= '0'; --deassert the pwm output
pwm_n_out(i) <= '1'; --assert the pwm inverse output
ELSIF(count(i) = period - half_duty(i)) THEN --phase's rising edge reached
pwm_out(i) <= '1'; --assert the pwm output
pwm_n_out(i) <= '0'; --deassert the pwm inverse output
END IF;
END LOOP;
END IF;
END PROCESS;
END logic;



pwm2.vhd:


LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY pwm2 IS
GENERIC(
sys_clk : INTEGER := 1_000_000; --system clock frequency in Hz
pwm_freq : INTEGER := 60_000; --PWM switching frequency in Hz
bits_resolution : INTEGER := 8; --bits of resolution setting the duty cycle
phases : INTEGER := 1); --number of output pwms and phases
PORT(
clk : IN STD_LOGIC; --system clock
reset_n : IN STD_LOGIC; --asynchronous reset
ena : IN STD_LOGIC; --latches in new duty cycle
duty : IN STD_LOGIC_VECTOR(bits_resolution-1 DOWNTO 0); --duty cycle
pwm_out : OUT STD_LOGIC_VECTOR(phases-1 DOWNTO 0); --pwm outputs
pwm_n_out : OUT STD_LOGIC_VECTOR(phases-1 DOWNTO 0)); --pwm inverse outputs
END pwm2;

ARCHITECTURE logic OF pwm2 IS
CONSTANT period : INTEGER := sys_clk/pwm_freq; --number of clocks in one pwm period
TYPE counters IS ARRAY (0 TO phases-1) OF INTEGER RANGE 0 TO period - 1; --data type for array of period counters
SIGNAL count : counters := (OTHERS => 0); --array of period counters
SIGNAL half_duty_new : INTEGER RANGE 0 TO period/2 := 0; --number of clocks in 1/2 duty cycle
TYPE half_duties IS ARRAY (0 TO phases-1) OF INTEGER RANGE 0 TO period/2; --data type for array of half duty values
SIGNAL half_duty : half_duties := (OTHERS => 0); --array of half duty values (for each phase)
BEGIN
PROCESS(clk, reset_n)
BEGIN
IF(reset_n = '0') THEN --asynchronous reset
count <= (OTHERS => 0); --clear counter
pwm_out <= (OTHERS => '0'); --clear pwm outputs
pwm_n_out <= (OTHERS => '0'); --clear pwm inverse outputs
ELSIF(clk'EVENT AND clk = '1') THEN --rising system clock edge
IF(ena = '1') THEN --latch in new duty cycle
half_duty_new <= conv_integer(duty)*period/(2**bits_resolution)/2; --determine clocks in 1/2 duty cycle
END IF;
FOR i IN 0 to phases-1 LOOP --create a counter for each phase
IF(count(0) = period - 1 - i*period/phases) THEN --end of period reached
count(i) <= 0; --reset counter
half_duty(i) <= half_duty_new; --set most recent duty cycle value
ELSE --end of period not reached
count(i) <= count(i) + 1; --increment counter
END IF;
END LOOP;
FOR i IN 0 to phases-1 LOOP --control outputs for each phase
IF(count(i) = half_duty(i)) THEN --phase's falling edge reached
pwm_out(i) <= '0'; --deassert the pwm output
pwm_n_out(i) <= '1'; --assert the pwm inverse output
ELSIF(count(i) = period - half_duty(i)) THEN --phase's rising edge reached
pwm_out(i) <= '1'; --assert the pwm output
pwm_n_out(i) <= '0'; --deassert the pwm inverse output
END IF;
END LOOP;
END IF;
END PROCESS;
END logic;



multiplekser1.vhd:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity multiplexer1 is
Port ( SEL : in STD_LOGIC;--tu sygnal DIRPIN
pwm1 : in STD_LOGIC_VECTOR (0 downto 0);
pwm2 : in STD_LOGIC_VECTOR (0 downto 0);
pwm : out STD_LOGIC_VECTOR (0 downto 0));
end multiplexer1;

architecture Behavioral of multiplexer1 is
begin
pwm <= pwm1 when (SEL = '0') else pwm2;
end Behavioral;


i ostatni plik - ProjektUCF.ucf (dla płytki FPGA ElbertV2):


NET "CLK" LOC = P129;
NET "CLK" PERIOD = 10MHz;

NET "RST" PULLUP;
NET "RST" LOC = "P76";

NET "bt1" PULLUP;
NET "bt1" LOC = "P80";

NET "bt2" PULLUP;
NET "bt2" LOC = "P79";

NET "krancEND" PULLUP;
NET "krancEND" LOC = "P58";

NET "krancPOC" PULLUP;
NET "krancPOC" LOC = "P59";

NET "OVERRIDE" PULLUP;
NET "OVERRIDE" LOC = "P78";

NET "DIRPIN" LOC = "P19";

NET "SLPPIN" LOC = "P21";

NET "AWARIA" LOC = "P15";

NET "LEDZAMKNIETE" LOC = "P55"; //LED1
NET "LEDOTWARTE" LOC = "P54"; //LED2
NET "LEDPRACA" LOC = "P51"; //LED3
NET "LEDAWARIA" LOC = "P46"; //LED8



Projekt się syntetyzuje i wygląda OK na podglądzie RTL (nie ma błędów ale ma sporo ostrzeżeń.

PozdrawiamTen post został edytowany przez Autora dnia 20.09.17 o godzinie 13:20
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

I jak zawsze dobrze pomyślałeś, ze znów odpowiem :) Projekt oczywiście pod względem koncepcji jak najbardziej udany. Niemniej parę rzeczy, żeby to już było "tiptop":

1. Jeśli chcesz użyć tego samego komponentu kilka razy tak jak w przypadku debouncera to nie musisz pisać kilka razy jego deklaracji przed początkiem architektury (tj. przed słowem kluczowym "begin"). Wystarczy dosłownie jedna deklaracja i możesz używać tego komponentu potem po słowie kluczowym "begin" ile razy chcesz. Tym samym zamiast:
component debounce1 is
port ( clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
end component;

component debounce2 is
port ( clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
end component;
piszesz po prostu:
 component debounce is
port ( clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
end component;
i po słowie kluczowym "begin" zamiast:
 C2: debounce1 port map (clk_1MHz, b1, b1_DB);
C3: debounce2 port map (clk_1MHz, b2, b2_DB);
piszesz
 C2: debounce port map (clk_1MHz, b1, b1_DB);
C3: debounce port map (clk_1MHz, b2, b2_DB);
To samo z wektorami wypełnień - zamiast:
 component WEKTORY1_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end component;
component WEKTORY2_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end component;
i
 C5: WEKTORY1_WYPELNIEN port map (clk_1MHz, pb1_Zam, data1_signal);
C6: WEKTORY2_WYPELNIEN port map (clk_1MHz, pb2_Otw, data2_signal);
piszesz
  component WEKTORY_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end component;
i
 C5: WEKTORY_WYPELNIEN port map (clk_1MHz, pb1_Zam, data1_signal);
C6: WEKTORY_WYPELNIEN port map (clk_1MHz, pb2_Otw, data2_signal);

To samo z generatorami PWM - zamiast:
  component pwm1 is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));
end component;

component pwm2 is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));
end component;
i
 C7: pwm1 port map (clk_1MHz, RST, '1', data1_signal, pwm1_out, pwm1_n_out);
C8: pwm2 port map (clk_1MHz, RST, '1', data2_signal, pwm2_out, pwm2_n_out);
piszesz
 component pwm is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));
end component;
  C7: pwm port map (clk_1MHz, RST, '1', data1_signal, pwm1_out, pwm1_n_out);
C8: pwm port map (clk_1MHz, RST, '1', data2_signal, pwm2_out, pwm2_n_out);
Tym samym zaoszczędzasz pisania kodu :)

2. Multiplekser - szkoda pisania na to odrębnego komponentu. To jest tak proste, że starczy marna jedna linijka kodu w pliku, a nie taki komponent. Innymi słowy wyrzucasz to:
 component multiplexer1 is
Port ( SEL : in STD_LOGIC;--tu sygnal DIRPIN
pwm1 : in STD_LOGIC_VECTOR (0 downto 0);
pwm2 : in STD_LOGIC_VECTOR (0 downto 0);
pwm : out STD_LOGIC_VECTOR (0 downto 0));
end component;
a zamiast tego:
 C9: multiplexer1 port map (DIRPIN, pwm1_out, pwm2_out, pwm);
piszesz to:
 pwm <= pwm1_out when DIRPIN = '0' else pwm2_out; 
lub to:
 with DIRPIN select pwm <= pwm1_out when '0', pwm2_out when others; 

Oszczędzasz w ten sposób kolejne linijki kodu.

3. Przy wybranych sygnałach często używasz typu "STD_LOGIC_VECTOR(0 downto 0)" co oczywiście można tak robić, ale praktycznie po co się tak męczyć jak informacja, że coś jest od zera do zera nic nie wnosi, poza tym, że to inaczej jest coś co jest jednobitowe. Zamiast tego pisz "STD_LOGIC" po prostu - wtedy masz mniej zamętu w kodzie i od razu widać co jest wielobitowe, a co jednobitowe. Co więcej - jeśli nie potrzebujesz symulacji to możesz jeszcze bardziej pójść na łątwiznę i zamiast typów: "STD_LOGIC" i "STD_LOGIC_VECTOR" możesz użyć "BIT" i "BIT_VECTOR" (pewnym co prawda problemem mogą się wtedy okazać funkcje konwersji na inne typy, ale do prostych zastosować to zupełnie starczy - ale tylko wtedy gdy cię nie obchodzą symulacje).

4. W miarę możliwości zamiast pisać np:
 signal data1_signal :STD_LOGIC_VECTOR (7 downto 0);
signal data2_signal :STD_LOGIC_VECTOR (7 downto 0);
signal b1_DB :STD_LOGIC;
signal b2_DB :STD_LOGIC;
signal clk_1MHz :STD_LOGIC;
signal pwm1_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm2_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm1_n_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm2_n_out :STD_LOGIC_VECTOR (0 downto 0);
--signal pwm :STD_LOGIC_VECTOR (0 downto 0);
signal OVERRIDE :STD_LOGIC;
signal pb1_Zam :STD_LOGIC;
signal pb2_Otw :STD_LOGIC;
signal AWARIA :STD_LOGIC;
stosuj grupowanie tak jak tutaj:
 signal data1_signal, data2_signal :STD_LOGIC_VECTOR (7 downto 0);
signal b1_DB,b2_DB :STD_LOGIC;
signal clk_1MHz :STD_LOGIC;
signal pwm1_out, pwm2_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm1_n_out, pwm2_n_out :STD_LOGIC_VECTOR (0 downto 0);
--signal pwm :STD_LOGIC_VECTOR (0 downto 0);
signal OVERRIDE :STD_LOGIC;
signal pb1_Zam, pb2_Otw :STD_LOGIC;
signal OVERRIDE, AWARIA :STD_LOGIC;
lub też od razu zrób np tak:
 signal data1_signal, data2_signal :STD_LOGIC_VECTOR (7 downto 0);
signal b1_DB,b2_DB, clk_1MHz,pwm1_out, pwm2_out,pwm1_n_out, pwm2_n_out, OVERRIDE, pb1_Zam, pb2_Otw, OVERRIDE, AWARIA :STD_LOGIC;
--signal pwm :STD_LOGIC;

Od razu w ten sposób oszczędzasz kolejne linijki kodu. To samo z portami w "entity" Project i w komponentach docelowo.

5. Jeśli przyporządkowujesz sygnały komponentom to na dłuższą metę stosuj takie przyporządkowanie:
 port => sygnał, który ma być podłączony do portu po lewo 
aniżeli to co stosujesz czyli, że kolejne sygnały przyporządkowane do kolejnych portów komponentów. Dzięki temu pierwszemu uniezależniasz się właśnie od kolejności portów, co się przydaje przy znacznie większych rzeczach (sam się o tym przekonałem pisząc swoje dotychczasowe prace dyplomowe). Krótko mówiąc: dla przykładu zamiast:
 C5: WEKTORY1_WYPELNIEN port map (clk_1MHz, pb1_Zam, data1_signal); 
napisz:
 C5: WEKTORY1_WYPELNIEN port map (clk => clk_1MHz, pushB => pb1_Zam, data => data1_signal); 
lub też:
 C5: WEKTORY1_WYPELNIEN port map (pushB => pb1_Zam, clk => clk_1MHz, data => data1_signal); 
lub też:
 C5: WEKTORY1_WYPELNIEN port map (pushB => pb1_Zam, data => data1_signal, clk => clk_1MHz); 

itd. Kombinacji jest co nie miara i jak widzisz każda z nich przy stosowaniu tego pierwszego rodzaju przypisywania pozwala przyspieszyć łączenie sygnałów do portów i szybsze usunięcie błędu gdyby się dokonało złego podłączenia (uwierz, że stosując ten drugi rodzaj przypisania t i kilka godzin się potrafiłem bujać zanim doszedłem co źle podłączyłem - odkąd to pierwsze stosuję to zajmuje mi to mniej niż minutę).

6. Wspominałem już wcześniej o tym co prawda, ale zachęcam cię do stosowania "STD_LOGIC_VECTOR"-ów lub "BIT_VECTOROW" jako portów wejściowych i wyjściowych oraz traktowania każdego sygnału jako jednego bitu. Oszczędzisz kolejne linijki znów w ten sposób, a w razie czego można aliasy napisać jeśli nie idzie się połapać, który bit to które wejście.

Tyle odnośnie głównego pliku. Błędów podłączeniowych nie zauważyłem :)Ten post został edytowany przez Autora dnia 20.09.17 o godzinie 14:13
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

Teraz jeszcze sprawa debouncera - może i działa co prawda, ale można i prościej tak jak tutaj np:
Obrazek
. To też tak na boku.
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Sterownik rolet na CPLD/FPGA

Jakub,

jak zwykle duże podziękowania za bardzo trafne uwagi.

Pozdrawiam
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

Drobiazg :)
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Sterownik rolet na CPLD/FPGA

Jakub,

twoje uwagi pozwoliły mi bardzo usprawnić projekt -nie mogę się już doczekać, gdy będę mógł zrobić praktyczne próby ;)
Szkoda tylko, że grupa HDL na Goldenline.pl ma tylko dwóch aktywnych uczestników.
Może będzie lepiej gdy kurs "FPGA" na Forbot.pl będzie skończony:

https://forbot.pl/blog/kurs-fpga-podstawy-vhdl-w-prakty...

Pozdrawiam.
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

Spokojnie - i dwie osoby to już coś :)
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Sterownik rolet na CPLD/FPGA

Jakub,

zmieniłem projekt zgodnie z twoimi uwagami ale mam błąd, którego nie mogę znaleźć :

Czepia się pliku "ucf" że nie może znaleźć sygnału: pwmOut który jest zadeklarowany w projekcie (i widzę go poprawnie na podglądzie RTL.

Komunikat błędu:

ConstraintSystem:59 - Constraint <NET "pwmOut" LOC = "P19";> [Project.ucf(29)]: NET "pwmOut" not found. Please verify that:
1. The specified design element actually exists in the original design.
2. The specified object is spelled correctly in the constraint source file.

A tak wygląda projekt:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Project is
Port ( CLK : in STD_LOGIC;
RST : in STD_LOGIC;
b1 : in STD_LOGIC;--button zamykaj
b2 : in STD_LOGIC;--button otwieraj
krancEND : in STD_LOGIC;--wyłącznik krancow
krancPOC : in STD_LOGIC;--wyłącznik krancow
OVERLOAD : inout STD_LOGIC;--z komparatora (za duży prąd płynie przez silnik)
DIRPIN : inout STD_LOGIC;
SLPPIN : out STD_LOGIC;
AWARIA : out STD_LOGIC;
pwmOut : out STD_LOGIC_VECTOR (0 downto 0);
LEDOTWARTE : out STD_LOGIC;
LEDZAMKNIETE : out STD_LOGIC;
LEDPRACA : out STD_LOGIC;
LEDAWARIA : out STD_LOGIC);--wyjscie pwm na mostek MOSFET
end Project;

architecture Behavioral of Project is

component divider is
Port ( clk : in STD_LOGIC;
clk_out : inout STD_LOGIC := '0');--clk_out 1MHZ
end component;

component debounce is
port ( clk : IN STD_LOGIC; --input clock
button : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
end component; component DMASM is
Port ( CLK : in STD_LOGIC;
RST : in STD_LOGIC;
bt1 : in STD_LOGIC;--btn zamykanie
bt2 : in STD_LOGIC;--btn otwieranie
krancEND : in STD_LOGIC;--active 0
krancPOC : in STD_LOGIC;--active 0
OVERLOAD : inout STD_LOGIC;--active 0 (przeciazenie silnika)
DIRPIN : out STD_LOGIC;--Direction Motor ( 0 - FORWARD)
SLPPIN : out STD_LOGIC;--SLEEP PIN Mostek (0 - mostek zablokowany)
pb1_Zam : out STD_LOGIC;--gdy 0 zamykanie
pb2_Otw : out STD_LOGIC;--gdy 0 otwieranie
AWARIA : out STD_LOGIC;--gdy 0 awaria ukladu
LEDZAMKNIETE : out STD_LOGIC;--gdy 1 LED zamkniete
LEDOTWARTE : out STD_LOGIC;--gdy 1 LED otwarte
LEDPRACA : out STD_LOGIC;--gdy 1 LED Zamykanie/otwieranie
LEDAWARIA : out STD_LOGIC);--gdy 1 LED awaria;
end component;
component WEKTORY_WYPELNIEN is
port ( clk :in std_logic;
pushB :in std_logic;
data : out std_logic_vector(7 downto 0) );
end component;

component pwm is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
ena : in STD_LOGIC;
duty : in STD_LOGIC_VECTOR (7 downto 0);
pwm_out : out STD_LOGIC_VECTOR (0 downto 0);
pwm_n_out : out STD_LOGIC_VECTOR (0 downto 0));
end component;

signal data1_signal :STD_LOGIC_VECTOR (7 downto 0);
signal data2_signal :STD_LOGIC_VECTOR (7 downto 0);
signal b1_DB :STD_LOGIC;
signal b2_DB :STD_LOGIC;
signal clk_1MHz :STD_LOGIC;
signal pwm1_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm2_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm1_n_out :STD_LOGIC_VECTOR (0 downto 0);
signal pwm2_n_out :STD_LOGIC_VECTOR (0 downto 0);
signal pb1_Zam :STD_LOGIC;
signal pb2_Otw :STD_LOGIC;
begin
C1: divider port map (CLK, clk_1MHz);
C2: debounce port map (clk_1MHz, b1, b1_DB);
C3: debounce port map (clk_1MHz, b2, b2_DB);
C4: DMASM port map (clk_1MHz,
RST,
b1_DB,
b2_DB,
krancEND,
krancPOC,
OVERLOAD,
DIRPIN,
SLPPIN,
pb1_Zam,
pb2_Otw,
AWARIA,
LEDZAMKNIETE,
LEDOTWARTE,
LEDPRACA,
LEDAWARIA);
C5: WEKTORY_WYPELNIEN port map (clk_1MHz, pb1_Zam, data1_signal);
C6: WEKTORY_WYPELNIEN port map (clk_1MHz, pb2_Otw, data2_signal);
C7: pwm port map (clk_1MHz, RST, '1', data1_signal, pwm1_out, pwm1_n_out);
C8: pwm port map (clk_1MHz, RST, '1', data2_signal, pwm2_out, pwm2_n_out);
pwmOut <= pwm1_out when DIRPIN = '0' else pwm2_out;
end Behavioral;



A to plik Projekt.ucf (dla Elbert V2):

[code]
NET "CLK" LOC = P129;
NET "CLK" PERIOD = 10MHz;

NET "RST" PULLUP;
NET "RST" LOC = "P76";

NET "b1" PULLUP;
NET "b1" LOC = "P80";

NET "b2" PULLUP;
NET "b2" LOC = "P79";

NET "OVERLOAD" PULLUP;
NET "OVERLOAD" LOC = "P78";

NET "krancEND" PULLUP;
NET "krancEND" LOC = "P58";

NET "krancPOC" PULLUP;
NET "krancPOC" LOC = "P59";


NET "DIRPIN" LOC = "P21";

NET "SLPPIN" LOC = "P18";

NET "AWARIA" LOC = "P20";

NET "pwmOut" LOC = "P19";

NET "LEDZAMKNIETE" LOC = "P55"; //LED1
NET "LEDOTWARTE" LOC = "P54"; //LED2
NET "LEDPRACA" LOC = "P51"; //LED3
NET "LEDAWARIA" LOC = "P46"; //LED8
[/code}

Reszta plików bez zmian (poza tym, ze teraz jest po jednej definicji dla każdego entity)

Może Ty podsuniesz mi jakiś pomysł co jest nie tak?

Pozdrawiam
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

Spróbuj przeczyścić projekt tj. wejdź w Project > Cleanup Project Files, a potem wszystko skompiluj od nowa.
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Sterownik rolet na CPLD/FPGA

Przyczyna była inna:

NET "WyjPWM<0>" LOC = "P19";

ten sygnał to był wektor brakowało '<0>' w opisie w pliku ucf

Uruchomiłem układ przy pomocy oscyloskopu i analizatora stanów i wydaje mi się iż działa całkiem prawidłowo ;)

PozdrawiamTen post został edytowany przez Autora dnia 21.09.17 o godzinie 15:27
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

Bo pewnie użyłeś tego: STD_LOGIC_VECTOR (0 downto 0) jako typu (co zresztą widać) - coś o czym mówiłem że się nie robi :D
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Sterownik rolet na CPLD/FPGA

Tak zgadza się, ale dlatego że ten generyczny generator pwm tego wymagał (on ogólnie był wielofazowy - stąd taki wymóg dla sygnału wyjściowego gdy była tylko jedna faza).

Pozdrawiam
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

Niezupełnie - otóż możesz śmiało zmienić typ wyjścia PWM z STD_LOGIC_VECTOR(0 downto 0) na STD_LOGIC. Wtedy wystarczy ze w przypadku multipleksera napiszesz:
  WyjPWM<= pwm1_out(0) when DIRPIN='0' else pwm2_out(0);
i masz problem z wektorami rozwiązany na portach :) To samo poczynić możesz w generatorze PWM tj. port wyjściowy pwm_out zamień na sygnał tj dopisz dodatkowy sygnał:
 pwm_out : STD_LOGIC_VECTOR(phases-1 DOWNTO 0); 
oraz dopisz nowe wyjście np:
 pwm_out_2 : out STD_LOGIC; 
. I teraz wystarczy, że za procesem dopiszesz
 pwm_out_2 <= pwm_out(0); 
i nie bawisz się w wektory. Tyle :)Ten post został edytowany przez Autora dnia 21.09.17 o godzinie 16:50
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

Jako ciekawostkę dodam, że o ile VHDL wymaga tego aby po obu stronach wyrażeń były obiekty tych samych typów dosłownie (po angielsku mówi się, że jest to "conservative"), o tyle w Verilogu czy SystemVerilogu masz po prostu dwa marne typy przeważnie - albo "wire" albo "reg" i czy użyjesz np: wire a, wire [0:0] b; i zrobisz przypisanie a = b czy b = a czy a = b[0] czy b[0] = a czy te 4 rzeczy ale z przypisaniem nieblokującym tj. "<="; to kompilatorowi to zwisa i sam ci dopasuje (to samo z reg czy też w przypadku mieszania i jednego i drugiego typu, zgodnie z regułami tego języka). Niemniej i w przypadku Veriloga koniec końców jeśli na porcie jest wektor to i w pliku UCF musi być też "nawias z numerem". To tak na boku :)Ten post został edytowany przez Autora dnia 21.09.17 o godzinie 17:26
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Sterownik rolet na CPLD/FPGA

Jakub,

uruchomiłem sterownik rolet z silnikiem (sterowanym przez mostek MOSFET) i układ działa poprawnie (trzeba jeszcze dostroić trochę zależności czasowe - ale z tym poradzę sobie już sam).
Jestem Ci bardzo wdzięczny, bez twojej pomocy nie udało by mi się osiągnąć tego celu.

Mam już też nowe pomysły na projekty z użyciem FPGA.

Pozdrawiam :)
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

Drobiazg :)
Maciej G.

Maciej G. Projektant /
Programista, Famor
S.A.

Temat: Sterownik rolet na CPLD/FPGA

Cześć,

ulepszyłem projekt sterownika rolet, teraz działa następująco:

1) Po naciśnięciu przycisku zamykania robi "miękki start silnika" - generuje wypełnienia rosnące przez 8 sekund (od zera do 100% w 32 krokach), a potem podtrzymuje wypełnienie 100% do zmiany stanu na zamknięte (sygnały z czujników krańcowych). Po wejściu do stanu zamknięte generuje przez 8 sekund malejące opóźnienia (od 100% do zera - dopchnięcie rolet).

2) To samo co w punkcie 1 tylko dla otwierania (czyli też mamy miękki start i zakończenie "dopchnięcie rolet" tylko w odwrotnej pozcji). Zredukowało to mocno stany nieustalone silnika podczas rozruchu i hamowania.

3) Sygnał "OVERLOAD" generowany przez komparator podłączony do wyjścia pomiarowego prądu płynącego prze mostek (silnik) powoduje przejście do stanu awaria, który trzeba zlikwidować przyciskiem "Reset" - oznacza on zacięcie rolet z jakiejś przyczyny.

Mam teraz 4 generatory PWM przełączane sygnałem sterującym z maszyny stanów oraz jednostki "WEKTORY_REVERSE" generujące wektory wypełnień dla PWM w odwrotnym porządku.

Układ zajmuje teraz ponad 500 LUT i działa poprawnie. Dalszy etap to będzie komunikacja po "CAN BUS" do wyświetlania stanu sterownika.

Pozdrawiam
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

No i gitara :) Pamiętaj tylko, że w przypadku układów programowalnych mówi się o elementach logicznych (LUT czyli tablica funkcji to tylko jeden ze składników całego elementu logicznego). Elementy te oczywiście zwane są inaczej i przez Alterę i przez Xilinxa (ta pierwsza firma mówi dosłownie o LE czyli Logic Elements lub też w nowszych układach takich jak Cyclone V o ALM czyli Adaptative Logic Modules, natomiast Xilinix mówi o CLB czyli Configurable Logic Block).
Jakub Tyburski

Jakub Tyburski Asystent dydaktyczny
- Wojskowa Akademia
Techniczna w War...

Temat: Sterownik rolet na CPLD/FPGA

Odnośnie jeszcze projektu: w przypadku silnika oczywista sprawa, że powinny być "miękkie starty" (zwane z angielska "soft starts") i są one jak najbardziej powszechnie stosowane w różnych rozwiązaniach (głównie po to, aby czasami nie powstały za duże prądy w trakcie rozruchów silnika, a tym samym aby czasami coś się nie "rozdupczyło"). Mało - zrobiłeś jeszcze "miękkie hamowanie" jak w przypadku wind, choć nie jest to hamowanie znane dobrze z wind ZREMB-u w 10-piętrowych blokach z wielkiej płyty - zapewne jako młodzieniec jeździłeś u siebie takowymi i pamiętasz jeszcze, że było tam zrobione jeszcze tak, że gdy winda dojeżdzała do wybranego pietra to przed samym końcem silnik nagle gwałtowniej zwalniał i przez chwilę kabina podjeżdzała już na centymetry do drzwi. Innymi słowy: było to prowizoryczne "miękkie hamowanie" (obecne wyparte już dawno przez dobrej klasy falowniki, które robią to znacznie płynniej). O dziwo takie windy jeszcze gdzieniegdzie w stolicy się uchowały i mam okazję nimi jeszcze jeździć od czasu do czasu :). Zastanawia mnie tylko kwestia aż czterech generatorów PWM - wcześniej były dwa i jeden był stosowany gdy silnik "kręcił się" do przodu, a drugi gdy "kręcił się" do tyłu (wejście DIRPIN) - a teraz co - po dwa generatory do przodu i tyłu ze względu na "miękkie starty" i "miękkie hamowania"? Nie to że się czepiam, bo zależy jak tam przyjąłeś założenia i też może to być - aczkolwiek ja pracownik "nauki" więc mnie za takie marnotrawstwo by zaraz zrugali, bo tak mnie uczyli, żeby wiele razy tam gdzie się da nie stosować tego samego komponentu (chyba, że to rozwiązanie paradoksalnie zajmie mniej elementów logicznych, bo czasem tak się zdarza to wtedy można tak zrobić, a w innych przypadkach już nie). I już :)Ten post został edytowany przez Autora dnia 01.10.17 o godzinie 13:39



Wyślij zaproszenie do