VHDL Kapazitätsmessung - das Ergebnis

Gespeichert von artbody am Fr., 25.08.2017 - 23:41

Wie im einleitenden Artikel bereits beschrieben und angedeutet entstand so mit einem CPLD ein hochgenaues Kapazitätsmessgerät mit SPI Interface und einem Pyboard, welches dann alle Daten in ein Logfile schrieb.

Zur Anwendung kam hier ein MAX II EPM240T100 von Altera. Ein PyBoard welches über SPI die Daten empfing und diese in ein LogFile auf eine Speicherkarte schrieb.

Zuerst der Plan welcher mit Quartus erstellt wurde.

m20171105test.jpg

Links oben sind die 6 Kanäle, welche vom Pyboard ausgewählt werden können. Rechts oben die entsprechende Auswerteinheit und im unteren Teil die SPI Schnittstelle.Die FSM (Finite State Machine) ungefähr in der Mitte zur Steuerung der Funktionsblöcke.

Nur ein paar wenige bereits im Program enthaltene Funktionsblöcke (MUX, LPM_Counter) wurden verwendet.

Alles andere wurde in VHDL erstellt.

Der Decoder

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;
 
entity Decoder is
   port(E : in std_logic;
        din : in std_logic_vector(2 downto 0);
        dout : out std_logic_vector(5 downto 0));
end Decoder;
 
architecture descript of Decoder is
begin
   process (E,din)
        begin
            if E='1' then
                case din is
                    when "000" =>
                        dout <="000001";
                    when "001" =>
                        dout <="000010";
                    when "010" =>
                        dout <="000100";
                    when "011" =>
                        dout <="001000";
                    when "100" =>
                        dout <="010000";
                    when "101" =>
                        dout <="100000";
                    when others =>
                        dout <="000000";
                end case;
            else
                dout <="000000";
            end if;
    end process;
    
 
end descript;

debounce ebenso

 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity debounce is
 
Port (
        CLK      : in  std_logic; -- system clock
        async_in : in  std_logic; --  output data are valid
          sync_out : out std_logic
    );
end debounce;
 
architecture RTL of debounce is
 
signal ff1_out  : std_logic;
--signal ff2_out  : std_logic;
begin
    process
        begin -- Einsynchronisieren
            wait until rising_edge(CLK);
            ff1_out <= async_in;
            sync_out <= ff1_out;
    end process;
 
end RTL;

Die Finite State Machine zur Steuerung der Einzelnen Funktionsblöcke

 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity FSM is
    Port (  
        rst : IN STD_LOGIC := '0';
      clock : IN STD_LOGIC;
      EN : IN STD_LOGIC := '0';
      --counter : IN STD_LOGIC_VECTOR(23 DOWNTO 0) := "000000000000000000000000";
      ENcounter : OUT STD_LOGIC;
      CLRcounter : OUT STD_LOGIC;
      EXTI4 : OUT STD_LOGIC;
      --EN_input : OUT STD_LOGIC;
      --EN_shift : OUT STD_LOGIC;
        ShiftClock : OUT STD_LOGIC
      --ShiftLoad : OUT STD_LOGIC
    );
end FSM;
 
 
architecture Verhalten of FSM is
type state_t is (reset, idle, count_up, shift_load, shift_loadin, done); --EXTI_4,
signal state : state_t := idle;
signal counter : unsigned(25 downto 0) := "00000000000000000000000000";
 
 
 
