stm32学习笔记- USART

一、通信。

通信就是指微处理机与外设交换数据的过程。

1.1 通信基本方式:

方式一:并行通信(多车道,多窗口)。

数据的各数位同时传输。

方式二:串行通信(单车道,单窗口)。

数据按位一位位的传输。

串行通信种类:

种类一:单工。

只允许一方发给另一方。
在这里插入图片描述

种类二:半双工。(对讲机)

双方可以相互通信,但是不能同时发送、同时接收。
在这里插入图片描述

种类三:全双工。

双方可以同时相互通信。
在这里插入图片描述

1.2 串行通信的标准:

在这里插入图片描述

1.3 同步通信与异步通信。

同步通信需要相同频率的时钟,逐字符发送接收,发一个收一个,收一个发一个,不能有间隙。
异步通信可以任意间隙,接受端随时准备,发送端任意时刻发送 ,需要加停止位和开始位。
UART异步通信 全双工 2线 RX TX
I2C 同步通信 半双工 2线 SDA SCL
SPI 同步通信 全双工 3线或四线 MOSI MISO SCLK SS(片选)

二、UART 异步通信

2.1 异步通信协议:

异步通信协议需要定义5个方面的内容:
1、起始位。
2、数据位。
3、奇偶校验位。
4、停止位。
5、波特率(通信速度,因为通信双方没有时钟)。
在这里插入图片描述

2.2 异步通信应用场合:

  1. 芯片间的近距离通信。
    在这里插入图片描述

  2. 与PC机的通信。
    在这里插入图片描述

  3. 模块之间的远距离通信。
    在这里插入图片描述
    用较高的电压差表示逻辑“0”、“1”

2.3 stm32f10x USART框图、引脚

在这里插入图片描述
在这里插入图片描述

2.4 stm32f10x波特率的计算

设波特率位9600,根据公式
在这里插入图片描述
9600 = 7210001000/(16*U)
分频值U = 72 * 1000 * 1000/(9600 * 16) = 468.75
DIV_Mantissa = 468 = 0x1d4;
DIV_Fraction = 16 * 0.75 = 12 = 0x0C

2.5 相关寄存器(了解)

  • USART_SR状态寄存器
    在这里插入图片描述
    位7 TXE:发送数据寄存器空 (Transmit data register empty)
    当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。
  • USART_DR数据寄存器
    在这里插入图片描述
    在这里插入图片描述
  • USART_BRR波特率寄存器
    在这里插入图片描述
    波特率的计算方法由此得出
  • USART_CR1 控制寄存器
    在这里插入图片描述
    在这里插入图片描述
    主要用来使能

2.6 USART库函数:

在这里插入图片描述
USART_init配置串口:
在这里插入图片描述
在这里插入图片描述
USART_Cmd使能串口:
在这里插入图片描述
USART_GetFlagStatus判断标志位:
在这里插入图片描述
RESET 和 SET 的值:
在这里插入图片描述

三、stm32串口编程

3.1 串口配置一般步骤

  1. 使能时钟
    RCC_APB2PeriphClockCmd();
  2. 串口复位(不是必须的)
    USART_DeInit();
  3. GPIO口初始化
    GPIO_Init(GPIOx,&gpiost),GPIO模式设置为GIPO_Mode_AF_PP
  4. 串口初始化
    USART_Init(USARTx,&usartst);
  5. 串口 开启
    USART_Cmd(USART1,ENABLE);
  6. 中断初始化
    NVIC_Init(&nvicst);
  7. 中断服务函数
    USARTx_IRQHandler()
  8. 接收,发送数据函数

3.2 串口发送

串口发送使用到来printf(),重定向。重定向链接usart1-printf()重定向

仿真代码

晶振调为8Mhz
在这里插入图片描述
usart1.h

#ifndef USART1_H
#define USART1_H
#include "stm32f10x.h"
#include <stdio.h>

void usart1_init(u32 baundRate);
void usart1_it_init(void);
#endif

usart1.c 寄存器代码

#include "usart1.h"

/*****************************************************************************************
* Function Name : fputc
* Descrption    : 重定向这个C库(stdio) printf函数 文件流->串口USART1
* Input					: ch ,*f
* Output				: None
* Return				: None 
*****************************************************************************************/
int fputc(int ch,FILE *f)
{
	//将ch送给USART1
	USART_SendData(USART1,ch);
	//等待发送完毕
 	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	//返回ch
	return ch;
}

void usart1_gpio_init()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
  
	//USART1_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
   
  //USART1_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  
}

