平台:
Code Composer Studio 6.2.0 + Grace 2.2.0
MSP430G2553 LaunchPad™ Development Kit (MSP-EXP430G2ET)


_  以下大部分内容摘自《LaunchPad口袋实验平台 —— MSP-EXP430G2篇》傅强、杨艳 编著(TI大学计划嵌入式微控制器技术丛书)_

概述

  USCI 全称为 Universal Serial Communication Interface,通用串行通信接口。MSP430G2553
单片机中带 1 个 USCI_A 模块和一个 USCI_B 模块。其中 USCI_A 可配置为 UART、LIN、IrDA、SPI 模式。USCI_B 可配置为 SPI 和 I2C 模式。
  我们完全可以用 CPU 和定时器纯软件的实现 UART 串口,但是这样会给 CPU 带来很大的负担。有了 USCI 模块,UART 串口通信将变得非常简单。举个例子:

  1. 使用纯软件实现 UART 串口通信,相当于没有邮政系统的原始社会,送信、收信甭管相隔多远都必须倾力亲为。
  2. 硬件通信模块相当于邮局(USCI),你只要填好收寄地址人员电话等信息(USCI 初始化),剩下的收发邮件(数据)都可以交给邮局(USCI)。
  3. 发邮件只需将邮件(数据)放入 TXBUF 发送缓冲器,邮局自动的会发出邮件,发送完毕还会电话通知(中断)。
  4. 收邮件只需查看自家邮箱接收缓冲器 RXBUF 就行,有邮件时,邮局同样会电话通知(中断)。
  5. 所以,初始化完成后,只需操作发送缓冲区 TXBUF 和接收缓冲器 RXBUF 即可。

UART 的初始化

使用 Grace 配置 UART

  如图所示,打开 Grace,在 USCI_A0 功能中点击选择 UART 模式。
在这里插入图片描述
在这里插入图片描述
  _该页部分内容机翻如下:_

介绍

  通用串行通信接口(USCI)模块支持多种串行通信模式。不同的USCI模块支持不同的模式。每个不同的USCI模块都用不同的字母命名。例如,USCI_A与USCI_B不同,等等。如果一个设备上实现了多个相同的USCI模块,这些模块用递增的数字命名。例如,如果一个设备有两个USCI_A模块,它们被命名为USCI_A0和USCI_A1。
  USCI_A0模块支持UART模式和SPI模式。

用例:UART模式

  在UART模式下,USCI以与另一设备异步的比特率发送和接收字符。每个字符的计时是基于USCI的选定波特率。UART发送和接收操作使用相同的波特率频率。两个外部引脚UCA0RXD和UCA0TXD用于通过UART将MSP430连接到外部系统。
  UCA0TXIFG中断标志由发送器设置,表示UCA0TXBUF准备接受另一个字符。如果UART TX中断和GIE(全局中断使能)被启用,就会产生一个中断请求。如果一个字符被写入UCA0TXBUF,UCA0TXIFG会自动复位。每次收到一个字符并加载到UCA0RXBUF时,UCA0RXIFG中断标志被设置。如果UART TX中断和GIE(全局中断使能)被启用,将产生一个中断请求。当UCA0RXBUF被读取时,UCA0RXIFG会自动复位。
  USCI模块为SMCLK提供自动时钟激活,以便与低功耗模式一起使用。当SMCLK是USCI时钟源,并且因为器件处于低功耗模式而不活动时,USCI模块会在需要时自动激活它,而不管时钟源的控制位设置如何。该时钟一直处于激活状态,直到USCI模块恢复到空闲状态。在USCI模块回到空闲状态后,对时钟源的控制将恢复到其控制位的设置。ACLK不提供自动时钟激活功能。请注意,当USCI模块激活一个非活动的时钟源时,该时钟源对整个设备来说是活动的,任何配置为使用该时钟源的外围设备都可能受到影响。
  例子。下面的应用案例显示了如何在USCI_A0中断服务例程中处理UART发送和接收操作。在这个例子中,UART接收中断被启用,UART在中断服务例程中对接收的字符进行回显。

  Grace配置

  1. 启用基本时钟系统,选择Basic User视图,为高速时钟源选择一个校准的频率。
  2. 启用USCI_A0并选择Basic User视图
  3. 在USCI_A0外围模式选择中选择UART模式

在这里插入图片描述

  1. 在引脚上选择适当的USCI_A0 RX和TX功能
  2. 选择适当的波特率,定义UART的时序
  3. 启用接收中断。你可以在 src/grace/InterruptVectors_init.c 中的 USCI0RX_ISR_HOOK 内插入你的自定义 ISR 代码。

在这里插入图片描述

在这里插入图片描述

  用户代码:

