串口USART(续二)
通过前面的分析知道,在LPC824中,USART所拥有的配置寄存器有很多,但在一般情况下,如果只是简单地使用收发功能,则只需要使用到配置寄存器CFG、波特率发生器寄存器BRG、中断使能读取和置位寄存器INTENSET、中断状态寄存器INTSTAT、接收器数据寄存器RXDAT、发送器数据寄存器TXDAT等6个主要寄存器,其余的寄存器取默认值即可。下面就来看一个实例。
例子:要求LPC824把USART接收到的数据原样发送回去,波特率115200,8位数据位,1位停止位。
完整代码如下。
#include <LPC82x.h>
//************************串口初始化***********************************
void uart_init(void)
{
LPC_SYSCON->SYSAHBCLKCTRL|=1<<7; //开启SWM时钟
LPC_SWM->PINASSIGN0 &= ~0xFFFF;
LPC_SWM->PINASSIGN0 |= 0x04;
LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<7); //关闭SWM时钟(使用完之后记得关闭,节省功耗)
LPC_SYSCON->SYSAHBCLKCTRL |= 1<<14; //开启USART0时钟
LPC_SYSCON->PRESETCTRL &= ~(1<<3); //开启复位USART0
LPC_SYSCON->PRESETCTRL |= (1<<3); //关闭复位USART0
LPC_USART0->CFG &= ~(1<<0); //串口USART0禁止
LPC_USART0->CFG|=1<<2; //配置USART0为8位数据方式
LPC_SYSCON->UARTCLKDIV = 1;
LPC_SYSCON->UARTFRGDIV = 0xFF;
LPC_SYSCON->UARTFRGMULT = 0; //配置串口的外设时钟U_PCLK
LPC_USART0->BRG = 12; //配置USART0的波特率为115200
LPC_USART0->CFG |= 1<<0; //串口USART0使能
}
//***************************主函数************************************
int main(void)
{
uart_init();
LPC_USART0->INTENSET |= 1<<0; //使能串口接收中断
NVIC_ClearPendingIRQ(UART0_IRQn); //清除挂起的中断
NVIC_EnableIRQ(UART0_IRQn); //使能NVIC串口中断
while(1)
;
}
//************************串口中断***********************************
void UART0_IRQHandler(void)
{
// if(LPC_USART0->STAT & 0x1)
if(LPC_USART0->INTSTAT & 0x1) //如果是接收到数据的中断
LPC_USART0->TXDAT = LPC_USART0->RXDATSTAT; //把接收到的数据发送出去
}
从上述代码中可以看出,在默认状态下,LPC824是没有把USART(包括USART0~USART2)的引脚映射到芯片的物理引脚上的,所以初始化串的第一步,就要把串口所需要的引脚映射出来。 在上述程序中,通过操作开关矩阵寄存器SWM,把USART0的RXD和TXD引脚分别映射到了PIO0_0和PIO0_4物理引脚上。引脚映射完成后,要关闭SWM时钟,以节约能耗。接下来需要开启USART0的时钟,并对其进行一次复位操作。开启USART0的时钟,配置的是SYSAHBCLKCTRL寄存器,详见前面时钟配置部分。复位USART0配置的是PRESETCTRL寄存器,详见前面多速率定时器部分。
接下来配置CFG寄存器,使能USART0,设置为8位数据方式。本例的波特率为115200,为了弄清波特率的配置,需要先讨论一下在LPC824中,串口的波特率是如何进行计算的。
先来看一下LPC824的USART时钟结构,如下图所示。
从图中可以看到USART的时钟分为两个部分,第一部分是由系统时钟(system clock)提供的USART模块时钟,它为整个USART模块提供正常工作所需的时钟,受SYSAHBCLKCTRL寄存器第15到17位的控制(详见前面时钟配置部分)。第二部分是由主时钟(main clock)分频后提供一个统一的外设时钟(U_PCLK),外设时钟再经由各自(USART0~USART2)的波特率时钟发生器,为各自的异步通信提供独立的波特率。
外设时钟的值为:U_PCLK = FRGCLKDIV/(1+MULT/DIV)。三个USART模块共用一个外设时钟,并且在需要时共用一个带小数的波特率发生器。
上面公式中的FRGCLKDIV项由USART时钟分频器寄存器UARTCLKDIV来实现,下面给出的是它全部位结构,其字节地址为0x40048094。
(1)第0到7位(DIV)为USART小数波特率生成器时钟分频器值,值从0到255,分别对应相应的分频值,其中值为0时时钟被禁止。
(2)第8到31位为保留位。
公式中的MULT项由USART小数生成器乘法器值寄存器UARTFRGMULT来实现,下面给出的是它全部位结构,其字节地址为0x400480F4。
(1)第0到7位(MULT)为小数分频器分子,MULT等于设定值。
(2)第8到31位为保留位。
公式中的DIV项由USART小数生成器分频器值寄存器UARTFRGDIV来实现,下面给出的是它全部位结构,其字节地址为0x400480F0。
(1)第0到7位(DIV)为小数分频器分母,DIV等于设定值+1。要使用小数波特率生成器,应当始终设为0xFF。
(2)第8到31位为保留位。
UARTFRGDIV寄存器位于分母上,其默认值为0,表明默认时分频不启用小数部分,如果分频需要启用小数部分,则它的值只能是255(即0xFF)。如果在启用后位于分子的UARTFRGMULT寄存器的值为0(默认值),则小数分频仍为1分频,即不分频,除非UARTFRGMULT的值大于0。综上所述,默认状态下不启用分频的小数部分,若要启用则UARTFRGDIV及UARTFRGMULT两个寄存器的值都不为0。
配置上述三个寄存器,可以得到一个统一的外设时钟U_PCLK。注意,U_PCLK时钟并不区分是哪个USART模块的,它是三个USART模块共用的时钟,如果USART0~USART2三个模块的波特率不一致,则还需要配置各自模块内的USART波特率发生器寄存器BRG,该寄存器的具体位结构参见前面“串口USART(续一)”部分。每个USART模块的波特率为:Baud rate=U_PCLK/(16×(BRGVAL+1))。
下面总结一下计算波特率的步骤:
1、主时钟(main clock)先除以UARTCLKDIV中的分频值,得到分频以后的时钟,如果分频值为1,则分频后的值与主时钟一致。
2、如果启用了分频的小数部分,则把第1步中得到的分频值再乘以小数部分,得到的值即为U_PCLK时钟,小数部分的值按公式 1/(1+UARTFRGMULT/256)进行计算。若未启用小数部分,则第1步中得到的分频值即为U_PCLK时钟。
3、对上面得到的U_PCLK时钟进行16的倍数的再分频,倍数值即为BRG寄存器的值加1(BRGVAL+1),再分频后的时钟值即为最终的波特率。
4、有时无论如何取三个寄存器的值,得到的波特率都不会完全等于预期值,仍会有一定的误差,一般来说只要该误差控制在2%以内,USART的异步通信就可以正常工作了。
另外,在LPC824中,波特率时钟一般是实际波特率的16倍(默认为16倍,可在过采样选择寄存器OSR中进行配置),这种超采样允许将数据采样时间集合到一个通信位的单元中,并可通过从接收数据中取三次采样来降低噪声和实行检测。比如说,在接收到的某一个位上是高电平1,按传统的方式只对该位采样一次,而在LPC824中会采样三次,若三次都为高电平则表明该位是1,若三次采样中有一次是低电平,则表明通信上存在噪声干扰,此时虽然该位仍然是1,但LPC824会在RXDATSTAT、STAT及INTSTAT寄存器中标记一个位(第15位),以供程序进行通信噪声检测。
在前面的示例程序中,要配置波特率为115200,则各寄存器的取值如下:UARTCLKDIV=1, UARTFRGMULT=0,UARTFRGDIV=255(DIV等于设定值+1),BRGVAL=12。计算如下。
主时钟(main clock)为24MHz。
U_PCLK的分频值为:1/(1+0/256)=1,即1分频,则外设时钟U_PCLK的值也为24MHz。
波特率为:Baud rate=24000000/(16×(12+1))=115384,该值最为接近115200,误差仅为0.2%以内。
同理,如果要把波特率改为9600,则各寄存器的取值改为如下:UARTCLKDIV=1, UARTFRGMULT=64,UARTFRGDIV=255,BRGVAL=124。通过这样的配置,可准确的产生出9600的波特率,没有误差。当然,也可以有其他配置,比如:UARTCLKDIV=2, UARTFRGMULT=0,UARTFRGDIV=255,BRGVAL=77。但这个配置存在误差,经过计算,该误差仍控制在0.2%以内。下表给出在24MHz的主时钟下,能产生出一部分精确波特率的配置情况。
在前面的示例程序中,采用了接收中断的方式来实现,在中断服务程序中,由于响应的是USART的NVIC中断,所以需要判断一下具体是什么类型的USART中断,有两种方式,一种是通过读取INTSTAT寄存器来判断,另一种是通过读取STAT寄存器来判断,效果是一样的。此外,在配置CFG寄存器时,一般建议先把该USART禁能,等配置完成后再使能,以避免出错。
该示例程序只是以最简单的方式来验证USART的异步收发功能,在实际的项目应用中,应该结合LPC824中丰富的各个串口配置寄存器,把USART的通信程序做的即高效又健壮,这里就不再进行深入讨论了。