欢迎光临散文网 会员登陆 & 注册

详解串行通信协议及其FPGA实现,5000字先马后看

2022-05-25 22:02 作者:大方老师单片机课堂  | 我要投稿

详解串行通信协议及FPGA实现5000字先马后看


\\\插播一条:

自己在今年整理一套单片机单片机相关论800余篇

论文制作思维导图

原理+源代+开题报++外文资料

想要的同学私信找我。


前言

好久没更新博客了,这篇文章写写停停,用了近一周的时间,终于写完了。本篇文章介绍,串口协议数据帧格式、串行通信的工作方式、电平标准、编码方式Verilog实现串口发送一个字节数据和接收一个字节数据。

MCU串口的发送接收,可能就1行代码就能实现串口的发送和接收:

STM32的串口接收和发送

//STM321个字节USART_SendData(USART1,'A');while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_TXE)==RESET);

//STM321个字节:uint8_tRes;while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET);Res=USART_ReceiveData(USART1);

51单片机的发送和接收

//51单片机发1个字节SBUF='A;while(!TI);TI=0;

//51单片机接1个字节:charRes;if(RI){

Res=SBUF;

RI=0;}

更方便一点的,通过重Cfput函数fgetc函数,还可以实printf直接重定向到串口,用来输出一些调试信息再方便不过了。

STM32实现输入输出重定向到串口发送接收

//可重定printf函数intfputc(intch,FILE*f){

USART_SendData(DEBUG_USARTx,(uint8_t)ch);

while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_TXE)==RESET);

return(ch);}//可重定scanf函数intfgetc(FILE*f){

while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_RXNE)==RESET);

return(int)USART_ReceiveData(DEBUG_USARTx);}

MCU上的串口是半导体厂商预先设计好的,几乎MCU的标配,高度集成,使用起来十分方便,但是串口的引脚基本上是固定的,不可以更改。对于硬件橡皮FPGA来说,需要使HDL从底层串口数据帧来实现,可以直接在任意一个引脚实现串口功能。为了Verilog HDL实现标准的串口通讯协议,我们有必要先来详细了解一下串口通讯协议。

串口数据帧格式

波特率

波特率,即比特率Baud rate),即通信双沟通的语,通信双方要设置为一样的波特率才可以正常通信。表示每秒发送的二进制位数,即传1位的时间是1/波特秒,如,波特9600bps,即每秒传9600bit,那么每一位的时间为1/9600 s = 104.1666us,常用的波特率有4800/9600/115200/12800等等,也可以根据需要自定义波特率大小,1M3M,但是有PCUSB-TTL模块不支持太高速度的波特率,常用USB-TTL芯片有CH340CP2102PL2103FT232等,其FT232HL芯片最大支12M的波特率,当然价格也比其他芯片高一些。

起始位和停止位

数据帧从起始位开始,到停止位结束。起始信号用逻0表示,而停止位是用逻1表示,一般0.511.52位停止位,常用的一般1位停止位,只要通信双方约定一致即可。

数据位

起始位之后,紧跟着的是数据位,低位LSB)在前,高位MSB)在后,一般5678位数据位,常用的8位数据位,因为一个字节正好8位。

校验位

校验位一般用来判断接收的数据位有无错误,校验方法有:奇校验odd)、偶校验even0校验space1校验mark)及无校验noparity)。奇校验要求有效数据和校验位1的个数为奇数,比如一8位长的有效数据为01101001,此时共41,为达到奇数"1"的效果,校验位11的个数变5个(奇数)。偶校验刚好相反,要求有效数据和校验位1数量为偶数,则此时为达到偶校验效果,校验位00校验,即校验位总是01校验校验位总是1。奇偶校验逻辑相反01校验逻辑相反。一般是奇偶校验或者是无校验位。

奇偶校验Verilog实现

Verilog中奇偶校验的计算非常简单,根据奇偶校验的原理,偶校验为数据位各位异或,奇校验是偶校验取反,通过使用单目运算符的缩减功能,可以非常简单的计算奇偶校验位:

input[7:0]data_in,//需要发送8位数据wireeven_bit;//偶校验 =各位异或wireodd_bit;//奇校验 = ~偶校验位assigneven_bit=^data_in;//一元约简运算符,等效data_in[0] ^ data_in[1] ^ .....assignodd_bit=~even_bit;

wirePOLARITY_BIT=even_bit;//偶校验

关于波特率允许的误差

经过我的实际测试,波特率是有一定的容错范围的,例如STM32配置115200波特率,10ms发送一30字节的字符串,串口芯片用CH340,上位机波特率设置113000-121000也可以接收,无乱码,差不多正2000的波特率,这容错范围也太大了,当然如果发送频率太快,数据量太大,误码率肯定会大大增加,所以还是建议通信双方使用同样的波特率以减少误差。

串口数据的实际波形

使用串口上位机连USB-TTL模块,发送一个字节数据1位停止+8位数据+1位奇校验+1位停止位,使用示波器的单次触发功能,可以USB-TTL模块TX引脚测得串口协议数据的实际波形,你知道这发送的是什么字符吗?

一个字符的实际波形


两个字符的实际波形


单工、半双工、全双工、异步和同步的区别

在介绍串口的电平标准之前,先来了解一下串行通信的工作方式,即单工、半双工、全双工,异步和同步的区别。

单工

单工,即数据传输只在一个方向上传输,只能你给我发送或者我给你发送,方向是固定的,不能实现双向通信,如:室外天线电视、调频广播等。

半双工

半双工比单工先进一点,传输方向可以切换,允许数据在两个方向上传输,但是某个时刻,只允许数据在一个方向上传输,可以基本双向通信,如:对讲机IIC通信。


全双工

比半双工更先进的是全双工,允许数据同时在两个方向传输。发送和接收完全独立,在发送的同时可以接收信号,或者在接收的同时可以发送。它要求发送和接收设备都要有独立的发送和接收能力,如:电话通信SPI通信,串口通信。

同步和异步的区别

串行通信可以分为两种类型,一种叫同步通信,另一种叫异步通信。


简单的说,就是同步通信需要时钟信号,而异步通信不需要时钟信号。

·同步:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。

·异步:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。

SPIIIC为同步通信UART为异步通信,USART为同&异步通信。

·USART:通用同步和异步收发器

·UART:通用异步收发器

USART支持同步和异步收发,UART只支持异步收发。

STM32的串口工作在同步模式时,即智能卡模式时,就需要连接同步时钟引脚。


常用的串行通信协/电平标准

TTL电平

即普MCU芯片输出的串口电平,如MCU输出的串口信号就TTL电平。低电平0-GND,高电平1-VCC,标准的数字电路逻辑。特点是速度快,延迟低,但是功耗大。基本上用于板内两个芯片之间短距离通信。

RS232

RS232是工业上常用的串口标准,无论PLC232接口,还是工控机上的串口,输出的串口电平都232电平标准232标准采用负逻辑电平,-15~-3v为逻1+3~+15为逻0,这里的电平是RXTX相对GND的电压,可见无论在电压范围还是电压极性上都TTL不同,显然这两种电平不能直接连接,需要使MAX232类似的电平转换芯片,对两种电平进行互相转换,全双工,传输距离一般控制20m以内,原因RS-232属单端信号传送,存在共地噪声和不能抑制共模干扰等问题。


RS485

在要求通信距离为几十米到上千米时,广泛采RS-485串行总线标准RS-485采用平衡发送和差分接收,因此具有抑制共模干扰的能力。加上总线收发器具有高灵敏度,能检测低200mV的电压,故传输信号能在千米以外得到恢复 RS-485采用半双工工作方式,任何时候只能有一点处于发送状态,因此,发送电路须由使能信号加以控制RS-485用于多点互连时非常方便,可以省掉许多信号线。应RS-485可以联网构成分布式系统,其允许最多并32台驱动器32台接收器。

RS422