// Enter appropriate LPM with global interrupt enabled
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0, interrupts enabled

  导航到InterruptVectors_init.c文件,在USCI0RX ISR的用户代码部分之间添加以下代码。

    /* USER CODE START (section: USCI0RX_ISR_HOOK) */
    // Device enters ISR when character received in Rx Buffer
    // Echo back RXed character, confirm TX buffer is ready first
    
    // USCI_A0 TX buffer ready?
    while (!(IFG2 & UCA0TXIFG)); // Poll TXIFG to until set
    UCA0TXBUF = UCA0RXBUF;       // TX -> RXed character
    /* USER CODE END (section: USCI0RX_ISR_HOOK) */

在这里插入图片描述

 

在这里插入图片描述

 

FIFO的思想

<a id=”_FIFOUART73”>使用 FIFO 发送 UART 数据

 

  UART 的发送速度很慢,每次传输时间长达几百 μs,而 CPU 往 UART 发送缓存中“扔数据”所花的时间还不到 1μs。如果用查询法判断是否发送完 1 字节,将会非常郁闷。如果用中断,日子会好过很多。如果再能模拟个 FIFO,就完美无缺了。下面举例说明,这三种情况下 UART 发送编程思想的区别。

 

  1. 查询法:主人(CPU)去邮局发 N 件包裹(N 个数据),从家里到邮局耗时 10 分钟(CPU 往 TxBUF 里“扔”数据的耗时)。邮局工作人员(UART)说,一次只能发一件包裹,要几天后包裹到达目的地(某波特率下的发送耗时),然后你才能发第二件。于是,摆在主人 CPU 面前有两个选择,要么就“不吃不喝”呆在邮局等消息,然后依次发送完所有包裹。另一个选择是,先回家该干嘛干嘛,定好闹钟(启动定时器轮询),隔一段时间去邮局看看上一个包裹发完没有。
  2. 中断法:故事的前半段和上面一样,只不过邮局工作人员(UART)说,我们最近在改进服务作风,你回家等消息去吧,能发下一个包裹的时候,会给你打电话的。于是 CPU 千恩万谢的回家该干嘛干嘛去了,不爽的就是家里堆了一堆包裹不能马上“扔出去”,还得老惦记着什么时候来电话。
  3. FIFO 法:故事的前半段和查询法一样,这回邮局工作人员(UART)说,我们开通VIP 服务了,您要是肯腾出自家房子(RAM)改造成中转仓库的话,包裹直接搁中转仓库就行了,我们保证先送来的包裹会先发送出去(FIFO 先进先出的原则)。就看您要改造多大的仓库(把 RAM 人工改造为 FIFO)?

<a id=”_FIFOUART__79”>使用 FIFO 接收 UART 数据

  比 UART 发送数据更郁闷的是 UART 接收数据,当发送数据时,好歹人(CPU)自己是知道要发送多少包裹的,就算要蹲在邮局等候包裹发完,那时间也是有限长的,不会是一辈子。接收数据则可能耗费 CPU 毕生的时间。

  1. 查询法:可能会有包裹寄给人(CPU),邮局工作人员说了,第一,包裹到了恕不电话通知;第二,包裹到了尽快取走,再来第二个包裹我们可没地放,第一个包裹就没收了。人这回无语了,天知道会不会有包裹来,天知道哪天来包裹,天知道包裹里是不是有巨款(重要数据),我的神呀。人还是两条选择,要么就“不吃不喝”呆在邮局等包裹消息,有包裹立刻取回来,拆开看看是不是巨款。另一个选择是,先回家该干嘛干嘛,定好闹钟(启动定时器轮询),隔一段时间去邮局看看有没有包裹,不过闹钟的间隔一定要小于两个包裹的间隔。
  2. 中断法:故事的前半段和上面一样,只不过邮局工作人员(UART)说,由于“随意”没收包裹没有尽到“告知”义务被投诉了,所以有包裹我们会电话通知,不过到时不取(下一个包裹到来),照样没收。于是人又千恩万谢的回家该干嘛干嘛去了,不爽的就是一接到邮局电话就得扔下锅碗瓢盆(终止其他任务),百米冲刺赶到邮局取包裹,天知道包裹会不会被找茬没收了。
  3. FIFO 法:故事的前半段和查询法一样,这回邮局工作人员(UART)说,我们开通VIP 服务了,您家房子腾出几间来(RAM)当仓库,包裹到了会按顺序(FIFO)放进您专属仓库,并且电话通知您(中断)。不过仓库放满了,我们就爱莫能助了,所以,有空的时候(没有其他紧急任务时),您还是要去清理仓库。

