您的位置:首页 > 其它

LPC2378串口高波特率通信解决方案

2013-01-18 11:56 239 查看
本文一二三四五章属转载,供参考学习,觉得三四章太繁琐,真正设置时没必要。

文章后是本人实验所总结,在项目中已经实现了高波特率通信,重点在外设时钟、小数波特率发生器的设置。

第一章 问题提出

由于LPC214x USB使用的晶振频率必须为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 <= 15

2. 0 <= DivAddVal <= 15

3. 如果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等,应越小越好,可按以下方法选取。

当被逼近的小数p < 1时,A 项和B 项的分子均选为1 。A 项的分母从 1 ,2 ,3等中选取,使 A 项略大于p; B 项的分母从 2 ,3 ,4 中选择, 使 B 项略小于 p, 显然 B 项的分母比 A 项的分母大 1 。

当被逼近的小数 p > 1 ,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,

按照我们刚才的分析,由于P < 1,那么初始化时A1 = B1 = 1

另外,因为分母最大不超过16,所以用一个循环来找到最接近P的B值分母B2(B < P),由于分母都是整数,所以得到最接近P的A值分母A2 = B2 – 1。

voidUart0BaudFix(float P)

{

int A1, A2, B1, B2;

A1 = B1 = 1;

for(B2 = 1; B2 <= 16; ++B2)

{

if( (float)B1 / B2 <= P)

break;

}

A2 = B2 - 1;

printf("Intial Status\nP = %f A =%d/%d B = %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(A2 > 16)

{

printf("%d/%d为最优A2\n", B1, B2);

return;

}

if(B2 > 16)

{

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(BP < PA)

{

if(BP< 0.0001)

{

printf("%d/%d为最优BP\n", B1, B2);

return;

}

}

else

{

if(PA< 0.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= (MulVal << 4) | DivAddVal;

第四节 其他

其他必须的代码,如串口UART0初始化、除数锁存器的设置以及发送字符和字符串的函数和通常情况下的代码大同小异,就不做详细介绍了。只要记得在初始化的时候为了高低波特率增加一个逻辑判断结构就行。

//UART0串口初始化函数,接受波特率作为参数

void Uart0Init(uint32 bps)

{

PINSEL0 = 0x00000005; // 设置I/O连接到UART0

if( bps >= 115200)

{

Uart0SetFDR(bps);

}

Uart0SetDLML(bps);

}

//设置除数因子函数,接受波特率作为参数

voidUart0SetDLML(uint32 bps)

{

uint32 Fdiv;

Fdiv = Uart0GetFdiv(bps);

U0LCR = 0x83; // DLAB = 1,可设置波特率

U0DLM = Fdiv / 256;

U0DLL= Fdiv % 256;

U0LCR = 0x03;

}

…………………………(其他代码省略)

第五章 总结

当由于某些原因使用12MHz晶振代替标准的11.0592MHz晶振时,串口通信波特率会有误差,本文对该误差进行了实验研究,得出一些有用的结论:

1、 当期望波特率在57600以下时,实际波特率与其误差较小,可以正常通信;而当期望波特率为115200时,实际波特率为125000,在这样大的误差下如果不修正实际波特率根本无法进行正确的通信;

2、 通过设置LPC214xCPU的小数波特率发生器可以校准非标准频率晶振所引起的波特率误差,这可以让期望波特率高达115200时可以正常通信。如图,此时的实际波特率为115384,误差较小,传送数据完全正常。

另外,本文首次在计算小数分频寄存器中的设置值时引入了科学的数学算法,摒弃了传统工程师使用经验技巧和反复试验获取MulVal和DivAddVal值的人工方法,实现了由程序自动、正确、快速地获取设置寄存器参数值,不仅提高了工作效率,更重要的是完全自动化了程序,无需再人工干预。

希望本文对嵌入式开发同行有一定的参考价值,如有错误,不吝赐教。

参考文献

1、 《ARM控制器基础与实战》周立功等

2、 《深入浅出ARM7-LPC214x》周立功等

3、 《深入浅出ARM7-LPC213x》周立功等

4、 《ARM基础实验教程》周立功等

5、 《8051微控制器和嵌入式系统》

6、 《多位小数的分数逼近法》陈其翔 北京联合大学学报1997 年 9 月

第 11 卷第 3 期总 29 期

以上属于转载,谢谢原创把自己的成果共享到网上,非常好的一篇技术文章!

在此有一点问题提出:

当我把cclk倍频到57600000、pclk为cclk/2时,设置波特率为115200,使用小数波特率发生器的话,根据公式:

UART1baudrate= PCLK/(16 *(256 * U1DLM + U1DLL)) * (MulVal / MulVal + DivAddVal)

则MulVal /( MulVal + DivAddVa)应该为24/25,超出了MulVal 和DivAddVa值范围,经过计算,原因是cclk和pclk过大,把串口的pclk改小的话,就可以用小数波特率发生器得到较准确地波特率。

也可以改小CCLK,改小CCLK的话,CPU的处理速度就降低了,功耗也降低了,CPU速度低点不影响,比单片机还是快多了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: