UART

作者: TI 工程师 Max Han

简介

MSP430FR2311是一款FRAM数字控制器,可以实现超低功耗,并且集成了丰富的外设模块,可以满足工业和消费等多种应用。MSP430FR2311中的eUSCI_A0支持UART通讯,本文对此UART模块的寄存器配置进行了详细的分析和计算,以帮助工程师对此UART模块进行深入理解和灵活配置。

UART通讯模块介绍

图1是MSP430FR2311的系统架构图,eUSCI_A0模块如红框所示,它支持UART通讯。

MSP430FR2311 中UART模块寄存器配置的分析和计算
图 1 MSP430FR2311系统架构图

UART属于异步通信模式,MSP430FR2311通过UCA0RXD和UCA0TXD与其它芯片相连,芯片之间并没有时钟信号CLK连接。

在工程应用中,首先根据工程需要选择合适的波特率(Baud Rate),在MSP430FR2311中成为BITCLK,然后选择UART模块的时钟源BRCLK,根据选择的波特率和时钟源频率,进行合适的寄存器配置,便可实现UART通讯。在MSP430FR2311中,UCA0CTLW0中的UCSSELx用来选择时钟源,波特率通过UCOS16, UCBRx, UCBRFx和 UCBRSx来设定。

低频波特率配置

如果BRCLK是BITCLK的整数倍,即BRCLK/BITCLK=N,这时比较容易理解,在UART传输时,每个数据bit时长包含了N个BRCLK。

但往往BRCLK不是BITCLK的整数倍,这时要实现UART通讯就需要进行合适的调制(modulation),以避免累积误差导致UART通讯失败。在MSP430FR2311中,UCBRSx用来实现合理调制。

以传输一个8bit数据为例,在发送的bit位中包含start bit,8bit数据,parity bit和stop bit。

在SLAA049中,用图标明了UCBRSx的modulation模式,如图二所示,它是以8bit为一个循环进行调制。

MSP430FR2311 中UART模块寄存器配置的分析和计算
图2 UCBRSx的modulation模式

以时钟源频率BRCLK 32768Hz,波特率BITCLK 2400Hz为例,32768/2400=13.6533,所以UCOS16=0,UCBRx=13,modulation的作用主要是用来消除累积误差,所以如下表所示,当累积误差大于等于0.5时,UCBRSx对应的位置1,否则清零。经过计算,UCBRSx=0xB6.

表1 UCBRSx的生成说明
MSP430FR2311 中UART模块寄存器配置的分析和计算

所以调制后的Timing如图3所示,图中13代表13个BRCLK时长,14代表14个BRCLK时长。

MSP430FR2311 中UART模块寄存器配置的分析和计算
图3 Modulated Timing

再以时钟源频率BRCLK 1048576Hz,波特率BITCLK 115200为例,1048576/115200=9.1022,所以UCOS16=0,UCBRx=9,对于m5,虽然小数部分大于0.5,但是由于在m4的位置已经增加了1个BRCLK的时长,所以需要小数部分累积到大于1.5后,UCBRSx的位才会设置为1。经过计算,UCBRSx=0x08.

表2 UCBRSx的生成说明
MSP430FR2311 中UART模块寄存器配置的分析和计算

MSP430FR2311 User’s Guide中提供了UCBRSx的调制数据,方便快速获得正确的UCBRSx值。如图4所示。

MSP430FR2311 中UART模块寄存器配置的分析和计算
图 4 UCBRSx设定值速查表

高频波特率配置

MSP430FR2311中包含了一个过采样波特率模式(oversampling baud-rate mode),用来支持高频时钟源。UCA0MCTLW中UCOS16用来使能过采样波特率模式。当UCOS16=1时,过采样波特率模式使能,此时UCBRx中1对应16个BRCLK时长,UCBRFx中1对应1个BRCLK时长。

以时钟源频率BRCLK 4000000Hz,波特率BITCLK 57600为例,4000000/57600=69.4444,再将69除以16,商为4,余数为5,所以UCOS16=1,UCBRx=4,UCBRFx=5. UCBRSx的设置如前面的讨论,不再赘述,UCBRSx=0x55.

在过采样波特率模式中,数值判定(majority votes)时,总是以1/16的数据bit时长(1/BITCLK)来分段。MSP430FR2311 User Guide中提供了UCBRFx的调制表格,如表3所示。

表3 UCBRFx调制模式
MSP430FR2311 中UART模块寄存器配置的分析和计算

所以在这个例子中,一个数据bit时长如图5所示。

MSP430FR2311 中UART模块寄存器配置的分析和计算
图5 UCBRFx调制

参考文献

MSP430FR231x Mixed-Signal Microcontrollers, datasheet, SLASE58C
MSP430 Universal Synchronous Asynchronous Receive/Transmit Communication Interface, SLAA049
MSP430FR4xx and MSP430FR2xx Family User's Guide, SLAU445H

转自:TI E2E™ 中文社区

围观 809

UART、SPI、IIC是经常用到的几个数据传输标准,下面就分别总结一下:

UART(Universal Asynchronous Receive Transmitter):也就是我们经常所说的串口,基本都用于调试。

