串口通讯控制器实现之----发送模块

MCU向FIFO写入数据后,则发送FIFO的空状态标志位变为非空,串口发送模块监测到非空状态后,读取1个8位并行数据,按照MCU配置的波特率向串口芯片发送数据,发送的格式为:1位起始位,8位数据位(数据位按照从低位到高位的方式,即发送bit0,bit1……,bit7),奇偶校验位(如MCU配置无奇偶校验位,则该位不发送,该奇偶校验位可设为奇校验或偶校验),停止位(可设为1位或2位),发送结束后,重新检测FIFO的空状态位,如果非空则继续读取数据向串口芯片发送串行数据,直到FIFO为空状态。

串口发送模块端口信号说明双端口

端口名称

位宽

类型

功能

clk

1

输入

时钟(上升沿)

rst

1

输入

复位信号(低有效)

fifo_empty

1

输入

发送FIFO的空状态标志位(高有效,即为空)

fifo_data

8

输入

发送FIFO的8位输出数据信号

fifo_rden

1

输出

发送FIFO的读使能信号

serial_tx

1

输出

串口输出信号

serial_baudrate

8

输入

串口波特率

serial_stop_bit

2

输入

串口停止位

serial_parity

2

输入

串口奇偶校验位

复位期间,状态机处于IDLE状态,在该状态下,串口的输出一直为高电平;

当检测到待发送FIFO为非空状态时,进入RD_FIFO状态,该状态维持5个时钟周期,在该状态完成读取FIFO中的一个8位数据的功能。首先产生FIFO的读使能有效信号,然后将FIFO的输出数据寄存下来,在该状态下,串口的输出一直为高电平;

接下来是START_BIT状态(发送起始位),该状态维持的时间为一个波特率宽度,即MCU如果将波特率配置为0x30,则维持48个时钟周期,如果将波特率设置为0xC0,则维持192个时钟周期。在该状态下,串口的输出一直为低电平;

接下来是SEND_DATA状态(发送数据位),该状态维持的时间为8个波特率宽度,8位数据中的每bit占用1个波特率的时间宽度。发送时,首先bit0,依次bit1……,bit6,bit7。需要说明的是:虽然RS232串口为负逻辑,但是如果待发送bit位为高电平,则FPGA仍然输出高电平,如果待发送bit位为低电平,则FPGA仍然输出低电平。负逻辑的转换在外部串口接口芯片完成;

SEND_DATA状态结束后,状态机判断serial_parity(MCU设置的奇偶校验位),如果为10或者01(进行奇偶校验),则进入PARITY_BIT状态,否则直接进入STOP_BIT状态。在PARITY_BIT状态维持一个波特率时间宽度,结束后进入STOP_BIT状态。在该状态产生校验位,并且将校验位输出给串口接口芯片;

最后一个状态是STOP_BIT状态。该状态保持的时间宽度为一个或两个波特率宽度,取决于MCU配置的serial_stop_bit信号。在该状态下,串口的输出一直为高电平。

串口发送模块的状态转移图见下图。

串口发送流程图.jpg

串口发送模块状态转移图

串口发送模块的逻辑代码如下:

module serial_t(

                            clk,

                            rst,

                            fifo_empty,

                            fifo_data,                          

                            fifo_rden,

                            serial_tx,

                            serial_baudrate,

                serial_stop_bit,

                serial_parity

                            );

//system signal

input clk;

input rst;

//fifo signal

input fifo_empty;//待发送FIFO的空状态标志

input [7:0] fifo_data; //待发送FIFO的8位输出数据

output reg fifo_rden; //待发送FIFO的读使能信号

//transmit serial signal

output reg serial_tx; //串口输出信号

//serial_configure registers

input [7:0] serial_baudrate;

//MCU配置的串口波特率,如为0x30,则为230400bps;如为0x60,则为115200bps;

//如为0xC0,则为57600bps,其他为115200bps;

input [1:0] serial_stop_bit;

//MCU配置的串口停止位,如为10,则停止位为2位;其他则为1位;

input [1:0] serial_parity;

//MCU配置的串口奇偶校验位,如为01,则为奇校验;如为10,则为偶校验;其他

//为无校验

reg [7:0] data;//将从FIFO读出的数据进行寄存的寄存器

reg [3:0] bit_cnt;//8位数据中bit位

parameter IDLE                  =6'h01;

parameter RD_FIFO         =6'h02;

