您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > LPC2148串口高波特率通信解决方案
第一章问题提出由于LPC214xUSB使用的晶振频率必须为48MHz(经过倍频以后),对外接晶振有特殊要求,不能使用标准的11.0592MHz晶振。因而在使用USB时,一般采用12MHz晶振。而12MHz这样的晶振和标准波特率不成倍数关系,实际波特率和期望波特率往往不同。。下表是低波特率时期望波特率和实际波特率的对比可以看出低波特率时,实际波特率的误差比较小,对传输影响不大。而当期望波特率高达115200时,通过计算得到实际波特率为125000,误差较大,同时实验结果也表明如果此时不对其进行修正,则根本无法进行正确传输。网上大多数说法是不要使用过高的波特率传输,避免其造成的丢码率太高,而作者通过实验研究证明,通过使用小数波特率发生器,完全可以像低波特率时一样的正确传输,并且在其设定过程中,实现并应用了多位小数的分数逼近法的算法,从而科学地避免了其他人一直以来根据经验或者反复试验得出参数的人工计算过程,彻底实现波特率计算和修正的自动化。第二章小数波特率发生器第一节简介UART1小数分频寄存器(U1FDR)控制产生波特率的时钟欲分频器,用户可根据需要进行读写。该预分频器接受VPB时钟,并经过指定的小数要求产生一个输出时钟,小数由该寄存器的值决定。UART1小数分频寄存器(U1FDR)位描述该寄存器控制波特率生成的始终预分频器。该寄存器的复位值为UART1禁能小数功能,以确保UART1的软件和硬件与没有该特性的UART完全兼容。故而代码中使用如下序列if(bps=115200){Uart0SetFDR(bps);}Uart0SetDLML(bps);可保证程序在高波特率时会设置小数波特率发生器,且低速率时也能正常工作。下面的等式用于计算UART1波特率:UART1baudrate=PCLK/(16*(256*U1DLM+U1DLL))*(MulVal/MulVal+DivAddVal)其中PCLK为VPB总线时钟,U1DLM和U1DLL为标准的UART1波特率除数寄存器,DivAddVal和MulVal为UART1小数波特率发生器特定的参数。DivAddVal和MulVal的值应遵循以下的条件:1.1=MulVal=152.0=DivAddVal=153.如果U1FDR寄存器值不遵循这两个要求,那么小数分频输出未定义。如果DivAddVal为0,那么小数分频禁止且时钟将不会分频。第二节使用小数波特率发生器——校准系数的确定根据以上公式很容易计算串口波特率,但是实际应用中,更多的是在使用非标准晶振的时候确定波特率校准系数,即设定U1FDR寄存器DivAddVal和MulVal的值。确定波特率校准系数可以分如下3步进行:(1)确定除数锁存器的值:根据需要的波特率bps,按照没有校准系数的波特率计算公式确定除数锁存器的值(DLM,DLL)。由于采用非标准晶振,得到的结果通常为小数。无论小数值大小,均舍弃小数部分的值,对结果进行取整操作(不是四舍五入),得到除数锁存器的值。(2)确定校准前的波特率:将(1)得到的除数值(DLM,DLL)代入不带校准系数的串口波特率计算公式,得到不经过校准的波特率BPS(3)确定校准系数p:p=bps/BPS=MulVal/(MulVal+DivAddVal)根据限制条件1=MulVal=15和0=DivAddVal=15,寻找合适的值,使得到误差尽可能小的校准系数。第三节具体实例作者采用的实验CPU为LPC2148,晶振为12MHz,拟设定波特率bps为115200,使用UART0,根据前面所述的校准系数的确定过程,第一步得到除数锁存器的值:12000000/(16*115200)=6.51,取整后得到除数为6.将6代入没有校准系数的波特率计算公式,得到没有校准的波特率BPS=12000000/(16*(256*0+6))=12500.那么校准系数p=bps/BPS=115200/125000=0.9216,即MulVal/(MulVal+DivAddVal)=0.9216现在问题转化成为如何在限制的范围内找到两个整数,使得上式最贴近0.9216第四节传统获取MulVal和DivAddVal整数值的方法传统获取MulVal和DivAddVal整数值的方法大多依靠经验技巧或者反复试验的手段。如参考文献二《深入浅出ARM7–LPC214x下》中,第77页描述的那样:根据1=MulVal=15和0=DivAddVal=15的限制,由于系数接近......1.,因而...DivAddVal.........取尽可能小的整数,多次试验取值...............得到12/(1+12)=0.9231与期望的系数0.9216最接近,能够使波特率误差最小,因而最后确定MulVal=12,DivAddVal=1.显然,按照这种方法是无法完成程序自动化的,必须在每次编程之前根据波特率来人工推算出MulVal和DivAddVal的值,并且还需要大量的误差分析和反复试验,导致工作效率的降低。下表是MulVal和DivAddVal取值的一些具体例子,可以看出,它们的取值确实没有什么简单规律,都是靠工程师的经验和反复试验结果得到的。而在本文中,作者将引入数学方法,实现程序计算得出MulVal和DivAddVal整数值,而彻底抛弃传统的人工计算方法,不仅节省人力,而且更加科学可靠。第三章数学引入——多位小数的分数逼近算法我们的问题在于寻找一个分数,它能够尽可能的接近校准系数。那么我们可以使用这样一种多位小数的分数逼近法,该算法认为:任何一个多位小数,无论是无理数还是有理数,均可以用一个分数来近似表示它,并可用一定的程序使其误差越来越小,直至达到所需的精确度。第一节算法描述:多位小数的分数逼近法的程序描述如下:程序1确定两个起始项。起始项是分数逼近的出发项,它们是两个简单的分数。两个起始项比被逼近的小数p必须一个较大,称A项;另一个较小,称B项。起始项的分子和分母均为极简单的整数,如1,2,3等,应越小越好,可按以下方法选取。当被逼近的小数p1时,A项和B项的分子均选为1。A项的分母从1,2,3等中选取,使A项略大于p;B项的分母从2,3,4中选择,使B项略小于p,显然B项的分母比A项的分母大1。当被逼近的小数p1,A、B两项的分母均选为1时,A项的分子选用比p较大的整数,B项的分子选用比p较小的整数,显然B项的分子比A项的分子小1。程序2逼近法———分式运算为了使误差逐步减小,可将有正误差的A项和有负误差的B项这两个分数,分别将其分母相加作为新的分母,分子相加作为新的分子,于是得到一个新的分数。如果得到的分数比p大,即有正误差,则作为新的A项;如果得到的分数比p小,即有负误差,则作为新的B项。程序3循环或加权循环把新的A项与上一次的B项,或新的B项与上一次的A项,重复执行程序2,称为循环。有时A项与B项的误差相差较大,为了避免循环次数过多,可将误差小的分数的分子和分母各乘以一定的整数,再重复执行程序2。这个乘数称为权,这样的重复程序2,称为加权循环。程序4达到逼近目的多次进行程序2和3,即多次进行循环或加权循环,可得到与给定小数中相逼近的分数,其误差可满足预定精确度的要求。逼近法的几点说明:1)无论是起始项,还是以后得到的各项,始终是A项具有正误差,B项具有负误差。逼近法———分式运算的目的就是把正、负误差相补偿,使得到的新的分数具有较小的误差。2)用分式运算得到的新分数的误差,若为正,它一定比原来的A项的误差小;若为负,它的绝对值一定比原来的B项误差的绝对值小。3)每次用新的分数进行逼近法的分式运算,就可得到逐次逼近,使误差越来越小。4)要得到精确的分数近似,必须有精确的被逼近的小数,即该小数的有效数字位数应较多。第二节算法分析与修改上面是一个通用的分数逼近算法描述,而我们在实际应用中可以根据应用进行适当裁减和修改。通过分析程序1,我们知道由于我们的p值一定小于1,故而初始化时A项和B项的分子均选为1。另外原算法关于加权循环适合在大分母高精确度的情况下使用,例如循环计算圆周率,精确到像355/113甚至208341/66317这样的大分母时,而我们由于MulVal和DivAddVal值被限定在0到15之间,几次循环即可获得最优解,故而可以完全不用权实现。第四章应用分数逼近算法对小数波特率发生器进行设置的程序实现第一节校准系数P的计算采用C语言的整数除法特点自动取整,而不需要像某些语言使用floor函数取整。P=115200.0/Uart0GetBps(Uart0GetFdiv(115200));由于P是浮点数,故而分子使用了115200.0这个浮点表达式,这里Uart0GetFdiv(bps)和Uart0GetBps(Fdiv)是两个宏函数,其定义如下://用波特率得到除数因子,并且自动丢弃小数部分#defineUart0GetFdiv(bps)((Fpclk/16)/bps)//用除数因子得到波特率#defineUart0GetBps(Fdiv)(Fpclk/(16*Fdiv))本例中P=0.9216第二节计算逼近P的分数最优解假设用A1/A2来表示A,B1/B2来表示B,按照我们刚才的分析,由于P1,那么初始化时A1=B1=1另外,因为分母最大不超过16,所以用一个循环来找到最接近P的B值分母B2(BP),由于分母都是整数,所以得到最接近P的A值分母A2=B2–1。voidUart0BaudFix(floatP){intA1,A2,B1,B2;A1=B1=1;for(B2=1;B2=16;++B2){if((float)B1/B2=P)break;}A2=B2-1;printf(IntialStatus\nP=%fA=%d/%dB=%d/%d\n\n,P,A1,A2,B1,B2);Uart0BaudFixCal(P,A1,A2,B1,B2);}算法描述中的程序3是通过递归函数Uart0BaudFixCal来实现的,它接受当前的B、P、A值,并检验它们是否达到要求,若没有达到则产生新的分数(A1+B1)/(A2+B2)继续迭代;若达到了要求,则函数返回,该程序实现了两个返回条件。一个是分母超出了限制的范围,代码中的常数16可以根据应用修改if(A216){printf(%d/%d为最优A2\n,B1,B2);return;}if(B216){printf(%d/%d为最优B2\n,A1,A2);return;}另一个是A或者B与P的接近精度达到了指定的误差,代码中的常数0.0001可以根据应用修改A=(float)A1/A2;B=(float)B1/B2;BP=P-B;PA=A-P;if(BPPA){if(BP0.0001){printf(%d/%d为最优BP\n,B1,B2);return;}}else{if(PA0.0001){printf(%d/%d为最优PA\n,A1,A2);return;}}迭代的部分代码如下:if((float)(A1+B1)/(A2+B2)P){Uart0BaudFixCal(P,A1,A2,A1+B1,A2+B2);}else{Uart0BaudFixCal(P,A1+B1,A2+B2,B1,B2);}如果新产生的值小于P,那么将它作为新的B值,反之则作为新的A值,该算法结构类似二分法。至此,我们得到了最优解MulVal=B1,而DivAddVal=B2–B1第三节设置小数分频寄存器用得到的MulVal和DivAddVal设置U0FDR很简单,根据其寄存器的位描述,可以用如下代码来实现:U0FDR=(MulVal4)|DivAddVal;第四节其他其他必须的代码,如串口UART0初始化、除数锁存器的设置以及发送字符和字符串的函数和通常情况下的代码大同小异,就不做详细介绍了。只要记得在初始化的时候为了高低波特率增加一个逻辑判断结构就行。//UART0串口初始化函数,接受波特率作为参数voidUart0Init(uint32b
本文标题:LPC2148串口高波特率通信解决方案
链接地址:https://www.777doc.com/doc-2881365 .html