主机和从机至少要接三根线,RX、TX和GND。TX用于发送数据,RX用于接受数据(收发不是一根线,所以是全双工方式)。注意A和B通信A.TX要接B.RX,A.RX要接B.TX(A用TX发B当然要用RX来收了!)

如果A是PC机,B是单片机,A和B之间还要接一块电平转换芯片,用于将TTL/CMOS(单片机电平)转换为RS232(PC机电平)。因为TTL/CMOS电平范围是0~1.8/2.5/3.3/5V(不同单片机范围不同),高电压表示1,低电压表示0。而RS232逻辑电平范围-12V~12V,-5~-12表示高电平,+5~+12V表示低电平(对!你没有听错)。为什么这么设置?这就要追溯到调制解调器出生时代了,有兴趣自己去查资料!

数据协议:以PC机A给单片机B发数据为例(1为高电平,0为低电平):A.TX to B.RX。刚开始B.RX的端口保持1,当A.TX发来一个0作为起始位告诉B我要发数据了!然后就开始发数据,发多少呢?通常一次是5位、6位、7位、8位,这个双方事先要用软件设置好。PC机一般会用串口助手设置,单片机会在uart的驱动中设置。一小帧数据发送完了以后,A.TX给个高电平告诉B.RX我发完了一帧。如果还有数据,就再给个0然后重复上一步。如果双方约定由校验位,还要在发停止位1之前发送个校验位,不过现在一般都不需要校验位了,因为出错的概率太小了,而且一般用于调试,所以...呵呵呵!

一般在串口助手上还有个RTS/CTS流控选项,也叫握手,我从来没用过。搬一段我能理解的介绍:RTS(请求发送),CTS(清除发送)。如果要用这两个功能,那就至少要接5根线:RX+TX+GND+RTS+CTS。当A要发送数据时,置RTS有效(可能是置1),告诉B我要发送数据了。当B准备好接受数据后,置CTS有效,告诉A你可以发了。然后他们就实现了两次握手!挺耽误时间是不是?这个RTS还可以当电源使用,如果你不用它的握手功能,且电源电流在50mA以下时,就可以把它置为高电平可以当电源用喔~!

IIC(Inter Integrated Circuit):两根线,一个时钟线SCL和一个数据线SDA。只有一根数据线,所以是半双工通信。接线不难,而且两根线上也可以挂很多设备(每个设备的IIC地址不同),数据协议比较麻烦:

还是假设A给B发数据(这里A.SCL接B.SCL, A.SDA接B.SDA)。起初SDA和SCL上的电平都为高电平。然后A先把SDA拉低,等SDA变为低电平后再把SCL拉低(以上两个动作构成了iic的起始位),此时SDA就可以发送数据了,与此同时,SCL发送一定周期的脉冲(周期和PCLK有关,一般会在IIC的控制寄存器中设置)。SDA发送数据和SCL发送脉冲的要符合的关系是:SDA必须在SCL是高电平是保持有效,在SCL是低电平时发送下一位(SCL会在上升沿对SDA进行采样)。规定一次必须传8位数据,8位数据传输结束后A释放SDA,但SCL再发一个脉冲(这是第九个脉冲),这会触发B通过将SDA置为低电平表示确认(该低电平称为ACK)。最后SCL先变为高电平,SDA再变为高电平(以上两个动作称为结束标志)如果B没有将SDA置为0,则A停止发送下一帧数据。IIC总线(即SDA和SCL)上的每个设备都有唯一地址,数据包传输时先发送地址位,接着才是数据。一个地址字节由7个地址位(可以挂128个设备)和1个指示位组成(7位寻址模式)。指示位是0表示写,1表示读。还有10位寻址模式,使用两个字节来保存地址,第一个字节的最低两位和第二个字节的8位合起来构成10位地址。

UART, SPI, IIC对比和总结

SPI(Serial Peripheral Interface, SPI):4条线:MOSI(master output and slave input),MISO,SCLK(时钟),CS(片选)。片选信号低电平有效。SPI有四种模式

区别和联系:

UART一帧可以传5/6/7/8位,IIC必须是8位。IIC和SPI都从最高位开始传。

SPI用片选信号选择从机,IIC用地址选择从机。

来源:TopDstar

围观 410

目前扩展串口的方法主要有以下方法,

①、采用串口扩展芯片实现,如ST16C550、ST16C554、SP2538、MAX3110等,虽然成本较高, 但系统的可靠性得到了保证,适用于数据量较大、串口需求较多的系统;

②、采用分时切换的方法将一个串口扩展与多个串口设备通信,分时复用的方法成本低, 但只适用于数据量不大的场合, 并且只能由这个单片机主动和多个设备通信,实时性差;

③、用软件模拟的方法扩展串口,其优势也是成本低、实时性好, 但要占用一些CPU时间。

一般的软件模拟扩展串口方法,使用1个I/O端口、1个INT外部中断和定时器,该方法扩展的串口有2个缺点,
①、由于使用了INT外部中断,故只能使用2个INT外部中断扩展2个串口。
②、文中的发送和接收数据的效率比较低,占用了CPU的大量时间,不能与其他任务同时进行,所以使用范围有限。

