stm32之串口通信


前言

很多时候,我们在编写程序时不可避免的要调试代码,或者输出一些调试信息,但设计硬件的调试不像纯软件一样,直接在黑窗口上就可以看到想要输出的调试信息。我们通过串口将硬件与上位机连接起来,就可输出一些调试信息,用电脑USB口接收十分方便,串口调试软件很多,接下来我们一起来体会一下串口通讯的特点。

一、什么是串口?

串口通讯 (Serial Communication) 是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式,电子工程师在调试设备时也经常使用该通讯方式输出调试信息。

串口通讯的数据由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。

  • 串口通信协议

在串口通讯的协议中,规定了数据包的内容,它由启始位、数据位、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据。

在这里插入图片描述

起始位:
在通信线上没有数据传送时处于逻辑“1”状态。当发送设备发送一个字符数据时,首先发出一个逻辑“0”信号,这个逻辑低电平就是起始位(下降沿)。起始位通过通信线传向接收设备,当接收设备检测到这个逻辑低电平后,就开始准备接收数据信号。因此,起始位所起的作用就是表示字符传送开始。

数据位:
数据位紧跟在起始位之后,是通信中的真正有效信息,即要传输的主体数据内容。数据位的位数可以由通信双方共同约定,一般可以是5位、7位或8位。

数据校验位:
在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输 数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验 (odd)、偶校验 (even)、 0 校验 (space)、 1校验 (mark) 以及无校验 (noparity)。

停止位:
停止位可以是是1位、1.5位或2位,可以由软件设定。它一定是“1”,标志着传输一个字符的结束。

二、使用串口给上位机发送“hello windows!”

  • 新建工程
    这部分在之前的实验中以及涉及到,就不再赘述了。

详情可参考下面博客,新建一个工程

keil下C与汇编语言混合编程

(一)串口初始化

配置步骤

  • 初始化串口所用到的GPIO引脚
  • 配置串口的工作模式
  • 重定义Printf函数
  • 向上位机(电脑)发送信息
  • GPIO初始化结构体介绍
 typedef struct
{
  uint16_t GPIO_Pin;             /*!选择需要配置的引脚*/

  GPIOSpeed_TypeDef GPIO_Speed;  /*配置所选引脚的输出速度 */

  GPIOMode_TypeDef GPIO_Mode;    /*!配置所选引脚的工作模式*/
}GPIO_InitTypeDef;

查看数据手册后,我们发现对应需要配置的GPIO引脚分别位PA9、PA10,我们等会儿需要配置的就是这两个引脚

在这里插入图片描述

  • USART初始化结构体介绍
 typedef struct
{
  uint32_t USART_BaudRate;            /*!选择串口通信的速度   波特率*/

  uint16_t USART_WordLength;          /*!选择数据位的长度 */

  uint16_t USART_StopBits;            /*!选择停止位的长度 */

  uint16_t USART_Parity;              /*!选择是否进行校验及校验方式 */
 
  uint16_t USART_Mode;                /*!配置串口工作模式 */

  uint16_t USART_HardwareFlowControl; /*!选择硬件流控制及其控制方式 */
} USART_InitTypeDef;
  • 重定向printf函数
    使用printf、 scanf等 C 语言标准函数库输入输出函数,我们要勾选下图中的use MicroLIB

在这里插入图片描述

  • 定义重定向函数
///重定向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);
}

(二)代码如下

  • usart.c文件
#include "usart.h"       //包含对应头文件

static void Usart_GPIO_Config(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA端口的时钟
	
	GPIO_InitTypeDef GPIO_InitStruct;                     //GPIO初始化结构体
	
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;            //将PA9配置为复用推挽输出
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;					//输出速度配置为50MHz
	
	GPIO_Init(GPIOA, &GPIO_InitStruct);                   //GPIO初始化函数,这里初始化的是PA9
	
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10; 
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;      //将PA10配置为模拟输入
	
	GPIO_Init(GPIOA, &GPIO_InitStruct);                    //GPIO初始化函数,这里初始化的是PA10
}


