基于RS232的PC与Altera板卡通信问题:多字符传输异常
问题:RS232双字符传输异常,单字符正常
我正在做一个学校项目,需要通过RS232线缆,用VS中的C#代码向Altera板卡传输ASCII字符。单字符(如'A')传输正常,但传输双字符(如"AB")时接收异常,LED亮灭不符合预期。以下是我的VHDL接收代码和C#发送代码,求解决办法。
VHDL接收代码
library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity UART_RX is -- clockls per bit = 50Mhz/9600baudrate generic( gClocks_per_Bit : integer := 5208 ); port( iClk : in std_logic; iReset : in std_logic; iRX : in std_logic:='0'; led1,led2 : out std_logic:='0'; oTX : out std_logic_vector (7 downto 0) ); end UART_RX; architecture Behavioral of UART_RX is type uart_rx_type is (idle,start,data,stop); signal sNext_state : uart_rx_type := idle; signal sClock_counter : integer range 0 to gClocks_per_Bit := 0; signal sIndex : integer range 0 to 15 := 0; signal sTemp : std_logic_vector (15 downto 0); signal receiver1, receiver2 : std_logic_vector (7 downto 0); signal oTX_1 : std_logic_vector (7 downto 0); begin process(iClk,iReset) begin if(iReset = '0') then sIndex <= 0; sClock_counter <= 0; elsif(rising_edge(iClk)) then case sNext_state is when idle => sClock_counter <= 0; sIndex <= 0; if(iRX = '0') then sNext_state <= start; elsif(iRX = '1') then sNext_state <= idle; end if; when start => if(sClock_counter < gClocks_per_Bit) then sClock_counter <= sClock_counter + 1; if(sClock_counter = gClocks_per_Bit/2 AND iRX ='0') then sNext_state <= start; elsif(sClock_counter = gClocks_per_Bit/2 AND iRX ='1') then sClock_counter <= 0; sNext_state <= idle; end if; sNext_state <= start; else sClock_counter <= 0; sNext_state <= data; end if; when data => if(sClock_counter < gClocks_per_Bit) then sClock_counter <= sClock_counter + 1; sNext_state <= data; if(sClock_counter = gClocks_per_Bit/2) then sTemp(sindex)<= iRX; end if; else sClock_counter <= 0; if(sIndex < 15) then sIndex <= sIndex + 1; sNext_state <= data; else sIndex <= 0; sNext_state <= stop; end if; end if; when stop => if(sClock_counter = gClocks_per_Bit / 2) then sClock_counter <= 0; sNext_state <= idle; elsif(sClock_counter < gClocks_per_Bit) then sClock_counter <= sClock_counter + 1; end if; when others => null; end case; end if; end process; receiver1<=sTemp(7 downto 0); receiver2<=sTemp(15 downto 8); -- oTX <= sTemp; process(receiver1,receiver2,iReset) begin if iReset = '0' then led2<='0'; led1<='0'; elsif receiver1 = "01000001" then -- A (led 1 on) led2 <= '0'; led1 <= '1'; elsif receiver1 = "01000010" then -- B led2 <= '1'; led1 <= '0'; elsif receiver1 = "01000011" then -- C led2 <= '0'; led1 <= '0'; elsif sTemp = "0100000101000010" then -- AB led2 <= '1'; led1 <= '1'; else -- What Ever You Write Turns Off/On Leds. null; end if; end process; oTX <= sTemp(7 downto 0); end Behavioral ;
C#发送代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.IO.Ports; using System.Threading; namespace COMPORT { public partial class Form1 : Form { string dataOUT; public Form1() { InitializeComponent(); } private void comboBox5_SelectedIndexChanged(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { try { serialPort1.PortName = cBoxCOMPORT.Text; serialPort1.BaudRate = Convert.ToInt32(CBoxBaudRate.Text); serialPort1.DataBits = Convert.ToInt32(cBoxDataBits.Text); serialPort1.StopBits = (StopBits)Enum.Parse(typeof(StopBits), cBoxStopBits.Text); serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), cBoxParityBits.Text); serialPort1.Open(); progressBar1.Value = 100; } catch (Exception err) { MessageBox.Show(err.Message,"Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void btnClose_Click(object sender, EventArgs e) { if(serialPort1.IsOpen) { serialPort1.Close(); progressBar1.Value = 0; } } private void Form1_Load(object sender, EventArgs e) { string[] ports = SerialPort.GetPortNames(); cBoxCOMPORT.Items.AddRange(ports); } private void btnSendData_Click(object sender, EventArgs e) { if(serialPort1.IsOpen) { dataOUT = tBoxDataOut.Text; //dataOUT = ConvertToHex(dataOUT); //dataOUT = hex2binary(dataOUT); serialPort1.Write(dataOUT); Thread.Sleep((int)TimeSpan.FromSeconds(2).TotalMilliseconds); MessageBox.Show(dataOUT); } } private void tBoxDataOut_TextChanged(object sender, EventArgs e) { } private void timer1_Tick(object sender, EventArgs e) { this.Text = DateTime.Now.ToString(); } //public static string ConvertToHex(string asciiString) //{ // string bin = ""; // foreach (char c in asciiString) // { // int tmp = c; // bin += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString())); // } // return bin; //} //private string hex2binary(string hexvalue) //{ // string binaryval = ""; // binaryval = Convert.ToString(Convert.ToInt32(hexvalue, 16), 2); // return binaryval; //} } }
问题原因及修复方案
核心问题
- UART帧格式误解:你的VHDL代码试图一次性接收16位连续数据,但标准UART中每个字符是独立的帧(起始位+8数据位+停止位)。发送"AB"实际是两个独立帧,而非连续16位数据流,导致
sTemp数据混乱。 - 判断优先级错误:LED控制逻辑中,单字符判断优先级高于双字符,即使接收到"AB",也会先触发单字符的LED逻辑,永远不会执行双字符分支。
修复方案
方案1:修正VHDL接收逻辑(推荐)
修改状态机,每次接收一个完整字节,存储到缓冲区,累计两个字节后再进行判断:
library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity UART_RX is generic( gClocks_per_Bit : integer := 5208 ); port( iClk : in std_logic; iReset : in std_logic; iRX : in std_logic:='0'; led1,led2 : out std_logic:='0'; oTX : out std_logic_vector (7 downto 0) ); end UART_RX; architecture Behavioral of UART_RX is type uart_rx_type is (idle,start,data,stop); signal sNext_state : uart_rx_type := idle; signal sClock_counter : integer range 0 to gClocks_per_Bit := 0; signal sIndex : integer range 0 to 7 := 0; signal sTemp_byte : std_logic_vector (7 downto 0); signal rx_buffer : std_logic_vector(15 downto 0); signal byte_count : integer range 0 to 1 := 0; signal rx_done : std_logic := '0'; begin process(iClk,iReset) begin if(iReset = '0') then sIndex <= 0; sClock_counter <= 0; byte_count <= 0; rx_done <= '0'; rx_buffer <= (others => '0'); elsif(rising_edge(iClk)) then rx_done <= '0'; case sNext_state is when idle => sClock_counter <= 0; sIndex <= 0; if(iRX = '0') then sNext_state <= start; else sNext_state <= idle; end if; when start => if(sClock_counter < gClocks_per_Bit) then sClock_counter <= sClock_counter + 1; if(sClock_counter = gClocks_per_Bit/2 AND iRX = '1') then sClock_counter <= 0; sNext_state <= idle; end if; else sClock_counter <= 0; sNext_state <= data; end if; when data => if(sClock_counter < gClocks_per_Bit) then sClock_counter <= sClock_counter + 1; if(sClock_counter = gClocks_per_Bit/2) then sTemp_byte(sindex) <= iRX; end if; else sClock_counter <= 0; if(sIndex < 7) then sIndex <= sIndex + 1; sNext_state <= data; else sIndex <= 0; sNext_state <= stop; end if; end if; when stop => if(sClock_counter < gClocks_per_Bit) then sClock_counter <= sClock_counter + 1; if(sClock_counter = gClocks_per_Bit) then if(byte_count = 0) then rx_buffer(7 downto 0) <= sTemp_byte; byte_count <= 1; else rx_buffer(15 downto 8) <= sTemp_byte; byte_count <= 0; rx_done <= '1'; end if; sNext_state <= idle; end if; end if; when others => null; end case; end if; end process; process(rx_done, rx_buffer, iReset) begin if iReset = '0' then led2 <= '0'; led1 <= '0'; elsif rx_done = '1' then if rx_buffer = "0100001001000001" then -- 先接A(低字节),后接B(高字节) led1 <= '1'; led2 <= '1'; elsif rx_buffer(7 downto 0) = "01000001" then led1 <= '1'; led2 <= '0'; elsif rx_buffer(7 downto 0) = "01000010" then led1 <= '0'; led2 <= '1'; elsif rx_buffer(7 downto 0) = "01000011" then led1 <= '0'; led2 <= '0'; end if; end if; end process; oTX <= rx_buffer(7 downto 0); end Behavioral ;
方案2:调整LED判断优先级(临时修复)
将双字符判断放在单字符之前,确保优先触发:
process(receiver1,receiver2,iReset) begin if iReset = '0' then led2<='0'; led1<='0'; elsif sTemp = "0100000101000010" then -- AB 判断移到最前面 led2 <= '1'; led1 <= '1'; elsif receiver1 = "01000001" then -- A led2 <= '0'; led1 <= '1'; elsif receiver1 = "01000010" then -- B led2 <= '1'; led1 <= '0'; elsif receiver1 = "01000011" then -- C led2 <= '0'; led1 <= '0'; else null; end if; end process;
C#代码验证
你的C#代码无需修改,serialPort1.Write(dataOUT)会自动将ASCII字符转换为标准UART帧发送。
内容的提问来源于stack exchange,提问作者Omer DT8
相关产品推荐
相关产品推荐