本文提出的模拟串口方法,仅使用2个普通I/O和1个定时器,由于不需要INT的限制,可以扩展出多个串口,且带FIFO的功能,该方法扩展模拟串口的收发数据在中断服务中完成,所以非常效率高,一般的单片机都支持定时器中断,所以所以该方法在大多数单片机上都可以应用。

对于低速度的单片机(如89S51)可以扩展出低速串口(9600、4800等),对于高速单片机(如AVR、PIC、C8051、STC12)可以扩展高速串口(如19200、28800、38400、57600等)。目前单片机的处理速度越来越高,而价格越来越便宜,本文使用的STC12C1052芯片就具有高速度和低价格,价格仅为每片人民币3.8元。电子产品的开发设计时,要求在保证性能的情况下降低硬件成本,软件模拟扩展串口提供了一种降低成本的好方法。

1、串口通讯原理

在串口的异步通信中,数据以字节为单位的字节帧进行传送,发送端和接收端必须按照相同的字节帧格式和波特率进行通信,其中字节帧格式规定了起始位、数据位、寄偶效验位、停止位。起始位是字节帧的开始,使数据线处于逻辑0状态,用于向接收端表明开始发送数据帧,起到使发送和接收设备实现同步。停止位是字节帧的终止,使数据线处于逻辑1状态,用于向接收端表明数据帧发送完毕。波特率采用标准速度,如4800、9600、19200、28800、38400、57600等。

2、软件UART的设计思想

在本设计对硬件要求方面,仅仅占用单片机的任意2个I/O端口和1个定时器,利用定时器的定时中断功能实现精确的波特率定时,发送和接收都在定时中断的控制之下进行。

数据发送的思想是,当启动字节发送时,通过TxD先发起始位,然后发数据位和奇偶数效验位,最后再发停止位,发送过程由发送状态机控制,每次中断只发送1个位,经过若干个定时中断完成1个字节帧的发送。

数据接收的思想是,当不在字节帧接收过程时,每次定时中断以3倍的波特率监视RxD的状态,当其连续3次采样电平依次为1、0、0时,就认为检测到了起始位,则开始启动一次字节帧接收,字节帧接收过程由接收状态机控制,每次中断只接收1个位,经过若干个定时中断完成1个字节帧的接收。

为了提高串口的性能,在发送和接收上都实现了FIFO功能,提高通信的实时性。FIFO的长度可以进行自由定义,适应用户的不同需要。

波特率的计算按照计算公式进行,在设置最高波特率时一定要考虑模拟串口程序代码的执行时间,该定时时间必须大于模拟串口的程序的规定时间。单片机的执行速度越快,则可以实现更高的串口通讯速度。

3、软件UART设计的实现

本程序在宏晶科技(深圳)生产的STC12C1052高速单片机上进行运行测试,STC12C1052单片机是单时钟/机器周期的MCS51内核单片机,与89C2051引脚完全兼容,其工作频率达35MHz,相当与420MHz的89C2051单片机,每片人民币3.8元。由于该单片机的高速度,使得软件扩展串口的方法,更方便实现高速的串口。

本扩展串口的设计中,STC12C1052使用的晶振频率为22.1184Mhz,以波特率的3倍计算定时时间,在接收过程中以此定时进行接收起始位的采样,在发送和接收过程中再3分频得到标准波特率定时,进行数据发送与接收。

3.1、数据定义

定义模拟串口程序所必须的一些资源,如I/O引脚、波特率、数据缓冲区等。

#define Fosc 22118400 //晶振频率
#define Baud 38400 //波特率
#define BaudT (Fosc/Baud/3/12)
#define BufLong 16 //FIFO长度
sbit RxD1=P1^7; //模拟接收RxD
sbit TxD1=P1^6; //模拟发送TxD
bit Brxd1,Srxd1;//RxD检测电平
BYTE Rbuf1[BufLong];//FIFO接收区
BYTE Rptr1,Rnum1;
BYTE Tbuf1[BufLong];//FIFO发送区
BYTE Tptr1,Tnum1;
BYTE TimCnt1A,TimCnt1B;
BYTE Mtbuf1,Mrbuf1,TxdCnt1,RxdCnt1;

3.2、数据接收子程序

数据接收过程中,依次存储RxD的逻辑位形成字节数据,当数据接收完毕且停止位为1时,表示接收到了有效数据,就将结果存储到接收FIFO队列中去。

void Recv()
{
if(RxdCnt1>0) //存数据位8个
{
Mrbuf1>>=1;
if(RxD1==1) Mrbuf1=Mrbuf1|0x80;
}
RxdCnt1–;
if(RxdCnt1==0&& RxD1==1) //数据接收完毕
{
Rbuf1[Rptr1]=Mrbuf1; //存储到FIFO队列
if(++Rptr1>BufLong-1) Rptr1=0;
if(++Rnum1>BufLong) Rnum1=BufLong;
}
}

3.3、数据发送子程序

该程序过程中,当数据发送状态结束时,检测发送FIFO队列是否为空,若非空则取出发送数据,然后启动发送状态;当处于发送状态时,则按照状态机的状态进行起始位、数据位和停止位的发送。

