串口USART和UART「建议收藏」

2022-08-31 15:44:44 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

串口通信:

UART是通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接受。UART主要用于主机与辅助设备通信。

UART的功能计算器内部采用并行数据,不能直接把数据发到Modem,必须经过UART整理才能进行异步通信。也就是CPU把准备写入串行设备的数据放到UART的寄存器中,再通过FIFO(First Input First Output,先入先出队列)传到串行设备,提供了RS232数据终端设备接口。

UART的主要功能分为:

1、将计算机内部传送过来的并行数据转换为输出的串行数据流。可以将计算机外部来的串行数据转换为字节,供计算机内部并行数据的器件使用。并且可以加上奇偶校验位,启停标志位中断信号。

USART是UART的升级版,通用同步/异步串行接收/发送器USART是一个全双工通用同步/异步串行收发模块。

USART特点:

1、全双工操作(相互独立的接收数据和发送数据)

2、同步操作时,可以主机时钟同步,也可以从机时钟同步;

3、独立高精度波特率发生器,不占用定时定数器;

4、有奇偶校验位和启停位起始位、数据位;

5、数据溢出检测;

6、帧错误检测;

7、三个独立中断:TX发送完成、RX发送数据寄存器空、接收完成

8、常用串口寄存器:1、USART_SR状态寄存器 2、USART_DR数据状态寄存器 3、USART_BRR 波特率寄存器

9、波特率的计算方法:Tx/Rx波特率=f_PCLKx/(16*USARTDIV)

f_PCLKx是串口时钟,(PCLK1用于USART2345,PCLK2用于USART1)

USARTDIV是一个无符号定点数

串口的工作方式:

一般有两种方式:查询和中断。

(1)查询:串口程序不断地循环查询,看看当前有没有数据要它传送。如果有,就帮助传送(可以从PC到STM32板子,也可以从STM32板子到PC)。

(2)中断:平时串口只要打开中断即可。如果发现有一个中断来,则意味着要它帮助传输数据——它就马上进行数据的传送。同样,可以从 PC到STM3板子,也可以从STM32板子到PC。

编程的配置过程:

1、RCC配置:由于UART的TX和RX和AFIO都挂在APB2桥上,因此采用固件库函数RCC_APB2PeriphClockCmd()进行初始化。UARTx需要分情况讨论,如果是UART1,则挂在APB2桥上,因此采用RCC_APB2PeriphClockCmd()进行初始化,其余的UART2~5均挂在APB1上。

1)、串口时钟使能、GPIO时钟使能RCC_APB2PeriphClockCmd();

2)、串口复位USART_Init()

2、GPIO配置:GPIO的属性包含在结构体GPIO_InitTypeDef,其中对于TX引脚,GPIO_Mode字段设置为GPIO_Mode_AF_PP(复用推挽输出),GPIO_Speed切换速率设置为GPIO_Speed_50MHz;对于RX引脚,GPIO_Mode字段设置为GPIO_Mode_IN_FLOATING(浮空输入),不需要设置切换速率。最后通过GPIO_Init()使能IO口。

1)、GPIO端口模式设置。GPIO_Init();

3、USART配置:

STM32在只有一个中断的情况下,仍然需要配置优先级,其作用是使能某条中断的触发通道。STM32的中断有至多两个层次,分别是抢占优先级(主优先级)和子优先级(从优先级),而整个优先级设置参数的长度为4位,因此需要首先划分抢占优先级位数和子优先级位数,通过NVIC_PriorityGroupConfig()实现;

特定设备的中断优先级NVIC的属性包含在结构体NVIC_InitTypeDef中,其中字段NVIC_IRQChannel包含了设备的中断向量,保存在启动代码中;字段NVIC_IRQChannelPreemptionPriority为主优先级NVIC_IRQChannelSubPriority为从优先级,取值的范围应根据位数划分的情况而定;最后NVIC_IRQChannelCmd字段是是否使能,一般置为ENABLE。最后通过NVIC_Init()来使能这一中断向量。

1)、串口初始化 USART_Init();

代码语言:javascript复制
USART_BaudRate:波特率(每秒能传输的数据位),缺省值为9600。

USART_WordLength:字长

USART_StopBits:停止位

USART_Parity:校验方式(奇偶校验)

USART_HardwareFlowControl:硬件流控制

USART_Mode:单/双工,即收发状态

4、NVIC配置

5、开启中断并且初始化NVIC

NVIC_Init();

USART_ITConfig();//使能相关中断

6、使能串口:USART_Cmd()

7、编写中断处理函数 USARTx_IRQHander();

8、串口数据收发

void USART_SendData();//发送数据到串口

uint16_t USART_ReceiveData();//接收数据

9、串口传输状态获取

注意在对数据进行发送和接收的时候,要检查USART的状态,只有等到数据发送或接收完毕之后才能进行下一帧数据的发送或接收。采用USART_GetFlagStatus()函数

