MSP430F5529实现双板间串口通信
年轻人,不要一上来就急着敲代码,串口通信用到的的13个寄存器,快来看看你都会了吗?
哈哈哈~~,可千万不要被这些牛鬼蛇神吓住啊,这次我们讲的是入门级的,不会设置这么多寄存器的(但也不少呦)。
准备好了吗?下面我们开讲了!
1.数据格式
(1)ST:起始位(低电平启动串口)
因为串口待机时处于长期高电平状态,当检测到有低电平时,就会启动准备接收数据。
(2)D0~D7:数据位(可以7位,也可以8位)
1对应高定平,0对应低电平(这应该都清楚吧)
(3)AD:地址位:(双板通信用不到)
当多机通信时(例如一个设备发送,多个设备接收)
需要添加地址位,这样每个设备只需要接收自己对应地址的数据就行,避免了错误通信。
(4)PA:奇偶校验位
信号传输过程中可能(几率很小)会出错,发送前统计数据位1的个数是奇数还是偶数,接收是再次确认奇偶。
(5)SP:停止位(可以1位,也可以2位)
读到SP的高电平就意味着整段的数据接收完成了
看完了串口通信的数据格式,是不是觉得很简单呢?本来也不难嘛~ ~
2.工作结构
这张图是我自己画的,为了助于理解做了相应简化
1) 首先,我们都知道单片机内部读写数据都是并行操作,将并行的数据变为串行,自然就会想到用移位寄存器啦。
2) 如果大家还记得数电里移位寄存器的波特图的话,肯定还记得CLK是个啥吧。什么是波特率?波特率就是字节传输的速率(单位时间内字节传输个数),接收端和发送端必须保证相同的波特率才能保证正确的通信。
3) 那么缓冲寄存器又是个啥子呢?我们都知道数据的串行传输要比并行传输慢很多,如果移位寄存器还没有将旧数据移位完就添加新数据,势必会造成干扰,所以我们需要一个缓冲寄存器来暂时存储,等旧数据移位完成自动导入新数据。
至于怎样向缓冲寄存器内读写数据,直接赋值不就ok了啦。
※3.操作流程(入门级)
流程概述:
1)串口初始化
<1>TX/RX引脚功能复用
<2>串口控制寄存器配置
<3>设置波特率
<4>串口使能清除
<5>开启串口中断
2)中断服务程序
具体操作:
串口初始化
1.TX/RX引脚功能复用:
P4SEL |=BIT4+BIT5;//引脚功能复用
msp430f5529有两个串口,UART0(P3^3、P3^4)和UART1(P4^4、P4^5),我们下面都以UART1为例进行介绍。
2.UART控制寄存器的介绍:
<1>UCPEN:奇偶使能
0/1 奇偶禁止/奇偶使能(奇偶位的产生和接受);
<2>UCPAR奇偶选择
0/1 奇校验 / 偶校验;
<3>UCMSB 控制接收和发送移位寄存器的方向
0/1 LSB先 / MSB先;
<4>UC7BIT 数据位长度
0/1 8位数据 / 7位数据;
<5>UCSPB 停止位个数
0/1 一个停止位 / 两个停止位;
<6>UCMODEx USCI模式
00/01/02/03 USART / 空闲线多机 / 地址位多机 / UART自动波特率检测模式;
<7>UCSYNC
0/1 异步模式 / 同步模式;
<1>UCSSELx 选择BRCLK时钟源
00/01/10/11: UCLK / ACLK / SMCLK / MCLK;
<2>UCRXEIE 接收错误字符中断使能
0/1 :错误字符丢弃 / 错误字符接收,
<3>UCBRKIE 接收暂停字符中断允许
0/1 接收暂停字符不置位UCA0RXIFG / 接收暂停字符置位UCA0RXIG;
<4>UCDORM 睡眠态
0/1 非睡眠态所有接收字符将置位UCA0RXIFG / 睡眠态,只有先于空闲态的带地址字符将置位UCA0RXIFG;
<5>UCTXADDR 发送地址标记地址
0/1 下一帧是数据 / 下一帧是地址;
<6>UCTXBRK 发送暂停
(对将要写到发送缓存的数据带暂停)
0/1 :发送下一帧不是暂停 / 是暂停;
<7>UCSWRST 软件复位使能
0/1 禁止(USCI复位释放)/ 使能(复位状态保持);
上面的控制位你随意配置,当然我们如果对数据的格式不是特别苛刻的话,只需要对几个必须的控制位进行配置,其他选择默认即可。默认就是控制位的值为零。
(例:UCPEN:奇偶使能,我没有设置默认,即默认UCA1CTL &= ~UCPEN,意味着奇偶使能的禁止)
UCA1CTL1 |= UCSWRST ;//必须要有欧
3.波特率的配置:
拿9600来举一个栗子吧
1)首先需要选择合适的时钟源
(我建议9600以下选择ACLK,以上选SMCLK)
我的ACLK(辅助时钟信号)选的是外部低频晶体振荡器 f = 32.768kHZ
UCA1CTL1 |= UCSSEL_1;//ACLK
(这是前面控制寄存器的内容)
2)波特率需要配置三个寄存器,分别是
UCA1BR0: USCI_A1波特率寄存器0 (存整数的低八位)
UCA1BR1: USCI_A1波特率寄存器1(存整数的高八位)
UCA1MCTL: USCI_A1调制控制寄存器(存小数部分)
UCOS16: UCAxMCTL的一个控制位(0/1:低频/高频模式)
<1>低频模式
int【32.768k / 9600】=int【3.41】=3
UCOS16 默认为0,选择低频模式
UCA1BR0 = 0x03;//低八位
UCA1BR1 = 0x00;//高八位
round【(3.41—3)x 8】=round【3.28】= 3
(round是指取离它最近的整数)
UCA1MCTL |= UCBRS_3;
<2>高频模式
因为32.768k/9600 < 16 所以不能选择高频模式,为了更好地理解,我们假设我们的晶体振荡频率f = 1MHz
int【(1M/ 9600)/16】=int【6.51】=6
UCA1BR0 = 0x06;//低八位
UCA1BR1 = 0x00;//高八位
round【(6.51—6)x 16】=round【8.16】= 8
UCA1MCTL |= UCBRS_0+UCBRF_8+UCOS16;
4.串口使能清除
UCA1CTL1 &= ~UCSWRST ;//必须要有欧
到此为止,串口初始化配置完成
5.开启中断
(1)串口中断(三选一欧)
UCA1IE |= UCTXIE;//开启发送中断
UCA1IE |= UCRXIE;//开启接收中断
UCA1IE |= UCRXIE + UCTXIE;//接收和发送中断都开启。
(2)全局中断
_EINT();//打开全局中断
中断处理
USCI通信接收中断和发送中断用的是同一个中断服务程序,所以我们需要在中断服务程序内部对中断类型进行判断,判断的方案有两种:标志位查询判断和中断向量值判断。
(1)标志位查询判断:
发送中断:
如果发送缓冲寄存器UCAxTXBUF 已经准备好接收另一个字符,则UCTXIFG。如果 UCTXIE 和 GIE 也置位的话,将产生中断请求。
如果将字符写入UCAxTXBUF,UCTXIFG 将自动复位。 PUC 之后或 UCSWRST = 1 时,UCTXIFG 置位。PUC 之后或 UCSWRST = 1 时,UCTXIE 复位。
接收中断:(跟发送中断类似)
接收到的数据载入到 UCA1RXBUF 时,UCRXIFG 中断标志置位。如果UCTXIE 和 GIE 也置位的话,将产生中断请求。
UCRXIFG 和 UCRXIE 可以通过系统复位 PUC 信号或 UCSWRST = 1 复位。 当读取 UCA1RXBUF 时,UCRXIFG 自动复位。
代码如下:
if (!(UCA1IFG&UCTXIFG)) // 判断是否是发送中断
{
UCA1TXBUF = data;
};
else if(!(UCA1IFG&UCRXIFG)) //判断是否是接收中断
{
data = UCA1RXBUF;
};
else
break;
(2)中断向量值的判断
switch(__even_in_range(UCA1IV,4))
{
case 0:break;
case 2:
{
while (!(UCA1IFG&UCTXIFG));
UCA1TXBUF = data;
}
case 4:
{
while (!(UCA1IFG&UCRXIFG));
data= UCA1RXBUF;
}
default: break;
}
ps:__even_in_range(UCA1IV,4)在[0,4]区间内对UCA1IV进行遍历,提高了switch的效率
最后附上代码(只贴一个板子的了,注释在教程里有)
#include
void uart1_init();//串口初始化
int main(void)
{
WDTCTL = WDTPW | WDTHOLD;// 关狗
uart1_init();
__bis_SR_register(LPM3_bits + GIE); // 低功耗lpm3+全局中断
__no_operation();
}
void uart1_init()
{
P4SEL |=BIT4+BIT5;
UCA1CTL1 |= UCSWRST;
UCA1CTL1 |= UCSSEL_1; // CLK = ACLK
UCA1BR0 = 0x03;
UCA1BR1 = 0x00;
UCA1MCTL = UCBRS_3+UCBRF_0;
UCA1CTL1 &= ~UCSWRST;
UCA1IE |= UCRXIE;
return;
}
#pragma vector=USCI_A1_VECTOR
__interrupt void USCI_A1_ISR(void)
{
switch(__even_in_range(UCA1IV,4))
{
case 0:break;
case 2:
{
while (!(UCA1IFG&UCTXIFG));
UCA1TXBUF = data;
}
case 4:
{
while (!(UCA1IFG&UCRXIFG));
data= UCA1RXBUF;
}
default: break;
}
return;
}
最后以我最喜欢的一句话结尾吧:
岁月悠悠,衰微只浸肌肤;热忱抛却,颓废必至灵魂。
不定期分享自动化专业知识
创作不易,跪求关注