FIFO编程

  单片机中本身是不带硬件 FIFO 的,我们是利用单片机的 RAM 构造一个全局变量数组FIFO[],,通过对数组的软件操作,模拟 FIFO 的功能。对于 FIFO 的软件操作,有以下分析:

  1. 假如 FIFO 共 4 字节,那么“读写 FIFO 函数”的操作顺序都应该依次是 FIFO[0]Rx_FIFO[1]  Rx_FIFO[2] Rx_FIFO[3]  Rx_FIFO[0] Rx_FIFO[1]…。所以读写 FIFO 各需要一个可以循环移位的指针变量。
  2. 为避免指针操作的复杂性,我们软件模拟 FIFO 时,用数组角标变量 FIFO_IndexW 代替指针的作用,FIFO[FIFO_IndexW]就是待操作的数组变量。
  3. 如果只写 FIFO,不读 FIFO,那么写到 FIFO[3]就必须停止,不能继续写 FIFO[0]。如果这时“读 FIFO 函数”开始工作,读 FIFO 最先读取的数据一定是 FIFO[0]。只要有数据被读走,则表示 FIFO 有空缺,可以继续写数据到 Rx_FIFO[0](覆盖数据)。以后,每次 FIFO 被读走一个数,就可以被写入一个新的数据。
  4. 假如“写函数”只写了 2 个数据,那么“读函数”在读完 Rx_FIFO[0] Rx_FIFO[1]后就不能继续循环读取 FIFO[2]。只有写入数据后,才能再次读数据。
  5. “读函数”怎么通知“写函数” FIFO 有空缺,“写函数”又如何通知“读函数”有新数据呢?可以通过对全局变量 FIFO_DataNum 的“拉锯战”来判别。每写一个数据, FIFO_DataNum++,每读一个数据FIFO_DataNum—
  6.  FIFO_DataNum = 0(空指示),表示 FIFO 中没有未读数据,只能写不能读。
  7.  FIFO_DataNum = 4(满指示),表示 FIFO 塞满了未读数据,只能读不能写。
  8.  0 < FIFO_DataNum < 4,表示既可读又可写。

  首先,分析一下我们需要多少个 FIFO。如图 10.3 所示,我们需要两个独立的 FIFO,分别用来缓存 CPU 与 Tx 硬件发送器之间的数据,CPU 与 Rx 硬件接收器之间的数据。就 FIFO本身而言,功能都是一样的,但具体 CPU 扮演的角色是读 FIFO 还是写 FIFO,则有区别。
在这里插入图片描述

  其次,分析一下需要多少全局变量:

  1. 构造 FIFO 的数组,Rx 和 Tx 各需要一个。
  2. 指示 FIFO 内数据数目(空满指示),Rx 和 Tx 各需要一个。
  3. 指示 FIFO 内“数据头”的读指针,Rx 和 Tx 各需要一个。
  4. 指示 FIFO 内“数据尾”的写指针,Rx 和 Tx 各需要一个。

  下面是 UART.c 的内容:

#include "UART_Global.h"
unsigned char Rx_FIFO[RX_FIFO_SIZE]={0}; //UART接收FIFO数组
unsigned int Rx_FIFO_DataNum=0; //UART接收FIFO的“空满”指示变量
unsigned int Rx_FIFO_IndexR=0; //UART接收FIFO的模拟“读指针”变量
unsigned int Rx_FIFO_IndexW=0; //UART接收FIFO的模拟“写指针”变量
unsigned char Tx_FIFO[TX_FIFO_SIZE]={0}; //UART发送FIFO数组
unsigned int Tx_FIFO_DataNum=0; //UART发送FIFO的“空满”指示变量
unsigned int Tx_FIFO_IndexR=0; //UART发送FIFO的模拟“读指针”变量
unsigned int Tx_FIFO_IndexW=0; //UART 发送 FIFO 的模拟“写指针”变量

  Rx 接收和 Tx 发送 FIFO 的大小之间没有 关联,根据 程序需要人为由宏定义
  RX_FIFO_SIZE  TX_FIFO_SIZE 设定,下面是 UART_Global.h 的内容。

#ifndef UART_GLOBAL_H_
#define UART_GLOBAL_H_
#define RX_FIFO_SIZE 16 //接收缓冲区大小宏定义
#define TX_FIFO_SIZE 64 //发送缓冲区大小宏定义
extern unsigned char Rx_FIFO[RX_FIFO_SIZE];
extern unsigned int Rx_FIFO_DataNum;
extern unsigned int Rx_FIFO_IndexR;
extern unsigned int Rx_FIFO_IndexW;
extern unsigned char Tx_FIFO[TX_FIFO_SIZE];
extern unsigned int Tx_FIFO_DataNum;
extern unsigned int Tx_FIFO_IndexR;
extern unsigned int Tx_FIFO_IndexW;
#endif /* UART_GLOBAL_H_ */

  写字节函数 Rx_FIFO_WriteChar()
  Rx_FIFO_WriteChar()将放在 UART 的 Rx 中断中执行,其作用是缓存 RxBuffer 寄存器的数据,也就是把 RxBuffer 寄存器数据依次“压入” Rx_FIFO。

  1. 写 FIFO 的第一件事就是判断 FIFO 是否已满,满了就不能写,并返回错误提示 0。
  2. 如果可以写,则关中断→ Rx_FIFO_DataNum++→写数据→写指针循环移位→开中断→返回 1(打完收工)