在发送数据的最开始,需要清除一下USART的标志位,否则,第1位数据会丢失。因为在硬件复位之后,USART的状态位TC是置位的。当包含有数据的一帧发送完成之后,由硬件将该位置位。只要当USART的状态位TC是置位的时候,就可以进行数据的发送。然后TC位的置零则是通过软件序列来清除的,具体的步骤是“先读USART_SR,然后写入USART_DR”,只有这样才能够清除标志位TC,但是在发送第一帧数据的时候,并没有进行读USART_SR的操作,而是直接进行写操作,因此TC标志位并没有清空,那么,当发送第一帧数据,然后用USART_GetFlagStatus()检测状态时返回的是已经发送完毕(因为TC位是置1的),所以程序会马上发送下一帧数据,那么这样,第一帧数据就被第二帧数据给覆盖了,所以看不到第一帧数据的发送。

建立模板USART.c

代码语言:javascript复制
#include "usart.h"
	  	 
//使UASRT串口可用printf函数发送
//在usart.h文件里可更换使用printf函数的串口号	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE {
	int handle; 
}; 
FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x){ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f){      
	while((USART_n->SR&0X40)==0);//循环发送,直到发送完毕   
    USART_n->DR = (u8) ch;      
	return ch;
}
#endif 
 
 
/*
USART1串口相关程序
*/
 
#if EN_USART1   //USART1使用与屏蔽选择
u8 USART1_RX_BUF[USART1_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART1_RX_STA=0;       //接收状态标记
/*
USART1专用的printf函数
当同时开启2个以上串口时,printf函数只能用于其中之一,其他串口要自创独立的printf函数
调用方法:USART1_printf("123"); //向USART2发送字符123
*/
void USART1_printf (char *fmt, ...){ 
	char buffer[USART1_REC_LEN 1];  // 数据长度
	u8 i = 0;	
	va_list arg_ptr;
	va_start(arg_ptr, fmt);  
	vsnprintf(buffer, USART1_REC_LEN 1, fmt, arg_ptr);
	while ((i < USART1_REC_LEN) && (i < strlen(buffer))){
        USART_SendData(USART1, (u8) buffer[i  ]);
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); 
	}
	va_end(arg_ptr);
}	  
void USART1_Init(u32 bound){ //串口1初始化并启动
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
     //USART1_TX   PA.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);  
    //USART1_RX	  PA.10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure); 
   //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器 
   //USART 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
    USART_Init(USART1, &USART_InitStructure); //初始化串口
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启ENABLE/关闭DISABLE中断接收到数据时中断 读寄存器DR清零,也可软件手动清零
    USART_Cmd(USART1, ENABLE);                    //使能串口 
}
 
void USART1_IRQHandler(void){ //串口1中断服务程序(固定的函数名不能修改)	
	u8 Res;
	//以下是字符串接收到USART1_RX_BUF[]的程序,(USART1_RX_STA&0x3FFF)是数据的长度(不包括回车)
	//当(USART1_RX_STA&0xC000)为真时表示数据接收完成,即超级终端里按下回车键。
	//在主函数里写判断if(USART1_RX_STA&0xC000),然后读USART1_RX_BUF[]数组,读到0x0d 0x0a即是结束。
	//注意在主函数处理完串口数据后,要将USART1_RX_STA清0
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){  //接收中断(接收到的数据必须是0x0d 0x0a结尾)		
		Res =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		printf("%c",Res); //把收到的数据以 a符号变量 发送回电脑		
		if((USART1_RX_STA&0x8000)==0){//接收未完成			
			if(USART1_RX_STA&0x4000){//接收到了0x0d				
				if(Res!=0x0a)USART1_RX_STA=0;//接收错误,重新开始
				else USART1_RX_STA|=0x8000;	//接收完成了 
			}else{ //还没收到0X0D					
				if(Res==0x0d)USART1_RX_STA|=0x4000;
				else{
					USART1_RX_BUF[USART1_RX_STA&0X3FFF]=Res ; //将收到的数据放入数组
					USART1_RX_STA  ;	//数据长度计数加1
					if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
	} 
} 
#endif

USART.h

代码语言:javascript复制
#ifndef __USART_H
#define __USART_H
#include <string.h>
#include "stdio.h"	
#include "sys.h" 
#define USART_n		USART1  //定义使用printf函数的串口,其他串口要使用USART_printf专用函数发送
 
#define USART1_REC_LEN  			200  	//定义USART1最大接收字节数
#define EN_USART1 			1		//使能(1)/禁止(0)串口1
extern u8  USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符  
extern u16 USART1_RX_STA;         		//接收状态标记	
 
//函数声明
void USART1_Init(u32 bound);//串口1初始化并启动
void USART1_printf(char* fmt,...); //串口1的专用printf函数
#endif

main.c函数

代码语言:javascript复制
#include "stm32f10x.h" //STM32头文件
#include "delay.h"
#include "usart.h"
 
 
int main (void){//主程序
	//初始化程序
	RCC_Configuration(); //时钟设置
	USART1_Init(115200); //串口初始化(参数是波特率)
	//主循环
	while(1){
		printf("hello windows "); //纯字符串发送数据到串口
 
        delay_ms(1000); //延时
	}
}

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/144386.html原文链接:https://javaforall.cn

0 人点赞