begin
 
    process begin
        wait until rising_edge(clock);
        ENcounter <= '0';
        CLRcounter <= '0';
        EXTI4 <= '0';
      --EN_input <= '0';
      --EN_shift <= '0';
        ShiftClock <= '0';
      --ShiftLoad <= '0';
        
      case state is
            when reset =>  
                counter <= (others=>'0');
                state   <= idle;
                CLRcounter <= '1';
                
            when idle  =>  
                CLRcounter <= '1';
                if (EN='1') then
                    state <= count_up;
                else
                    state   <= idle;
                end if;
            when count_up =>
                CLRcounter <= '0';
                ENcounter <= '1';
                counter <= counter+1;
                IF ((counter(25 DOWNTO 0) = "10111110101111000010000000")) THEN
                                ---------5 000   00000000000001001110001000
                                --------50 000   00000000001100001101010000
                                -----50 000 00   00010011000100101101000000
                                ----50 000 000   10111110101111000010000000  
                    state <= shift_load;
                    -- Inserting 'else' block to prevent latch inference
            ELSE
               state <= count_up;
            END IF;
            when shift_load =>
                ENcounter <= '0';
                --EN_shift <= '1';
                --ShiftLoad <= '1';
                state <= shift_loadin;
            when shift_loadin =>
                
                --EN_shift <= '1';
                --ShiftLoad <= '1';
                ShiftClock <= '1';
                --state <= EXTI_4;
                state <= done;
            --when EXTI_4 =>
                --EN_shift <= '1';
                
                --state <= done;
            when done =>
                EXTI4 <= '1';
                state <= done;
      end case;
        
      if (rst='1') then
         state <= reset;
      end if;
   end process;
   --rowNr <= std_logic_vector(reg_rowNr);
    --colNr <= std_logic_vector(reg_colNr);
   --dout <= std_logic_vector(counter);
    
 
end Verhalten;              
 

 

Nun fehlte mir noch das SPI Interface, welches aber nicht von mir geschrieben ist

von AUTHORS: Jakub Cabal :

--------------------------------------------------------------------------------
-- PROJECT: SPI MASTER AND SLAVE FOR FPGA
--------------------------------------------------------------------------------
-- NAME:    SPI_SLAVE
-- AUTHORS: Jakub Cabal <jakubcabal@gmail.com>
-- LICENSE: LGPL-3.0, please read LICENSE file
-- WEBSITE: https://github.com/jakubcabal/spi-fpga
--------------------------------------------------------------------------------
-- COPYRIGHT NOTICE:
--------------------------------------------------------------------------------
-- SPI MASTER AND SLAVE FOR FPGA
-- Copyright (C) 2016 Jakub Cabal
--
-- This source file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This source file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/&gt;.
--------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

-- THE SPI SLAVE MODULE SUPPORT ONLY SPI MODE 0 (CPOL=0, CPHA=0)!!!

entity SPI_SLAVE is
    Port (
        CLK      : in  std_logic; -- system clock
        RST      : in  std_logic; -- high active synchronous reset
        -- SPI SLAVE INTERFACE
        SCLK     : in  std_logic; -- SPI clock
        CS_N     : in  std_logic; -- SPI chip select, active in low
        MOSI     : in  std_logic; -- SPI serial data from master to slave
        MISO     : out std_logic; -- SPI serial data from slave to master
        -- USER INTERFACE
        DIN      : in  std_logic_vector(31 downto 0); -- input data for SPI master
        DIN_VLD  : in  std_logic; -- when DIN_VLD = 1, input data are valid
        READY    : out std_logic; -- when READY = 1, valid input data are accept
        DOUT     : out std_logic_vector(31 downto 0); -- output data from SPI master
        DOUT_VLD : out std_logic  -- when DOUT_VLD = 1, output data are valid
    );
end SPI_SLAVE;

architecture RTL of SPI_SLAVE is

    signal spi_clk_reg        : std_logic;
    signal spi_clk_redge_en   : std_logic;
    signal spi_clk_fedge_en   : std_logic;
    signal bit_cnt            : unsigned(4 downto 0);
    signal bit_cnt_max        : std_logic;
    signal last_bit_en        : std_logic;
    signal load_data_en       : std_logic;
    signal data_shreg         : std_logic_vector(31 downto 0);
    signal slave_ready        : std_logic;
    signal shreg_busy         : std_logic;
    signal rx_data_vld        : std_logic;

