本帖最后由 hongniu 于 2015-6-25 22:57 編輯
今年“XILINX&高教社”杯SOC專項比賽使今年的省賽有意思了。我有幸參加了吉林省的比賽,測試時第32號,一共40隊。測試完畢后是第一名,成績比第二名多一倍。從8月29號開始學習FPGA,到9月12號比賽,雖然學的時間沒幾天,但是對這個題目還是有比較深刻的認識的,比賽時我帶實驗室兩個大一的師弟比賽,我負責所有程序的編寫和一半的硬件。比賽題目不知道的網上去下,這里不說了。
首先說我對這個開發板的評價--相當的不爽,首先電源,沒有單獨的3.3V輸出,只在普通I/O口每組有兩個,而且那個普通I/O口相當惡心,還不如做成插針的。還把大部分I/O弄成那種借口,我都忘了叫什么了,根本就買不到那個座,所以大部分I/O口都用不了...
對于ISE,BUG實在太多,第一個就是我的筆記本裝上用不了,XPS總是不能生成BIT文件,網上也沒有解決方案最后只能借別人電腦來用。用的時候也問題多多。。。
言歸正傳~~
我看到很多人的解決方案都是外面用個單片機,把FPGA當元件用(我感覺就是沒用,往那一擺)。這事鉆試題的空子,嚴格來說就是沒做出來。
分析一下試題:
1.阻容雙T網絡:開始一看挺蒙,后面一分析,就是個無源帶阻濾波器
2.中心頻率30K,帶寬100HZ...我是做不出來,當時就找了一個壓差比較大的參數(最大壓差有1.4V左右)
3.要掃頻,必須有DDS,無論你是用SPWM實現+低通,還是用D/A,或者現成DDS。
4.要測幅頻特性,就得有A/D,還要有峰值檢測電路。
5.要測相頻特性,就得把正弦波弄成方波,這里用比較器。
6.要在一個示波器上用XY同時顯示兩條曲線(有絕大多數人都用的兩個示波器的YT模式,雖然給分了,但嚴格說
不服合題意),要用到兩路D/A,這里我用的濾波整流,沒用芯片,下文詳細說。
7.顯示部分,題目要求用數碼管,可板上就4個數碼管,怎么顯示那么多東西啊!(這里題目有點問題,最可氣
的是我用12864同時顯示所有數據,竟然有評委說不符合要求!!!說必須用數碼管。。。其實我數碼管也做
了嘻嘻)
首先聲明,我說的純屬個人經驗,要是有技術上的問題還請雅正~
首先安裝ISE10.1我安了2天,每次都是ISE好用,XPS點生成BIT流文件后就是不運行,每次打開之前還有個警告,反正就是不好使。查了好多資料也不知道為什么。最后不行向師弟借的電腦,同樣的安裝步驟,人家的就好用...無語了。
對于教程,比賽之前何賓老師給每個賽區的參賽帶隊老師都做了培訓,那個培訓資料非常的好,網上能下到,大家按照那個教程(5篇PPT)看一看,就會用ISE和EDK了。最重要的就是講ISE和EDK那兩篇,其余了解。
使用軟件時要注意幾點:
1.所有路徑不能包涵中文,包括你的安裝路徑和打開的工程路徑。
2.使用EDK更新自定義IP時,make.file經常出問題,需要手動改動make.file文件,具體怎么該到官網上查。
3.使用EDK時,如果你的SOPC系統有結構錯誤,EDK往往不能提示(似乎只能檢查語法錯誤)。會表現為打開工程非常的慢,可能剛開始要30秒就能打開你的EDK工程,改一改后可能得10分鐘才能打開,甚至卡死。這時候不要以為是軟件有錯(我添加4個GPIO后,就會出現這個情況,當時我以為是軟件BUG),其實是你在配置時有某些細節沒有注意,不是軟件的錯。
還有些記不太清了,有新問題給我留言,我知道的一定解答。
這些日子忙著找工作,一直沒更新 現在終于簽了 呵呵
關于阻容雙T網絡,不是我做的,我對模電也不怎么感興趣。但有一點可以肯定,必須做成帶阻濾波器。因為當時比賽的時候我看到有人做成帶通的,似乎沒給分。我們做的帶寬要有1K左右,實在做不了太好了,電位最大差值有1.3V差不多就行,這塊沒怎么追求。
大體硬件結構就是:FPGA控制產生一個可調正弦波輸入雙T網絡。輸出后分兩路,一路經過峰值檢測電路,然后接AD求伏頻(這個電路上網去查吧)。另一路和輸入分別進過比較器過零比較,轉換成兩路方波,直接輸入兩個FPGA引腳,用門電路自己做相拼檢測電路自定義IP,這代碼之后我會發給大家。顯示部分用12864做的。
關于曲線顯示硬件:(為了省事,沒用DA)FPGA輸出X,Y兩路PWM,經低通整成直流,經電壓跟隨器->加法器(調零點用的,因為我需要負電壓才能用XY模式顯示
兩個曲線)->放大器(調峰值用的,讓曲線填滿示波器)->電壓跟隨器->輸入到示波器。
時間久了可能有部分忘了,哪里不明白大家問。
DDS叫數字頻率合成,一般有兩種實現方式:一種是控制AD輸出模擬量,另一種是通過SPWM和低通整流而來
本程序是第二種
工作機理有兩部分組成:PWM的產生和表
產生部分由兩個寄存器和一個計數器組成,計數器不聽累加同時與兩個寄存器作比較,兩個寄存器一個控制置底一個控制拉高,一般把一個固定就行了(有的地方說是一個控制周期一個控制高電平時間,其實是一樣的)
表里放的是一個正弦數組(產生正弦波),之后有一個寄存器控制讀取表的速度(間隔),把讀取出的表值存入上面那個沒固定的寄存器就行了
經過實驗 主頻50M的這種DDS最大產生到20K的正弦波就不穩定了不符合題意
1、用自定義IP向導新建IP模板后,在程序目錄的pcores文件夾下回出現IP核文件夾 文件夾中有三個子文件夾:datadevl hdl 首先手動修改data下的.mbp文件,將需要的IO口聲明:
......
PARAMETER C_FAMILY = virtex5, DT = STRING
## Ports
PORT DDS_PWM="", DIR= O,(這句是要添加的)
PORT SPLB_Clk = "", DIR = I, SIGIS = CLK, BUS= SPLB
2、在hdl文件件夾下的VHDL文件夾中的dds_ip.vhd中再次聲明端口
port
(
-- ADD USERPORTS BELOW THIS LINE ------------------
--USER portsadded here
DDS_PWM : out std_logic;
-- ADD USER PORTS ABOVE THIS LINE ------------------
同時在此文件中添加端口對應關系
port map
(
-- MAP USER PORTS BELOW THIS LINE ------------------
--USER ports mapped here
DDS_PWM =>DDS_PWM,
-- MAP USER PORTS ABOVE THIS LINE ------------------
3、在第二部路徑下的user_logic.vhd中編寫邏輯代碼:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
library proc_common_v2_00_a;
use proc_common_v2_00_a.proc_common_pkg.all;
entity user_logic is
generic
(
C_SLV_DWIDTH :integer := 32;
C_NUM_REG :integer := 1
);
port
(
DDS_PWM :out std_logic;
Bus2IP_Clk : in std_logic;
Bus2IP_Reset : in std_logic;
Bus2IP_Data : in std_logic_vector(0 to C_SLV_DWIDTH-1);
Bus2IP_BE : in std_logic_vector(0 toC_SLV_DWIDTH/8-1);
Bus2IP_RdCE : in std_logic_vector(0 to C_NUM_REG-1);
Bus2IP_WrCE : in std_logic_vector(0 to C_NUM_REG-1);
IP2Bus_Data : out std_logic_vector(0 to C_SLV_DWIDTH-1);
IP2Bus_RdAck : out std_logic;
IP2Bus_WrAck : out std_logic;
IP2Bus_Error : out std_logic;
);
attribute SIGIS : string;
attribute SIGIS ofBus2IP_Clk :signal is "CLK";
attribute SIGIS of Bus2IP_Reset : signal is"RST";
end entity user_logic;
architecture IMP of user_logic is
constantpwm_0000 : std_logic_vector(0 to 10):="11111010000";
constantpwm_0001 : std_logic_vector(0 to 10):="11111001111";
constantpwm_0002 : std_logic_vector(0 to 10):="11111001110";
。
正弦表,省略2000行
。
constantpwm_1498 : std_logic_vector(0 to 10):="10011100011";
constantpwm_1499 : std_logic_vector(0 to 10):="10011100010";
constantpwm_1500 : std_logic_vector(0 to 10):="10011100010";
signalslv_reg0 : std_logic_vector(0 to 31);
signalslv_reg_write_sel : std_logic_vector(0 to 0);
signalslv_reg_read_sel : std_logic_vector(0 to 0);
signalslv_ip2bus_data : std_logic_vector(0 to C_SLV_DWIDTH-1);
signalslv_read_ack : std_logic;
signalslv_write_ack : std_logic;
signal cycle : std_logic_vector(0 to31):="00000000000000000000011111010000";
signal hightime : std_logic_vector(0 to31):="00000000000000000000001111101000";
signal tcnt : std_logic_vector(0 to 31);
signal pwm : std_logic;
begin
DDS_PWM<=pwm;
hightime(1 to 31)<=cycle(0 to 30);
process(Bus2IP_Clk,Bus2IP_Reset)
begin
if(Bus2IP_Reset='1')then
tcnt<="00000000000000000000000000000000";
elsif(Bus2IP_Clk'event and Bus2IP_Clk='1')then
if(tcnt=hightime)then
pwm<='0';
tcnt<=tcnt+'1';
elsif(tcnt=cycle)then
pwm<='1';
tcnt<="00000000000000000000000000000000";
else tcnt<=tcnt+'1';
end if;
end if;
end process;
process(Bus2IP_Clk,Bus2IP_Reset)
begin
if(Bus2IP_Reset='1')then
cycle<="00000000000000000000011111010000";
elsif(Bus2IP_Clk'event and Bus2IP_Clk='1')then
case slv_reg0(21 to 31) is
when "00000000000" =>
cycle(21 to 31)<=pwm_0000(0 to 10);
when "00000000001" =>
cycle(21 to 31)<=pwm_0001(0 to 10);
when "00000000010" =>
cycle(21 to 31)<=pwm_0002(0 to 10);
...此出省略4000行 都是和上面一樣的格式
when "10111011011" =>
cycle(21 to 31)<=pwm_1499(0 to 10);
when "10111011100" =>
cycle(21 to 31)<=pwm_1500(0 to 10);
whenothers=>cycle<="00000000000000000000011111010000";
end case;
end if;
end process;
slv_reg_write_sel <= Bus2IP_WrCE(0 to 0);
slv_reg_read_sel <= Bus2IP_RdCE(0 to 0);
slv_write_ack <= Bus2IP_WrCE(0);
slv_read_ack <= Bus2IP_RdCE(0);
SLAVE_REG_WRITE_PROC : process( Bus2IP_Clk )is
begin
ifBus2IP_Clk'event and Bus2IP_Clk = '1' then
if Bus2IP_Reset = '1' then
slv_reg0 <= (others => '0');
else
case slv_reg_write_sel is
when "1" =>
for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
if ( Bus2IP_BE(byte_index) = '1' ) then
slv_reg0(byte_index*8 to byte_index*8+7) <=Bus2IP_Data(byte_index*8 to byte_index*8+7);
end if;
end loop;
when others => null;
end case;
end if;
end if;
end process SLAVE_REG_WRITE_PROC;
SLAVE_REG_READ_PROC : process(slv_reg_read_sel, slv_reg0 ) is
begin
caseslv_reg_read_sel is
when "1" => slv_ip2bus_data <=slv_reg0;
when others => slv_ip2bus_data <=(others => '0');
endcase;
end process SLAVE_REG_READ_PROC;
IP2Bus_Data <= slv_ip2bus_datawhen slv_read_ack = '1' else
(others => '0');
IP2Bus_WrAck <=slv_write_ack;
IP2Bus_RdAck <=slv_read_ack;
IP2Bus_Error <= '0';
end IMP;
比賽中提到要用數碼管顯示,一般單片機的做法是用軟件去掃描。這樣既浪費時間,又浪費FPGA的資源。這里發一個數碼管硬件掃描的自定義IP核,只要對四個寄存器寫入顯示的數,硬件就自動完成掃描了。
首先,在打data\smg_ip_v2_1_0.mbp文件中添加端口聲明:
...
...
## Ports
PORT SMGs_data_8Bit="", DIR = O, VEC= [0:7]
PORT SMGs_cs_4Bit="", DIR = O, VEC= [0:3]
PORT SPLB_Clk= "", DIR = I, SIGIS = CLK, BUS = SPLB
...
...
第二步,在hdl\vhdl\smg_ip.vhd中添加端口和端口映射:
...
...
port
(
-- ADD USERPORTS BELOW THIS LINE ------------------
--USER portsadded here
SMGs_data_8Bit : out std_logic_vector(0 to 7);
SMGs_cs_4Bit : outstd_logic_vector(0 to3);
-- ADD USER PORTS ABOVE THIS LINE ------------------
...
...
port map
(
-- MAP USER PORTS BELOW THIS LINE ------------------
--USER ports mapped here
SMGs_cs_4Bit => SMGs_cs_4Bit,
SMGs_data_8Bit =>SMGs_data_8Bit,
-- MAP USER PORTS ABOVE THIS LINE ------------------
...
...
最后就是user_logic.vhd里的邏輯代碼:
很多東西都是自動生成的,需要手動編寫的就是前面的自定義端口和邏輯進程
-- DO NOT EDIT BELOW THIS LINE --------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
library proc_common_v2_00_a;
use proc_common_v2_00_a.proc_common_pkg.all;
-- DO NOT EDIT ABOVE THIS LINE --------------------
--USER libraries added here
------------------------------------------------------------------------------
-- Entity section
------------------------------------------------------------------------------
-- Definition of Generics:
-- C_SLV_DWIDTH -- Slave interface data bus width
-- C_NUM_REG -- Number of software accessible registers
--
-- Definition of Ports:
-- Bus2IP_Clk -- Bus to IP clock
-- Bus2IP_Reset -- Bus to IP reset
-- Bus2IP_Data -- Bus to IP data bus
-- Bus2IP_BE -- Bus to IP byte enables
-- Bus2IP_RdCE -- Bus to IP read chip enable
-- Bus2IP_WrCE -- Bus to IP write chip enable
-- IP2Bus_Data -- IP to Bus data bus
-- IP2Bus_RdAck -- IP to Bus read transfer acknowledgement
-- IP2Bus_WrAck -- IP to Bus write transfer acknowledgement
-- IP2Bus_Error -- IP to Bus error response
------------------------------------------------------------------------------
entity user_logic is
generic
(
-- ADD USERGENERICS BELOW THIS LINE ---------------
--USERgenerics added here
-- ADD USERGENERICS ABOVE THIS LINE ---------------
-- DO NOTEDIT BELOW THIS LINE ---------------------
-- Busprotocol parameters, do not add to or delete
C_SLV_DWIDTH :integer := 32;
C_NUM_REG :integer := 1
-- DO NOTEDIT ABOVE THIS LINE ---------------------
);
port
(
-- ADD USERPORTS BELOW THIS LINE ------------------
--USER portsadded here
SMGs_data_8Bit : out std_logic_vector(0 to 7);
SMGs_cs_4Bit : out std_logic_vector(0 to 3);
-- ADD USERPORTS ABOVE THIS LINE ------------------
-- DO NOTEDIT BELOW THIS LINE ---------------------
-- Busprotocol ports, do not add to or delete
Bus2IP_Clk : in std_logic;
Bus2IP_Reset : in std_logic;
Bus2IP_Data : in std_logic_vector(0 to C_SLV_DWIDTH-1);
Bus2IP_BE : in std_logic_vector(0 toC_SLV_DWIDTH/8-1);
Bus2IP_RdCE : in std_logic_vector(0 to C_NUM_REG-1);
Bus2IP_WrCE : in std_logic_vector(0 to C_NUM_REG-1);
IP2Bus_Data : out std_logic_vector(0 to C_SLV_DWIDTH-1);
IP2Bus_RdAck : out std_logic;
IP2Bus_WrAck : out std_logic;
IP2Bus_Error : out std_logic
-- DO NOTEDIT ABOVE THIS LINE ---------------------
);
attribute SIGIS : string;
attribute SIGIS ofBus2IP_Clk :signal is "CLK";
attribute SIGIS ofBus2IP_Reset : signal is "RST";
end entity user_logic;
------------------------------------------------------------------------------
-- Architecture section
------------------------------------------------------------------------------
architecture IMP of user_logic is
--USER signal declarations added here, asneeded for user logic
signalsmg_data_out_i : std_logic_vector(0 to 7);
signalsmg_cs_i : std_logic_vector(0 to 3);
signaldiv_cnt : std_logic_vector(0 to 18);
signaldata4 : std_logic_vector(0 to 3);
------------------------------------------
-- Signals for user logic slave model s/waccessible register example
------------------------------------------
signalslv_reg0 : std_logic_vector(0 to C_SLV_DWIDTH-1);
signalslv_reg_write_sel : std_logic_vector(0 to 0);
signalslv_reg_read_sel : std_logic_vector(0 to 0);
signalslv_ip2bus_data : std_logic_vector(0 to C_SLV_DWIDTH-1);
signalslv_read_ack : std_logic;
signalslv_write_ack : std_logic;
begin
--USER logic implementation added here
------------------------------------------
-- Example code to read/write user logic slavemodel s/w accessible registers
--
-- Note:
-- The example code presented here is to showyou one way of reading/writing
-- software accessible registers implemented inthe user logic slave model.
-- Each bit of the Bus2IP_WrCE/Bus2IP_RdCEsignals is configured to correspond
-- to one software accessible register by thetop level template. For example,
-- if you have four 32 bit software accessibleregisters in the user logic,
-- you are basically operating on the followingmemory mapped registers:
--
-- Bus2IP_WrCE/Bus2IP_RdCE MemoryMapped Register
-- "1000" C_BASEADDR + 0x0
-- "0100" C_BASEADDR + 0x4
-- "0010" C_BASEADDR + 0x8
-- "0001" C_BASEADDR + 0xC
--
------------------------------------------
slv_reg_write_sel <=Bus2IP_WrCE(0 to 0);
slv_reg_read_sel <= Bus2IP_RdCE(0 to 0);
slv_write_ack <= Bus2IP_WrCE(0);
slv_read_ack <= Bus2IP_RdCE(0);
SMGs_data_8Bit <= smg_data_out_i;
SMGs_cs_4Bit <= smg_cs_i;
--其實這個文件真正需要自己編的就是以下幾個進程:
process(Bus2IP_Clk,Bus2IP_Reset) --用計數器產生分頻,因為掃描不能太快
begin
if(Bus2IP_Reset='1')then
div_cnt<="0000000000000000000";
elsif(Bus2IP_Clk'event andBus2IP_Clk='1')then
div_cnt<=div_cnt+1;
end if;
end process;
process(Bus2IP_Reset,Bus2IP_Clk,div_cnt(0 to1))--產生掃描信號
begin
if(Bus2IP_Reset='1')then
smg_cs_i<="0000";
elsif(Bus2IP_Clk 'event and Bus2IP_Clk='1')then
casediv_cnt(0 to 1) is
when"00"=> smg_cs_i(0 to3)<="0001";
when"10"=> smg_cs_i(0 to3)<="0010";
when"01"=> smg_cs_i(0 to3)<="0100";
when"11"=> smg_cs_i(0 to3)<="1000";
when others => smg_cs_i(0 to3)<="0000";
endcase;
end if;
end process;
process(smg_cs_i,slv_reg0)--根據不同的掃描信號,數據線上放不同的值
begin
case smg_cs_i(0 to 3)is
when "0001"=>data4(0 to 3)<=slv_reg0(0 to 3);--4位正好表示0-f
when "0010"=>data4(0 to 3)<=slv_reg0(4 to 7);
when "0100"=>data4(0 to 3)<=slv_reg0(8 to 11);
when "1000"=>data4(0 to 3)<=slv_reg0(12 to15);
whenothers => data4<="0000";
end case;
end process;
process(data4)--這個進程相當于解碼
begin
case data4(0 to 3) is
WHEN "0000"=>
smg_data_out_i(0 to 7) <="11111100";
WHEN "0001" =>
smg_data_out_i(0 to 7) <="01100000";
WHEN "0010" =>
smg_data_out_i(0 to 7) <="11011010";
WHEN "0011" =>
smg_data_out_i(0 to 7) <="11110010";
WHEN "0100" =>
smg_data_out_i(0 to 7) <="01100110";
WHEN "0101" =>
smg_data_out_i(0 to 7) <="10110110";
WHEN "0110" =>
smg_data_out_i(0 to 7) <="10111110";
WHEN "0111" =>
smg_data_out_i(0 to 7) <="11100000";
WHEN "1000" =>
smg_data_out_i(0 to 7) <="11111110";
WHEN "1001" =>
smg_data_out_i(0 to 7) <= "11110110";
WHEN "1010" =>
smg_data_out_i(0 to 7) <="11111010";
WHEN "1011" =>
smg_data_out_i(0 to 7) <="00111110";
WHEN "1100" =>
smg_data_out_i(0 to 7) <="10011100";
WHEN "1101" =>
smg_data_out_i(0 to 7) <="01111010";
WHEN "1110" =>
smg_data_out_i(0 to 7) <="10011110";
WHEN "1111" =>
smg_data_out_i(0 to 7) <="10001110";
WHEN OTHERS =>
smg_data_out_i(0 to 7) <= "00000000";
END CASE;
END PROCESS;
--以下是程序自動生成的,分別是與SOC接口寄存器的讀和寫。如果你要對reg讀取的話,要手動將下面的讀取進程刪除,因為VHDL語言中同一個寄存器不能在兩個進程里讀取。
-- implement slave model softwareaccessible register(s)
SLAVE_REG_WRITE_PROC : process( Bus2IP_Clk )is
begin
ifBus2IP_Clk'event and Bus2IP_Clk = '1' then
if Bus2IP_Reset = '1' then
slv_reg0 <= (others => '0');
else
case slv_reg_write_sel is
when "1" =>
for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
if ( Bus2IP_BE(byte_index) = '1' ) then
slv_reg0(byte_index*8 to byte_index*8+7) <=Bus2IP_Data(byte_index*8 to byte_index*8+7);
end if;
end loop;
when others => null;
end case;
end if;
end if;
end process SLAVE_REG_WRITE_PROC;
-- implement slave model software accessibleregister(s) read mux
SLAVE_REG_READ_PROC : process( slv_reg_read_sel,slv_reg0 ) is
begin
caseslv_reg_read_sel is
when "1" => slv_ip2bus_data <=slv_reg0;
when others => slv_ip2bus_data <=(others => '0');
endcase;
end process SLAVE_REG_READ_PROC;
------------------------------------------
-- Example code to drive IP to Bus signals
------------------------------------------
IP2Bus_Data <=slv_ip2bus_data when slv_read_ack = '1' else
(others => '0');
IP2Bus_WrAck <=slv_write_ack;
IP2Bus_RdAck <=slv_read_ack;
IP2Bus_Error <= '0';
end IMP;
發一下作品圖片吧:
這個是沒有開始顯示時的示波器圖片:用放大器和減法器調的示波器原點位置,示波器用的XY模式

這張是顯示的伏頻(上)相頻(下)曲線,伏頻還行吧相拼差了點...其實走了一個口字型掃描,之后把兩邊的豎線移到屏幕兩邊就行了,當時評委看到非常奇怪的去調那個秒格旋鈕,可是曲線不動,就問我怎么做的,而且很不理解怎么出來的兩條曲線,呵呵
這是顯示部分,不過評委說不合格,因為題目說要用數碼管顯示...無語

相位的正負判斷不出來..獻丑了

這個板子的IO口實在是頭痛,不得已自己做的轉接板...
數碼管也顯示了 呵呵 顯示的電壓值

長春工業大學的體育館外面,組織的挺正式

長春工業體育館二樓,體育館確實挺好,沒話說

這是比賽前做的北京賽區題目,兵乓球游戲機,練習用的

|