RS-422RS-485电路原理基本相同,都是以差分方式发送和接受,不需要数字地线RS-422通过两对双绞线可以全双工工作收发互不影响,RS485只能半双工工作,发收不能同时进行,但它只需要一对双绞线RS422RS48519kpbs下能传1200RS-422的电气性能RS-485完全一样。主要的区别在于RS-4224根信号线:两根发送YZ)、两根接收AB)。由RS-422的收与发是分开的所以可以同时收和发(全双工)。

串行通信的编码方式

RZ编码

RZ编码也成为归零码,归零码的特性就是在一个周期内,用二进制传输数据位,在数据位脉冲结束后,需要维持一段时间的低电平。如图:


上图表示的是单极性归零码,即低电平表0,正电平表1。对于双极性归零码来说,则是高电平表1,负电平表0。如下图所示:


NRZ编码

NRZ编码也成为不归零编码,也是我们最常见的一种编码,即正电平表1,低电平表0。它RZ码的区别就是它不用归零,也就是说,一个周期可以全部用来传输数据,这样传输的带宽就可以完全利用。


NRZI编码

NRZI编码的全称为反向不归零编码,这种编码方式集成了前两种编码的优点,即既能传输时钟信号,又能尽量不损失系统带宽。对USB2.0通信的编码方式就NRZI编码。其NRZI编码方式非常的简单,即信号电平翻转表0,信号电平不变表1。例如想要表00100010(B),则信号波形如下图所示:


例如有一段数据为1111 1111 (B)要发送,则整个传输线上的电平状态是这样的:


Manchester编码

曼彻斯特编码,又称数字双向码、分相码或相位编(PE),是一种常用的的二元码线路编码方式。常用在以太网通信,列车总线控制,工业总线等领域。在曼彻斯特编码中,每一位的中间有一跳变,位中间的跳变既作时钟信号,又作数据信号;从高到低跳变表0,从低到高跳变表1。其中非常值得注意的是,在每一位""必有一跳变,根据此规则,可以得出曼彻斯特编码波形图的画法。例如:传输二进制信0,若0看作一位,我们0为中心,在两边用虚线界定这一位的范围,然后在这一位的中间画出一个电平由高到低的跳变。后面的每一位以此类推即可画出整个波形图。举个图例吧,若要表示数1001 1010(B),则信号波形图如下图所示:


曼彻斯特编码方式也如前面所说,虽然传输了时钟信号,但也损失了一部分的带宽,主要表现在相邻相同数据上。但对于高速数据来说,这种编码方式无疑是这几种编码方式中最优的,相NRZI编码,曼彻斯特编码不存在长时间信号状态不变导致的时钟信号丢失的情况,所以在这种编码方式在以太网通信中是十分常用的。

串行和并行哪个速度快?

串口,即串行通信接口,与之对应的是并行接口。在实际时钟频率比较低的情况下,并口因为可以同时传输若干比特,速率确实比串口快。但是,随着技术的发展,时钟频率越来越高,当时钟频率提高到一定的程度时,并行接口因为有多条并行且紧密的导线,导线之间的相互干扰越来越严重。而串口因为导线少,线间干扰容易控制,况且加上差分信号的加持,抗干扰性能大大提升,因此可以通过不断提高时钟频率来提高传输速率,这就是为什么现在高速传输都采用串行方式的原因。例如常见USBSATAPCIe、以太网等。

如果有人问关于串行传输与并行传输谁更好的问题,你也许会脱口而出:串行通信好!但是,串行传输之所以走红,是由于将单端信号传输转变为差分信号传输,并提升了控制器工作频率的原因,在相同频率下并行通信速度更这个基本道理是永远不会错的,通过增加位宽来提高数据传输率的并行策略仍将发挥重要作用。当然,前提是有更好的措施来解决并行传输过程中的种种问题。

标准串口协议Verilog实现

Verilog实现标准串口协议发8位数据:起始 + 8位数据 +校验 +停止 = 11位,1位的时间16个时钟周期,所以输入时钟应该为:波特*16Busy忙信号输出。实现方法比较简单,数据帧的拼接、计数器计时钟周期,16个时钟周期输出一位数据即可。