void Send()
{
if(TxdCnt1!=0) //字节发送状态机
{
if(TxdCnt1==11) TxD1=0;//发起始位0
else if(TxdCnt1>2) //发数据位
{ Mtbuf1>>=1; TxD1=CY;}
else TxD1=1; //发终止位1
TxdCnt1–;
}
else if(Tnum1>0) //检测FIFO队列
{
Tnum1–;
Mtbuf1=Tbuf1[Tptr1]; //读取FIFO数据
if(++Tptr1>=BufLong) Tptr1=0;
TxdCnt1=11; //启动发送状态机
}
}

3.4、中断程序

中断定时时间为波特率定时的1/3,即以3倍的波特率对RxD进行采样,实现起始位的判别,当起始位到达时启动接收过程状态机。将该定时进行3分频再调用数据的发送和接收过程,进行准确波特率下的串口通信。

void Uart() interrupt 1 using 1
{
if(RxdCnt1==0 ) //接收起始识别
{
if(RxD1==0 && Brxd1==0 && Srxd1==1) { RxdCnt1=8; TimCnt1B=0;}
}
Srxd1=Brxd1; Brxd1=RxD1;
if(++TimCnt1B>=3 && RxdCnt1!=0) { TimCnt1B=0; Recv();}//数据接收
if(++TimCnt1A>=3) { TimCnt1A=0; Send();} //数据发送
}

3.5、串口初始化

打开定时器的中断,将定时器的设置为自装载模式,依照波特率设置定时中断的定时间隔,启动定时器,并进行UART各变量的初始化。

void IniUart()
{
IE=”0x82″; TMOD=”0x22″;
TH0=-BaudT; TL0=-BaudT; TR0=1;
Rptr1=0;Rnum1=0;Tptr1=0;Tnum1=0;
}

4、结束语

本文提出的模拟串口设计方法,其独特之处在于:仅仅使用任意2个普通I/O引脚和1个定时中断实现了全双工串口,对硬件的占用较少,具有多可串口扩展能力;在串口接收的起始位判别时采用了连续3次采样的判别方法,该方法实现简单、准确率高;用定时中断实现了串口数据的发送和接收,并实现了FIFO队列,使串口发送和接收工作效率高。

来源: 快易购

围观 495

UART(Universal Asynchronous Receiver and Transmitter)通用异步收发器(异步串行通信口)是MCU的一个重要的数字接口,市面上很多的传感器、通信模块等外围器件都采用了UART接口,同时工程师在软件开发调试过程中UART打印输出作为一种最直观的输出方式可以检查程序的运行情况,所以UART在MCU中的作用不言而喻。

首先普及一下并行通信、串行通信(同步通信和异步通信)两种通信方式的特点:
并行通信:并行通信是指数据的各个位同时传送,可以字或字节为单位并行进行。
-传输原理:数据各个位同时传输。
-优点:速度快,位数多
-缺点:占用引脚资源多,线路复杂,成本高

串行通信:串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度,其只需要少数几条线就可以在系统间交换信息。
-传输原理:数据按位顺序传输。
-优点:占用引脚资源少,传输线少
-缺点:速度相对较慢,耗时长

串行通信的通信方式又分为:同步通信和异步通信两种方式
同步通信:带时钟同步信号传输,发送方和接收方时钟需要建立连接,使双方的时钟
到完全同步。比如:SPI,IIC通信接口等。
异步通信:接收器和发送器使用各自的时钟,不带时钟同步信号。每一个字符要用起始位和停止位作为字符开始和结束的标志,以字符为单位的一个个地发送和接收。比如:UART通信接口等。

MM32系列MCU的通用异步收发器(UART)提供了一种灵活的方法与使用工业标准 NRZ 异步串行数据格式的外部设备之间进行全双工数据交换。UART 利用分数波特率发生器提供宽范围的波特率选择。它支持同步单向通信和半双工单线通信,以及调制解调器(CTS/RTS)操作。

有很多工程师在使用UART时,在设计PCB的UART接口时,一直对UART采用两线、三线、四线的问题一直都是很模糊情况,在这里我简单讲一下这个问题。

有些客户采用两线主要是单工模式,两线分别是:GND, TX 或者 RX,相当于MCU是做发送或者接收功能,将接收设备和发送设备共地,是要把参考电压调节成一致,避免接收设备和发送设备双方对高低电平的判断不一致的情况。

采用两线主要是双工模式,三线分别是:GND,TX,RX,接收设备和发送设备都是双向通信设备,且都有各自的供电电源,只需要将双方的基准电压调节一致就可以实现双方的串口通信功能,客户在使用ISP下载程序时一般都采用这种方式,预留一个三线接口。

采用四线主要是通信双方有一方需要为另一方提供电源,供另一方芯片运行,所以四线分别为:GND,TX,RX,VDD。
GND:共地,提供基准电压。
RX:接收数据串行输入。通过过采样技术来区别数据和噪音,从而恢复数据。
TX:发送数据串行输出。当发送器被禁止时,输出引脚恢复到它的I/O端口配置。当发送器被激活,并且不发送数据时,TX引脚处于高电平。在单线和智能卡模式里,此I/O口被同时用于数据的发送和接收。
VDD:供电源。