#include "MSP430G2553.h"
#include "UART_Global.h"
/******************************************************************************************************
* 名 称:Rx_FIFO_WriteChar()
* 功 能:往Rx接收FIFO中写1字节
* 入口参数:Data:待写入FIFO的数据
* 出口参数:1:写入数据成功,0:写入数据失败
* 说 明:操作FIFO时需要关闭总中断
* 范 例:无
******************************************************************************************************/
char Rx_FIFO_WriteChar(unsigned char Data)
{
	if(Rx_FIFO_DataNum==RX_FIFO_SIZE) return(0); //判断FIFO是否已装满未读数据,如果装满返回0
	_disable_interrupts(); //操作FIFO前一定要关总中断
	Rx_FIFO_DataNum++; //未读取数据个数加一
	Rx_FIFO[Rx_FIFO_IndexW]=Data; //将数据写入写读指针位置的FIFO数组
	Rx_FIFO_IndexW++; //写指针移位
	if (Rx_FIFO_IndexW>=RX_FIFO_SIZE) //判断指针是否越界
		Rx_FIFO_IndexW=0; //写指针循环归零
	_enable_interrupts(); //恢复总中断使能
	return(1); //返回成功
}

  读字节函数 Rx_FIFO_ReadChar()
  Rx_FIFO_ReadChar()的作用是 CPU 从 FIFO 中把硬件 UART 的 Rx 接收数据依次取出到指定的变量里。

  1. 读 FIFO 的第一件事就是判断 FIFO 是否为空,空了就不能读,并返回错误提示 0。
  2. 如果可以读,则关中断→Rx_FIFO_DataNum—→读数据→读指针循环移位→开中断→返回 1(打完收工)。
/******************************************************************************************************
* 名 称:Rx_FIFO_ReadChar()
* 功 能:从Rx接收FIFO中读1字节
* 入口参数:*Chr:待存放字节变量的指针
* 出口参数:“1”读取数据成功,“0”读取数据失败
* 说 明:操作FIFO时需要关闭总中断
* 范 例:无
******************************************************************************************************/
char Rx_FIFO_ReadChar(unsigned char *Chr)
{
	if(Rx_FIFO_DataNum==0) return(0); //判断FIFO是是否有未读数据,如果没有返回0
	_disable_interrupts(); //操作FIFO前一定要关总中断
	Rx_FIFO_DataNum--; //待读取数据个数减一
	*Chr=Rx_FIFO[Rx_FIFO_IndexR]; //将读指针位置的FIFO数据赋给指针所指变量
	Rx_FIFO_IndexR++; //读指针移位
	if (Rx_FIFO_IndexR>=RX_FIFO_SIZE) //判断指针是否越界
		Rx_FIFO_IndexR=0; //读指针循环归零
	_enable_interrupts(); //恢复总中断使能
	return(1);
}

  FIFO 清空函数 Rx_FIFO_Clear()
  Rx_FIFO_Clear()的作用是复位 FIFO,只需将 3 大全局变量清零即可,FIFO 里面装的数据则不用理会。其实我们的计算机硬盘删除数据,即使是按Shift + Del删除也仅仅是做了一个标记,表明这段存储空间可用,并没有真正“删除”数据。
  可以想象一下,真正删除数据就是要把所有存储单元写 0(或全写 1),写数据要花多长时间,删除数据就要花多长时间,如果不是克格勃和 CIA 的硬盘,一般没这个必要。

/******************************************************************************************************
* 名 称:Rx_FIFO_Clear()
* 功 能:清空Rx接收FIFO区
* 入口参数:无
* 出口参数:无
* 说 明:清空并不需要真的去将FIFO每一个字节的数据写0,
* 只需读写指针清零和空满计数清零即可。
* 范 例:无
******************************************************************************************************/
void Rx_FIFO_Clear()
{
	_disable_interrupts(); //操作FIFO前一定要关总中断
	Rx_FIFO_DataNum=0; //FIFO中未读取数据数目清零
	Rx_FIFO_IndexR=0; //FIFO中模拟读指针清零
	Rx_FIFO_IndexW=0; //FIFO中模拟写指针清零
	_enable_interrupts(); //恢复总中断使能
}

  写字节函数 Tx_FIFO_WriteChar()
  Tx_FIFO_WriteChar()的功能非常重要,是 CPU 把要通过 UART 发送的数据“压入”Tx发送 FIFO 中,待发送。

  UART 的 Tx 发送器功能非常像一把扳机永远处于扣动状态的连发机枪,Tx_FIFO 好比是供弹带,拉动枪栓上膛好比是第一次开枪需要人工置 Tx 中断标志位。有以下分析结论:

  1. 由于扳机永远处于扣动状态,所以,只要拉一次枪栓上膛,全部弹带的子弹都将依次发送出去。
  2. 在子弹打完之前,随时可以不断的补充弹药。
  3. 一旦子弹打完了之后,若要补充弹药,就需要再次拉枪栓,机枪才会开火。
