【STM32】【串口通信】【学习笔记】

基本知识

串口通讯 (Serial Communication)
串口中断其实也是依靠判断寄存器的状态进行中断服务的

通信的速率

Bitrate—比特率:每秒钟传输的二进制位数,单位为比特每秒(bit/s) 
Baudrate—波特率:表示每秒钟传输的码元个数 
一个二进制位表示一个码元 0V       ——      0  
两个二进制位表示一个码元

TTL电平转USB电平

处理器与外部设备通信的两种方式

![image.png](https://img-blog.csdnimg.cn/img_convert/9879a6a68487adf6f484c69449c33699.png#align=left&display=inline&height=337&margin=[object Object]&name=image.png&originHeight=674&originWidth=813&size=306771&status=done&style=none&width=406.5)

并行通信

  • 传输原理:数据各个位同时传输。
  • 优点:速度快
  • 缺点:占用引脚资源多

串行通信

  • 传输原理:数据按位顺序传输。
  • 优点:占用引脚资源少
  • 缺点:速度相对较慢

![image.png](https://img-blog.csdnimg.cn/img_convert/628c2219469bb0df87c3d2eac1010e6d.png#align=left&display=inline&height=101&margin=[object Object]&name=image.png&originHeight=202&originWidth=1094&size=41026&status=done&style=none&width=547)

按照数据传送方向
  • 单工:数据传输只支持数据在一个方向上传输
  • 半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信
  • 全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力

![image.png](https://img-blog.csdnimg.cn/img_convert/792e1f274fcd13b5fd585306b2440c82.png#align=left&display=inline&height=250&margin=[object Object]&name=image.png&originHeight=499&originWidth=616&size=116084&status=done&style=none&width=308)

串行通信的通信方式
  • 同步通信:带时钟同步信号传输。SPI,IIC通信接口

  • 异步通信:不带时钟同步信号。UART(通用异步收发器),单总线 
    | 通信标准 | 引脚说明 | 通信方式 | 通信方向 |
    | — | — | — | — |
    |   UART
    (通用异步收发器) | TXD:发送端
    RXD:接受端
    GND:公共地 | 异步通信 | 全双工 |
    |   单总线
    (1-wire) | DQ:发送/接受端 | 异步通信 | 半双工 |
    | SPI | SCK:同步时钟
    MISO:主机输入,从机输出
    MOSI:主机输出,从机输入 | 同步通信 | 全双工 |
    | I2C | SCL:同步时钟
    SDA:数据输入/输出端 | 同步通信 | 半双工 |

  • UART:通用异步收发器 USART:通用同步异步收发器

  • RXD:数据输入引脚。数据接受。 TXD:数据发送引脚。数据发送。

![image.png](https://img-blog.csdnimg.cn/img_convert/9ba658c3df19da2671bd1278a115cb18.png#align=left&display=inline&height=119&margin=[object Object]&name=image.png&originHeight=237&originWidth=590&size=36281&status=done&style=none&width=295)

串口号RXDTXD
1PA10PA9
2PA3PA2
3PB11PB10
4PC11PC10
5PD2PC12

![image.png](https://img-blog.csdnimg.cn/img_convert/4c8a7b2f19bd00c408ea439dcd52e826.png#align=left&display=inline&height=86&margin=[object Object]&name=image.png&originHeight=171&originWidth=555&size=86944&status=done&style=none&width=277.5)
![image.png](https://img-blog.csdnimg.cn/img_convert/f0e0079863d73f58850f8bdf86c4f966.png#align=left&display=inline&height=164&margin=[object Object]&name=image.png&originHeight=328&originWidth=686&size=102771&status=done&style=none&width=343)

物理层

RS-232 串口标准常用于计算机、路由与调制调解器(MODEN,俗称“猫”) 之 间的通讯,在这种通讯系统中,设备被分为数据终端设备 DTE(计算机、路由) 和数据通讯设备 DCE(调制调解器)
![image.png](https://img-blog.csdnimg.cn/img_convert/68ba0822569ef0525ce3d063e6a840f4.png#align=left&display=inline&height=77&margin=[object Object]&name=image.png&originHeight=153&originWidth=663&size=69493&status=done&style=none&width=331.5)
![image.png](https://img-blog.csdnimg.cn/img_convert/b3ce358836ceeb2d2b125c9ecd53dd28.png#align=left&display=inline&height=123&margin=[object Object]&name=image.png&originHeight=246&originWidth=425&size=24068&status=done&style=none&width=212.5)

  • 公头与母头
  • 使用逻辑 1 表示信号有效,逻辑 0 表示信号无效
  • 一般只使用 RXD、TXD 以及 GND 三条信号线,直 接传输数据信号

![image.png](https://img-blog.csdnimg.cn/img_convert/0d3c2483c66fb9eb57769dde05a642f0.png#align=left&display=inline&height=54&margin=[object Object]&name=image.png&originHeight=107&originWidth=667&size=26511&status=done&style=none&width=333.5)
![image.png](https://img-blog.csdnimg.cn/img_convert/ed9b78c7cb28b782b71d1710a54ddd6d.png#align=left&display=inline&height=117&margin=[object Object]&name=image.png&originHeight=234&originWidth=442&size=63502&status=done&style=none&width=221)

协议层

![image.png](https://img-blog.csdnimg.cn/img_convert/89eeb806619b0dbc5016661e2bfdbcd3.png#align=left&display=inline&height=56&margin=[object Object]&name=image.png&originHeight=112&originWidth=649&size=53210&status=done&style=none&width=324.5)
异步通讯中由于没有时钟信号 (如前面讲解的DB9接口中是 没有时钟信号的),所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码。

在stm32中需要定义的参数

  1. 起始位
  2. 数据位(8位或者9位)
  3. 奇偶校验位(第9位)
  4. 停止位(1,15,2位)
  5. 波特率设置

![image.png](https://img-blog.csdnimg.cn/img_convert/9f4917800acb6570bc1c4b183a47f56e.png#align=left&display=inline&height=88&margin=[object Object]&name=image.png&originHeight=175&originWidth=1012&size=29539&status=done&style=none&width=506)
虚线分开的每一格就是代表一个码元。
波特率
通讯的起始和停止信号
有效数据
数据校验:奇校验 (odd)、偶校验 (even)、0 校验 (space)、1 校验 (mark) 以及无校验 (noparity)。

![image.png](https://img-blog.csdnimg.cn/img_convert/c9c5b1a39245cb26dec9d9c9b07dc1fe.png#align=left&display=inline&height=343&margin=[object Object]&name=image.png&originHeight=686&originWidth=680&size=172203&status=done&style=none&width=340)
![image.png](https://img-blog.csdnimg.cn/img_convert/a0532cd12690d3b0f2f947debb1b6c7f.png#align=left&display=inline&height=227&margin=[object Object]&name=image.png&originHeight=453&originWidth=552&size=66480&status=done&style=none&width=276)


5.3 节对串口有过简单的介绍
在 4.4.1 章节端口复用功能已经讲解过
8.1.11 外设的 GPIO 配置
当然要初始化 NVIC 设置中 断优先级别,最后编写中断服务函数

串口初始化程序

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

USART_SR状态寄存器
USART_DR数据寄存器
USART_BRR波特率寄存器

波特率的计算
![image.png](https://img-blog.csdnimg.cn/img_convert/e4ab64c77472ebc8ae883d57c8f15a61.png#align=left&display=inline&height=139&margin=[object Object]&name=image.png&originHeight=277&originWidth=908&size=110979&status=done&style=none&width=454)
![image.png](https://img-blog.csdnimg.cn/img_convert/9b442fecafe2dec9b5cded413b4ea79f.png#align=left&display=inline&height=142&margin=[object Object]&name=image.png&originHeight=283&originWidth=927&size=101376&status=done&style=none&width=463.5)
![image.png](https://img-blog.csdnimg.cn/img_convert/03a19760ca635681adee94a95e334c89.png#align=left&display=inline&height=166&margin=[object Object]&name=image.png&originHeight=332&originWidth=593&size=206292&status=done&style=none&width=296.5)

实现效果

1.STM32 通过串口和上位机的对话, STM32 在收到上位机发过来的字符串后,原原本本的返回给上位机。
2.电脑给单片机发命令,用于控制开发板上的灯。

相应的接口

代码理解

1.我都不知的正点原子是写了个啥玩野,还是去看看程序讲解视频吧
2.正点原子的是真**看不懂,好像有搞到寄存器中去了

main.c

main函数中没有什么,就是调用串口函数就好了

#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"

 int main(void)
 {	
  /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
  USART_Config();
  while(1)
	{	
		
	}	
 }


stm32f10x_it.c

配置串口中断服务函数,就是把串口中断服务函数加入其中了

#include "stm32f10x_it.h" 
#include "usart.h"
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
  uint8_t ucTemp;
	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
	{		
		ucTemp = USART_ReceiveData(DEBUG_USARTx);
    USART_SendData(DEBUG_USARTx,ucTemp);    
	}	 
}
 
void NMI_Handler(void)
{
}
 
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}
 
void MemManage_Handler(void)
{
  /* Go to infinite loop when Memory Manage exception occurs */
  while (1)
  {
  }
}

 
void BusFault_Handler(void)
{
  /* Go to infinite loop when Bus Fault exception occurs */
  while (1)
  {
  }
}
 
void UsageFault_Handler(void)
{
  /* Go to infinite loop when Usage Fault exception occurs */
  while (1)
  {
  }
}
 
void SVC_Handler(void)
{
}
 
void DebugMon_Handler(void)
{
}
 
void PendSV_Handler(void)
{
}
 
void SysTick_Handler(void)
{
}

usar.h

基本的配置函数,一个发送数据,然后一个打印到上位机中,重点是重定向

#include "sys.h"
#include "usart.h"	  

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	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(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

/*****************  发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
  uint8_t i;
	
	for(i=0; i<num; i++)
  {
	    /* 发送一个字节数据到USART */
	    Usart_SendByte(pUSARTx,array[i]);	
  
  }
	/* 等待发送完成 */
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	/* 取出高八位 */
	temp_h = (ch&0XFF00)>>8;
	/* 取出低八位 */
	temp_l = ch&0XFF;
	
	/* 发送高八位 */
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	/* 发送低八位 */
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
}




代码实践

出现的问题|经验之谈

1-如何发送16位的数据?
2-如何发送一个数组?
3-如何使用printf()?如何使用scanf()?
4-电脑端发送过来的数据是什么格式的?十进制?十六进制?字符? 
5.在野火的代码中体会到了宏定义的好处了,方便移植和应用
6.中断服务函数不用声明

总结

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值