-- -- * Raspberry Pi BLDC/PMSM motor control design for RPi-MI-1 board * -- The toplevel component file -- -- (c) 2015 Martin Prudek -- Czech Technical University in Prague -- -- Project supervision and original project idea -- idea by Pavel Pisa -- -- Related RPi-MI-1 hardware is designed by Petr Porazil, -- PiKRON Ltd -- -- VHDL design reuses some components and concepts from -- LXPWR motion power stage board and LX_RoCoN system -- developed at PiKRON Ltd with base code implemented -- by Marek Peca -- -- license: GNU LGPL and GPLv3+ -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.util.all; entity rpi_pmsm_control is generic( pwm_width : natural:=11 ); port ( gpio2: in std_logic; -- SDA gpio3: in std_logic; -- SCL gpio4: in std_logic; -- CLK (gpio_clk) gpio14: in std_logic; -- Tx gpio15: in std_logic; -- Rx gpio17: inout std_logic; -- RTS (irc_i) gpio18: in std_logic; -- PWM0/PCMCLK (pwm_in/pwm1) gpio27: inout std_logic; -- SD1DAT3 (irc_b) gpio22: in std_logic; -- SD1CLK (pwm_dir_in/pwm1_en) gpio23: inout std_logic; -- SD1CMD (irc_a) gpio24: inout std_logic; -- SD1DAT0 (irc_a) gpio10: in std_logic; -- SPI0MOSI (spi_mosi) gpio9: out std_logic; -- SPI0MISO (spi_miso) gpio25: inout std_logic; -- SD1DAT1 (irc_b) gpio11: in std_logic; -- SPI0SCLK (spi_clk) gpio8: in std_logic; -- SPI0CE0 gpio7: in std_logic; -- SPI0CE1 (spi_ce) gpio5: in std_logic; -- GPCLK1 gpio6: in std_logic; -- GPCLK2 gpio12: in std_logic; -- PWM0 (pwm3) gpio13: in std_logic; -- PWM1 (pwm2) gpio19: in std_logic; -- PWM1/SPI1MISO/PCMFS (pwm2_en) gpio16: in std_logic; -- SPI1CE2 gpio26: in std_logic; -- SD1DAT2 (pwm3_en) gpio20: in std_logic; -- SPI1MOSI/PCMDIN/GPCLK0 gpio21: in std_logic; -- SPI1SCLK/PCMDOUT/GPCLK1 -- -- PWM -- Each PWM signal has cooresponding shutdown pwm: out std_logic_vector (1 to 3); shdn: out std_logic_vector (1 to 3); -- Fault/power stage status stat: in std_logic_vector (1 to 3); -- HAL inputs hal_in: in std_logic_vector (1 to 3); -- IRC inputs irc_a: in std_logic; irc_b: in std_logic; irc_i: in std_logic; -- Power status power_stat: in std_logic; -- ADC for current adc_miso: in std_logic; adc_mosi: out std_logic; adc_sclk: out std_logic; adc_scs: out std_logic; -- Extarnal SPI ext_miso: in std_logic; --master in slave out ext_mosi: in std_logic; --master out slave in ext_sclk: in std_logic; ext_scs0: in std_logic; ext_scs1: in std_logic; ext_scs2: in std_logic; -- RS-485 Transceiver rs485_rxd: in std_logic; rs485_txd: out std_logic; rs485_dir: out std_logic; -- CAN Transceiver can_rx: in std_logic; can_tx: in std_logic; -- DIP switch dip_sw: in std_logic_vector (1 to 3); --na desce je prohozene cislovanni -- Unused terminal to keep design tools silent dummy_unused : out std_logic ); end rpi_pmsm_control; architecture behavioral of rpi_pmsm_control is attribute syn_noprune :boolean; attribute syn_preserve :boolean; attribute syn_keep :boolean; attribute syn_hier :boolean; -- Actel lib component pll50to200 port ( powerdown, clka: in std_logic; lock, gla: out std_logic ); end component; component CLKINT port (A: in std_logic; Y: out std_logic); end component; component qcounter port ( clock: in std_logic; reset: in std_logic; a0, b0: in std_logic; qcount: out std_logic_vector (31 downto 0); a_rise, a_fall, b_rise, b_fall, ab_event: out std_logic; ab_error: out std_logic ); end component; component mcpwm is generic ( pwm_width: natural ); port ( clock: in std_logic; sync: in std_logic; --flag that counter "restarts-overflows" data_valid:in std_logic; --indicates data is consistent failsafe: in std_logic; --turn off both transistors en_p, en_n: in std_logic; --enable positive & enable shutdown match: in std_logic_vector (pwm_width-1 downto 0); --posion of counter when we swap output logic count: in std_logic_vector (pwm_width-1 downto 0); --we use an external counter out_p, out_n: out std_logic --pwm outputs: positive & shutdown --TODO add the rest of pwm signals, swap match to pwm_word ); end component; --frequency division by 12 component cnt_div is generic ( cnt_width_g : natural := 4 ); port ( clk_i : in std_logic; --clk to divide en_i : in std_logic; --enable bit? reset_i : in std_logic; --asynch. reset ratio_i : in std_logic_vector(cnt_width_g-1 downto 0);--initial value q_out_o : out std_logic --generates puls when counter underflows ); end component; component adc_reader is port ( clk: in std_logic; --input clk divided_clk : in std_logic; --divided clk - value suitable to sourcing voltage adc_reset: in std_logic; adc_miso: in std_logic; --spi master in slave out adc_channels: out std_logic_vector (35 downto 0); --consistent data of 3 channels adc_sclk: out std_logic; --spi clk adc_scs: out std_logic; --spi slave select adc_mosi: out std_logic; --spi master out slave in measur_count: out std_logic_vector(8 downto 0) --number of accumulated measurments ); end component; component dff3 is port( clk_i : in std_logic; d_i : in std_logic; q_o : out std_logic ); end component; --resetovatelna delicka component div128 is port ( clk_in: in std_logic; rst: in std_logic; fail_safe: out std_logic ); end component; component div256 is port ( clk_in: in std_logic; div256: out std_logic ); end component; signal adc_channels: std_logic_vector(71 downto 0); signal adc_m_count: std_logic_vector(8 downto 0); --clock signals for logic and master fail monitoring signal gpio_clk: std_logic; signal pll_clkin, pll_clkout, pll_lock: std_logic; signal clkmon_dly1, clkmon_dly2: std_logic; signal clkmon_fail, clkmon_fail_next: std_logic; signal clkmon_wdg: integer range 0 to 6; signal reset_sync, reset_async: std_logic; signal failsafe, next_failsafe: std_logic; --RPi SPI interface signals named aliases signal spi_clk, spi_ce, spi_mosi, spi_miso : std_logic; signal spiclk_old: std_logic_vector(1 downto 0); --pro detekci hrany SPI hodin --signal pwm_in, pwm_dir_in: std_logic; signal dat_reg : STD_LOGIC_VECTOR (127 downto 0); --shift register for spi signal position: std_logic_vector(31 downto 0); --pozice z qcounteru signal index_position: std_logic_vector(11 downto 0); --pozice irc_i signal ce0_old: std_logic_vector(1 downto 0); --pwm signals constant pwm_n: natural := 3; --number of pwm outputs --number of ticks per pwm cycle, 2^11=2048 constant pwm_period : std_logic_vector (pwm_width-1 downto 0) := (others=>'1'); type pwm_res_type is array(1 to 3) of std_logic_vector (pwm_width-1 downto 0); signal pwm_match: pwm_res_type; --point of reversion of pwm output, 0 to 2047 signal pwm_count: std_logic_vector (pwm_width-1 downto 0); --counter, 0 to 2047 signal pwm_sync_at_next: std_logic; signal pwm_sync: std_logic; signal pwm_en_p: std_logic_vector(1 to 3); signal pwm_en_n: std_logic_vector(1 to 3); signal pwm_sig: std_logic_vector(1 to 3); signal shdn_sig: std_logic_vector (1 to 3); signal income_data_valid: std_logic; signal spi_timout_pulse: std_logic; signal clk_4M17: std_logic; -- irc signals processing signal irc_i_prev: std_logic; -- function configuration options -- direct IRC channel A, B and I output to RPi/SoC signal fnccfg_direct_irc: std_logic; -- direct 3 phase PWM output signal fnccfg_direct_3ph_pwm: std_logic; -- PWM1 and PWM2 controlled by PWM input and direction signal fnccfg_pwm12_by_pwm_and_dir: std_logic; --filetered irc signals signal irc_a_dff3: std_logic; signal irc_b_dff3: std_logic; --16k3 clk signal signal clk_16k3: std_logic; --detekce prichazejicich prikazu po SPI -- attribute syn_noprune of gpio2 : signal is true; -- attribute syn_preserve of gpio2 : signal is true; -- attribute syn_keep of gpio2 : signal is true; -- attribute syn_hier of gpio2 : signal is true; begin -- PLL as a reset generator --zesileni signalu GPIO CLK copyclk2: CLKINT port map ( a => gpio4, y => gpio_clk ); pll: pll50to200 port map ( powerdown => '1', clka => pll_clkin, gla => pll_clkout, lock => pll_lock); reset_async <= not pll_lock or clkmon_fail; pll_clkin <= gpio_clk; qcount: qcounter port map ( clock => gpio_clk, reset => '0', a0 => irc_a_dff3, b0 => irc_b_dff3, qcount => position, a_rise => open, a_fall => open, b_rise => open, b_fall => open, ab_event => open, ab_error => open ); pwm_block: for i in pwm_n downto 1 generate pwm_map: mcpwm generic map ( pwm_width => pwm_width ) port map ( clock => gpio_clk, --50 Mhz clk from gpclk on raspberry sync => pwm_sync, --counter restarts data_valid => pwm_sync_at_next, failsafe => failsafe, -- -- pwm config bits & match word -- en_n => pwm_en_n(i), --enable positive pwm en_p => pwm_en_p(i), --enable "negative" ->activate shutdown match => pwm_match(i), count => pwm_count, -- outputs out_p => pwm_sig(i), --positive signal out_n => shdn_sig(i) --reverse signal is in shutdown mode ); end generate; div12_map: cnt_div generic map ( cnt_width_g => 4 ) port map( clk_i => gpio_clk, en_i =>'1', reset_i =>'0', ratio_i =>"1101", --POZN.: counter detekuje cnt<=1 q_out_o =>clk_4M17 ); clk_16k3_div: cnt_div generic map ( cnt_width_g => 8 ) port map( clk_i => gpio_clk, en_i => clk_4M17, reset_i => '0', ratio_i => "11111111", q_out_o => clk_16k3 ); spi_timeout_div : cnt_div generic map ( cnt_width_g => 7 ) port map( clk_i => gpio_clk, en_i => clk_16k3, reset_i => income_data_valid, ratio_i => "1111111", q_out_o => spi_timout_pulse ); -- ADC needs 3.2 MHz clk when powered from +5V Vcc -- 2.0 MHz clk when +2.7V Vcc -- on the input is 4.17Mhz,but this frequency is divided inside adc_reader by 2 to 2.08 Mhz, -- while we use +3.3V Vcc adc_reader_map: adc_reader port map( clk => gpio_clk, divided_clk => clk_4M17, adc_reset => income_data_valid, --reset at each SPI cycle,TODO: replace with PLL reset adc_miso => adc_miso, adc_channels => adc_channels, adc_sclk => adc_sclk, adc_scs => adc_scs, adc_mosi => adc_mosi, measur_count => adc_m_count ); dff3_a: dff3 port map( clk_i => gpio_clk, d_i => irc_a, q_o => irc_a_dff3 ); dff3_b: dff3 port map( clk_i => gpio_clk, d_i => irc_b, q_o => irc_b_dff3 ); dummy_unused <= gpio2 and gpio3 and gpio5 and gpio6 and gpio12 and gpio13 and gpio14 and gpio15 and gpio16 and gpio19 and gpio20 and gpio21 and gpio26 and stat(1) and stat(2) and stat(3) and hal_in(1) and hal_in(2) and hal_in(3) and irc_i and power_stat and adc_miso and rs485_rxd and can_rx and can_tx and irc_a and irc_b and gpio17 and gpio18 and gpio27 and gpio22 and gpio23 and gpio24 and gpio25 and gpio8 and ext_scs1 and ext_scs2 and ext_miso and ext_mosi and ext_sclk and ext_scs0; fnccfg_direct_irc <= not dip_sw(1); fnccfg_direct_3ph_pwm <= not dip_sw(2) and dip_sw(3); fnccfg_pwm12_by_pwm_and_dir <= not dip_sw(2) and not dip_sw(3); rs485_txd <= '1'; rs485_dir <= '0'; spi_clk <= gpio11; spi_ce <= gpio7; spi_mosi <= gpio10; gpio9 <= spi_miso; irc_direct_output_selection: process(fnccfg_direct_irc, irc_a, irc_b, irc_i) begin if fnccfg_direct_irc = '1' then gpio23 <= irc_a; gpio24 <= irc_a; gpio27 <= irc_b; gpio25 <= irc_b; gpio17 <= irc_i; else gpio23 <= 'Z'; gpio24 <= 'Z'; gpio27 <= 'Z'; gpio25 <= 'Z'; gpio17 <= 'Z'; end if; end process; pwm_output_selection: process(pwm_sig, shdn_sig, fnccfg_direct_3ph_pwm, fnccfg_pwm12_by_pwm_and_dir, fnccfg_pwm12_by_pwm_and_dir, gpio12, gpio13, gpio18, gpio19, gpio22, gpio26) begin if fnccfg_direct_3ph_pwm = '1' then pwm(1) <= gpio18; pwm(2) <= gpio13; pwm(3) <= gpio12; shdn(1) <= not gpio22; shdn(2) <= not gpio19; shdn(3) <= not gpio26; elsif fnccfg_pwm12_by_pwm_and_dir = '1' then -- pwm(1) <= pwm_in and not pwm_dir_in; pwm(1) <= gpio18 and not gpio22; -- pwm(2) <= pwm_in and pwm_dir_in;; pwm(2) <= gpio18 and gpio22; pwm(3) <= '0'; shdn(1) <= '0'; shdn(2) <= '0'; shdn(3) <= '1'; else pwm <= pwm_sig; shdn <= shdn_sig; end if; end process; process begin wait until (gpio_clk'event and gpio_clk='1'); if irc_i_prev = '0' and irc_i = '1' then index_position(11 downto 0)<=position(11 downto 0); end if; irc_i_prev<=irc_i; end process; process begin wait until (gpio_clk'event and gpio_clk='1'); IF pwm_count = std_logic_vector(unsigned(pwm_period) - 1) THEN --end of period nearly reached --fetch new pwm match data pwm_sync_at_next <= '1'; else pwm_sync_at_next <= '0'; end if; if pwm_sync_at_next='1' then --end of period reached pwm_count <= (others=>'0'); --reset counter pwm_sync <= '1'; -- inform PWM logic about new period start ELSE --end of period not reached pwm_count <= std_logic_vector(unsigned(pwm_count)+1); --increment counter pwm_sync <= '0'; END IF; end process; process begin --position is obtained on rising edge -> we should write it on next cycle wait until (gpio_clk'event and gpio_clk='1'); --SCLK edge detection spiclk_old(0)<=spi_clk; spiclk_old(1)<=spiclk_old(0); --SS edge detection ce0_old(0)<=spi_ce; ce0_old(1)<=ce0_old(0); if (spiclk_old="01") then --rising edge, faze cteni if (spi_ce = '0') then -- SPI CS must be selected -- shift serial data into dat_reg on each rising edge -- of SCK, MSB first dat_reg(127 downto 0) <= dat_reg(126 downto 0) & spi_mosi; end if; elsif (spiclk_old="10" ) then --falling edge, faze zapisu if (spi_ce = '0') then spi_miso <= dat_reg(127); --zapisujeme nejdriv MSB end if; end if; if (ce0_old = "10" ) then income_data_valid <= '1'; else income_data_valid <= '0'; end if; --sestupna hrana SS, pripravime data pro prenos if (ce0_old = "10" ) then dat_reg(127 downto 96) <= position(31 downto 0); --pozice dat_reg(95 downto 93) <= hal_in(1 to 3); --halovy sondy dat_reg(92 downto 81) <= index_position(11 downto 0); --position of irc_i dat_reg(80 downto 72) <=adc_m_count(8 downto 0); --count of measurments --data order schould be: ch2 downto ch0 downto ch1 dat_reg(71 downto 0) <= adc_channels(71 downto 0); --current mesurments spi_miso <= position(31); --prepare the first bit on SE activation elsif (ce0_old = "01") then --rising edge of SS, we should read the data pwm_en_p(1 to 3)<=dat_reg(126 downto 124); pwm_en_n(1 to 3)<=dat_reg(123 downto 121); --usable for up to 16-bit PWM duty cycle resolution (pwm_width): pwm_match(1)(pwm_width-1 downto 0)<=dat_reg(pwm_width+31 downto 32); pwm_match(2)(pwm_width-1 downto 0)<=dat_reg(pwm_width+15 downto 16); pwm_match(3)(pwm_width-1 downto 0)<=dat_reg(pwm_width-1 downto 0); end if; end process; clock_monitor: process (pll_clkout, gpio_clk, clkmon_dly1, clkmon_wdg, clkmon_fail_next) begin if pll_clkout'event and pll_clkout = '1' then clkmon_dly1 <= gpio_clk; clkmon_dly2 <= clkmon_dly1; if clkmon_dly1 = '0' and clkmon_dly2 = '1' then clkmon_wdg <= 6; clkmon_fail_next <= '0'; elsif clkmon_wdg > 0 then clkmon_wdg <= clkmon_wdg - 1; clkmon_fail_next <= '0'; else clkmon_wdg <= 0; clkmon_fail_next <= '1'; end if; clkmon_fail <= clkmon_fail_next; end if; end process; failsafe_spi_monitor: process (failsafe, spi_timout_pulse, income_data_valid) begin -- the failasfe signal from communication block if CRC is used -- or simple watchdog for SPI communication if income_data_valid = '1' then next_failsafe <= '0'; elsif spi_timout_pulse = '1' then next_failsafe <= '1'; else next_failsafe <= failsafe; end if; end process; async_rst: process (gpio_clk, reset_async, reset_sync) begin if reset_async = '1' then failsafe <= '1'; elsif gpio_clk'event and gpio_clk = '1' then failsafe <= next_failsafe or reset_sync; end if; end process; sync_rst: process (gpio_clk, reset_async) begin if gpio_clk'event and gpio_clk = '1' then reset_sync <= reset_async; end if; end process; end behavioral;