parameter START_BIT     =6'h04;

parameter SEND_DATA    =6'h08;

parameter PARITY_BIT    =6'h10;

parameter STOP_BIT      =6'h20;

reg [5:0] cstate;//当前状态

reg [5:0] nstate;//下一状态

reg [2:0] rfifo_cnt;//读FIFO计数器

reg [7:0] start_counter; //发送起始位计数器

reg [8:0] stop_counter; //发送停止位计数器

reg [7:0] send_data_counter; //发送数据位计数器

reg [8:0] stop_reg; //停止位总数

reg [7:0] parity_counter; //发送奇偶校验位计数器

wire start_done; //发送起始位结束

wire parity_done; //发送奇偶校验位结束

wire stop_done; //发送停止位结束

always @ (posedge clk or negedge rst)

       if(rst==0)

              rfifo_cnt<=3'b0;

       else

              if(nstate==RD_FIFO || cstate==RD_FIFO)

                     rfifo_cnt<=rfifo_cnt+1;

              else

                     rfifo_cnt<=3'b0;

//产生读FIFO计数器

always @ (posedge clk or negedge rst)

       if(rst==0)

              start_counter<=8'b0;

       else

              if(nstate==START_BIT || cstate==START_BIT)

                     start_counter<=start_counter+1;

              else

                     start_counter<=8'b0;

//产生发送起始位计数器

always @ (posedge clk or negedge rst)

       if(rst==0)

              send_data_counter<=8'b0;

       else

              if(nstate==SEND_DATA || cstate==SEND_DATA)

                     begin

                            if(send_data_counter==serial_baudrate)

                                   send_data_counter<=8'b0;

                            else

                                   send_data_counter<=send_data_counter+1;

                     end

              else

                     send_data_counter<=8'b0;

//产生发送数据位计数器

always @ (posedge clk or negedge rst)

       if(rst==0)

              parity_counter<=8'b0;

       else

              if(nstate==PARITY_BIT || cstate==PARITY_BIT)

                     parity_counter<=parity_counter+1;

              else

                     parity_counter<=8'b0;

//产生发送奇偶校验位计数器

always @ (posedge clk or negedge rst)

       if(rst==0)

              stop_counter<=9'b0;

       else

              if(nstate==STOP_BIT || cstate==STOP_BIT)

                     stop_counter<=stop_counter+1;

              else

                     stop_counter<=9'b0;

//产生发送停止位位计数器

always @ (posedge clk or negedge rst)

    if(rst==0)

        cstate<=IDLE;

    else

        cstate<=nstate;

//时钟沿到来时当前状态等于下一状态

assign start_done=(start_counter==serial_baudrate)?1'b1:1'b0;

assign parity_done=(parity_counter==serial_baudrate)?1'b1:1'b0;

assign stop_done=(stop_counter==stop_reg);