void usart1_init(u32 baundRate)
{	
	usart1_gpio_init();
	USART_InitTypeDef usartst;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	usartst.USART_BaudRate = baundRate;
	usartst.USART_WordLength = USART_WordLength_9b;
	usartst.USART_StopBits = USART_StopBits_1;
	usartst.USART_Parity = USART_Parity_No;
	usartst.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	usartst.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
	USART_Init(USART1,&usartst);
	USART_Cmd(USART1,ENABLE);
}

main.c 库函数代码

#include "stm32f10x.h"
#include "bsp_systick.h"
#include "led.h"
#include "exti.h"
#include "bitBandTool.h"
#include "usart1.h"


void Stm32_Clock_Init(void)
{
	/*----------使用外部RC晶振----------*/
	RCC_DeInit() ;
	//初始化为缺省值
	RCC_HSEConfig(RCC_HSE_ON); //使 能外部的高速时钟
	while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); //等 待外部高速时钟使能就绪
	
	//Flash 2 wait state
	RCC_HCLKConfig (RCC_SYSCLK_Div1) ;
	//HCLK = SYSCLK
	RCC_PCLK2Config(RCC_HCLK_Div1) ;
	//PCLK2 =HCLK
	RCC_PCLK1Config(RCC_HCLK_Div2) ;
	//PCLK1 = HCLR/2
	RCC_PLLConfig (RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHZ * 9 =72MHZ
	RCC_PLLCmd(ENABLE) ;
	//Enable PLLCLK
	while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLLCLK is ready
	RCC_SYSCLKConfig (RCC_SYSCLKSource_PLLCLK) ;
	//Select PLL as system clock
	while (RCC_GetSYSCLKSource () !=0x08) ;
	//wait till PLL is used as system clock source

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);
}

int main(void)
{
	Stm32_Clock_Init();
	usart1_init(9600);
	printf("system start!\n");//这里可以在电脑的串口助手看到数据
}

在这里插入图片描述

3.3 串口接收

串口接收可以以轮询的方式接收,也可以以中断的方式接受。以轮询的方式接收可能会由于cpu正处理其他程序,导致接收失败。故串口接收使用中断的方式。

代码

usart1.h

#ifndef USART1_H
#define USART1_H
#include "stm32f10x.h"
#include <stdio.h>

void usart1_init(u32 baundRate);
void usart1_it_init(void);
#endif

usart1.c

#include "usart1.h"

/*****************************************************************************************
* Function Name : fputc
* Descrption    : 重定向这个C库(stdio) printf函数 文件流->串口USART1
* Input					: ch ,*f
* Output				: None
* Return				: None 
*****************************************************************************************/
int fputc(int ch,FILE *f)
{
	//将ch送给USART1
	USART_SendData(USART1,ch);
	//等待发送完毕
 	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	//返回ch
	return ch;
}

void usart1_gpio_init()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
  
	//USART1_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
   
  //USART1_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  
}

void usart1_init(u32 baundRate)
{	
	usart1_gpio_init();
	USART_InitTypeDef usartst;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	usartst.USART_BaudRate = baundRate;
	usartst.USART_WordLength = USART_WordLength_8b;
	usartst.USART_StopBits = USART_StopBits_1;
	usartst.USART_Parity = USART_Parity_No;
	usartst.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	usartst.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
	USART_Init(USART1,&usartst);
	USART_Cmd(USART1,ENABLE);
}

void usart1_it_init(void)
{
	NVIC_InitTypeDef nvicst;
	
	nvicst.NVIC_IRQChannel = USART1_IRQn;
	nvicst.NVIC_IRQChannelPreemptionPriority = 3;
	nvicst.NVIC_IRQChannelSubPriority = 1;
	nvicst.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&nvicst);	//根据指定的参数初始化VIC寄存器
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断

}

//USART1中断服务程序
void USART1_IRQHandler(void)
{
	u8 Res;
	//USART1中断服务程序只有一个,得判断是串口的什么中断
	//指令判断是否是接收中断
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		printf("%c",Res);
	}
}

main.c

#include "bitBandTool.h"
#include "usart1.h"

void Stm32_Clock_Init(void)
{
	/*----------使用外部RC晶振----------*/
	RCC_DeInit() ;
	//初始化为缺省值
	RCC_HSEConfig(RCC_HSE_ON); //使 能外部的高速时钟
	while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); //等 待外部高速时钟使能就绪
	
	//Flash 2 wait state
	RCC_HCLKConfig (RCC_SYSCLK_Div1) ;
	//HCLK = SYSCLK
	RCC_PCLK2Config(RCC_HCLK_Div1) ;
	//PCLK2 =HCLK
	RCC_PCLK1Config(RCC_HCLK_Div2) ;
	//PCLK1 = HCLR/2
	RCC_PLLConfig (RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHZ * 9 =72MHZ
	RCC_PLLCmd(ENABLE) ;
	//Enable PLLCLK
	while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLLCLK is ready
	RCC_SYSCLKConfig (RCC_SYSCLKSource_PLLCLK) ;
	//Select PLL as system clock
	while (RCC_GetSYSCLKSource () !=0x08) ;
	//wait till PLL is used as system clock source

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);
}