/******************************************************************************************************
* 名 称:Tx_FIFO_WriteChar()
* 功 能:往Tx发送FIFO中写1字节
* 入口参数:Data:待写入FIFO的数据
* 出口参数:1:写入数据成功,0:写入数据失败
* 说 明:“全新”一次发送数据必须手动触发Tx中断;“非全新”发送一定不能手动触发Tx中断。
全新发送的判据必须同时满足FIFO无数据和Tx不Busy两个条件。
* 范 例:无
******************************************************************************************************/
char Tx_FIFO_WriteChar(unsigned char Data)
{
	if(Tx_FIFO_DataNum==TX_FIFO_SIZE)
		return 0;						//判断FIFO是否已装满未读数据,如果装满返回0
	_disable_interrupts();				//操作FIFO前一定要关总中断

	if (!(UCA0STAT & UCBUSY))
		IFG2 |=UCA0TXIFG; 				// 手动触发一次

	Tx_FIFO_DataNum++;					//未读取数据个数加一
	Tx_FIFO[Tx_FIFO_IndexW]=Data;		//将数据写入写读指针位置的FIFO数组
	Tx_FIFO_IndexW++;					//写指针移位
	if (Tx_FIFO_IndexW >= TX_FIFO_SIZE)	//判断指针是否越界
		Tx_FIFO_IndexW=0;				//写指针循环归零
	_enable_interrupts();				//恢复总中断使能
	return 1;							//返回成功
}

  读字节函数 Tx_FIFO_ReadChar()
  Tx_FIFO_ReadChar()将放在 UART 的 Tx 中断中执行,其功能是硬件 UART 从发送 FIFO中读取数据到 TxBuffer 寄存器中,进而把 TxBuffer 的数据发送出去。如果用上面机枪的例子来说的话,Tx_FIFO_ReadChar()功能就是从弹链中取一颗子弹放入枪膛。枪上膛的能量(调用一次 Tx_FIFO_ReadChar()函数)有两种来源:

  1. 人工拉第一次枪栓(人工置 Tx 标识位 UCA0TXIFG)。
  2. 前一个子弹发射后,火药后坐力产生的能量(Tx 发送中断)。
/******************************************************************************************************
* 名 称:Tx_FIFO_ReadChar()
* 功 能:从Tx发送FIFO中读1字节
* 入口参数:*Chr:待存放字节变量的指针
* 出口参数:“1”读取数据成功,“0”读取数据失败
* 说 明:操作FIFO时需要关闭总中断
* 范 例:无
******************************************************************************************************/
char Tx_FIFO_ReadChar(unsigned char *Chr)
{
	if(Tx_FIFO_DataNum==0) return(0); //判断FIFO是是否有未读数据,如果没有返回0
	_disable_interrupts(); //操作FIFO前一定要关总中断
	Tx_FIFO_DataNum--; //待读取数据个数减一
	*Chr=Tx_FIFO[Tx_FIFO_IndexR]; //将读指针位置的FIFO数据赋给指针所指变量
	Tx_FIFO_IndexR++; //读指针移位
	if (Tx_FIFO_IndexR>=TX_FIFO_SIZE) //判断指针是否越界
		Tx_FIFO_IndexR=0; //读指针循环归零
	_enable_interrupts(); //恢复总中断使能
	return(1); //返回成功
}

  FIFO 清空函数 Tx_FIFO_Clear()
  这个和 Rx_FIFO_Clear()道理一样,不解释。

/******************************************************************************************************
* 名 称:Tx_FIFO_Clear()
* 功 能:清空Tx发送FIFO区
* 入口参数:无
* 出口参数:无
* 说 明:清空并不需要真的去将FIFO每一个字节的数据写0,
* 只需读写指针清零和空满计数清零即可。
* 范 例:无
******************************************************************************************************/
void Tx_FIFO_Clear()
{
	_disable_interrupts(); //操作FIFO前一定要关总中断
	Tx_FIFO_DataNum=0; //FIFO中未读取数据数目清零
	Tx_FIFO_IndexR=0; //FIFO中模拟读指针清零
	Tx_FIFO_IndexW=0; //FIFO中模拟写指针清零
	_enable_interrupts(); //恢复总中断使能
}

  综上我们有:
在这里插入图片描述

UART_FIFO.c

#include <../UART_FIFO.h>

#include <string.h>
#include <stdarg.h>
#include <stdio.h>

