单片机学习小组
直播中

李微波

7年用户 917经验值
私信 关注

怎样使用两块开发板通过RS-485串口通信并分别控制对方呢

RS485为什么只能实现半双工传输呢?
怎样使用两块开发板通过RS-485串口通信并分别控制对方呢?


回帖(2)

蒋荣艳

2022-2-16 14:56:53

  • RS485只有两根数据线,只能实现半双工传输
    系统框图

    1.按键模块


  • module key_debounce(    //按键消抖
  •     input            sys_clk,          //外部50M时钟
  •     input            sys_rst_n,        //外部复位信号,低有效
  •    
  •     input      [3:0] key,              //外部按键输入
  •    
  •     output reg       key_flag,         //按键数据有效信号
  •     output reg [3:0] key_value         //按键消抖后的数据
  •     );

  • //reg define   
  • reg [31:0] delay_cnt;    //按键消抖计数器
  • reg [ 3:0] key_reg;     //按键值寄存器

  • //*****************************************************
  • //**                    main code
  • //*****************************************************
  • /*******按键消抖*******************/
  • always @(posedge sys_clk or negedge sys_rst_n) begin
  •     if (!sys_rst_n) begin
  •         key_reg   <= 4'b1111;
  •         delay_cnt <= 32'd0;
  •     end
  •     else begin
  •         key_reg <= key;
  •         if(key_reg != key)             //一旦检测到按键状态发生变化(有按键被按下或释放)
  •             delay_cnt <= 32'd1000000;  //给延时计数器重新装载初始值(计数时间为20ms)
  •         else if(key_reg == key) begin  //在按键状态稳定时,计数器递减,开始20ms倒计时
  •                  if(delay_cnt > 32'd0)
  •                      delay_cnt <= delay_cnt - 1'b1;
  •                  else
  •                      delay_cnt <= delay_cnt;
  •              end           
  •     end   
  • end
  • /**************得到按键值*******************/
  • always @(posedge sys_clk or negedge sys_rst_n) begin
  •     if (!sys_rst_n) begin
  •         key_flag  <= 1'b0;
  •         key_value <= 4'b1111;         
  •     end
  •     else begin
  •         if(delay_cnt == 32'd1) begin   //当计数器递减到1时,说明按键稳定状态维持了20ms
  •             key_flag  <= 1'b1;         //此时消抖过程结束,给出一个时钟周期的标志信号
  •             key_value <= key;          //并寄存此时按键的值
  •         end
  •         else begin
  •             key_flag  <= 1'b0;
  •             key_value <= key_value;
  •         end  
  •     end   
  • end
  •    
  • endmodule
  • 2.发送模块:


  • module uart_send(      //得到按键值,进行发送
  •     input              sys_clk,                  //系统时钟
  •     input         sys_rst_n,                //系统复位,低电平有效
  •    
  •     input         uart_en,                  //接收到key_flag按键数据有效信号后,发送使能信号
  •     input  [7:0]  uart_din,                 //接收到按键有效数据,待发送数据
  •     output  reg   uart_txd,                 //UART发送端口,将8位数据串行发送出去
  •     output  reg   tx_flag                   //发送使能,高有效
  •     );
  •    
  • //parameter define
  • parameter  CLK_FREQ = 50000000;             //系统时钟频率
  • parameter  UART_BPS = 9600;                 //串口波特率
  • localparam BPS_CNT  = CLK_FREQ/UART_BPS;    //为得到指定波特率,对系统时钟计数BPS_CNT次

  • //reg define
  • reg        uart_en_d0;
  • reg        uart_en_d1;  
  • reg [15:0] clk_cnt;                         //系统时钟计数器
  • reg [ 3:0] tx_cnt;                          //发送数据计数器
  • reg [ 7:0] tx_data;                         //寄存发送数据

  • //wire define
  • wire       en_flag;

  • //*****************************************************
  • //**                    main code
  • //*****************************************************
  • //捕获uart_en上升沿,得到一个时钟周期的脉冲信号
  • assign en_flag = (~uart_en_d1) & uart_en_d0;   
  •                                                 
  • //对发送使能信号uart_en延迟两个时钟周期
  • always @(posedge sys_clk or negedge sys_rst_n) begin         
  •     if (!sys_rst_n) begin
  •         uart_en_d0 <= 1'b0;                                 
  •         uart_en_d1 <= 1'b0;
  •     end                                                      
  •     else begin                           //当有效时,传回来1                    
  •         uart_en_d0 <= uart_en;                              
  •         uart_en_d1 <= uart_en_d0;      //默认的时候是低电平                        
  •     end
  • end

  • //当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程         
  • always @(posedge sys_clk or negedge sys_rst_n) begin         
  •     if (!sys_rst_n) begin                                 
  •         tx_flag <= 1'b0;
  •         tx_data <= 8'd0;
  •     end
  •     else if (en_flag) begin                 //检测到发送使能上升沿                     
  •             tx_flag <= 1'b1;                //进入发送过程,标志位tx_flag拉高
  •             tx_data <= uart_din;            //寄存待发送的数据
  •         end
  •         else
  •         if ((tx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
  •         begin                               //计数到最后一位停止位的中间时,停止发送过程
  •             tx_flag <= 1'b0;                //发送过程结束,标志位tx_flag拉低
  •             tx_data <= 8'd0;
  •         end
  •         else begin
  •             tx_flag <= tx_flag;
  •             tx_data <= tx_data;
  •         end
  • end

  • //进入发送过程后,启动系统时钟计数器与发送数据计数器
  • always @(posedge sys_clk or negedge sys_rst_n) begin         
  •     if (!sys_rst_n) begin                             
  •         clk_cnt <= 16'd0;                                 
  •         tx_cnt  <= 4'd0;
  •     end                                                      
  •     else if (tx_flag) begin                 //处于发送过程
  •         if (clk_cnt < BPS_CNT - 1) begin
  •             clk_cnt <= clk_cnt + 1'b1;
  •             tx_cnt  <= tx_cnt;
  •         end
  •         else begin
  •             clk_cnt <= 16'd0;               //对系统时钟计数达一个波特率周期后清零
  •             tx_cnt  <= tx_cnt + 1'b1;       //此时发送数据计数器加1,即已经经过一个发送周期,发送完一位数据
  •         end
  •     end
  •     else begin                              //发送过程结束
  •         clk_cnt <= 16'd0;
  •         tx_cnt  <= 4'd0;
  •     end
  • end

  • //根据发送数据计数器来给uart发送端口赋值
  • always @(posedge sys_clk or negedge sys_rst_n) begin        
  •     if (!sys_rst_n)  
  •         uart_txd <= 1'b1;        
  •     else if (tx_flag)
  •         case(tx_cnt)
  •             4'd0: uart_txd <= 1'b0;         //起始位
  •             4'd1: uart_txd <= tx_data[0];   //数据位最低位
  •             4'd2: uart_txd <= tx_data[1];
  •             4'd3: uart_txd <= tx_data[2];
  •             4'd4: uart_txd <= tx_data[3];
  •             4'd5: uart_txd <= tx_data[4];
  •             4'd6: uart_txd <= tx_data[5];
  •             4'd7: uart_txd <= tx_data[6];
  •             4'd8: uart_txd <= tx_data[7];   //数据位最高位
  •             4'd9: uart_txd <= 1'b1;         //停止位
  •             default: ;
  •         endcase
  •     else
  •         uart_txd <= 1'b1;                   //空闲时发送端口为高电平
  • end

  • endmodule                  

  • 3.接收模块:


  • module uart_recv(
  •     input                          sys_clk,                  //系统时钟
  •     input             sys_rst_n,                //系统复位,低电平有效
  •    
  •     input             uart_rxd,                 //UART接收端口,即接收发送端的串行数据
  •     output  reg       uart_done,                //接收一帧数据完成标志信号
  •     output  reg [7:0] uart_data                 //接收的数据
  •     );
  •    
  • //parameter define
  • parameter  CLK_FREQ = 50000000;                 //系统时钟频率
  • parameter  UART_BPS = 9600;                     //串口波特率
  • localparam BPS_CNT  = CLK_FREQ/UART_BPS;        //为得到指定波特率,
  •                                                 //需要对系统时钟计数BPS_CNT次
  • //reg define
  • reg        uart_rxd_d0;
  • reg        uart_rxd_d1;
  • reg [15:0] clk_cnt;                             //系统时钟计数器
  • reg [ 3:0] rx_cnt;                              //接收数据计数器
  • reg        rx_flag;                             //接收过程标志信号
  • reg [ 7:0] rxdata;                              //接收数据寄存器

  • //wire define
  • wire       start_flag;

  • //*****************************************************
  • //**                    main code
  • //*****************************************************
  • //捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
  • assign  start_flag = uart_rxd_d1 & (~uart_rxd_d0);   

  • //对UART接收端口的数据延迟两个时钟周期
  • always @(posedge sys_clk or negedge sys_rst_n) begin
  •     if (!sys_rst_n) begin
  •         uart_rxd_d0 <= 1'b0;
  •         uart_rxd_d1 <= 1'b0;         
  •     end
  •     else begin
  •         uart_rxd_d0  <= uart_rxd;     //所以接收到有效位为0                  
  •         uart_rxd_d1  <= uart_rxd_d0;  //发送端空闲时为高电平
  •     end   
  • end

  • //当脉冲信号start_flag到达时,进入接收过程           
  • always @(posedge sys_clk or negedge sys_rst_n) begin         
  •     if (!sys_rst_n)                                 
  •         rx_flag <= 1'b0;
  •     else begin
  •         if(start_flag)                          //检测到起始位
  •             rx_flag <= 1'b1;                    //进入接收过程,标志位rx_flag拉高
  •         else if((rx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
  •             rx_flag <= 1'b0;                    //计数到最后一位停止位中间时,停止接收过程
  •         else
  •             rx_flag <= rx_flag;
  •     end
  • end

  • //进入接收过程后,启动系统时钟计数器与接收数据计数器
  • always @(posedge sys_clk or negedge sys_rst_n) begin         
  •     if (!sys_rst_n) begin                             
  •         clk_cnt <= 16'd0;                                 
  •         rx_cnt  <= 4'd0;
  •     end                                                      
  •     else if ( rx_flag ) begin                   //处于接收过程
  •             if (clk_cnt < BPS_CNT - 1) begin
  •                 clk_cnt <= clk_cnt + 1'b1;
  •                 rx_cnt  <= rx_cnt;
  •             end
  •             else begin
  •                 clk_cnt <= 16'd0;               //对系统时钟计数达一个波特率周期后清零
  •                 rx_cnt  <= rx_cnt + 1'b1;       //此时接收数据计数器加1
  •             end
  •         end
  •         else begin                              //接收过程结束,计数器清零
  •             clk_cnt <= 16'd0;
  •             rx_cnt  <= 4'd0;
  •         end
  • end

  • //根据接收数据计数器来寄存uart接收端口数据
  • always @(posedge sys_clk or negedge sys_rst_n) begin
  •     if ( !sys_rst_n)  
  •         rxdata <= 8'd0;                                    
  •     else if(rx_flag)                            //系统处于接收过程
  •         if (clk_cnt == BPS_CNT/2) begin         //判断系统时钟计数器计数到数据位中间
  •             case ( rx_cnt )
  •              4'd1 : rxdata[0] <= uart_rxd_d1;   //寄存数据位最低位
  •              4'd2 : rxdata[1] <= uart_rxd_d1;
  •              4'd3 : rxdata[2] <= uart_rxd_d1;
  •              4'd4 : rxdata[3] <= uart_rxd_d1;
  •              4'd5 : rxdata[4] <= uart_rxd_d1;
  •              4'd6 : rxdata[5] <= uart_rxd_d1;
  •              4'd7 : rxdata[6] <= uart_rxd_d1;
  •              4'd8 : rxdata[7] <= uart_rxd_d1;   //寄存数据位最高位
  •              default:;                                    
  •             endcase
  •         end
  •         else
  •             rxdata <= rxdata;
  •     else
  •         rxdata <= 8'd0;
  • end

  • //数据接收完毕后给出标志信号并寄存输出接收到的数据
  • always @(posedge sys_clk or negedge sys_rst_n) begin        
  •     if (!sys_rst_n) begin
  •         uart_data <= 8'd0;                              
  •         uart_done <= 1'b0;
  •     end
  •     else if(rx_cnt == 4'd9) begin               //接收数据计数器计数到停止位时           
  •         uart_data <= rxdata;                    //寄存输出接收到的数据
  •         uart_done <= 1'b1;                      //并将接收完成标志位拉高
  •     end
  •     else begin
  •         uart_data <= 8'd0;                                   
  •         uart_done <= 1'b0;
  •     end   
  • end

  • endmodule       


  • Q:为啥这两个不一样啊
    A:串口都是默认是高电平的,有数据之后就是低电平,停止位是回到高(我老是以为uart_en_d0和uart_en_d1是复位的时候给的值,全是0,应该复位完之后,就已经在传值了)


举报

区沛仪

2022-2-16 14:56:58

  • 4.led模块:



  • module led_ctrl(
  •     input            sys_clk,          //外部50M时钟
  •     input            sys_rst_n,        //外部复位信号,低有效
  •    
  •     input            led_en,           //led控制使能,接收到uart_done
  •     input      [3:0] led_data,         //led控制数据
  •    
  •     output reg [3:0] led               //led灯
  •     );

  • //reg define
  • reg led_en_d0;
  • reg led_en_d1;

  • //wire define
  • wire led_en_flag;

  • //*****************************************************
  • //**                    main code
  • //*****************************************************
  • //捕获led_en上升沿,得到一个时钟周期的脉冲信号
  • assign led_en_flag = (~led_en_d1) & led_en_d0;

  • always @(posedge sys_clk or negedge sys_rst_n) begin
  •     if (!sys_rst_n) begin s
  •         led_en_d0 <= 1'b0;
  •         led_en_d1 <= 1'b0;
  •     end
  •     else begin
  •         led_en_d0 <= led_en;
  •         led_en_d1 <= led_en_d0;    //当没有接收完时为0
  •     end
  • end

  • always @(posedge sys_clk or negedge sys_rst_n) begin
  •     if (!sys_rst_n)
  •         led <= 4'b0000;
  •     else if(led_en_flag)               //在led_en上升沿到来时,改变led灯的状态
  •             led <= ~led_data;          //按键按下时为低电平,而led高电平时点亮
  •         else
  •             led <= led;
  • end
  •    
  • endmodule


  • 5.顶层模块:






  • module rs485_uart_top(
  •     input           sys_clk,           //外部50M时钟
  •     input           sys_rst_n,         //外部复位信号,低有效
  •    
  •     input  [3:0]    key,               //按键
  •     output [3:0]    led,               //led灯
  •     //uart接口
  •     input           rs485_uart_rxd,    //rs485串口接收端口
  •     output          rs485_uart_txd,    //rs485串口发送端口
  •     output          rs485_tx_en        //rs485发送使能,高有效
  •     );
  •    
  • //parameter define
  • parameter  CLK_FREQ = 50000000;        //定义系统时钟频率50M
  • parameter  UART_BPS = 115200;          //定义串口波特率
  •    
  • //wire define   
  • wire       tx_en_w;                    //UART发送使能
  • wire       rx_done_w;                  //UART接收完毕信号
  • wire [7:0] tx_data_w;                  //UART发送数据
  • wire [7:0] rx_data_w;                  //UART接收数据
  • wire [3:0] key_value_w;                //消抖后的按键数据

  • //*****************************************************
  • //**                    main code
  • //*****************************************************   
  • assign tx_data_w = {4'd0,key_value_w}; //将按键消抖后的值送到发送模块

  • uart_recv #(                           //串口接收模块
  •     .CLK_FREQ       (CLK_FREQ),        //设置系统时钟频率
  •     .UART_BPS       (UART_BPS))        //设置串口接收波特率
  • u_uart_recv(                 
  •     .sys_clk        (sys_clk),
  •     .sys_rst_n      (sys_rst_n),
  •    
  •     .uart_rxd       (rs485_uart_rxd),
  •     .uart_done      (rx_done_w),
  •     .uart_data      (rx_data_w)
  •     );
  •    
  • uart_send #(                           //串口发送模块
  •     .CLK_FREQ       (CLK_FREQ),        //设置系统时钟频率
  •     .UART_BPS       (UART_BPS))        //设置串口发送波特率
  • u_uart_send(                 
  •     .sys_clk        (sys_clk),
  •     .sys_rst_n      (sys_rst_n),
  •      
  •     .uart_en        (tx_en_w),
  •     .uart_din       (tx_data_w),
  •     .uart_txd       (rs485_uart_txd),
  •     .tx_flag        (rs485_tx_en)      //rs485串口发送使能,高有效  
  •     );
  •    
  • key_debounce u_key_debounce(
  •     .sys_clk        (sys_clk),
  •     .sys_rst_n      (sys_rst_n),
  •    
  •     .key            (key),
  •     .key_flag       (tx_en_w),         //按键有效通知信号
  •     .key_value      (key_value_w)      //按键消抖后的数据
  •     );
  •    
  • led_ctrl u_led_ctrl(
  •     .sys_clk        (sys_clk),
  •     .sys_rst_n      (sys_rst_n),
  •    
  •     .led_en         (rx_done_w),       //led控制使能
  •     .led_data       (rx_data_w[3:0]),  //led控制数据
  •     .led            (led)
  • );

  • endmodule
举报

更多回帖

发帖
×
20
完善资料,
赚取积分