以STC89C52RC平台为例。STC89C52RC只有1个UART口,P3.0对应RXD,P3.1对应TXD。
1. 时钟
MCU外部晶体时钟为11.05926MHz.
#define FOSC 11059200L
对于STC89C52来说,MCU可以是6T或者12T(51内核默认时钟分频)
#define CLKDIV 6
2. 波特率
UART的波特率是通过T1或者T2得到的。
2.1 定时器/计数器T1
2.1.1 设置T1的工作模式为8位自动重装载模式。
TMOD &= 0x0F;
TMOD |= 0x20; //Set Timer1 as 8-bit auto reload mode
2.1.2 根据波特率设置重装载值
TH1 = TL1 = -(FOSC / CLKDIV / 32 / baudrate); //Set auto-reload vaule
即采用T1作为波特率发生器时,最大的波特率时FOSC / CLKDIV / 32,对于FOSC=11.05926M,CLKDIV = 6时,最大波特率为57600;而CLKDIV=12时,最大波特率为28800。
2.1.3 波特率翻倍设置
PCON的位7 SMOD是波特率选择位,默认为0,即波特率不翻倍,当设置为1时,波特率翻倍。对于FOSC=11.05926M,CLKDIV = 6时,最大波特率变为115200;而CLKDIV=12时,最大波特率为57600。
PCON |= 0x80; //速率加倍
2.1.4 使能T1
TR1 = 1; //Timer1 start run
2.1.5 完整的初始化代码
一般情况下波特率不会选择太低的(例如110,300,600之类的),所以默认波特率翻倍。
TMOD &= 0x0F;
TMOD |= 0x20; //Set Timer1 as 8-bit auto reload mode
PCON |= 0x80; //速率加倍
TH1 = TL1 = -(FOSC / CLKDIV / 16 / baudrate); //Set auto-reload vaule
TR1 = 1; //Timer1 start run
2.2 定时器/计数器T2
2.2.1 根据波特率设置重装载值
T2是16位自动重装。
TL2 = RCAP2L = (65536 - (FOSC / (32 / (12 / CLKDIV)) / baudrate)); //Set auto-reload vaule
TH2 = RCAP2H = (65536 - (FOSC / (32 / (12 / CLKDIV)) / baudrate)) >> 8;
RCAP2L和RCAP2H保存的就是自动重载值。
PCON的位7对T2无效,测试最大波特率230400。
2.2.2 配置T2
T2CON = 0x34; //Timer2 start run
RCLK和TCLK为0(默认)时,波特率发生器选择T1,而设置为1时选择T2。TR2 = 1启动T2。
3. 校验方式
校验是第9位实现,有5种形式的校验。
typedef enum eUartParity
{
UART_PARITY_NONE = 0,
UART_PARITY_EVEN,
UART_PARITY_ODD,
UART_PARITY_MARK,
UART_PARITY_SPACE,
}uartparity_e;
UART_PARITY_NONE --- 无校验,此时UART工作在8位模式
UART_PARITY_EVEN --- 偶校验,数据有偶个逻辑高位
UART_PARITY_ODD --- 奇校验,数据有奇个逻辑高位
UART_PARITY_MARK --- 高位校验,第9位一直为逻辑高
UART_PARITY_SAPCE --- 低位校验,第9位一直为逻辑低
设置UART的控制寄存器
SM0和SM1组合设置UART工作模式,
方式1对应NONE情况,而方式3对应其他校验方式。
switch(parity)
{
case UART_PARITY_NONE:
default:
SCON = 0x50; //8bit variable UART, parity is none.
break;
case UART_PARITY_EVEN:
case UART_PARITY_ODD:
case UART_PARITY_MARK:
SCON = 0xda; //9-bit variable UART, parity bit initial to 1
break;
case UART_PARITY_SPACE:
SCON = 0xd2; //9-bit variable UART, parity bit initial to 0
break;
}
4. 使能中断
ES = 1; //Enable UART interrupt
EA = 1; //Open master interrupt switch
5. UART中断处理
UART的中断号为4,一般只需要处理接收中断。RI = 1表示接收中断,SBUF中是接收到的数据。
void uartIsr() interrupt 4
{
if (RI)
{
RI = 0; //Clear receive interrupt flag
gUart0RecvBuf[gUartRecvIn++] = SBUF;
gUartRecvIn %= sizeof(gUart0RecvBuf);
}
else
{
TI = 0; //Clear transmit interrupt flag
}
}
注意,对于SDCC来说,中断函数必须要在main函数所在文件内有申明。
6. UART发送
如果用到了奇偶校验,则先把数据赋值给ACC,MCU会自动将奇偶校验结果放在PSW的P位(bit 0)。
ACC = *buf;
#if(UART_PARITY_EN == 1)
if(gUartParity == UART_PARITY_EVEN)
{
TB8 = P;
}
else if(gUartParity == UART_PARITY_ODD)
{
TB8 = ~P;
}
#endif
ES = 0;
SBUF = ACC;
while(TI = 0); //等待发送完毕
TI = 0; //清TI中断
ES = 1; //打开中断