unsigned char Rx_FIFO[RX_FIFO_SIZE]={0};	//UART接收FIFO数组
unsigned int Rx_FIFO_DataNum=0;				//UART接收FIFO的“空满”指示变量
unsigned int Rx_FIFO_IndexR=0;				//UART接收FIFO的模拟“读指针”变量
unsigned int Rx_FIFO_IndexW=0;				//UART接收FIFO的模拟“写指针”变量
unsigned char Tx_FIFO[TX_FIFO_SIZE]={0};	//UART发送FIFO数组
unsigned int Tx_FIFO_DataNum=0;				//UART发送FIFO的“空满”指示变量
unsigned int Tx_FIFO_IndexR=0;				//UART发送FIFO的模拟“读指针”变量
unsigned int Tx_FIFO_IndexW=0;				//UART 发送 FIFO 的模拟“写指针”变量

/******************************************************************************************************
* 名 称:Rx_FIFO_WriteChar()
* 功 能:往Rx接收FIFO中写1字节
* 入口参数:Data:待写入FIFO的数据
* 出口参数:1:写入数据成功,0:写入数据失败
* 说 明:操作FIFO时需要关闭总中断
* 范 例:无
******************************************************************************************************/
char Rx_FIFO_WriteChar(unsigned char Data)
{
	if(Rx_FIFO_DataNum==RX_FIFO_SIZE)
		return(0);						//判断FIFO是否已装满未读数据,如果装满返回0
	_disable_interrupts();				//操作FIFO前一定要关总中断
	Rx_FIFO_DataNum++;					//未读取数据个数加一
	Rx_FIFO[Rx_FIFO_IndexW]=Data;		//将数据写入写读指针位置的FIFO数组
	Rx_FIFO_IndexW++; 					//写指针移位
	if (Rx_FIFO_IndexW>=RX_FIFO_SIZE)	//判断指针是否越界
		Rx_FIFO_IndexW=0;				//写指针循环归零
	_enable_interrupts();				//恢复总中断使能
	return(1);							//返回成功
}

/******************************************************************************************************
* 名 称:Rx_FIFO_ReadChar()
* 功 能:从Rx接收FIFO中读1字节
* 入口参数:*Chr:待存放字节变量的指针
* 出口参数:“1”读取数据成功,“0”读取数据失败
* 说 明:操作FIFO时需要关闭总中断
* 范 例:无
******************************************************************************************************/
char Rx_FIFO_ReadChar(unsigned char *Chr)
{
	if(Rx_FIFO_DataNum==0)
		return 0;					//判断FIFO是是否有未读数据,如果没有返回0
	_disable_interrupts();			//操作FIFO前一定要关总中断
	Rx_FIFO_DataNum--;				//待读取数据个数减一
	*Chr=Rx_FIFO[Rx_FIFO_IndexR];	//将读指针位置的FIFO数据赋给指针所指变量
	Rx_FIFO_IndexR++;				//读指针移位
	if(Rx_FIFO_IndexR>=RX_FIFO_SIZE)//判断指针是否越界
		Rx_FIFO_IndexR=0;			//读指针循环归零
	_enable_interrupts();			//恢复总中断使能
	return(1);
}

/******************************************************************************************************
* 名 称:Rx_FIFO_Clear()
* 功 能:清空Rx接收FIFO区
* 入口参数:无
* 出口参数:无
* 说 明:清空并不需要真的去将FIFO每一个字节的数据写0,
* 只需读写指针清零和空满计数清零即可。
* 范 例:无
******************************************************************************************************/
void Rx_FIFO_Clear()
{
	_disable_interrupts();	//操作FIFO前一定要关总中断
	Rx_FIFO_DataNum=0;		//FIFO中未读取数据数目清零
	Rx_FIFO_IndexR=0;		//FIFO中模拟读指针清零
	Rx_FIFO_IndexW=0;		//FIFO中模拟写指针清零
	_enable_interrupts();	//恢复总中断使能
}

/******************************************************************************************************
* 名 称:Tx_FIFO_WriteChar()
* 功 能:往Tx发送FIFO中写1字节
* 入口参数:Data:待写入FIFO的数据
* 出口参数:1:写入数据成功,0:写入数据失败
* 范 例:无
******************************************************************************************************/
char Tx_FIFO_WriteChar(unsigned char Data)
{
	if(Tx_FIFO_DataNum==TX_FIFO_SIZE)
		return 0;						//判断FIFO是否已装满未读数据,如果装满返回0
	_disable_interrupts();				//操作FIFO前一定要关总中断

	if (!(UCA0STAT & UCBUSY))
		IFG2 |=UCA0TXIFG; 				// 手动触发一次

	Tx_FIFO_DataNum++;					//未读取数据个数加一
	Tx_FIFO[Tx_FIFO_IndexW]=Data;		//将数据写入写读指针位置的FIFO数组
	Tx_FIFO_IndexW++;					//写指针移位
	if (Tx_FIFO_IndexW >= TX_FIFO_SIZE)	//判断指针是否越界
		Tx_FIFO_IndexW=0;				//写指针循环归零
	_enable_interrupts();				//恢复总中断使能
	return 1;							//返回成功
}

