本节我们来使用LPC1343的UART接口做一个简单的收发实验。
大家之前应该都有使用51或AVR一类单片机做过异步串行收发实验,当然串口在电子开发中的应用地位就无需多言。我们直接进入主题。
本次试验这样设计,用PC作为上位机向UART发送一个(串)字节,然后LPC1343收到这个(串)字节后再发回UART,在PC上的串口观察软件显示出来。
我们来看NXP带给我们的UART例程来看看UART的设置以及工作过程。首先是主函数:
int main (void)
{
UARTInit(115200);//初始化UART接口并设置为波特率115200,NVIC也在内一并设置
while (1)
{
if ( UARTCount != 0 )
{
LPC_UART->IER = IER_THRE | IER_RLS;/*禁用接受缓存寄存器*/
UARTSend( (uint8_t *)UARTBuffer, UARTCount );//发送数据
UARTCount = 0;
/* 重新使能接收缓冲寄存器*/
LPC_UART->IER = IER_THRE | IER_RLS | IER_RBR;
}
}
}
从主函数就可以看到本次例程的目的了:初始化UART——一旦接收到数据之后立即停止接收——发送——开启下一次接收。最重要的当然是初始化函数UARTInit():
/*****************************************************************************
** Function name: UARTInit
**
** Descriptions: Initialize UART0 port, setup pin select,
** clock, parity, stop bits, FIFO, etc.
**
** parameters: UART baudrate
** Returned value: None
**
*****************************************************************************/
void UARTInit(uint32_t baudrate)
{
uint32_t Fdiv;
uint32_t regVal;
UARTTxEmpty = 1;
UARTCount = 0;
NVIC_DisableIRQ(UART_IRQn); //关闭UART中断,避免此后的初始化有中断打断
LPC_IOCON->PIO1_6 &= ~0x07; /* UART的IO口设置★ */
LPC_IOCON->PIO1_6 |= 0x01; /* UART 接收IO */
LPC_IOCON->PIO1_7 &= ~0x07;
LPC_IOCON->PIO1_7 |= 0x01; /* UART 发送IO */
/* Enable UART clock */
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<12);
LPC_SYSCON->UARTCLKDIV = 0x1; /*UART时钟分频器进行1分频★*/
LPC_UART->LCR = 0x83; /* 8位,无校验,1停止位,并开启除数锁存器★*/
regVal = LPC_SYSCON->UARTCLKDIV;
Fdiv=(((SystemCoreClock*LPC_SYSCON->SYSAHBCLKDIV)/regVal)/16)
//baudrate ; /*计算波特/*率★*/
LPC_UART->DLM = Fdiv / 256; //写入波特率计算值高位
LPC_UART->DLL = Fdiv % 256; //写入波特率计算值低位
LPC_UART->LCR = 0x03; /* 关闭除数锁存器★*/
LPC_UART->FCR = 0x07; /* Enable and reset TX and RX FIFO.开启与复位RX和TX*/
/*先入先出功能 */
regVal = LPC_UART->LSR;/*读取线状态寄存器以清除该寄存器内容★*/
/*确保TX和RX的FIFO队列都是清空的*/
while (( LPC_UART->LSR & (LSR_THRE|LSR_TEMT)) != (LSR_THRE|LSR_TEMT) );
while ( LPC_UART->LSR & LSR_RDR )
{
regVal = LPC_UART->RBR; /* FIFO中有未读数据则读出*/
}
/*使能UART中断 */
NVIC_EnableIRQ(UART_IRQn);
#if TX_INTERRUPT //是否使用发送中断,本次例程使用
LPC_UART->IER = IER_RBR | IER_THRE | IER_RLS; /* Enable UART interrupt */
#else //所以执行此else
LPC_UART->IER = IER_RBR | IER_RLS; /*启用缓存数据可用中断、线状态中断,同时也*/
/*开启了字符超时中断*/
#endif
return;
}
打星号的地方是笔者认为比较值得关注的地方:
1、UART的IO口设置,根据上述函数中的语句查找相关寄存器,可以发现它将P16、P17设置为:UART_RXD和UART_TXD功能;
2、选择UART时钟分频数,此处1分频,和波特率设置有直接关系;
3、选择数据格式,此处选择数据长度8位,无校验,1位停止位,并开启除数锁存;
4、除数锁存器:分为LSB(8位)和MSB(8位),用来填入对应某波特率的计数值,更改之前解除锁定,更改完毕恢复锁定,这样就可以锁定波特率了(可以这样简单的理解);
5、计算波特率,此处是重点了。首先我们肯定知道系统核心频率为72MHz,即SystemCoreClock=72 000 000(参考本系列前几章内容)。而UART作为AHB总线上的设备,自然要经过AHB分频器,在此处,AHB分频系数并未做过特别设置,所以为默认值1。时钟经过AHB分频之后要经过UART分频器进行再分频,分频系数仍为1(第2点)。因此我们来计算这个公式:
Fdiv = (((SystemCoreClock*LPC_SYSCON->SYSAHBCLKDIV)/regVal)/16)/baudrate
其中SystemCoreClock=72000000,LPC_SYSCON->SYSAHBCLKDI=1,regVal=1,baudrate=115200,所以可以计算出:
Fdiv=39.0625≈0x27
这个便是产生115200波特率所要填入除数锁存器的值。逆过来就可以计算出计数值对应的波特率。
6、线状态寄存器(下文稍加讲述)是以读操作来清空的;
其实这个函数,对于用户来说,只需要填入想要产生的波特率作为函数参数就可以完成LPC1343的UART初始化以及波特率设定工作。
设定完成之后,UART就开始工作了,因为初始化函数里面开启了“启用缓存数据可用中断、线状态中断”所以当有数据从上位机向UART发送数据时,进入中断服务函数:
void UART_IRQHandler(void)
{
uint8_t IIRValue, LSRValue;
uint8_t Dummy = Dummy;
IIRValue = LPC_UART->IIR;
IIRValue >>= 1;
IIRValue &= 0x07; /*确定中断源*/
if (IIRValue == IIR_RLS) /* 是否为线中断 */
{
LSRValue = LPC_UART->LSR;
/* 判断是哪一个中断源 */
if (LSRValue & (LSR_OE | LSR_PE | LSR_FE | LSR_RXFE | LSR_BI))
{
/* 有错误或打断中断 */
UARTStatus = LSRValue; /* 清楚线中断寄存器*/
Dummy = LPC_UART->RBR; /*读出数据,并舍弃(因为是错误的),并退出中断服*/
/*务函数*/
return;
}
if (LSRValue & LSR_RDR) /* 准备接受数据 */
{
/* 如果没有错误信息引发线中断,则保存数据缓存中的数据*/
UARTBuffer[UARTCount++] = LPC_UART->RBR;
if (UARTCount == BUFSIZE)
{
UARTCount = 0; /* 当缓存溢出时清零计数标志 */
}
}
}
else if (IIRValue == IIR_RDA) /* 接受到可用数据★*/
{
/* Receive Data Available */
UARTBuffer[UARTCount++] = LPC_UART->RBR;
if (UARTCount == BUFSIZE)
{
UARTCount = 0; /* buffer overflow */
}
}
else if (IIRValue == IIR_CTI) /* 字符超时指示*/
{
/* Character Time-out indicator */
UARTStatus |= 0x100; /* Bit 9 as the CTI error */
}
else if (IIRValue == IIR_THRE) /* 发送完成中断 */
{
/* THRE interrupt */
LSRValue = LPC_UART->LSR; /* Check status in the LSR to see if
valid data in U0THR or not */
if (LSRValue & LSR_THRE)
{
UARTTxEmpty = 1;
}
else
{
UARTTxEmpty = 0;
}
}
return;
} 这个中断服务函数是一个if…else if….else if结构。在进入此中断服务函数后,读取 |