static void Usart_Mode_Config(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);   //开启UART1外设的时钟
	
	 USART_InitTypeDef   USART_InitStruct;                   //UART1初始化结构体
	
	USART_InitStruct.USART_BaudRate=115200;                   //设置波特率为115200
	USART_InitStruct.USART_WordLength=USART_WordLength_8b;    //数据位长度为8位
	USART_InitStruct.USART_StopBits=USART_StopBits_1;         //停止位长度为1位
	USART_InitStruct.USART_Parity=USART_Parity_No ;             //无校验位
	USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx ;   //开启串口发送、接收功能
	USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;  //关闭硬件控制流,我们这里采用软件控制
	
	USART_Init(USART1, &USART_InitStruct);                       //USART初始化函数
	
	 USART_Cmd(USART1, ENABLE);                                  //使能串口1,使其工作
}

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

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

		return (int)USART_ReceiveData(USART1);
}
void Usart_Init(void)     //初始化函数
{
	Usart_GPIO_Config();    //初始化GPIO
	Usart_Mode_Config();     //初始化USART工作模式
}
  • usart.h文件
#ifndef _USART_H    //条件编译,避免头文件被多次编译
#define _USART_H
#include <stdio.h>
#include "stm32f10x.h"  //包含标准库文件,方便调用
void Usart_Init(void);
int fputc(int ch, FILE *f);  //用户自定义函数声明
int fgetc(FILE *f);
#endif

-main.c文件

#include  "stm32f10x.h"
#include "delay.h"
#include "usart.h"
int main(void)
{			  
	Usart_Init();                 //串口初始化
	delay_init();                 //延时初始化
	while(1)
	{
		printf("Hello windows\n"); //输出测试信息
		delay_ms(1000);            //延时1s
	}
	
}

注意: while(1)循环里一定要有延时或者执行其他操作,不然串口发送速率过快,会出现问题。

此外,我在循环函数中只实现了串口的循环发送,除此之外并没有实现其他功能的函数,这种方式效率不高,后面我们使用串口查询方式控制LED亮、灭。

(三)串口调试

  • 下载串口调试助手
    我这里使用的是野火多功能调试助手

下载链接
提取码:u659

  • 打开串口调试助手,将串口配置好(端口号要连接到开发板才会显示),打开串口

在这里插入图片描述

  • 串口打开成功

在这里插入图片描述
我这里是每隔1s开发板发送一次数据,串口调试助手每隔1s接收数据。

三、使用串口控制LED灯

使用getchar()函数接收发送的字符,将接收的字符与switch语句中的case比较,执行符合条件的语句

  • main.c文件
#include  "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include  "usart.h"
int main(void)
{			  
	char ch;
	Usart_Init();
	LED_Init();	     //LED初始化
	delay_init();	   //延时初始化                     
	
	while(1)
	{
		 /* 获取字符指令 */
    ch=getchar();
    
    /* 根据字符指令控制彩灯颜色 */
    switch(ch)
    {
	
	  case '0':
		LED_Off();        //LED灯全灭
			break;
      case '1':
        LED_R_TOGGLE();   //红灯闪烁
            break;
      case '2':
       LED_Y_TOGGLE();    //黄灯闪烁
            break;
      case '3':
        LED_G_TOGGLE();    //绿灯闪烁
            break;
      case '4':
       LED_G_On();       //绿灯常亮
           break;
      case '5':
        LED_Y_On();     //黄灯常亮
           break;
      case '6':
       LED_R_On();      //红灯常亮
           break;
     
      default:
       LED_On();       //LED灯全亮
           break;      
	}
}	
}
  • 接线方式

在这里插入图片描述

  • 实现效果

通过串口调试助手发送指令‘7’

在这里插入图片描述
LED灯全亮

在这里插入图片描述
通过串口调试助手发送指令‘4’

在这里插入图片描述
绿灯常亮

在这里插入图片描述
关于通过串口控制LED灯,还有其他实现方式,在这里通过简单的发送特定的数据控制LED的变化,如果你有兴趣,可以试着控制更多的LED状态变化。

总结

以上是对于串口通信方式的一些理解,如有不当之处,敬请指教。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值