/******************************************************************************************************
* 名 称:Tx_FIFO_ReadChar()
* 功 能:从Tx发送FIFO中读1字节
* 入口参数:*Chr:待存放字节变量的指针
* 出口参数:“1”读取数据成功,“0”读取数据失败
* 说 明:操作FIFO时需要关闭总中断
* 范 例:无
******************************************************************************************************/
char Tx_FIFO_ReadChar(unsigned char *Chr)
{
	if(Tx_FIFO_DataNum==0)
		return 0;					//判断FIFO是是否有未读数据,如果没有返回0
	_disable_interrupts();			//操作FIFO前一定要关总中断
	Tx_FIFO_DataNum--;				//待读取数据个数减一
	*Chr=Tx_FIFO[Tx_FIFO_IndexR];	//将读指针位置的FIFO数据赋给指针所指变量
	Tx_FIFO_IndexR++;				//读指针移位
	if(Tx_FIFO_IndexR>=TX_FIFO_SIZE)//判断指针是否越界
		Tx_FIFO_IndexR=0;			//读指针循环归零
	_enable_interrupts();			//恢复总中断使能
	return 1;						//返回成功
}

/******************************************************************************************************
* 名 称:Tx_FIFO_Clear()
* 功 能:清空Tx发送FIFO区
* 入口参数:无
* 出口参数:无
* 说 明:清空并不需要真的去将FIFO每一个字节的数据写0,
* 只需读写指针清零和空满计数清零即可。
* 范 例:无
******************************************************************************************************/
void Tx_FIFO_Clear()
{
	_disable_interrupts();	//操作FIFO前一定要关总中断
	Tx_FIFO_DataNum=0;		//FIFO中未读取数据数目清零
	Tx_FIFO_IndexR=0;		//FIFO中模拟读指针清零
	Tx_FIFO_IndexW=0;		//FIFO中模拟写指针清零
	_enable_interrupts();	//恢复总中断使能
}



/******************************************************************************************************
* 名 称:UART_OnTx()
* 功 能:UART的Tx事件处理函数
* 入口参数:无
* 出口参数:无
* 说 明:Tx_FIFO里有数据就将数据移到Tx Buffer寄存器中去
* 范 例:无
******************************************************************************************************/
void UART_OnTx(void)
{
	unsigned char Temp=0;
	if(Tx_FIFO_DataNum > 0)
	{
		Tx_FIFO_ReadChar(&Temp); //调用FIFO库函数
		UCA0TXBUF= Temp;
	}
}

/******************************************************************************************************
* 名 称:UART_OnRx()
* 功 能:UART的Rx事件处理函数
* 入口参数:无
* 出口参数:无
* 说 明:对接收到的数据,区别对待进行处理
* 范 例:无
******************************************************************************************************/
void UART_OnRx(void)
{
	char Temp = 0;
	Temp=UCA0RXBUF;				//预存下Tx Buffer数据

	Tx_FIFO_WriteChar(Temp);	//回显数据
	Rx_FIFO_WriteChar(Temp);	//写FIFO
	if(Temp == '\n')
	{							//如果是回车,表明可以做个“了断”了
		Command_match();		//判断命令是什么
		Rx_FIFO_Clear();		//清空FIFO
	}
}


/******************************************************************************************************
* 名 称:Command_match()
* 功 能:对接收到的命令数据进行匹配,根据匹配结果控制LED并回显处理结果
* 入口参数:无
* 出口参数:无
* 说 明:共4种预先约定的命令字:LED1_ON,LED1_OFF,LED2_ON,LED2_OFF
* 范 例:无
******************************************************************************************************/
void Command_match() // 字符匹配命令
{
	//-----命令共4种:LED1_ON,LED1_OFF,LED2_ON,LED2_OFF-----
	if((Rx_FIFO[0]=='L')&&(Rx_FIFO[1]=='E')&&(Rx_FIFO[2]=='D')
	&&(Rx_FIFO[4]=='_')&&(Rx_FIFO[5]=='O')) //先匹配共有字母LED?_O??
	{
		if((Rx_FIFO[3]=='1')&&(Rx_FIFO[6]=='N'))
		{
			LED1_ON; UART_printf("I was born for these!\r\n");	//匹配上命令字LED1_ON
		}
		if((Rx_FIFO[3]=='1')&&(Rx_FIFO[6]=='F')&&(Rx_FIFO[7]=='F'))
		{
			LED1_OFF; UART_printf("I have got it!\r\n");	//匹配上命令字LED1_OFF
		}
		if((Rx_FIFO[3]=='2')&&(Rx_FIFO[6]=='N'))
		{
			LED2_ON; UART_printf("It is easy for me!\r\n");	//匹配上命令字LED2_ON
		}
		if((Rx_FIFO[3]=='2')&&(Rx_FIFO[6]=='F')&&(Rx_FIFO[7]=='F'))
		{
			LED2_OFF; UART_printf("As your wish!\r\n");	//匹配上命令字LED2_OFF
		}
	}
	else
	{
		UART_printf("Are you crazy?\r\n");	//没匹配上任何命令,发送错误提示语
	}
}

