?? uart.vhd
字號(hào):
--總體設(shè)計(jì)思路:
--1)預(yù)計(jì)實(shí)現(xiàn)效果:通過(guò)串口調(diào)試助手,由PC發(fā)送數(shù)據(jù)給小系統(tǒng)板,板子接收到的數(shù)據(jù)在數(shù)碼管上即時(shí)顯示
-- 并且通過(guò)發(fā)送模塊發(fā)送回PC,在串口調(diào)試助手上顯示出來(lái)。
--2)模塊劃分:六大模塊,1:波特率發(fā)送模塊(clock_tx);2:fpga_to_pc 發(fā)送模塊 (uart_tx);3:采樣時(shí)鐘模塊(uart_re);
-- 4:pc_to_fpga接收模塊(uart_re);5:數(shù)碼管顯示模塊(segment);6:頂層模塊(top_level).
--3)基本設(shè)定:板上晶振頻率40M,發(fā)送數(shù)據(jù)波特率為19200,數(shù)據(jù)格式為1bit起始位(0)+8bit有效數(shù)據(jù)+1bit停止位(1)構(gòu)成一幀數(shù)據(jù).
--注:
--1)一般編寫程序在quartus中完成,在該目錄下新建modelsim的仿真目錄,
-- 調(diào)用modelsim仿真時(shí),添加quartus中的原始設(shè)計(jì)文件時(shí),
-- 若選擇copy形式,則quartus與modelsim中文件互相不影響;若選擇refenrence形式,
-- 則quartus中設(shè)計(jì)文件改變時(shí),會(huì)影響到modelsim中文件.
--2)若要精確的指向電平跳變點(diǎn),可以使用波形操作界面上的圖標(biāo)find next transition.
--模塊1:clock_tx:發(fā)送時(shí)鐘模塊,即波特率發(fā)生器.
--1) 設(shè)定
-- 預(yù)設(shè)發(fā)送數(shù)據(jù)波特率為19200,分頻時(shí)鐘需要利用計(jì)數(shù)器來(lái)實(shí)現(xiàn),計(jì)數(shù)值為40M/19200=2084.
--2) 設(shè)計(jì)
-- 通過(guò)計(jì)數(shù)器來(lái)分頻,在原始時(shí)鐘周期下計(jì)數(shù),判斷滿計(jì)數(shù)值一半時(shí)清零,同時(shí)對(duì)中間值取反,否則自加.則該中間值即為所分頻率.
--3) 仿真
-- 在modelsim中進(jìn)行功能仿真,原始時(shí)鐘為40M(25000ns)
--4) 調(diào)試
-- 下載程序,本部分沒有出現(xiàn)問(wèn)題.
--design file:clock_tx
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY clock_tx IS
PORT(
clk_in: IN STD_LOGIC;
rst: IN STD_LOGIC;
clk_out: OUT STD_LOGIC
);
END clock_tx;
ARCHITECTURE behav OF clock_tx IS
SIGNAL clk_count: STD_LOGIC_VECTOR(15 DOWNTO 0);
SIGNAL clk_bit: STD_LOGIC;
BEGIN
PROCESS(rst,clk_in)
BEGIN
IF rst='0' THEN
clk_count<="0000000000000000";
clk_bit<='0';
ELSIF clk_in'EVENT AND clk_in='1' THEN
IF(clk_count=1042) THEN
clk_count<="0000000000000000";
clk_bit<=not clk_bit;
ELSE
clk_count<=clk_count+1;
END IF;
END IF;
END PROCESS;
clk_out<=clk_bit;
END behav;
--testbench: tb1
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY tb1 IS
END tb1;
ARCHITECTURE be OF tb1 IS
COMPONENT clock_tx
PORT(
clk_in: IN STD_LOGIC;
rst: IN STD_LOGIC;
clk_out: OUT STD_LOGIC
);
END COMPONENT;
SIGNAL clk_in: STD_LOGIC;
SIGNAL rst: STD_LOGIC;
SIGNAL clk_out: STD_LOGIC;
CONSTANT clk_period: time:=25 ns;
BEGIN
uut:clock_tx PORT MAP(clk_in=>clk_in,rst=>rst,clk_out=>clk_out);
clk_gen: PROCESS
BEGIN
clk_in<='0';
WAIT FOR clk_period/2;
clk_in<='1';
WAIT FOR clk_period/2;
END PROCESS;
rst_gen: PROCESS
BEGIN
rst<='0';
WAIT FOR clk_period;
rst<='1';
WAIT ;
END PROCESS;
END be;
--模塊2 uart_tx:發(fā)送模塊
--1) 設(shè)定
-- 根據(jù)模塊1生成的發(fā)送時(shí)鐘波特率,按位將數(shù)據(jù)通過(guò)串口發(fā)送給PC機(jī),在串口通信助手上顯示出來(lái).
--2)設(shè)計(jì)
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY uart_tx IS
PORT(
rst: IN STD_LOGIC;
clk_in: IN STD_LOGIC;
send_data: IN STD_LOGIC; --來(lái)自接收模塊,表示一幀數(shù)據(jù)準(zhǔn)備好,等待本模塊接收.
data_in: IN STD_LOGIC_VECTOR(7 DOWNTO 0); --data from uart_re
busy: OUT STD_LOGIC; --when high ,it means data is sending
data_out: OUT STD_LOGIC --parallel data in , serial data out
);
END uart_tx;
ARCHITECTURE behav OF uart_tx IS
COMPONENT clock_tx
PORT(
clk_in: IN STD_LOGIC;
rst: IN STD_LOGIC;
clk_out: OUT STD_LOGIC
);
END COMPONENT;
COMPONENT clock_re
PORT(
rst: IN STD_LOGIC;
clk_in: IN STD_LOGIC;
clk_out: OUT STD_LOGIC
);
END COMPONENT;
SIGNAL clk_tx: STD_LOGIC;
SIGNAL data_count: INTEGER RANGE 0 TO 10;
SIGNAL data_buf: STD_LOGIC_VECTOR(9 DOWNTO 0);
SIGNAL data_temp: STD_LOGIC_VECTOR(7 DOWNTO 0);
signal clk_re: std_logic:='0';
signal num: std_logic_vector(2 downto 0);
BEGIN
uut: clock_tx PORT MAP(clk_in,rst,clk_tx);
uut_re:clock_re PORT MAP(rst,clk_in,clk_re);
PROCESS(rst,clk_in)
BEGIN
IF rst='0' THEN
data_temp<="11111111";
ELSIF clk_in'EVENT AND clk_in='1' THEN
IF send_data='1' THEN --uart_re模塊數(shù)據(jù)準(zhǔn)備好,將其接收進(jìn)來(lái).
data_temp<=data_in;
else
data_temp<=data_temp;
END IF;
END IF;
END PROCESS;
process(rst,clk_re) --因?yàn)閟end_data=1只持續(xù)很短的時(shí)間,約為1個(gè)clk_re周期,所以敏感信號(hào)采樣clk_re
begin
if rst='0' then
data_count<=10;
num<="000";
elsif clk_re'event and clk_re='1' then
if send_data='1' and data_count=10 then --數(shù)據(jù)接收進(jìn)來(lái),同時(shí)總線空閑.
data_count<=0; --重新開始一次新的發(fā)送
elsif data_count<10 then
num<=num+"001";
if num="111" then --8個(gè)clk_re周期構(gòu)成一個(gè)clk_tx周期,在此周期下數(shù)據(jù)位加1
num<="000";
data_count<=data_count+1; --data_count為10后,不清0,保證接收一幀數(shù)據(jù),發(fā)送一幀數(shù)據(jù).
end if;
else
data_count<=data_count; --對(duì)應(yīng)send_data/=1 and data_count=10
end if;
end if;
end process;
--之所以data_count要計(jì)數(shù)到10,是因?yàn)楦鶕?jù)上一進(jìn)程,計(jì)數(shù)歸零是在接收進(jìn)來(lái)數(shù)據(jù)的同時(shí),
--而此時(shí)clk_tx并沒有觸發(fā),參考modelsim仿真波形可知,發(fā)送第一位數(shù)據(jù)的起始,是在data_count
--數(shù)值為0和1處,所以,要發(fā)送完10位數(shù)據(jù),停止位所占的時(shí)間在data_count為9時(shí)開始,在為10時(shí)
--才能結(jié)束,因此data_count必須計(jì)數(shù)到10,否則有可能檢測(cè)不到停止位,從而產(chǎn)生錯(cuò)誤.
process(rst,clk_tx)
begin
if rst='0' then
data_buf<="1111111111"; --保證總線空閑時(shí)被拉高
elsif clk_tx'event and clk_tx='1' then
if data_count=0 then --data_count為0時(shí),裝入待發(fā)送數(shù)據(jù)
data_buf<='1' & data_temp & '0';
elsif data_count<10 then --移位發(fā)送
data_buf<='1' & data_buf(9 downto 1); --高位為1,保證發(fā)送完一幀后,總線拉高.
elsif data_count=10 then --為10后,data_buf為全1,保證總線拉高.
data_buf<=data_buf;
end if;
end if;
end process;
busy<='1' WHEN data_count<10 ELSE '0';
data_out<=data_buf(0) ; --發(fā)送最低位.
END behav;
--testbench: tb2
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY tb2 IS
END tb2;
ARCHITECTURE be OF tb2 IS
COMPONENT uart_tx IS
PORT(
rst: IN STD_LOGIC;
clk_in: IN STD_LOGIC;
send_data: IN STD_LOGIC;
data_in: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
busy: OUT STD_LOGIC;
data_out: OUT STD_LOGIC
);
END COMPONENT;
SIGNAL rst: STD_LOGIC;
SIGNAL clk_in: STD_LOGIC;
SIGNAL send_data: STD_LOGIC;
SIGNAL data_in: STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL busy: STD_LOGIC;
SIGNAL data_out: STD_LOGIC;
CONSTANT clk_in_period: time:=25ns;
CONSTANT clk_tx_period: time:=52083ns;
BEGIN
uut: uart_tx PORT MAP
(rst=>rst,clk_in=>clk_in,send_data=>send_data,data_in=>data_in,busy=>busy,data_out=>data_out);
clk_gen: PROCESS
BEGIN
clk_in<='0';
WAIT FOR clk_in_period/2;
clk_in<='1';
WAIT FOR clk_in_period/2;
END PROCESS;
rst_gen: PROCESS
BEGIN
rst<='0';
WAIT FOR clk_tx_period;
rst<='1';
WAIT ;
END PROCESS;
others_gen: PROCESS
BEGIN
send_data<='0';
data_in<="00000000";
WAIT FOR clk_tx_period;
send_data<='1';
data_in<="10101010";
WAIT FOR clk_tx_period/8;
send_data<='0';
data_in<="11111111";
WAIT;
END PROCESS;
END be;
--模塊3 接收時(shí)鐘
--1)設(shè)定:
-- 接收時(shí)鐘是波特率時(shí)鐘的8倍頻,即clk_re=clk_tx/8.
-- 這樣可以保證在發(fā)送時(shí)鐘周期的特定時(shí)刻對(duì)數(shù)據(jù)進(jìn)行采樣.
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
--USE IEEE.NUMERIC_STD.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY clock_re IS
PORT(
rst: IN STD_LOGIC;
clk_in: IN STD_LOGIC;
clk_out: OUT STD_LOGIC
);
END clock_re;
ARCHITECTURE behav OF clock_re IS
SIGNAL clk_count: STD_LOGIC_VECTOR(9 DOWNTO 0);
SIGNAL clk_bit: STD_LOGIC;
BEGIN
PROCESS(rst,clk_in)
BEGIN
IF rst='0' THEN
clk_count<="0000000000";
clk_bit<='0';
ELSIF clk_in'EVENT AND clk_in='1' THEN
IF(clk_count=130) THEN
clk_count<="0000000000";
clk_bit<=not clk_bit;
ELSE
clk_count<=clk_count+1;
END IF;
END IF;
END PROCESS;
clk_out<=clk_bit;
END behav;
--testbench:tb3
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY tb3 IS
END tb3;
ARCHITECTURE be OF tb3 IS
COMPONENT clock_re
PORT(
rst: IN STD_LOGIC;
clk_in: IN STD_LOGIC;
clk_out: OUT STD_LOGIC
);
END COMPONENT;
SIGNAL clk_in: STD_LOGIC;
SIGNAL rst: STD_LOGIC;
SIGNAL clk_out: STD_LOGIC;
CONSTANT clk_period: time:=25ns;
BEGIN
uut:clock_re PORT MAP(clk_in=>clk_in,rst=>rst,clk_out=>clk_out);
clk_gen: PROCESS
BEGIN
clk_in<='0';
WAIT FOR clk_period/2;
clk_in<='1';
WAIT FOR clk_period/2;
END PROCESS;
rst_gen: PROCESS
BEGIN
rst<='0';
WAIT FOR 25ns;
rst<='1';
WAIT ;
END PROCESS;
END be;
--模塊4 接收模塊
--1)設(shè)定
-- 在接收數(shù)據(jù)時(shí),如果接收時(shí)鐘與發(fā)送時(shí)鐘相同的話,則只需要輕微的頻率偏移,就能產(chǎn)生極大的誤差,
--所以,為了提高接收的準(zhǔn)確性,減少誤碼率,接收時(shí)鐘一般都必須采用高頻時(shí)鐘,本例中,每一位數(shù)據(jù)都采用8倍頻的
--波特率來(lái)對(duì)數(shù)據(jù)進(jìn)行采樣,即在每發(fā)送一位數(shù)據(jù)的時(shí)間內(nèi),接收模塊已經(jīng)進(jìn)行了8次接收,這樣保證了數(shù)據(jù)的正確性。
--接收數(shù)據(jù)起始位與停止位采用四次采樣判斷,即在第2,3,4,5個(gè)接收數(shù)據(jù)時(shí)鐘周期,對(duì)數(shù)據(jù)進(jìn)行判斷,在起始位,若四次采樣均為0,
--則判定該位為0,即為有效的起始位,在停止位,則四次為1,判定為1.
--接收數(shù)據(jù)有效部分采用三次采樣判斷,即在第4,5,6個(gè)接收數(shù)據(jù)時(shí)鐘周期,對(duì)數(shù)據(jù)進(jìn)行判斷,也即是在第4,5,6次采樣值上,
--判斷接收到的數(shù)據(jù)的值,判斷時(shí)采用多數(shù)表決原則,即三次采樣中有兩次為某個(gè)值,則判斷為該值。
--2)設(shè)計(jì)
-- 采樣狀態(tài)機(jī)實(shí)現(xiàn),分為四個(gè)狀態(tài),初始化,采樣,得到數(shù)值,數(shù)據(jù)準(zhǔn)備好.
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY uart_re IS
PORT(
rst: IN STD_LOGIC;
clk_in: IN STD_LOGIC;
ser_in: IN STD_LOGIC;--來(lái)自外部(PC機(jī))的串行輸入數(shù)據(jù)
data_ready: INOUT STD_LOGIC;--一幀數(shù)據(jù)接收完畢,可以輸出進(jìn)行處理.
data_out: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)--有效數(shù)據(jù)8位并行輸出
);
END uart_re;
ARCHITECTURE behav OF uart_re IS
COMPONENT clock_re
PORT(
rst: IN STD_LOGIC;
clk_in: IN STD_LOGIC;
clk_out: OUT STD_LOGIC
);
END COMPONENT;
COMPONENT clock_tx
PORT(
rst: IN STD_LOGIC;
clk_in: IN STD_LOGIC;
clk_out: OUT STD_LOGIC
);
END COMPONENT;
--串口接收一般采用狀態(tài)機(jī)實(shí)現(xiàn),定義為:初始化,采樣,得到數(shù)據(jù),幀格式準(zhǔn)備好.
TYPE uart_states IS (idle,sampling,get_data,data_ok);
SIGNAL state: uart_states;
SIGNAL clk_re: STD_LOGIC;
SIGNAL clk_tx: STD_LOGIC;
SIGNAL ser_buf1: STD_LOGIC; --級(jí)聯(lián)D觸發(fā)器,接收輸入數(shù)據(jù),根據(jù)跳變沿判斷起始位到來(lái).
SIGNAL ser_buf2: STD_LOGIC;
SIGNAL bit_count: STD_LOGIC_VECTOR(3 DOWNTO 0);--位計(jì)數(shù)器
SIGNAL data_buf: STD_LOGIC_VECTOR(9 DOWNTO 0);
SIGNAL ser_sig2: STD_LOGIC;--采樣值寄存器
SIGNAL ser_sig3: STD_LOGIC;
SIGNAL ser_sig4: STD_LOGIC;
SIGNAL ser_sig5: STD_LOGIC;
SIGNAL ser_sig6: STD_LOGIC;
SIGNAL sample: STD_LOGIC; --采樣點(diǎn)處理所得結(jié)果值
SIGNAL sample_count: STD_LOGIC_VECTOR(2 DOWNTO 0);--采樣個(gè)數(shù)計(jì)數(shù)值
BEGIN
uut_re:clock_re PORT MAP(rst,clk_in,clk_re);
uut_tx:clock_tx PORT MAP(rst,clk_in,clk_tx);
process(rst,clk_tx)
begin
if rst='0' then
ser_buf1<='1'; --對(duì)應(yīng)總線空閑時(shí)為1
ser_buf2<='1';
elsif clk_tx'event and clk_tx='1' then --clk_tx觸發(fā),因?yàn)檩斎氪袛?shù)據(jù)在clk_tx周期下變化,保證能夠采樣到變化值,
ser_buf1<=ser_in; --又保證了ser_buf1/2的值會(huì)保持足夠長(zhǎng)的時(shí)間,使得clk_re時(shí)鐘下能夠采樣得到
ser_buf2<=ser_buf1; --所需要的正確值.
end if;
end process;
process(rst,clk_re) --clk_re觸發(fā),保證采樣
begin
if rst='0' then
state<=idle; --復(fù)位下為初始狀態(tài)
data_ready<='0';--不發(fā)送數(shù)據(jù)
sample_count<="000";--采樣計(jì)數(shù)器清0
bit_count<="0000"; --位計(jì)數(shù)器清0
sample<='1'; --采樣結(jié)果值置1,與sampling狀態(tài)下第一個(gè)起始位采樣值為0區(qū)分
ser_sig2<='1';--采樣寄存器初始值全為1,保證了采樣起始位時(shí),根據(jù)要求,必須全變化為0方能得到代表起始位的0
ser_sig3<='1';
ser_sig4<='1';
ser_sig5<='1';
ser_sig6<='1';
data_buf<="1111111111";
elsif clk_re'event and clk_re='1' then
case state is
when idle=>
if ser_buf2='1' and ser_buf1='0' then --ser_buf1先變化,此處對(duì)應(yīng)為接收到一個(gè)下跳變
state<=sampling; --檢測(cè)到下跳變脈沖,啟動(dòng)采樣
else
state<=idle;
end if;
data_ready<='0';
data_buf<="1111111111"; --對(duì)應(yīng)總線空閑掛起為1
bit_count<="0000";
sample_count<="000";
when sampling=>
if bit_count="0000" then --start bit,代表起始位
--采樣賦值來(lái)源是根據(jù)ser_buf1,個(gè)人理解也是針對(duì)先變化的這個(gè)值,
--參考一些程序是根據(jù)ser_buf2,
--總之可以先仿真,再根據(jù)仿真圖上波形,來(lái)決定是ser_buf1/2能夠得到正確采樣結(jié)果.
--或者可以在實(shí)際運(yùn)行時(shí),分別比較兩者的正確性.
if sample_count="001" then --第二個(gè)采樣點(diǎn)
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -