老师虽然讲了几次,自己翻书看了几次,但总是感觉懵懵懂懂,不能理解的透彻,今天我就把自己所想写下来,方便以后查看,也为一些开发者提供方便。
UART这个单词的意思是:通用异步收发传输器,是一种异步收发传输器,可以异步收取和发送信号的传输器件。
UART的结构
今天我就通过STM32单片机的UART进行介绍。上面说可以发送可以接收,所以UART最简单的情况就是三根线,数据发送(Tx)、数据接收(Rx)、地线(GND)[提供通信双方的参考电平],
如下图所示
电平转换器的作用就是完成通信双方之间的电平转换,STM32F103输出的电平是CMOS电平(VCC=3.3V),而对于CMOS电平主要是用高低电平来表示逻辑状态:输入电压:VIL〈0.3*VCC,VIH〉0.7*VCC;输出电压:VOL〈0.1*VCC,VOH〉0.9*VCC。而PC机上使用的RS232最早是一种用在公用电话网的串行通信标准,传输距离不超过15M,而RS232电平主要是用正负电压来表示逻辑状态:逻辑0:+3V~+15V;逻辑1:-3V~-15V。所以两者在通信的时候需要使用电平转换器对电路之间进行电平和逻辑关系的变换。
UART的特性
STM32的UART模块基本特性是:
1、全双工的(全双工就是数据通信允许数据同时在两个方向上传输,通信双方都能在同一时刻进行发送和接收操作,好处在于迟延小,速度快,举个简单例子,就是双车道马路。半双工就是数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,举个简单例子,一条窄窄的马路,同时只能有一辆车通过,当目前有两辆车对开,这种情况下就只能一辆先过,等到头儿后另一辆再开。);异步通信;
2、UART的发送和接收共用可编程波特率;
3、可编程数据字长度,可配置停止位支持1或者2个停止位;
4、单独的发送器和接收器使能位。
UART的固件库
学习STM32,是因为他有强大的固件库,所以可以简化学习没必要的引脚描述和相关的寄存器。
使用UART的时候,一般情况下需要注意的是:收发引脚的配置、串口通信波特率的配置函数、收发的函数等,使用中断的时候,需要关注收发中断标志函数、清除相应的中断标志位等。
下面通过一款STM32的UART进行程序的编写:
复用功能 |
USART1_REMAP=0(不使用复用I/O口) |
USART1_REMAP=1(使用复用I/O口) |
USART1_TX |
PA9 |
PB6 |
USART1_RX |
PA10 |
PB7 |
管脚采用不复用I/O口,配置如下:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //USART1 TX
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; //USART1 RX
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//复用开漏输入
GPIO_Init(GPIOA,&GPIO_InitStructure);
而对于管脚的模式配置方式可能有一些疑问,一般猜哦那个如下表的模式配置:
USART引脚 |
配置 |
GPIO配置 |
USART_TX |
全双工模式 |
推挽复用输出 |
半双工同步模式 |
推挽复用输出 |
|
USART_RX |
全双工模式 |
浮空输入或带上拉输入 |
半双工同步模式 |
做通用的IO口 |
|
USART_CK |
同步模式 |
推挽复用输出 |
USART_RTS |
硬件流量控制 |
推挽复用输出 |
USART_CTS |
硬件流量控制 |
浮空输入或带上拉输入 |
然后配置UART,波特率115200,无校验位,8位数据位,1位停止位,无硬件流控
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate =115200;//波特率115200
USART_InitStructure.USART_WordLength =USART_WordLength_8b;//8位数据位
USART_InitStructure.USART_StopBits =USART_StopBits_1; //1位停止位
USART_InitStructure.USART_Parity =USART_Parity_No; //无校验位
USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None;//无硬件流控
USART_InitStructure.USART_Mode =USART_Mode_Tx;
USART_Init(USART1,&USART_InitStructure);
实验程序;功能就是发送两个字节给PC机,PC机通过串口调试助手显示接收到的字符(可能读者会疑问单片机又没有键盘,怎么会发送字符,这就是在PC机安装的Keil中写好程序,把字符写到程序里,然后下载到开发板上,然后打开串口调试助手,配置好串口,运行单片机,然后就会在串口助手显示提前写好的字符)
#include"stm32f10x.h"
#include"stm32f10x_usart.h"
void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART_Config(void);
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
USART_Config();
USART_ClearFlag(USART1,USART_FLAG_TC); //数据发送前需要调用USART_ClearFlag函数清除USART_FLAG_TC标志,否则容易造成第一个数据发送不出去
USART_SendData(USART1,'A');
while(USART_GetFlagStatus(USART,USART_FLAG_TC)==RESET); //发送数据前需要调用USART_GetFlagStatus,以确定前面的数据已经发送完毕
USART_SendData(USART1,'B');
while(USART_GetFlagStatus(USART,USART_FLAG_TC)==RESET);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //USART1 TX
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; //USART1 RX
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//复用开漏输入
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
void RCC_Configuration(void)
{
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
}
void USART_Config(void)
{
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;//波特率115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无校验位
USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;//无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1,ENABLE);
}
上面只是简单的介绍领略USART的魅力所在。
下面将介绍UART的高级实验,就是采用printf()进行输出,在C语言中,使用printf()进行格式化输出非常方便
在C语言中printf()函数为:int printf(const char*format,…)
那么如果在UART中写一个类似于printf()的函数,那么输出会更加方便。
需要注意的是:在使用可变参数函数时会用到C库里面的宏,需要包含头文件stdarg.h.
下面就逐步编写程序:
1、 编写发送一个字节的函数,代码如下:
voidUart_SendByte(int data)
{
if(data=='\n')
{
while(USART_GetFlagStatus(USART,USART_FLAG_TC)==RESET);
USART_SendData(USART1,'\r')
}
while(USART_GetFlagStatus(USART,USART_FLAG_TC)==RESET);
USART_SendData(USART1,'data')
}
2、 编写发送字符串的函数,代码如下:
void Uart_SendString(char *pt)
{
Char *dst=pt;
while(dst { Uart_SendByte(*dst++); } { 3、 实现可变参数函数,代码如下: voidUart_Printf(const char*dmt...) { va_list ap; //定义一个指向可变参数列表指针 char string[50]; va_start(ap,fmt);//使列表指针ap指向函数参数列表中的第一个可变参数,fmt为最后一个固定参数 vsprintf(string,fmt,ap);//将ap按格式fmt写入字符串string中 va_end(ap);//清空参数列表,并置参数指针ap无效,结束可变参数的获取 Uart_SendString(string); } Main.c文件如下: #include”stm32f10x.h” #include”stm32f10x_usart.h” #include { void RCC_Configuration(void); void GPIO_Configuration(void); void USART_Config(void); void Uart_SendByte(int data); void Uart_SendString(char*pt); void Uart_Printf(const char*dmt...); int main(void) { RCC_Configuration(); GPIO_Configuration(); USART_Config(); USART_ClearFlag(USART1,USART_FLAG_TC); //数据发送前需要调用USART_ClearFlag函数清除USART_FLAG_TC标志,否则容易造成第一个数据发送不出去 Uart_Printf(“佛系90s的博客欢迎你\n”); Uart_Printf(“网站:%s\n”,”hahahahahaha”); Uart_Printf(“a=%d\n”,10); } void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //USART1 TX GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出 GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; //USART1 RX GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//复用开漏输入 GPIO_Init(GPIOA,&GPIO_InitStructure); } void RCC_Configuration(void) { SystemInit(); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); } void USART_Config(void) { USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 115200;//波特率115200 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位 USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位 USART_InitStructure.USART_Parity = USART_Parity_No; //无校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控 USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART1,&USART_InitStructure); USART_Cmd(USART1,ENABLE); } void Uart_SendByte(int data) { if(data=='\n') { while(USART_GetFlagStatus(USART,USART_FLAG_TC)==RESET); USART_SendData(USART1,'\r') } while(USART_GetFlagStatus(USART,USART_FLAG_TC)==RESET); USART_SendData(USART1,'data') } void Uart_SendString(char *pt) { Char *dst=pt; while(dst { Uart_SendByte(*dst++); } { void Uart_Printf(const char*dmt...) { va_list ap; //定义一个指向可变参数列表指针 char string[50]; va_start(ap,fmt);//使列表指针ap指向函数参数列表中的第一个可变参数,fmt为最后一个固定参数 vsprintf(string,fmt,ap);//将ap按格式fmt写入字符串string中 va_end(ap);//清空参数列表,并置参数指针ap无效,结束可变参数的获取 Uart_SendString(string); } }