void UART_printf(const char *format,...)
{
    unsigned char length;
    va_list args;
    unsigned char i;
    char TxBuffer[TX_BUFFER_LEN] = {0};

    va_start(args, format);
    length = vsnprintf((char*)TxBuffer, sizeof(TxBuffer), (char*)format, args);
    va_end(args);

    for(i = 0; i < length; i++)
    	Tx_FIFO_WriteChar(TxBuffer[i]);
}

UART_FIFO.h

/*
 * UART_FIFO.h
 *
 *  Created on: 2022年10月6日
 *      Author: Royic
 */

#ifndef UART_FIFO_H_
#define UART_FIFO_H_

#include <msp430.h>

#define RX_FIFO_SIZE 16 //接收缓冲区大小宏定义
#define TX_FIFO_SIZE 64 //发送缓冲区大小宏定义
#define TX_BUFFER_LEN 64

//-----对于硬件有关的代码宏定义处理-----
#define LED1_ON P1DIR|=BIT0; P1OUT|=BIT0
#define LED1_OFF P1DIR|=BIT0; P1OUT&=~BIT0
#define LED2_ON P1DIR|=BIT6; P1OUT|=BIT6
#define LED2_OFF P1DIR|=BIT6; P1OUT&=~BIT6

char Rx_FIFO_WriteChar(unsigned char Data);
char Rx_FIFO_ReadChar(unsigned char *Chr);
void Rx_FIFO_Clear();
char Tx_FIFO_WriteChar(unsigned char Data);
char Tx_FIFO_ReadChar(unsigned char *Chr);
void Tx_FIFO_Clear();
void UART_OnTx(void);
void UART_OnRx(void);
void Command_match(); // 字符匹配命令
void UART_printf(const char *format,...);

#endif /* UART_FIFO_H_ */

InterruptVectors_init.c

/*
 *  ...
 */
#include <msp430.h>

/* USER CODE START (section: InterruptVectors_init_c_prologue) */
/* User defined includes, defines, global variables and functions */
void UART_OnTx(void);
void UART_OnRx(void);
/* USER CODE END (section: InterruptVectors_init_c_prologue) */

/*
 *  ...
 */

#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR_HOOK(void)
{
    /* USER CODE START (section: USCI0TX_ISR_HOOK) */
    /* replace this comment with your code */
	if (IFG2 & UCA0TXIFG)
	{
		IFG2&=~UCA0TXIFG;	//手动清除标志位
		UART_OnTx();		//调用Tx事件处理函数
	}
    /* USER CODE END (section: USCI0TX_ISR_HOOK) */
}

/*
 *  ...
 */
 
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR_HOOK(void)
{
    /* USER CODE START (section: USCI0RX_ISR_HOOK) */
    /* replace this comment with your code */
	if (IFG2 & UCA0RXIFG)
	{
		IFG2&=~UCA0RXIFG;	//手动清除标志位
		UART_OnRx();		//调用Rx事件处理函数
	}
    /* USER CODE END (section: USCI0RX_ISR_HOOK) */
}

测试

 

main.c

/*
 * ======== Standard MSP430 includes ========
 */
#include <msp430.h>

/*
 * ======== Grace related includes ========
 */
#include <ti/mcu/msp430/Grace.h>
#include <../UART_FIFO.h>

#define MCLK_IN_HZ      16000000

#define delay_us(x)     __delay_cycles((MCLK_IN_HZ/1000000*(x)))
#define delay_ms(x)     __delay_cycles((MCLK_IN_HZ/1000*(x)))

/*
 *  ======== main ========
 */
int main(void)
{
    Grace_init(); // Activate Grace-generated configuration
    while(1)
    {
    	UART_printf("int test: wo%dne\r\n", 22);
		delay_ms(500);
    }
}

在这里插入图片描述
在这里插入图片描述
  发送

LED1_ON
LED1_OFF
LED2_ON
LED2_OFF

  可实现对应功能:
在这里插入图片描述
在这里插入图片描述
  尝试打印浮点数:

int main(void)
{
    Grace_init(); // Activate Grace-generated configuration
    while(1)
    {
    	UART_printf("float test: wo%fne\r\n", 22.0);
		delay_ms(500);
    }
}

发现 不剑了:

在这里插入图片描述
  右键工程选择Properties
在这里插入图片描述
  进入Build->Advanced Options->Library Function Assumptions
  Level of printf/scanf support required选择full

在这里插入图片描述
  再次尝试打印浮点数:

int main(void)
{
    Grace_init(); // Activate Grace-generated configuration
    while(1)
    {
    	UART_printf("  float test: wo%fne\r\n", 22.0);
		delay_ms(500);
    }
}

  可以看到能打印出结果,但有小瑕疵:头一个字符打印出错
在这里插入图片描述