目录
平台:
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 串口通信将变得非常简单。举个例子:
- 使用纯软件实现 UART 串口通信,相当于没有邮政系统的原始社会,送信、收信甭管相隔多远都必须倾力亲为。
- 硬件通信模块相当于邮局(USCI),你只要填好收寄地址人员电话等信息(USCI 初始化),剩下的收发邮件(数据)都可以交给邮局(USCI)。
- 发邮件只需将邮件(数据)放入 TXBUF 发送缓冲器,邮局自动的会发出邮件,发送完毕还会电话通知(中断)。
- 收邮件只需查看自家邮箱接收缓冲器 RXBUF 就行,有邮件时,邮局同样会电话通知(中断)。
- 所以,初始化完成后,只需操作发送缓冲区 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配置
- 启用基本时钟系统,选择Basic User视图,为高速时钟源选择一个校准的频率。
- 启用USCI_A0并选择Basic User视图
- 在USCI_A0外围模式选择中选择UART模式
- 在引脚上选择适当的USCI_A0 RX和TX功能
- 选择适当的波特率,定义UART的时序
- 启用接收中断。你可以在
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 发送编程思想的区别。
- 查询法:主人(CPU)去邮局发 N 件包裹(N 个数据),从家里到邮局耗时 10 分钟(CPU 往 TxBUF 里“扔”数据的耗时)。邮局工作人员(UART)说,一次只能发一件包裹,要几天后包裹到达目的地(某波特率下的发送耗时),然后你才能发第二件。于是,摆在主人 CPU 面前有两个选择,要么就“不吃不喝”呆在邮局等消息,然后依次发送完所有包裹。另一个选择是,先回家该干嘛干嘛,定好闹钟(启动定时器轮询),隔一段时间去邮局看看上一个包裹发完没有。
- 中断法:故事的前半段和上面一样,只不过邮局工作人员(UART)说,我们最近在改进服务作风,你回家等消息去吧,能发下一个包裹的时候,会给你打电话的。于是 CPU 千恩万谢的回家该干嘛干嘛去了,不爽的就是家里堆了一堆包裹不能马上“扔出去”,还得老惦记着什么时候来电话。
- FIFO 法:故事的前半段和查询法一样,这回邮局工作人员(UART)说,我们开通VIP 服务了,您要是肯腾出自家房子(RAM)改造成中转仓库的话,包裹直接搁中转仓库就行了,我们保证先送来的包裹会先发送出去(FIFO 先进先出的原则)。就看您要改造多大的仓库(把 RAM 人工改造为 FIFO)?
<a id=”_FIFOUART__79”>使用 FIFO 接收 UART 数据
比 UART 发送数据更郁闷的是 UART 接收数据,当发送数据时,好歹人(CPU)自己是知道要发送多少包裹的,就算要蹲在邮局等候包裹发完,那时间也是有限长的,不会是一辈子。接收数据则可能耗费 CPU 毕生的时间。
- 查询法:可能会有包裹寄给人(CPU),邮局工作人员说了,第一,包裹到了恕不电话通知;第二,包裹到了尽快取走,再来第二个包裹我们可没地放,第一个包裹就没收了。人这回无语了,天知道会不会有包裹来,天知道哪天来包裹,天知道包裹里是不是有巨款(重要数据),我的神呀。人还是两条选择,要么就“不吃不喝”呆在邮局等包裹消息,有包裹立刻取回来,拆开看看是不是巨款。另一个选择是,先回家该干嘛干嘛,定好闹钟(启动定时器轮询),隔一段时间去邮局看看有没有包裹,不过闹钟的间隔一定要小于两个包裹的间隔。
- 中断法:故事的前半段和上面一样,只不过邮局工作人员(UART)说,由于“随意”没收包裹没有尽到“告知”义务被投诉了,所以有包裹我们会电话通知,不过到时不取(下一个包裹到来),照样没收。于是人又千恩万谢的回家该干嘛干嘛去了,不爽的就是一接到邮局电话就得扔下锅碗瓢盆(终止其他任务),百米冲刺赶到邮局取包裹,天知道包裹会不会被找茬没收了。
- FIFO 法:故事的前半段和查询法一样,这回邮局工作人员(UART)说,我们开通VIP 服务了,您家房子腾出几间来(RAM)当仓库,包裹到了会按顺序(FIFO)放进您专属仓库,并且电话通知您(中断)。不过仓库放满了,我们就爱莫能助了,所以,有空的时候(没有其他紧急任务时),您还是要去清理仓库。
FIFO编程
单片机中本身是不带硬件 FIFO 的,我们是利用单片机的 RAM 构造一个全局变量数组FIFO[]
,,通过对数组的软件操作,模拟 FIFO 的功能。对于 FIFO 的软件操作,有以下分析:
- 假如 FIFO 共 4 字节,那么“读写 FIFO 函数”的操作顺序都应该依次是
FIFO[0]
→Rx_FIFO[1]
→Rx_FIFO[2]
→Rx_FIFO[3]
→Rx_FIFO[0]
→Rx_FIFO[1]
…。所以读写 FIFO 各需要一个可以循环移位的指针变量。 - 为避免指针操作的复杂性,我们软件模拟 FIFO 时,用数组角标变量
FIFO_IndexW
代替指针的作用,FIFO[FIFO_IndexW]
就是待操作的数组变量。 - 如果只写 FIFO,不读 FIFO,那么写到
FIFO[3]
就必须停止,不能继续写FIFO[0]
。如果这时“读 FIFO 函数”开始工作,读 FIFO 最先读取的数据一定是FIFO[0]
。只要有数据被读走,则表示 FIFO 有空缺,可以继续写数据到Rx_FIFO[0]
(覆盖数据)。以后,每次 FIFO 被读走一个数,就可以被写入一个新的数据。 - 假如“写函数”只写了 2 个数据,那么“读函数”在读完
Rx_FIFO[0]
和Rx_FIFO[1]
后就不能继续循环读取FIFO[2]
。只有写入数据后,才能再次读数据。 - “读函数”怎么通知“写函数” FIFO 有空缺,“写函数”又如何通知“读函数”有新数据呢?可以通过对全局变量
FIFO_DataNum
的“拉锯战”来判别。每写一个数据,FIFO_DataNum++
,每读一个数据FIFO_DataNum—
。 - 当
FIFO_DataNum = 0
(空指示),表示 FIFO 中没有未读数据,只能写不能读。 - 当
FIFO_DataNum = 4
(满指示),表示 FIFO 塞满了未读数据,只能读不能写。 - 当
0 < FIFO_DataNum < 4
,表示既可读又可写。
首先,分析一下我们需要多少个 FIFO。如图 10.3 所示,我们需要两个独立的 FIFO,分别用来缓存 CPU 与 Tx 硬件发送器之间的数据,CPU 与 Rx 硬件接收器之间的数据。就 FIFO本身而言,功能都是一样的,但具体 CPU 扮演的角色是读 FIFO 还是写 FIFO,则有区别。
其次,分析一下需要多少全局变量:
- 构造 FIFO 的数组,Rx 和 Tx 各需要一个。
- 指示 FIFO 内数据数目(空满指示),Rx 和 Tx 各需要一个。
- 指示 FIFO 内“数据头”的读指针,Rx 和 Tx 各需要一个。
- 指示 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。
- 写 FIFO 的第一件事就是判断 FIFO 是否已满,满了就不能写,并返回错误提示 0。
- 如果可以写,则关中断→
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 接收数据依次取出到指定的变量里。
- 读 FIFO 的第一件事就是判断 FIFO 是否为空,空了就不能读,并返回错误提示 0。
- 如果可以读,则关中断→
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 中断标志位。有以下分析结论:
- 由于扳机永远处于扣动状态,所以,只要拉一次枪栓上膛,全部弹带的子弹都将依次发送出去。
- 在子弹打完之前,随时可以不断的补充弹药。
- 一旦子弹打完了之后,若要补充弹药,就需要再次拉枪栓,机枪才会开火。
/******************************************************************************************************
* 名 称: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()
函数)有两种来源:
- 人工拉第一次枪栓(人工置 Tx 标识位 UCA0TXIFG)。
- 前一个子弹发射后,火药后坐力产生的能量(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);
}
}
可以看到能打印出结果,但有小瑕疵:头一个字符打印出错
评论(0)
您还未登录,请登录后发表或查看评论