begin

    -- -------------------------------------------------------------------------
    --  SPI CLOCK REGISTER
    -- -------------------------------------------------------------------------

    -- The SPI clock register is necessary for clock edge detection.
    spi_clk_reg_p : process (CLK)
    begin
        if (rising_edge(CLK)) then
            if (RST = '1') then
                spi_clk_reg <= '0';
            else
                spi_clk_reg <= SCLK;
            end if;
        end if;
    end process;

    -- -------------------------------------------------------------------------
    --  SPI CLOCK EDGES FLAGS
    -- -------------------------------------------------------------------------

    -- Falling edge is detect when SCLK=0 and spi_clk_reg=1.
    spi_clk_fedge_en <= not SCLK and spi_clk_reg;
    -- Rising edge is detect when SCLK=1 and spi_clk_reg=0.
    spi_clk_redge_en <= SCLK and not spi_clk_reg;

    -- -------------------------------------------------------------------------
    --  RECEIVED BITS COUNTER
    -- -------------------------------------------------------------------------

    -- The counter counts received bits from the master. Counter is enabled when
    -- falling edge of SPI clock is detected and not asserted CS_N.
    bit_cnt_p : process (CLK)
    begin
        if (rising_edge(CLK)) then
            if (RST = '1') then
                bit_cnt <= (others => '0');
            elsif (spi_clk_fedge_en = '1' and CS_N = '0') then
                if (bit_cnt_max = '1') then
                    bit_cnt <= (others => '0');
                else
                    bit_cnt <= bit_cnt + 1;
                end if;
            end if;
        end if;
    end process;

    -- The flag of maximal value of the bit counter.
    bit_cnt_max <= '1' when (bit_cnt = "11111") else '0';

    -- -------------------------------------------------------------------------
    --  LAST BIT FLAG REGISTER
    -- -------------------------------------------------------------------------

    -- The flag of last bit of received byte is only registered the flag of
    -- maximal value of the bit counter.
    last_bit_en_p : process (CLK)
    begin
        if (rising_edge(CLK)) then
            if (RST = '1') then
                last_bit_en <= '0';
            else
                last_bit_en <= bit_cnt_max;
            end if;
        end if;
    end process;

    -- -------------------------------------------------------------------------
    --  RECEIVED DATA VALID FLAG
    -- -------------------------------------------------------------------------

    -- Received data from master are valid when falling edge of SPI clock is
    -- detected and the last bit of received byte is detected.
    rx_data_vld <= spi_clk_fedge_en and last_bit_en;

    -- -------------------------------------------------------------------------
    --  SHIFT REGISTER BUSY FLAG REGISTER
    -- -------------------------------------------------------------------------

    -- Data shift register is busy until it sends all input data to SPI master.
    shreg_busy_p : process (CLK)
    begin
        if (rising_edge(CLK)) then
            if (RST = '1') then
                shreg_busy <= '0';
            else
                if (DIN_VLD = '1' and (CS_N = '1' or rx_data_vld = '1')) then
                    shreg_busy <= '1';
                elsif (rx_data_vld = '1') then
                    shreg_busy <= '0';
                else
                    shreg_busy <= shreg_busy;
                end if;
            end if;
        end if;
    end process;

    -- The SPI slave is ready for accept new input data when CS_N is assert and
    -- shift register not busy or when received data are valid.
    slave_ready <= (CS_N and not shreg_busy) or rx_data_vld;
    
    -- The new input data is loaded into the shift register when the SPI slave
    -- is ready and input data are valid.
    load_data_en <= slave_ready and DIN_VLD;

    -- -------------------------------------------------------------------------
    --  DATA SHIFT REGISTER
    -- -------------------------------------------------------------------------

    -- The shift register holds data for sending to master, capture and store
    -- incoming data from master.
    data_shreg_p : process (CLK)
    begin
        if (rising_edge(CLK)) then
            if (load_data_en = '1') then
                data_shreg <= DIN;
            elsif (spi_clk_redge_en = '1' and CS_N = '0') then
                data_shreg <= data_shreg(30 downto 0) & MOSI;
            end if;
        end if;
    end process;

    -- -------------------------------------------------------------------------
    --  MISO REGISTER
    -- -------------------------------------------------------------------------

    -- The output MISO register ensures that the bits are transmit to the master
    -- when is not assert CS_N and falling edge of SPI clock is detected.
    miso_p : process (CLK)
    begin
        if (rising_edge(CLK)) then
            if (load_data_en = '1') then
                MISO <= DIN(31);
            elsif (spi_clk_fedge_en = '1' and CS_N = '0') then
                MISO <= data_shreg(31);
            end if;
        end if;
    end process;

    -- -------------------------------------------------------------------------
    --  ASSIGNING OUTPUT SIGNALS
    -- -------------------------------------------------------------------------
    
    READY    <= slave_ready;
    DOUT     <= data_shreg;
    DOUT_VLD <= rx_data_vld;