一般的通信模块还会有另外两个引脚在硬件流控模式中需要使用:
nCTS:清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送。
nRTS:发送请求,若是低电平,表明 UART 准备好接收数据。

两个UART间的通信接线方法

MM32 UART中断通信

两个UART间的硬件流控
MM32 UART中断通信

UART特征:

字长可以通过编程 UART_CCR 寄存器中的 CHAR 位,选择 5 ~ 8 位。在起始位期间, TX 脚处于低电平,在停止位期间处于高电平。

空闲符号被视为完全由‘1’组成的一个完整的数据帧,后面跟着包含了数据的下一帧的开始位(‘1’的位数也包括了停止位的位数)。

断开符号被视为在一个帧周期内全部收到‘0’(包括停止位期间,也是‘0’)。在断开帧结束时,发送器再插入 1 或 2 个停止位(‘1’)来应答起始位。

发送和接收由一个共用的波特率发生器驱动,当发送器和接收器的使能位分别置位时,分别为其产生时钟。

UART时序

MM32 UART中断通信

串口设置的步骤可分为如下几个流程:

1) 串口复位,GPIO复位
2) 串口时钟使能, GPIO 时钟使能
3) GPIO 端口模式设置
4) 串口参数初始化
5) 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
6) 编写中断处理函数

1、串口复位,GPIO复位

在系统开始配置外设时,建议先执行复位相对应的外设,然后重新配置该外设,使其达到自己所期望的工作模式。

UART_DeInit(UART1);//复位串口1
GPIO_DeInit(GPIOA);//复位GPIOA

2、串口时钟使能, GPIO 时钟使能

串口1(UART1)是挂载在 APB2 下面的外设,GPIOA的时钟是挂载在AHB上,所以使能函数为:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE);//使能UART1
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);//使能GPIOA的时钟

3、GPIO 端口模式设置

在上一章节讲述了GPIO的使用,使用GPIO的UART功能需要配置端口复用功能,根据DS_MM32L073_Ver1.7手册表4.PA端口功能复用可知PA9和PA10的UART功能的复用配置AF1。
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_1);
外设的GPIO配置:

MM32 UART中断通信

接下来的两段代码就是将 TX(PA9)设置为推挽复用输出模式,将 RX(PA10)设置为浮空输入模式:
GPIO_InitTypeDef GPIO_InitStructure;
//UART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 GPIOA.9

//UART1_RX GPIOA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化 GPIOA.10

4、串口参数初始化

4.1、分数波特率发生器

接收器和发送器的波特率在 BRR 的整数寄存器和 FRA 的小数寄存器中的值应设置成相同。
Tx/Rx 波特率 = fCK /(16 *UARTDIV )

这里的 fCK 是给外设的时钟(PCLK1 用于 UART2, PCLK2 用于 UART1)。UARTDIV 是一个无符号的定点数。这 16 位的值设置在 UART_BRR 寄存器。

例:如果BaudRate=9600,PCLK2=72MHz
则UARTDIV= 468.75

4.2、字长配置

字长可以通过编程 UART_CCR 寄存器中的 CHAR 位,选择 5 ~ 8 位。

4.3、奇偶校验位

奇偶控制(发送时生成一个奇偶位,接收时进行奇偶校验)可以通过设置 UART_CCR 寄存器上的 PEN位而激活。如果奇偶校验出错,无效数据不会从移位寄存器传送到 UART_RDR 寄存器。

偶校验:校验位使得一帧中的数据以及校验位中‘1’的个数为偶数。

例如:数据 = 00110101,有 4 个‘1’,如果选择偶校验(在 UART_CCR 中的 PSEL = 0),校验位将是‘0’。

奇校验:此校验位使得一帧中的数据以及校验位中‘1’的个数为奇数。

例如:数据=00110101,有 4 个‘1’,如果选择奇校验(在 UART_CCR 中的 PSEL = 1),校验位将是‘1’。

传输模式:如果 UART_CCR 的 PEN 位被置位,写进数据寄存器的数据的 MSB 位被校验位替换后发送出去(如果选择偶校验偶数个‘1’,如果选择奇校验奇数个‘1’)。如果奇偶校验失败, UART_ISR 寄存器中的 RXPERR_INTF 标志被置‘1’,并且如果 RXPERREN 在被预先设置的话,中断产生。

4.4、硬件数据流流控

RTS 流控制

如果 RTS 流控制被使能,只要 UART 接收器准备好接收新的数据, nRTS 就变成有效(接低电平)。当接收寄存器内有数据到达时, nRTS 被释放,由此表明希望在当前帧结束时停止数据传输。下图是一个启用 RTS 流控制的通信的例子。

MM32 UART中断通信

CTS 流控制

如果 CTS 流控制被使能,发送器在发送下一帧前检查 nCTS 输入。如果 nCTS 有效(被拉成低电平),则下一个数据被发送(假设那个数据是准备发送的),否则下一帧数据不被发出去。若 nCTS 在传输期间被变成无效,当前的传输完成后停止发送。下图是一个 CTS 流控制被启用的通信的例子。

