|
MSP430F4XX单片机串口通讯程序库(精简版)
[复制链接]
- 说明:该程序库包括串口初始化、串口单字节收/发函数,以及串口终端设备接口
- 函数。可以作为各种程序的底层驱动使用。
- 要使用该库函数,需要将本文件(UART.c)添加进工程,并在需要调用
- 串口函数的文件开头处包含"UART.h"
- 本程序库中的串口初始化函数会根据波特率为串口收发模块自动选择最
- 合适的时钟源,自动配置波特率分频系数,用户不需要了解串口底层寄存器。
- 对于4800bps及以下的波特率,会选择ACLK作为时钟;对于4800bps以上设置,
- 会选择SMCLK作时钟。若串口一直打开,为了降低功耗,尽量使用4800以下波
- 特率;若使用4800以上波特率,推荐间歇使用串口。使用38400bps以上波特率
- 最好适当提高CPU主频(2~4MHz)。
- 串口收发的等待过程中会将CPU置于休眠模式(LPM0或LPM3),以降低耗电。
- 但要注意某些应用中可能不允许关闭时钟(比如TimerA用SMCLK作时钟,若串
- 口等待过程中进入LPM3,关闭了SMCLK,造成计时错误),遇到这类情况请修
- 改UART_LPM()函数,删除休眠语句或降低休眠深度,但功耗会有所增加。
- 程序后半部分是标准终端输入输出函数,对标准终端设备输入的数据流进
- 行解析。通过串口与PC机相连;在PC机运行超级终端程序,从而将PC的屏幕和键
- 盘映射成单片机的输入/输出终端。这样在单片机程序中可以使用格式化输入/
- 输出函数,如printf/scanf等。详细的原理请参考《超级终端人机对话范例》
- 程序。终端输入函数带有退格功能,因此有输入缓冲区。请根据需要自行修改
- 输入行缓冲区大小(LINE_LENGTH),该值限定了终端设备每行最大能输入的字符数。
-
- (C)西安电子科技大学 测控技术与仪器教研中心 编写:谢楷 2008/02/21
-
- */
- //******************************************************************************
- //
- //
- // MSP430FE425
- // +-----------------+
- // /|\| XIN|-
- // | | | 32kHz
- // --|RST XOUT|-
- // | |
- // | (TXD)P2.4|-----------> // ----> RXD(2)
- // | | 300~115200 bps
- // | (RXD)P2.5|<----------- // <---- TXD(3) PC(DB9)
- // | |
- // | GND |------------------------GND(5)
- //
- //******************************************************************************
- #include "msp430x42x.h"
- #define F_ACLK (32768) /*ACLK对应晶体频率,勿修改*/
- char TxFlag=1;
- char RxFlag=0;
- /****************************************************************************
- * 名 称:UART_Init()
- * 功 能:初始化串口。设置其工作模式及波特率。
- * 入口参数:
- * Baud 波特率 (300~115200)
- Parity 奇偶校验位('n'=无校验 'p'=偶校验 'o'=奇校验)
- DatsBits 数据位位数 (7或8)
- StopBits 停止位位数 (1或2)
- * 出口参数:返回值为1时表示初化成功,为0表示参数出错
- * 范 例: UART_Init(9600,'n',8,1) //设成9600bps,无校验,8位数据,1位停止位
- UART_Init(2400,'p',7,2) //设成2400bps,偶校验,7位数据,2位停止位
- ****************************************************************************/
- char UART_Init(long int Baud,char Parity,char DataBits,char StopBits)
- {
- unsigned long int BRCLK; //波特率发生器时钟频率
- int FreqMul,FLLDx,BRDIV,BRMOD; //倍频系数、DCO倍频、波特率分频系数、分频尾数
- int i;
- unsigned char const ModTable[8]={0x00,0x08,0x88,0x2A,0x55,0x6B,0xdd,0xef};
- //分频尾数所对应的调制系数(将0~7个"1"均匀分布在一个字节的8bit中)
- //-------------设置波特率发生器时钟源,并计算波特率时钟频率--------------
- UTCTL0 &=~(SSEL0+SSEL1); //清除之前的时钟设置
- if(Baud<=4800)
- {
- UTCTL0 |= SSEL0; //低于4800的波特率,用ACLK,降低功耗
- BRCLK=F_ACLK; //波特率发生器时钟频率=ACLK
- }
- else
- {
- UTCTL0 |= SSEL1; //高于4800的波特率,用SMCLK,保证速度
- FreqMul=(SCFQCTL&0x7F)+1; //获得倍频系数
- FLLDx=((SCFI0&0xC0)>>6)+1; //获得DCO倍频系数(DCOPLUS所带来的额外倍频)
- BRCLK=F_ACLK*FreqMul; //计算波特率发生器时钟频率=ACLK*倍频系数
- if(FLL_CTL0&DCOPLUS) BRCLK*=FLLDx; //若开启了DCOPLUS,还要计算额外倍频
- }
- //------------------------设置波特率-------------------------
- if((Baud<300)||(Baud>115200)) return(0); //波特率范围300-115200bps
- BRDIV=BRCLK/Baud; //计算波特率分频系数(整数部分)
- BRMOD=((BRCLK*8)/Baud)%8; //计算波特率分频尾数(除不尽的余数)
- UBR00 = BRDIV%256;
- UBR10 = BRDIV/256; //整数部分系数
- UMCTL0 = ModTable[BRMOD]; //余数部分系数
- //------------------------设置校验位-------------------------
- switch(Parity)
- {
- case 'n':case'N': U0CTL&=~PENA; break; //无校验
- case 'p':case'P': U0CTL|= PENA+PEV ;break; //偶校验
- case 'o':case'O': U0CTL|= PENA; U0CTL&=~PEV;break;//奇校验
- default : return(0); //参数错误
- }
- //------------------------设置数据位-------------------------
- switch(DataBits)
- {
- case 7:case'7': U0CTL&=~CHAR; break; //7位数据
- case 8:case'8': U0CTL|= CHAR; break; //8位数据
- default : return(0); //参数错误
- }
- //------------------------设置停止位-------------------------
- switch(StopBits)
- {
- case 1:case'1': U0CTL&=~SPB; break; //1位停止位
- case 2:case'2': U0CTL|= SPB; break; //2位停止位
- default : return(0); //参数错误
- }
- P2SEL |= 0x30; // P2.4,5 = USART0 TXD/RXD
- ME1 |= UTXE0 + URXE0; // Enable USART0 TXD/RXD
- UCTL0 &= ~SWRST; // Initialize USART state machine
- IE1 |= URXIE0+UTXIE0; // Enable USART0 RX interrupt
- _EINT();
- for(i=0;i<4000;i++); //略延迟,等待波特率分频稳定
- return(1); //设置成功
- }
- /****************************************************************************
- * 名 称:UART_LPM()
- * 功 能:串口收/发等待过程中,将CPU及时钟系统关闭,休眠省电
- * 入口参数:无
- * 出口参数:无
- * 说 明: 若与其他外设的时钟冲突,可注释掉该函数,但会增加功耗。
- ****************************************************************************/
- void UART_LPM()
- {
- if(UTCTL0&SSEL0) LPM3; //若以ACLK 作时钟,进入LPM3休眠(仅打开ACLK)
- else LPM0; //若以SMCLK作时钟,进入LPM0休眠(不关闭SMCLK)
- }
- /****************************************************************************
- * 名 称:UART_PutChar()
- * 功 能:从串口发送1字节数据
- * 入口参数:Chr: 待发送的一字节数据
- * 出口参数:无
- * 说 明: 在等待发送完毕的过程中,CPU会休眠
- ****************************************************************************/
- void UART_PutChar(char Chr)
- {
- while (TxFlag==0) UART_LPM(); // 等待上一字节发完,并休眠
- TxFlag=0; //小技巧:等上一字节发完,再发本字节,把字节间隔等待的时间让给计算
- TXBUF0=Chr;
- }
- /****************************************************************************
- * 名 称:UART_GetChar()
- * 功 能:从串口接收1字节数据
- * 入口参数:无
- * 出口参数:收到的一字节数据
- * 说 明: 如果串口没有数据,会一直等待。等待过程中,CPU会休眠
- ****************************************************************************/
- char UART_GetChar(void)
- {
- while (RxFlag==0) UART_LPM(); // 收到一字节?
- RxFlag=0;
- return(RXBUF0);
- }
- /****************************************************************************
- * 名 称:UART_IsRcvChar()
- * 功 能:判断串口是否收到1字节数据
- * 入口参数:无
- * 出口参数:1表示有数据,0表示无数据
- * 说 明: 配合UART_GetChar()函数使用,先检查到有数据再接收,可以消除阻塞
- ****************************************************************************/
- char UART_IsRcvChar()
- {
- return(RxFlag);
- }
- /****************************************************************************
- * 名 称:UART_RX()
- * 功 能:串口接收中断,每接收到1字节会发生一次中断
- ****************************************************************************/
- #pragma vector=UART0RX_VECTOR
- __interrupt void UART_RX (void)
- {
- RxFlag=1;
- /*在这里添加用户中断服务程序代码,如将数据压入接收缓冲等*/
- __low_power_mode_off_on_exit();
- }
- /****************************************************************************
- * 名 称:UART_TX()
- * 功 能:串口发送中断,每发完1字节会发生一次中断
- ****************************************************************************/
- #pragma vector=UART0TX_VECTOR
- __interrupt void UART_TX (void)
- {
- TxFlag=1;
- /*在这里添加用户中断服务程序代码,如将数据从缓冲取出等*/
- __low_power_mode_off_on_exit();
- }
- /*===================以下是串口终端设备接口函数库==========================*/
- #define LINE_LENGTH 20 /* 行缓冲区大小,决定每行最多输入的字符数*/
- /*标准终端设备中,特殊ASCII码定义,请勿修改*/
- #define In_BACKSP 0x08 /* ASCII <-- (退格键) */
- #define In_DELETE 0x7F /* ASCII
(DEL 键) */
- #define In_EOL '\r' /* ASCII (回车键) */
- #define In_SKIP '\3' /* ASCII control-C */
- #define In_EOF '\x1A' /* ASCII control-Z */
- #define Out_DELETE "\x8 \x8" /* VT100 backspace and clear */
- #define Out_SKIP "^C\n" /* ^C and new line */
- #define Out_EOF "^Z" /* ^Z and return EOF */
- #include "stdio.h"
- /****************************************************************************
- * 名 称:putchar()
- * 功 能:向标准终端设备发送一字节数据(1个字符)
- * 入口参数:ch: 待发送的字符
- * 出口参数:发出的字符
- * 说 明: printf函数会调用该函数作为底层输出。这里从串口输出字符到PC机的超
- 级终端软件上,printf的结果将打印到超级终端上。若修改该函数,将字
- 符以其他方式输出,如显示到LCD上,printf的结果将显示在LCD上。
- ****************************************************************************/
- int putchar(int ch)
- {
- if (ch == '\n') // '\n'(回车)扩展成 '\n''\r' (回车+换行)
- {
- UART_PutChar(0x0d) ; //'\r'
- }
- UART_PutChar(ch); //从串口发出数据
- return (ch);
- }
- /****************************************************************************
- * 名 称:put_message()
- * 功 能:向标准终端设备发送一个字符串
- * 入口参数:*s: 字符串(数组)头指针(数组名)
- * 出口参数:无
- ****************************************************************************/
- static void put_message(char *s)
- {
- while (*s) //当前字符不为空 (字符串以0x00结尾)
- putchar(*s++); //输出一个字符,指针指向下一字符
- }
- /****************************************************************************
- * 名 称:getchar()
- * 功 能:从标准终端设备接收一字节数据(1个字符)
- * 入口参数:无
- * 出口参数:收到的字符
- * 说 明: scanf函数会调用该函数作为底层输入。这里从PC机键盘借助超级终端软
- 件通过串口输入字符到单片机上。scanf函数的输入即源自PC机键盘。若
- 修改该函数,将字符以其他方式输入,如单片机IO口,可用按钮向scanf
- 函数输入数据。本函数带有缓存,能够处理退格等删除操作。若不需要删
- 除操作,可直接调用UART_GetChar()函数。
- ****************************************************************************/
- int getchar(void)
- {
- static char io_buffer[LINE_LENGTH + 2]; /* Where to put chars */
- static int ptr; /* Pointer in buffer */
- char c;
- for (;;)
- {
- if (io_buffer[ptr]) //如果缓冲区有字符
- return (io_buffer[ptr++]); //则逐个返回字符
- ptr = 0; //直到发送完毕为止,清空缓冲区指针
- while(1) //缓冲区没有字符,才会执行到这里,开始等待字符输入
- { c = UART_GetChar(); //等待串口接收一个字符
- if (c == In_EOF && !ptr) //----EOF键(Ctrl+Z)----
- { //EOF符只能在未输入其他字符时才有效
- put_message(Out_EOF); //让终端显示EOF符
- return EOF; //返回EOF符
- }
- if ((c == In_DELETE)||(c==In_BACKSP)) //----退格或DEL键----
- {
- if (ptr)
- {
- ptr--; //从缓冲区删掉一个字符
- put_message(Out_DELETE); //让终端显示也删掉一个字符
- }
- }
- else if (c == In_SKIP) //-----取消键 Ctrl + C----
- {
- put_message(Out_SKIP); //让终端显示放弃本行跳到下一行
- ptr = LINE_LENGTH + 1; /* 这里永远是0(结束符) */
- break;
- }
- else if (c == In_EOL) //--------遇到回车键------
- {
- putchar(io_buffer[ptr++] = '\n'); //让终端显示换行
- io_buffer[ptr] = 0; //末尾增添字符串结束字符NULL
- ptr = 0; //指针清空
- break; //跳出后开始返回数据
- }
- else if (ptr < LINE_LENGTH) //----正常ASCII码字符----
- {
- if (c >= ' ') //删除0x20以下的其他ASCII码
- {
- putchar(io_buffer[ptr++] = c); //存入缓冲区
- }
- }
- else //--------冲区已满--------
- {
- putchar('\7'); //向终端发送鸣响符,PC会响一声,提示已满
- }
- }
- }
- }
|
|