int main(void)
{
	Stm32_Clock_Init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组
	usart1_init(9600);
	usart1_it_init();
	printf("system start!\n");
	
	while(1)
	{
		
	}

}

调试结果:
在这里插入图片描述

3.4、串口批量输出

串口接收中断批量接收数据中断服务程序:

//首先在usart.h中事先定义全局变量,USART1_RX_STA、USART1_RX_BUF
#define USART1_REC_LEN 200

extern u16 USART1_RX_STA;
extern u8 USART1_RX_BUF[USART1_REC_LEN];
//在usart.c中初始化全局变量,并且编写中断服务程序
u16 USART1_RX_STA = 0;
u8 USART1_RX_BUF[USART1_REC_LEN];

//USART1中断服务程序
void USART1_IRQHandler(void)
{
	u8 Res;
	//USART1中断服务程序只有一个,得判断是串口的什么中断
	//指令判断是否是接收中断
	if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
	{
		Res = USART_ReceiveData(USART1);
		
		//接收数据,判断数据
		//末尾数据是0x0d,回车表示数据接收完毕,STA高位置1表示接受完,
		//回车后会有换行,接到换行 0x0a,初始化数组指针
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		if((USART1_RX_STA&0x8000) == 0)
		{
			//判断接收了\r后,是否接收到了\n
			if(USART1_RX_STA&0x4000)
			{
				//接受到了\n ,接结束结束,置位结束接收标志
				if(Res == 0x0a)		
					USART1_RX_STA |= 0x8000;
				//没有接收到\n,表示数据错误
				else							
				{
					USART1_RX_STA = 0;//接收标志全清除
					memset(USART1_RX_BUF,0x00,USART1_REC_LEN);//接收buf清0
				}
			}
			//没有接收到\n
			else
			{
				//判断是否是回车的第一个字符 \n,是的话置位,否则接收数据入buf
				if(Res == 0x0d) 	
					USART1_RX_STA |= 0x4000;
				else
				{
					USART1_RX_BUF[USART1_RX_STA&0x3fff] = Res;
					USART1_RX_STA++;
					//接收数据过大
					if(USART1_RX_STA > (USART1_REC_LEN-1))
					{
						USART1_RX_STA = 0;//数据清0
						memset(USART1_RX_BUF,0x00,USART1_REC_LEN);//接收buf清0
					}	
				}
			}
		}
	}
}

USART1_IRQHandler解析:

  1. 利用一个变量USART1_RX_STA来表示串口接收数据的情况。
  2. 我们以回车作为数据结束符,回车符就是 \r\n 。串口接收到 \r\n 就表示接收数据完毕。
  3. USART1_RX_STA次高位置1表示接收到了\r,这时我们要判断下一个字符是否时 \n 。
  4. USART1_RX_STA的最高位置1表示接收到了\n,即数据接收完毕。
  5. USART1_RX_STA的除最高位、最低位 的 低位 表示的是接收数据的长度。

主函数:

int main(void)
{
	u8 i;
	Stm32_Clock_Init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	usart1_init(9600);
	usart1_it_init();
	printf("system start!\n");
	
	while(1)
	{
		if(USART1_RX_STA&0x8000)
		{
			for(i=0;i<(USART1_RX_STA&0x3fff);i++)
				printf("%c",USART1_RX_BUF[i]);
			USART1_RX_STA = 0;
		}			
	}

}

四、利用虚拟串口对软件仿真USART进行调试。

由于MDK 的UART没有输入功能,不方便调试,我们使虚拟串口软件VSPD来虚拟串口,实现MDK的仿真调试的串口输入。

  1. 首先下载虚拟串口软件VSPD

  2. 建立虚拟串口对
    在这里插入图片描述

  3. 在MDK中j进入仿真调试,在command输入如下指令:
    mode com2 9600,0,8,1
    assign com2 <s1in> s1out
    在这里插入图片描述
    可以看到com2已经配置到mdk
    在这里插入图片描述

  4. 打开串口助手。某人使用的是 野人4.04 版本的串口助手。
    可以看到虚拟串口已经被串口助手占用。在这里插入图片描述
    在这里插入图片描述

  5. 现在就可以全速运行仿真,使用串口调试啦!在这里插入图片描述
    程序是接收到什么数据(以回车为结束符的数据),发送相应的数据回去。
    在这里插入图片描述

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值