MM32 UART中断通信

UART_InitTypeDef UART_InitStructure;
UART_InitStructure.UART_BaudRate = 115200;//波特率设置
UART_InitStructure.UART_WordLength= UART_WordLength_8b;//字长为8
UART_InitStructure.UART_StopBits = UART_StopBits_1;//一个停止位
UART_InitStructure.UART_Parity = UART_Parity_No;//无奇偶校验位
UART_InitStructure.UART_HardwareFlowControl=UART_HardwareFlowControl_None;//无硬件数据流流控
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;//开启收发模式
UART_Init(UART1, &UART_InitStructure); //初始化串口
UART_Cmd(UART1, ENABLE); //UART1使能

4.5、开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)

NVIC_InitTypeDef NVIC_InitStructure;
UART_ITConfig(UART1, UART_IT_RXIEN, ENABLE);//开启串口接收中断
NVIC_InitStructure.NVIC_IRQChannel = UART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

4.6、编写中断处理函数

void UART1_IRQHandler(void) //中断服务函数
{
u8 Res;
if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET) //接收中断产生
{
UART_ClearITPendingBit(UART1,UART_IT_RXIEN);//清中断标志
Res =UART_ReceiveData(UART1);
UART_SendData (Res);
}
}

按照上述配置完成后,在main函数中将上述函数调用,将程序下载到MiniBorad中,打开串口助手,在对话框中输入您需要输入的内容,MCU的UART将您发送的数据转发并且打印在串口助手对话框,可以在显示串口看到您输入的内容。打印结果如下图所示:

MM32 UART中断通信

转自: 灵动微电子

围观 713

在 Cube 软件包中,为不同系列 MCU、不同外设提供了对应的例程方便开发参考。其中,针对STM324xG-EVAL 平台提供了 UART 中断发送接收的例程。开发者参考了这个例程进行 UART 功能开发,并且为了实现不间断的接收功能,在接收回调函数中,再次调用中断接收函数。在这种情况下,出现了例程执行异常。本文分析了这种情况出现原因及解决方法。

问题描述

测试验证板: STM3240G-EVAL
参考例程路径:
STM32Cube_FW_F4_V1.15.0\Projects\STM324xG_EVAL\Examples\UART\UART_Hyperterminal_IT

基于上述例程,出于前言中交代的应用目的,在接收回调函数 HAL_UART_RxCpltCallback 中,再次调用 HAL_UART_Receive_IT。
随后出现例程执行卡死在下面红色标识的语句处,导致了 UART 中断发送无法正确被执行。

 if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)
 {
 /* Turn LED3 on: Transfer error in reception process */
 BSP_LED_On(LED3);
 while(1)
 {
 }
 }
 /*##-4- Wait for the end of the transfer ###################################*/
 /* Before starting a new communication transfer, you need to check the current
 state of the peripheral; if it 抯 busy you need to wait for the end of current
 transfer before starting a new one.
 For simplicity reasons, this example is just waiting till the end of the
 transfer, but application may perform other tasks while transfer operation
 is ongoing. */
 while (HAL_UART_GetState(&UartHandle) != HAL_UART_STATE_READY)
 {
 }
 /*##-5- Send the received Buffer ###########################################*/
 if(HAL_UART_Transmit_IT(&UartHandle, (uint8_t*)aRxBuffer, RXBUFFERSIZE)!= HAL_OK)

问题分析及解决

根据描述,首先考虑到是否由于 UART 始终处于接收忙状态,导致中断发送收到影响。但是 UART 外设具有发送数据寄存器和接收数据寄存器,以及互不影响的接收、发送中断。

继续对问题进行定位,发现在 HAL_UART_GetState 函数中,会同时获取发送和接收状态。这意味着,只有在发送和接收同时处于就绪状态时,中断发送函数才会被执行。而开发者的应用实现中,使得UART 始终处于接收状态,从而判断无法通过。

清楚了产生原因后,问题解决就一目了然了。只需将对发送和接收状态的判断,改写成仅对发送的状
态的判断,如下所示。其中 gState 对应着发送状态。

//while (HAL_UART_GetState(&UartHandle) != HAL_UART_STATE_READY)
 while( UartHandle.gState != HAL_UART_STATE_READY)

总结

在使用 Cube 软件包例程时,最好能够对各驱动接口函数有一定认识。例如上述问题,对于例程来说,没有问题。但是转移到应用时,就需要考虑到例程中调用的判断函数是否符合应用目的了。

而对于各驱动接口函数,在函数定义处,都给出了介绍,包括各参数说明。同时,在 Cube 软件包的Drivers 目录下,提供了对驱动接口函数介绍的文档。

来源:ST

围观 589

前言:Cube 软件包的提供,极大的降低了开发难度。使用者在开发的过程中,只需参考 Cube 包中提供的例
程就能快速的实现对应功能开发。开发者为了快速开发 UART 功能,参考 Cube 包中的 UART 例程,
并根据应用情况,扩展了另一组 UART 接口。但是在应用过程中,发现两路 UART 不能共存。本文分
析了这种情况出现的原因。