end RTL;

 

So nun fehlt nur noch das PyBoard

# main.py -- put your code here!
import pyb
from pyb import Pin
import math, micropython, array
import stm
from stm import mem32
 
debug=0
 
#PA0 >  8  MOSI EN - in
#PC0 > 7  EN - in
#PC1 < 4 EXTI4 - out
#PA1 > 1 MSCK -in
#PC2 < 3 MISO
 
#PA2 > 27 sel2
#PA3 > 28 sel1
#PA4 > 29 sel0
 
MISO_EN=Pin('A0',Pin.OUT)
EN=Pin('C0',Pin.OUT)
MSCK=Pin('A1',Pin.OUT)
MISO = Pin('C2', Pin.IN)
EXTI4 = Pin('C1', Pin.IN)
 
 
sel3=Pin('A5',Pin.OUT)
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! change top down TODO
sel2=Pin('A2',Pin.OUT)
sel1=Pin('A3',Pin.OUT)
sel0=Pin('A4',Pin.OUT)
 
chn=0
sel2.value(0)
sel1.value(0)
sel0.value(0)
channel_firstrun=[0,0,0,0,0,0]
channel_mask=[1,1,0,0,1,1]
channel_erg=[0,0,0,0,0,0,0,0,0,0,0,0]
channel_mittel=[0,0,0,0,0,0]
MISO_EN.value(1)
 
#MISO_EN.value(0)
EN.value(1)
varEXTI4=EXTI4.value()
 
MSCK.value(0)
MISO.value()
#x=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
 
 
pyb.LED(1).on()
pyb.delay(4000)  
pyb.LED(1).off()
pyb.LED(2).off()
pyb.LED(3).off()
pyb.LED(4).off()
#print(x)
 
blue = pyb.LED(4)
switch = pyb.Switch()
 
irq_request=0
firstrun=0
mittel=0
 
