异步串口初始化(UartInit):完成波特率,停止位以及其他相关的设置。
串口初始化,首先是波特率寄存器值的计算和设置:本程序选用第二种:通过运算,选取误差最小的寄存器所需值进行设置。
波特率寄存器值根据所选时钟频率和所需波特率值进行设置,计算方法:从m0(UxMCTL最低位)开始计算,根据这一位的误差(0或1时)误差较小的bit值,直到计算完成。
为了更好的写这个程序,我先用C语言写了一个简单的波特率计算软件,为了让设置波特率的函数能够在单片机程序中复用,程序用宏定义模拟的MSP430单片机的波特率寄存器。完整程序如下:
#include<stdio.h>
#include<math.h>
//函数声明
void SetBaudRateRegisters(long clk,int baud);
/************************宏定义***********************/
#define UxBR1 a[0]
#define UxBR0 a[1]
#define UxMCTL a[2]
unsigned char a[3]; //数组模拟寄存器
void main()
{
long clk; //时钟
long baud; //波特率
printf("\t---波特率计算软件!---\n");
printf("\n请输入时钟频率(Hz):");
scanf("%ld",&clk);
printf("\n请输入波特率:");
scanf("%ld",&baud);
getchar(); //读取多余回车符
SetBaudRateRegisters(clk,baud); //设置寄存器值
//显示寄存器值
printf("\nUxBR1:0x%x\tUxBR0:0x%x\tUxMCTL:0x%x\n",UxBR1,UxBR0,UxMCTL);
getchar();
}
/****************************************************************************
* 名 称:SetBaudRateRegisters
* 功 能:根据时钟 波特率设置对应寄存器
* 入口参数:
* clk: 所选时钟频率(如:32768)
baud 波特率 (300~115200)
* 出口参数:无
* 范 例: SetBaudRateRegisters(32768,9600) //用时钟频率32768产生9600的波特率
****************************************************************************/
void SetBaudRateRegisters(long clk,long baud)
{
int n = clk / baud; //整数波特率
char mSum = 0; //Σmi
int txEr0; //对应位为0时错误率
int txEr1; //对应位为1时错误率
char i = 0; //循环计数
UxBR1 = n >> 8; //高8位
UxBR0 = n & 0xff; //低8位
UxMCTL = 0;
//循环 比较错误率大小 设置UxMCTL
for(;i < 8;i++)
{
txEr0 = 100 * baud * ((i + 1) * n + mSum) / clk - 100 * (i + 1);
txEr1 = 100 * baud * ((i + 1) * n + mSum + 1) / clk - 100 * (i + 1);
if(abs(txEr1) < abs(txEr0))
{
mSum++;
UxMCTL |= (1<<i);
}
}
}
程序可以使用任何的C语言编译器编译运行,可供网友们复用此程序。我使用vs2010编译运行的,运行结果如下:
运行效果很好,和官方给出的值一样,但是也不全都是这样,4800的波特率(时钟:32768)时就不一样,可能是我计算式只是用了发送时的误差计算,没有用接收误差,计算结果稍有出入,如果有兴趣,网友可以自行添加接收误差,判断;应该就和官方给出的数值完全一样了。
初始化函数:初始化函数完成串口时钟源选择,波特率初始化,奇偶校验,数据位,停止位,以及其他相关设置。
时钟源选择:根据波特率选取时钟源,波特率大于9600,选1M的SMCLK时钟(需要初始化时钟系统对应函数参考使用示例),小于9600,选ACLK(32768)以使功耗降低(低功耗3仍能正常收发数据)
UxTCTL &=~ (SSEL0+SSEL1); //清除之前的时钟设置
if(baud<=9600) //brclk为时钟源频率
{
UxTCTL |= SSEL0; //ACLK,降低功耗
brclk = 32768; //波特率发生器时钟频率=ACLK(32768)
}
else
{
UxTCTL |= SSEL1; //SMCLK,保证速度
brclk = 1000000; //波特率发生器时钟频率=SMCLK(1MHz)
}
波特率设置:直接调用之前实现的设置寄存器函数即可,当波特率在正常范围外时,返回0。
//------------------------设置波特率-------------------------
if(baud < 300||baud > 115200) //波特率超出范围
{
return 0;
}
SetBaudRateRegisters(); //设置波特率寄存器
奇偶校验、数据位位数、停止位数设置:比较简单,直接根据参数值设置对应寄存器即可。
//------------------------设置校验位-------------------------
switch(parity)
{
case 'n':case'N': UxCTL &=~ PENA; break; //无校验
case 'p':case'P': UxCTL |= PENA + PEV; break; //偶校验
case 'o':case'O': UxCTL |= PENA; UxCTL &=~ PEV; break; //奇校验
default : return(0); //参数错误
}
//------------------------设置数据位-------------------------
switch(dataBits)
{
case 7:case'7': UxCTL &=~ CHAR; break; //7位数据
case 8:case'8': UxCTL |= CHAR; break; //8位数据
default : return(0); //参数错误
}
//------------------------设置停止位-------------------------
switch(stopBits)
{
case 1:case'1': UxCTL &= ~SPB; break; //1位停止位
case 2:case'2': UxCTL |= SPB; break; //2位停止位
default : return(0); //参数错误
}
其他:包括串口收发使能,串口接收和发送中断设置,第二功能打开等。
UARTON; //端口使能
UxME |= UTXEx + URXEx; //发送 接收使能
UCTL0 &= ~SWRST; // Initialize USART state machine
UxIE |= URXIEx + UTXIEx; // Enable USART0 RX interrupt
到此,MSP430异步串行口的初始化工作全部完成,如果需要其他的方式,只需对应设置寄存器即可。
- 写字符(UartxWriteChar):向UARTx模块写(发送)一个字符。
写字符:向串口写入一个字符,通过串口向终端发送一个字符。
void UartWriteChar(char c)
{
while (TxFlag==0) UartLpm(); // 等待上一字节发完,并休眠
TxFlag=0; //
UxTXBUF=c;
}
这个函数根据程序标志TxFlag判断上一字符是否发送完成,此标志位将在发送中断中被置位,表示本字符发送完成。发送中断程序如下:
#pragma vector=UARTxTX_VECTOR
__interrupt void UartTx ()
{
TxFlag=1;
__low_power_mode_off_on_exit();
}
发送字符时,先等待上一字符发送完成,然后把字符放入发送缓冲区,待发送完成,中断置标志位,指示发送完成。
- 读取字符(UartxReadChar):从UARTx模块读取(获取)一个字符。
读取字符和写字符类似:调用读取函数后,等待标志位,接收到字符后,读出来。
char UartReadChar()
{
while (RxFlag==0) UartLpm(); // 收到一字节?
RxFlag=0;
return(UxRXBUF);
}
同样,RxFlag指示收到一个字符,并且在中断中被置位。中断程序如下:
#pragma vector=UARTxRX_VECTOR
__interrupt void UartRx()
{
RxFlag=1;
/*在这里添加用户中断服务程序代码,如将数据压入接收缓冲等*/
__low_power_mode_off_on_exit();
}
读取函数将阻塞,如果收不到字符,CPU将一直处于低功耗状态。
- 写字符串(UartxWriteStr):向UARTx模块写(发送)一个字符串。
写字符串只需调用写字符函数即可,比较简单,程序如下:
void UartWriteStr(char *s)
{
while(*s)
{
UartWriteChar(*s++);
}
}
这样,即可调用这个函数通过串口发送字符串。
- 头文件:头文件把要调用的函数声明放进去,需要使用函数时只需包含此文件,不需要再进行函数声明。
头文件内容如下:
#ifndef __UART_H
#define __UART_H
char UartInit(long baud,char parity,char dataBits,char stopBits);
void UartWriteChar(char c);
void UartWriteStr(char *s);
char UartReadChar();
#endif /* __UART_H */
其中#ifndef 等预编译用来防止重复包含。