背景介绍

在 Cube 软件包中 CDC 例程实现了虚拟串口通信功能,数据传输链路如下图所示。

STM32 MCU - 增加 UART 接口应用时的异常分析

在开发者的应用中,需要实现下图数据传输链路。根据应用需求,推荐参考 Cube 软件包中的 USB
CDC 例程。

STM32 MCU - 增加 UART 接口应用时的异常分析

问题描述

问题复现平台: STM324xG-Eval
例程路径:
STM32Cube_FW_F4_V1.15.0\Projects\STM324xG_EVAL\Applications\USB_Device\CDC_Standalon
e
在对 CDC 例程了解后,参考已有的 UART 应用,新增了一路 UART。对应初始化程序如下所示。其中
USARTx 为例程原有部分,而 USARTy 为新增的一路 UART 初始化部分。

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
 static DMA_HandleTypeDef hdma_tx;
 GPIO_InitTypeDef GPIO_InitStruct;

 if(huart->Instance == USARTx )
 {
 /*##-1- Enable peripherals and GPIO Clocks ####################*/
 /* Enable GPIO clock */
 USARTx_TX_GPIO_CLK_ENABLE();
 USARTx_RX_GPIO_CLK_ENABLE();
 /* Enable USARTx clock */
 USARTx_CLK_ENABLE();
 /* Enable DMAx clock */
 DMAx_CLK_ENABLE();

 /*##-2- Configure peripheral GPIO #############################*/
 /* UART TX GPIO pin configuration */
 GPIO_InitStruct.Pin = USARTx_TX_PIN;
 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 GPIO_InitStruct.Pull = GPIO_PULLUP;
 GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
 GPIO_InitStruct.Alternate = USARTx_TX_AF;

 HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);

 /* UART RX GPIO pin configuration */
 GPIO_InitStruct.Pin = USARTx_RX_PIN;
 GPIO_InitStruct.Alternate = USARTx_RX_AF;

 HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);

 /*##-3- Configure the NVIC for UART ###########################*/
 HAL_NVIC_SetPriority(USARTx_IRQn, 5, 0);
 HAL_NVIC_EnableIRQ(USARTx_IRQn);

 /*##-4- Configure the DMA streams #############################*/
 /* Configure the DMA handler for Transmission process */
 hdma_tx.Instance = USARTx_TX_DMA_STREAM;

 hdma_tx.Init.Channel = USARTx_TX_DMA_CHANNEL;
 hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
 hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
 hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
 hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
 hdma_tx.Init.Mode = DMA_NORMAL;
 hdma_tx.Init.Priority = DMA_PRIORITY_LOW;
 hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
 hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
 hdma_tx.Init.MemBurst = DMA_MBURST_INC4;
 hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;

 HAL_DMA_Init(&hdma_tx);

 /* Associate the initialized DMA handle to the UART handle */
 __HAL_LINKDMA(huart, hdmatx, hdma_tx);

 /*##-5- Configure the NVIC for DMA ############################*/
 /* NVIC configuration for DMA transfer complete interrupt (USARTx_TX) */
 HAL_NVIC_SetPriority(USARTx_DMA_TX_IRQn, 6, 0);
 HAL_NVIC_EnableIRQ(USARTx_DMA_TX_IRQn);

 }
 if(huart->Instance == USARTy )
 {
 /*##-1- Enable peripherals and GPIO Clocks ####################*/
 /* Enable GPIO clock */
 USARTy_TX_GPIO_CLK_ENABLE();
 USARTy_RX_GPIO_CLK_ENABLE();
 /* Enable USARTx clock */
 USARTy_CLK_ENABLE();
 /* Enable DMAx clock */
 DMAy_CLK_ENABLE();

 /*##-2- Configure peripheral GPIO #############################*/
 /* UART TX GPIO pin configuration */
 GPIO_InitStruct.Pin = USARTy_TX_PIN;
 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 GPIO_InitStruct.Pull = GPIO_PULLUP;
 GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
 GPIO_InitStruct.Alternate = USARTy_TX_AF;

 HAL_GPIO_Init(USARTy_TX_GPIO_PORT, &GPIO_InitStruct);

 /* UART RX GPIO pin configuration */
 GPIO_InitStruct.Pin = USARTy_RX_PIN;
 GPIO_InitStruct.Alternate = USARTy_RX_AF;

 HAL_GPIO_Init(USARTy_RX_GPIO_PORT, &GPIO_InitStruct);

 /*##-3- Configure the NVIC for UART ###########################*/
 HAL_NVIC_SetPriority(USARTy_IRQn, 7, 0);
 HAL_NVIC_EnableIRQ(USARTy_IRQn);

 /*##-4- Configure the DMA streams #############################*/

 /* Configure the DMA handler for Transmission process */
 hdma_tx.Instance = USARTy_TX_DMA_STREAM;

 hdma_tx.Init.Channel = USARTy_TX_DMA_CHANNEL;
 hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
 hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
 hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
 hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
 hdma_tx.Init.Mode = DMA_NORMAL;
 hdma_tx.Init.Priority = DMA_PRIORITY_LOW;
 hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
 hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
 hdma_tx.Init.MemBurst = DMA_MBURST_INC4;
 hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;

 HAL_DMA_Init(&hdma_tx);

 /* Associate the initialized DMA handle to the UART handle */
 __HAL_LINKDMA(huart, hdmatx, hdma_tx);

 /*##-5- Configure the NVIC for DMA ############################*/
 /* NVIC configuration for DMA transfer complete interrupt (USARTx_TX) */
 HAL_NVIC_SetPriority(USARTy_DMA_TX_IRQn, 8, 0);
 HAL_NVIC_EnableIRQ(USARTy_DMA_TX_IRQn);
 }
 …