always @ (cstate,fifo_empty,rfifo_cnt,bit_cnt,start_done,parity_done,stop_done,serial_parity)

       begin

     nstate=IDLE;

     case(cstate)

          IDLE:

                  if(fifo_empty==1'b0)

                      nstate=RD_FIFO;

                else

                    nstate=IDLE;

          RD_FIFO:

                     if(rfifo_cnt==3'd5)

                            nstate=START_BIT;

                     else

                            nstate=RD_FIFO;

          START_BIT:

                     if(start_done)

                    nstate=SEND_DATA;

                else

                    nstate=START_BIT;

          SEND_DATA:

                     if(bit_cnt==4'd8)

                      begin

                             if(serial_parity==2'b01 || serial_parity==2'b10)  

                              nstate=PARITY_BIT;

                        else

                              nstate=STOP_BIT;

                      end

                else

                    nstate=SEND_DATA;

          PARITY_BIT:

                     if(parity_done)

                    nstate=STOP_BIT;

                else

                      nstate=PARITY_BIT;

          STOP_BIT:

                     if(stop_done)

                      nstate=IDLE;

                else

                    nstate=STOP_BIT;

          default:nstate=IDLE;

      endcase

       end

//产生下一状态

always @ (posedge clk or negedge rst)

       if(rst==0)

      begin

      serial_tx<=1'b1;

     end

       else

      case(nstate)

        IDLE:

               begin

                  serial_tx<=1'b1;

            end

        RD_FIFO:

               begin

                      serial_tx<=1'b1;

               end

        START_BIT:serial_tx<=1'b0;

        SEND_DATA:serial_tx<=data[bit_cnt[2:0]];

        PARITY_BIT:

               begin

                      if(serial_parity==2'b01)

                             serial_tx<=!(^data);

                      else

                             serial_tx<=^data;

               end

        STOP_BIT:serial_tx<=1'b1;

        default:serial_tx<=1'b1;

      endcase

//输出串口信号

 

always @ (posedge clk or negedge rst)

       if(rst==0)

          bit_cnt<=4'b0;

    else if(nstate==IDLE)

        bit_cnt<=4'b0;

       else if(nstate==SEND_DATA && send_data_counter==serial_baudrate)

          bit_cnt<=bit_cnt+1;

       else

          bit_cnt<=bit_cnt;

//产生8位数据中bit位

always @ (posedge clk or negedge rst)

       if(rst==0)

          fifo_rden<=1'b1;

       else if(rfifo_cnt==3'd1)

          fifo_rden<=1'b0;

       else

          fifo_rden<=1'b1;

//产生FIFO的读信号

    

always @ (posedge clk or negedge rst)

       if(rst==0)

          data<=8'b0;

       else if(rfifo_cnt==3'd4)

           data<=fifo_data;

       else

          data<=data;

//将FIFO输出的数据进行寄存

    

always @ (posedge clk or negedge rst)

       if(rst==0)

          stop_reg<={1'b0,serial_baudrate};

       else if(serial_stop_bit==2'b10)

          stop_reg<={serial_baudrate,1'b0};

       else

          stop_reg<={1'b0,serial_baudrate};

//生成停止位总数

endmodule

串口发送模块仿真波形图见下图。

1.jpg

串口发送模块仿真波形图1(数据为33、44、52,无奇偶偶校验,2个停止位)

2.jpg

串口发送模块仿真波形图2(数据为AA、55,偶校验,2个停止位)

3.jpg
 
 
 
 
 
串口通讯控制器实现之MCU配置模块设计
 
 

该模块接口信号包括mcu_rd、mcu_wr、mcu_data、mcu_addr、mcu_cs、mcu_int等。

异步时钟域之间传输数据在FPGA设计中是一个非常重要的技术,处理不好很容易出现不稳定,或者某次布局布线后功能正常,重新布局布线后,功能就不正常,并且多数情况下,错误的现象没有明确的规律,问题定位非常困难。解决的办法是由本地时钟进行同步化处理。

mcu读模块中首先检测mcu_rd的下降沿,检测到下降沿之后,再判断片选信号和地址信号,给不同的地址分配特定的寄存器或者FIFO空间;

mcu写模块中首先检测mcu_wr的下降沿,检测到下降沿之后,再判断片选信号和地址信号,给不同的地址分配特定的寄存器或者FIFO空间。

具体的FPGA逻辑代码如下:

module mcu_configure(

                    clk,

                    rst,

                    mcu_rd,

                    mcu_wr,

                    mcu_data,

                    mcu_addr,

                    mcu_cs,

                    mcu_int,

                    serial_baudrate,

                    serial_stop_bit,

                    serial_parity,

                    serial_rfifo_empty,

                    serial_rfifo_data,

                    serial_rfifo_rden,

                    serial_tfifo_data,

                    serial_tfifo_wren);

//system signal

input clk;

input rst;

//mcu interface

input mcu_rd;//MCU的读信号

input mcu_wr; //MCU的写信号

inout [7:0] mcu_data; //MCU的数据信号

input [12:0] mcu_addr; //MCU的地址信号

input mcu_cs; //MCU的片选信号

output reg mcu_int; //MCU的中断信号

//serial_configure registers

output reg [7:0] serial_baudrate;

//MCU配置的串口波特率,如为0x30,则为230400bps;如为0x60,则为115200bps;

//如为0xC0,则为57600bps,其他为115200bps;

output reg [1:0] serial_stop_bit;

//MCU配置的串口停止位,如为10,则停止位为2位;其他则为1位;

output reg [1:0] serial_parity;

//MCU配置的串口奇偶校验位,如为01,则为奇校验;如为10,则为偶校验;其他//为无校验

//receive fifo

input serial_rfifo_empty;//接收FIFO的空状态标志位;

input [7:0] serial_rfifo_data;//接收FIFO的输出8位数据;

output reg serial_rfifo_rden;//接收FIFO的读使能信号;

//transmit fifo

output [7:0] serial_tfifo_data;//发送FIFO的写入数据信号;

output reg   serial_tfifo_wren;//发送FIFO的写使能信号;

//MCU bidirection control

reg [7:0] mcu_data_reg;

assign mcu_data=(mcu_rd==0 && mcu_cs==0)?mcu_data_reg:8'bZZZZZZZZ;

//MCU的双向数据总线设置

//transmit fifo data

assign serial_tfifo_data=mcu_data;

reg mcu_rd_reg1;

reg mcu_rd_reg2;

reg mcu_rd_reg3;

reg [3:0] mcu_rd_counter;

always @ (posedge clk or negedge rst)

    if(rst==0)

        begin

            mcu_rd_reg1<=1'b1;

            mcu_rd_reg2<=1'b1;

            mcu_rd_reg3<=1'b1;

       end

    else

        begin

            mcu_rd_reg1<=mcu_rd;

            mcu_rd_reg2<=mcu_rd_reg1;

            mcu_rd_reg3<=mcu_rd_reg2;

        end

//将MCU的读信号同步3拍,同步2拍和3拍的寄存器信号用来判断MCU的读信号的

//下降沿

always @ (posedge clk or negedge rst)

    if(rst==0)

        mcu_rd_counter<=4'b0;

    else if(mcu_rd_reg3==1'b0)

        if(mcu_rd_counter==4'd8)

            mcu_rd_counter<=4'd8;

        else

            mcu_rd_counter<=mcu_rd_counter+1;

    else

        mcu_rd_counter<=4'b0;

//对MCU的读信号宽度进行计数;

always @ (posedge clk or negedge rst)

    if(rst==0)

        begin

            mcu_data_reg<=8'b0;

            serial_rfifo_rden<=1'b1;

        end

    else if(mcu_rd_reg3==1'b0 && mcu_cs==0)

    begin

        case(mcu_addr)

         13'h0000:

                 mcu_data_reg<=serial_baudrate;

         13'h0001:

                 mcu_data_reg<={6'b0,serial_parity};

         13'h0002:

                 mcu_data_reg<={6'b0,serial_stop_bit};

         13'h0020:

                 mcu_data_reg<={7'b0,serial_rfifo_empty};

         13'h0021:

                 begin

                        mcu_data_reg<=serial_rfifo_data;

                        if(mcu_rd_counter==4'd0)

                            serial_rfifo_rden<=1'b0;

                        else

                            serial_rfifo_rden<=1'b1;

                 end

         default:

                 mcu_data_reg<=8'b0;

        endcase

   end

   else

   begin

        mcu_data_reg<=8'b0;

        serial_rfifo_rden<=1'b1;

   end

//上面这段代码实现了MCU的读取的寄存器地址定义

reg mcu_wr_reg1;

reg mcu_wr_reg2;

reg mcu_wr_reg3;

always @ (posedge clk or negedge rst)

    if(rst==0)

        begin

            mcu_wr_reg1<=1'b1;

            mcu_wr_reg2<=1'b1;

            mcu_wr_reg3<=1'b1;

       end

    else

        begin

            mcu_wr_reg1<=mcu_wr;

            mcu_wr_reg2<=mcu_wr_reg1;

            mcu_wr_reg3<=mcu_wr_reg2;

       end

//将MCU的写信号同步3拍,同步2拍和3拍的寄存器信号用来判断MCU的写信号的

//下降沿

always @ (posedge clk or negedge rst)

    if(rst==0)

        begin

            serial_baudrate<=8'h60;

            serial_parity<=2'b00;

            serial_stop_bit<=2'b00;

            serial_tfifo_wren<=1'b1;

       end

    else if(mcu_wr_reg2==0 && mcu_wr_reg3==1 && mcu_cs==0)

        begin

            case(mcu_addr)

            13'h0000:

                serial_baudrate<=mcu_data;

            13'h0001:

                serial_parity<=mcu_data[1:0];

            13'h0002:

                serial_stop_bit<=mcu_data[1:0];

            13'h0010:

                serial_tfifo_wren<=1'b0;

            default:

               begin

                    serial_baudrate<=serial_baudrate;

                   serial_parity<=serial_parity;

                    serial_stop_bit<=serial_stop_bit;

                    serial_tfifo_wren<=1'b1;

               end

            endcase

        end

  else

    begin

        serial_baudrate<=serial_baudrate;

        serial_parity<=serial_parity;

        serial_stop_bit<=serial_stop_bit;

        serial_tfifo_wren<=1'b1;

    end

//上面这段代码实现了MCU的写入的寄存器地址定义

always @ (posedge clk or negedge rst)

    if(rst==0)

        mcu_int<=1'b1;

    else if(serial_rfifo_empty==1'b0)

        mcu_int<=1'b0;

    else

        mcu_int<=1'b1;

//MCU中断信号的产生,接收FIFO非空则产生中断信号

endmodule

下图为MCU配置模块仿真波形图。可以看到MCU向地址0x000写入0x60后正确读出,即设置波特率为115200bps。

mcu.jpg

串口发送模块仿真波形图3(数据为AA、55、52,奇校验,1个停止位)

串口控制12路晶体管输出板JMDM-28DIOMT串口控制器rar,串口控制12路继电器(晶体管)输出板 28点工业级高可靠单片机控制板 RS232串口控制器 步进电机控制器 继电器气缸电磁阀控制器 精密转速控制 精密运动控制 精密机械加工控制器 包括晶体管输出(JMDM-28DIOMT)和继电器输出(JMDM-28DIOMR)两种本产品研发制造商:深圳市精敏数字机器有限公司一、特性1.8位高性能单片机作为主控制芯片,64K程序存储器,也可以用来保存数据,断电数据不丢失;2.16路光电隔离数字量输入,NPN输入形式,输入电流为10mA;其中有两路可作中断源用于计数;3.工作电源:JMDM-28DIOMR: 12V交流电或直流电,标准功率为10W;JMDM-28DIOMT:9V交流电,外围驱动电源:12V或24V交流电。4.输出形式:JMDM-28DIOMR:12路继电器输出,输出电压为0~220V,最大输出电流为5A, 输出口状态可回读;JMDM-28DIOMT:12路光电隔离数字量输出,集电极开路输出形式,输出电压为24/12V,最大输出电流为1A, 输出口状态可回读;5.系统采用光电隔离和启用内部看门狗及严格的高频滤除特性,使系统工作稳定可靠,无死机现象;6.有两盏LED灯显示工作状态,LED灯和拨码开关是复用的,也可用来设置两个参数;7.有1路标准的RS232串行通信接口(抗15KV静电冲击),可直接与电脑或触摸屏等外设通信;8.可直接通过RS232接口下载程序,无需烧录器,方便程序修改、升级;9.紧凑型 (适合任何尺寸的机箱) ,PCB尺寸:155mm*110mm;10.用Keil C 或汇编编程;二、规格 1.工作温度:-10~ 65°C;2.储存温度:-20~ 80°C ;3.湿度:5~95% 无凝结 ;4.功耗(无外部设备): 9V@0.5A(典型值) ;三、用途1.可用于各种28个点以内的控制系统,可控制气缸,电磁阀,继电器,步进电机,可外接光电式,电容式,电感式,霍尔式等各种传感器;2.可用于替代28点以内的PLC用于各种控制场合(如各种机器控制);3.可用于单片机学习和试验。四、JMDM-28DIOMR/MT应用:JMDM-28DIOMR/MT以其出色的多功能、稳定可靠性、高性价比,成为业内广受客户喜爱的一款28点单片机控制器,广泛应用在多种场合。(一)可采用RS232串口控制步进电机等应用;步进电机控制应用案例如下:1、串口通讯自动化设备2、自动化质量检测仪3、生产线智能检测系统4、定长控制系统5、候车岗亭广告牌自动切换系统6、精密转速控制、精密运动控制、精密机械加工等场合。(二)可采用RS-485总线作为各个控制板之间的通信载体,最多可由255个控制板构成一个由4080个输入点,3060个输出点的大系统;可采用电脑或主PLC作为主控制单元。RS-485 总线型远程测控系统应用案例如下:1、用于各种集散型控制系统;2、用于各种远程测控系统;3、可用于灯光控制,电厂控制,自动化车间的大中型控制项目;4、可用作远程IO模块;5、可用于电脑集中控制。更多产品详细资料,欢迎垂询:电话:0755- 29769287;13427969290 陶小姐;传真:0755-29958512 E-mail:jingmingk@163.com;QQ: 957302591 MSN:jingmingk@163.com; 阿里旺旺:jingminsz阿里巴巴网上店:http://china.alibaba.com/company/detail/jingminsz.html公司网址:www.jingmindm.com 中华工控网店铺:http://www.gkong.com/comm/userdetail.asp?id=66456
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值