//函数声明 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(\"\---波特率计算软件!---\\n\"); printf(\"\\n请输入时钟频率(Hz):\"); scanf(\"%ld\",&clk); printf(\"\\n请输入波特率:\"); scanf(\"%ld\",&baud); getchar(); //读取多余回车符 SetBaudRateRegisters(clk,baud); //设置寄存器值 //显示寄存器值 printf(\"\\nUxBR1:0x%x\UxBR0:0x%x\UxMCTL: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<运行效果很好,和官方给出的值一样,但是也不全都是这样,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 等预编译用来防止重复包含。
• 程序调用示例:
要调用这个函数库,首先要包含Uart.h头文件;把Uart.h拷到对应文件夹中,然后
在要调用程序的源程序文件中添加文件包含:
#include \"msp430x16x.h\" //430寄存器头文件 #include \"Uart.h\" //串口通讯程序库头文件 然后,在项目中加入Uart.c,把Uart.c拷入项目文件夹中,在项目中添加文件,加入后文件结构大致如下图:
如果要用9600以上的波特率,需要把SMCLK设为1M,我的程序调用了以下的这个函数,把MCLK设为8MHz,SMCLK设为1MHz:
void ClkInit() { char i;
BCSCTL1 &= ~XT2OFF; //打开XT2振荡器 IFG1&=~OFIFG; //清除振荡错误标志 while((IFG1&OFIFG)!=0) { for(i=0;i<0xff;i++); IFG1&=~OFIFG; //清除振荡错误标志 } BCSCTL2 |= SELM_2+SELS+DIVS_3; //MCLK为8MHz,SMCLK为1MHz } 调用示例程序如下:
ClkInit(); UartInit(38400,'n',8,1); //串口初始化,设置成38400bps,无校验,8位数据,1位停止 _EINT();
UartWriteStr(str); UartWriteChar(0x0d); //发送\"换行\"(\\r)\" UartWriteChar(0x0a); //发送\"回车\"(\\n)\" UartWriteStr(\"下面测试串口收发函数\\r\\n\"); while(1) //串口测试 { chr=UartReadChar(); //收1字节 UartWriteChar(chr); //将收到的数据返回 } 如果是9600以下的波特率,可以不调用时钟系统初始化函数,否则必须调用这个函数,或者用其他的方法把SMCLK频率设为1MHz;初始化完成之后,还要开中断(因为Uart函数库用到了中断);然后才能正常的使用这些函数。
这样,这个程序库就完成了,欢迎大家下载使用。
另外,网上还有一个用预编译的方法计算波特率的,个人认为比较好,没有选用它的原因是:它只能在编译时确定寄存器内容,无法再运行的时候进行设置。http://blog.21i
c.com/user1/1453/archives/2009/62696.html 还有一个用网页计算波特率的,可以上网时计算波特率很方便:http://www.838dz.com/calculator/1805.html