在之前的 UART 发送和接收处,同样新增了一路 UART 的发送和接收。为了方便描述,这里不对应用
层面进行描述。而是直接在例程中,时钟配置后执行下述语句,复现问题。

#ifdef USART_X_INIT_ENABLE
 UartHandle.Instance = USARTx;
 …
 if(HAL_UART_Init(&UartHandle) != HAL_OK)
 …
 if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)UserTxBuffer, 1) != HAL_OK)
 …
#endif // USART_X_INIT_ENABLE
#ifdef USART_Y_INIT_ENABLE
 UartHandley.Instance = USARTy;
 …
 if(HAL_UART_Init(&UartHandley) != HAL_OK)
 …
 if(HAL_UART_Receive_IT(&UartHandley, (uint8_t *)UserTxBuffer, 1) != HAL_OK)
 …
#endif //USART_Y_INIT_ENABLE
while(1)
{
#ifdef USART_X_ENABLE
 HAL_UART_Transmit_DMA(&UartHandle, “UARTx Test”, 11);
#endif //USART_X_ENABLE
#ifdef USART_Y_ENABLE
 HAL_UART_Transmit_DMA(&UartHandley,”UARTy Test”, 11);
#endif // USART_Y_ENABLE
 HAL_Delay(100);
}

结果如下表。

STM32 MCU - 增加 UART 接口应用时的异常分析

问题分析

不同 UART 接口相互独立。UART IP 设计上,不同 UART 同时发送不会相互影响。初步判断应该是
UART 初始化过程中,存在冲突。
通过对结果分析,在 UARTx 和 UARTy 都进行初始化后,只执行 UARTx DMA 发送时,执行异常;只
执行 UARTy DMA 发送时,执行正常。而在初始化中,UARTx 先于 UARTy 初始化,进一步确认是初
始化时,存在冲突。并且将问题范围缩小到 HAL_UART_MspInit()函数中。
在线调试,发现在执行 UARTy 初始化时(UARTx 已经在之前初始化结束),UARTx 发送 DMA 对应
的 Instant 和 Channel 值都被改变。其中 Instant 中保存着对应 DMA 的寄存器基地址。
仔细检查 HAL_UART_MspInit()函数,发现对于 DMA 的初试化,两个 UART 共用了例程中原有的
DMA 句柄变量,为上述程序中的 static DMA_HandleTypeDef hdma_tx;
而在 HAL_UART_Transmit_DMA 函数中,会对对应的 DMA 进行配置并使能,而配置过程会依据
DMA 句柄中的参数。从而导致发送异常。
对问题的产生原因清楚后,在新增 UART 接口的时候,初始化函数 HAL_UART_MspInit()中同样需要
新增 DMA 句柄变量,并利用在新增 UART 的 DMA 初始化中。如下所示。

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
 static DMA_HandleTypeDef hdma_tx;
 static DMA_HandleTypeDef hdma_tx_y;
 …
 if(huart->Instance == USARTy )
 {
 …
 hdma_tx_y.Instance = USARTy_TX_DMA_STREAM;
 hdma_tx_y.Init.Channel = USARTy_TX_DMA_CHANNEL;
 hdma_tx_y.Init.Direction = DMA_MEMORY_TO_PERIPH;
 hdma_tx_y.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_tx_y.Init.MemInc = DMA_MINC_ENABLE;
 hdma_tx_y.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
 hdma_tx_y.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
 hdma_tx_y.Init.Mode = DMA_NORMAL;
 hdma_tx_y.Init.Priority = DMA_PRIORITY_LOW;
 hdma_tx_y.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
 hdma_tx_y.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
 hdma_tx_y.Init.MemBurst = DMA_MBURST_INC4;
 hdma_tx_y.Init.PeriphBurst = DMA_PBURST_INC4;
 HAL_DMA_Init(&hdma_tx_y);
 /* Associate the initialized DMA handle to the UART handle */
 __HAL_LINKDMA(huart, hdmatx, hdma_tx_y);
…
}

总结

所描述的问题,可以归结为一个开发漏洞。而导致这个开发漏洞的原因,更多的可能是在参考 Cube 例
程时,对于各函数的了解以及变量的使用情况,没有逐个了解。建议在基于 Cube 例程进行开发时,能
够对各函数及变量等加以了解,在针对应用进行修改过程中,能够减小错误率,从而减少排错时间。

围观 629

页面

订阅 RSS - UART