log = open('/sd/log.csv', 'w')  
# loop
while not switch(): #True:
 
    # wait for interrupt
    # this reduces power consumption while waiting for switch press
    #pyb.wfi()
 
    # start if switch is pressed
    #if switch():
         
        pyb.LED(1).on()                           # blue LED indicates file open
                        # delay avoids detection of multiple presses
         
         
    
    if chn > 5:
        chn=0
        if debug>0:
            print(channel_erg)
        log.write('{}\n'.format(channel_erg))  # write data to file
        channel_erg=[0,0,0,0,0,0,0,0,0,0,0,0]
    if debug==1:
            print(chn)
    
    if chn==0:
        sel2.value(0)
        sel1.value(0)
        sel0.value(0)
    if chn==1:
        sel2.value(0)
        sel1.value(0)
        sel0.value(1)
    if chn==2:
        sel2.value(0)
        sel1.value(1)
        sel0.value(0)
    if chn==3:
        sel2.value(0)
        sel1.value(1)
        sel0.value(1)
    if chn==4:
        sel2.value(1)
        sel1.value(0)
        sel0.value(0)
    if chn==5:
        sel2.value(1)
        sel1.value(0)
        sel0.value(1)
        
    EN.value(0)
    pyb.delay(10)
    pyb.LED(3).off()
    pyb.delay(200)
    pyb.LED(3).on()
    EN.value(1)
    #~ while not  EXTI4.value():    #warte auf interupt
        #~ pyb.LED(3).off()
        #~ pyb.delay(200)
        #~ pyb.LED(3).on()
        #~ pyb.delay(200)
    pyb.delay(2000)      
    
    
 
    pyb.LED(3).off()    
    
    n=0
    z=1
    MISO_EN.value(0)
    MSCK.value(0)
    xf=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    #x=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    for i in range(31):
        pyb.delay(1)
        MSCK.value(1)
        pyb.delay(1)
        
        MSCK.value(0)
        pyb.delay(1)
        xf[i+1]=MISO.value()
        
    z=1
    MISO_EN.value(1)
    for i in range(31):
        hn=xf[31-i]
        if hn== 1:
            n=n+z
        z=z*2
        
    if  n>4000000:
        channel_mask[chn] =0
        if debug==1:
            print(channel_mask)
    #if 200<n<600000:    
    if channel_firstrun[chn]==0:
            #if firstrun==0:
            #mittel=32*n
            channel_mittel[chn]=n
            firstrun=1
            channel_firstrun[chn]=1
            print("first run")
            print(channel_firstrun)
            
    mittel=channel_mittel[chn]
    mittel_tmp=mittel*32
    #if  mittel_tmp-10000<n<mittel_tmp+10000:
    #mittel=mittel_tmp-mittel+n
    #erg=mittel/32
    erg=(mittel_tmp-mittel+n)/32
    
    channel_mittel[chn]=erg
    channel_erg[chn*2]=n
    channel_erg[chn*2+1]=erg
        #print(n,erg,chn)
    #log.write('{},{},{}\n'.format(n,erg,chn))  # write data to file
    
    # chn erhöhen
    chn=chn+1
    if chn<5:
        while  channel_mask[chn] == 0:
        
            chn=chn+1
    #end if
    #print(chn,channel_mask,channel_erg)
    pyb.LED(1).off()                          # blue LED indicates file closed
    
    pyb.delay(200) # delay avoids detection of multiple presses
log.close()                         # close file    
pyb.LED(1).on()
pyb.LED(2).on()
pyb.LED(3).on()
pyb.LED(4).on()
pyb.delay(10000)
pyb.LED(1).off()
pyb.LED(2).off()
pyb.LED(3).off()
pyb.LED(4).off()

Entsprechend dem Plan wurden beide DevBoards miteinander verkabelt.

#PA0 >  8  MOSI EN - in
#PC0 > 7  EN - in
#PC1 < 4 EXTI4 - out
#PA1 > 1 MSCK -in
#PC2 < 3 MISO
 
#PA2 > 27 sel2
#PA3 > 28 sel1
#PA4 > 29 sel0

und los ging es mit Messungen

Sehr schön im unteren Teil zu erkennen ist dass es SO GUT WIE KEIN UNTERSCHIED an den Sensoren im Verlauf gibt. Ob mit oder ohne Pflanze.

Nur die Temperaturabhängigkeit ist am Keramikkondensator am stärksten.

Aber nochmal zurück zur reinen PflanzenSensor Messung

Hier besonders schön zu sehen sind die Schwankungen,

 

Ergebnis dieses Projektes

Eine kapazitive Messung der Bodenfeuchte ist nur in einer speziell geeichten Erde kurzzeitig möglich.

Über einen längeren Messzeitraum verändern sich die Werte bereits mit dem Wasser selbst. Kalk und Salzgehalt spielen dabei eine entscheidende Rolle und wenn dann noch Flüssigdünger mit ins Spiel kommt, kann eine Vorhersage, wann gegossen werden soll nicht mehr bestimmt werden.

 

bookpics