串口发1个字节实现

/*串口协议发送:起始 + 8位数据 +校验 +停止 = 11 * 16 = 176个时钟周clk =波特 * 16*/

moduleuart_tx_8bit(

//inputinputclk,//UART=16*波特率inputrst_n,input[7:0]data_in,//需要发送的数据inputtrig,//上升沿发送数//outputoutputbusy,//高电平:数据正在发送中outputregtx//发送数据信号);

reg[7:0]cnt;//计数器regtrig_buf;regtrig_posedge_flag;// reg trig_negedge_flag;regsend;

reg[10:0]data_in_buf;//trig上升沿读取输入的字节,拼接数据帧wireodd_bit;//奇校验 = ~偶校验位wireeven_bit;//偶校验 =各位异或wirePOLARITY_BIT=even_bit;//偶校// wire POLARITY_BIT = odd_bit; //奇校验assigneven_bit=^data_in;//一元约简= data_in[0] ^ data_in[1] ^ .....assignodd_bit=~even_bit;assignbusy=send;//输出的忙信//起始+8位数据+校验+停止 = 11 * 16 = 176个时钟周期parameterCNT_MAX=176;

always@(posedgeclk)begin

if(!rst_n)

begin

trig_buf0;

trig_posedge_flag0;

// trig_negedge_flag end

else

begin

trig_buftrig;

trig_posedge_flag(~trig_buf)&trig;//trig信号上升沿时产1个时钟周期的高电平// trig_negedge_flag /trig信号下降沿时产1个时钟周期的高电endend

always@(posedgeclk)begin

if(!rst_n)

send0;

elseif(trig_posedge_flag&(~busy))//当发送命令有效且线路为空闲时,启动新的数据发送进程send1;

elseif(cnt==CNT_MAX)//一帧资料发送结束send0;end

always@(posedgeclk)begin

if(!rst_n)

data_in_buf11'b0;

elseif(trig_posedge_flag&(~busy))//只读取一次数据,一帧数据发送过程中,改变输入无效data_in_buf{1'b1,POLARITY_BIT,data_in[7:0],1'b0};//数据帧拼接end

always@(posedgeclk)begin

if(!rst_n)

cnt0;

elseif(!send||cnt>=CNT_MAX)

cnt0;

elseif(send)

cntcnt+1;end

always@(posedgeclk)begin

if(!rst_n)

tx1;

elseif(send)

begin

case(cnt)//1位占16个时钟周期0:txdata_in_buf[0];//低位在前,高位在后16:txdata_in_buf[1];//bit0,占用16~31个时钟32:txdata_in_buf[2];//bit1,占用47~32个时钟48:txdata_in_buf[3];//bit2,占用63~48个时钟64:txdata_in_buf[4];//bit3,占用79~64个时钟80:txdata_in_buf[5];//bit4,占用95~80个时钟96:txdata_in_buf[6];//bit5,占用111~96个时钟112:txdata_in_buf[7];//bit6,占用127~112个时钟128:txdata_in_buf[8];//bit7,占用143~128个时钟144:txdata_in_buf[9];//发送奇偶校验位,占用159~144个时钟160:txdata_in_buf[10];//发送停止位,占用160~167个时CNT_MAX:tx1;//无空闲位default:;

endcase

end

elseif(!send)

tx1;end

endmodule

仿真波形


串口接1个字节实现

串口接收部分的实现,涉及到串口数据的采样,对MCU来说,不同单片机集成外设的处理方式有所不同,具体采样原理可以参考内核Reference Manual。以传51内核为例,按照所设置的波特率,每个位时间被分16个时间片UART接收器会在789三个时间片进行采样,按照三取二的逻辑获得该位时间内的采样结果。其它一些类型的单片机则可能会更加严苛,例如有些工业单片机会五取三甚至七取五(设置成抗干扰模式时)。

本程序中采用的中间值采样,即16个时钟周期中的中间位作为当前的采样值。

//Verilog实现串口协议接收,带错误指示,校验错误和停止位错/*16个时钟周期接1位,中间采*/modulemy_uart_rx(

inputclk,//采样时钟inputrst_n,inputrx,//UART数据输入outputreg[7:0]dataout,//接收数据输出outputregrx_ok,//接收数据有效,高说明接收到一个字节outputregerr_check,//数据出错指示outputregerr_frame//帧出错指示);

reg[7:0]cnt;reg[10:0]dataout_buf;

regrx_buf;regrx_negedge_flag;regreceive;

wirebusy;wireodd_bit;//奇校验 = ~偶校验位wireeven_bit;//偶校验 =各位异或wirePOLARITY_BIT;//本地计算的奇偶校// wire polarity_ok;// assign polarity_ok = (POLARITY_BIT == dataout_buf[9]) ? 1 : 0; //校验正=1,否=0assignbusy=rx_ok;assigneven_bit=^dataout;//一元约简= data_in[0] ^ data_in[1] ^ .....assignodd_bit=~even_bit;assignPOLARITY_BIT=even_bit;//偶校// assign POLARITY_BIT = odd_bit; //奇校验parameterCNT_MAX=176;

//rx信号下降沿标志位always@(posedgeclk)begin

if(!rst_n)

begin

rx_buf0;

rx_negedge_flag0;

end

else

begin

rx_bufrx;

rx_negedge_flagrx_buf&(~rx);

endend//在接收期间,保持高电平always@(posedgeclk)begin

if(!rst_n)

receive0;

elseif(rx_negedge_flag&&(~busy))//检测到线路的下降沿并且原先线路为空闲,启动接收数据进程receive1;//开始接收数据elseif(cnt==CNT_MAX)//接收数据完成receive0;end//起始+8位数据+校验+停止 = 11 * 16 = 176个时钟周期always@(posedgeclk)begin

if(!rst_n)

cnt0;

elseif(!receive||cnt>=CNT_MAX)

cnt0;

elseif(receive)

cntcnt+1;end//校验错:奇偶校验不一致always@(posedgeclk)begin

if(!rst_n)

err_check0;

elseif(cnt==152)

begin

// if(POLARITY_BIT == rx)if(POLARITY_BIT!=dataout_buf[9])//奇偶校验正确err_check1;//锁存// else// err_check endend//帧错:停止位不1always@(posedgeclk)begin

if(!rst_n)

err_frame0;

elseif(cnt==CNT_MAX)

begin

if(dataout_buf[10]!=1)//停止位err_frame1;

// else// err_frame /如果没有接收到停止位,表示帧出错endend

always@(posedgeclk)begin

if(!rst_n)

dataout11'h00;

elseif(receive)

begin

// if(rx_ok)if(cnt>=137)

dataoutdataout_buf[8:1];//数据:8-1// else if(!rx_ok)// dataout endend

always@(posedgeclk)begin

if(!rst_n)

rx_ok0;

elseif(receive)

begin

if(cnt>=137)//137-169rx_ok1;

else

rx_ok0;

end

else

rx_ok0;end

//起始+8位数+奇偶校验+停止 = 11 * 16 = 176always@(posedgeclk)begin

if(!rst_n)

dataout_buf8'h00;

elseif(receive)

begin

case(cnt)//中间采样8'd8:dataout_buf[0]rx;//起始=08'd24:dataout_buf[1]rx;//LSB低位在前8'd40:dataout_buf[2]rx;

8'd56:dataout_buf[3]rx;

8'd72:dataout_buf[4]rx;

8'd88:dataout_buf[5]rx;

8'd104:dataout_buf[6]rx;

8'd120:dataout_buf[7]rx;

8'd136:dataout_buf[8]rx;//MSB高位在后8'd152:dataout_buf[9]rx;//奇偶校验位8'd168:dataout_buf[10]rx;//停止=1default:;

endcase

endend

endmodule


【文章福利】:小编整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!~点击绿色通讯软件搜airuimcu加入。

详解串行通信协议及其FPGA实现,5000字先马后看的评论 (共 条)

分享到微博请遵守国家法律