STM32串口通信入门

一、串口协议和RS-232标准,以及RS232电平与TTL电平的区别

1.串口通信协议

是一种数据传输协议,常用于计算机和其他设备之间的通信。
它定义了数据传输的规则,如数据格式、传输速率等。

2.RS-232标准

是一种标准的串口通信协议。
定义了数据传输的电压范围、信号线功能、连接方式和传输速率等
RS-232 标准主要规定了信号的用途,通讯接口以及信号的电平标准。

由于RS-232电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL校准”的电平信号,才能实现通讯。

3.RS232电平与TTL电平的区别

根据通讯使用的电平标准不同,串口通讯可分为 TTL标准和 RS-232标准。

在这里插入图片描述

从表中可以看出,逻辑电压不同,在电子电路中常使用 TTL 的电平标准,理想状态下,使用 5V 表示二进制逻辑 1,使用 0V 表示逻辑 0;而为了增加串口通讯的远距离传输及抗干扰能力,它使用-15V表示逻辑 1,+15V 表示逻辑 0。

4.USB/TTL转232“模块(CH340芯片为例)

USB主机检测到USB转串口设备插入后,首先会对设备复位,然后开始USB枚举过程。USB枚举时过程会获取设备描述符、配置描述符、接口描述符等。描述符中会包含USB设备的厂商ID,设备ID和Class类别等信息。操作系统会根据该信息为设备匹配相应的USB设备驱动。
USB虚拟串口的实现在系统上依赖于USB转串口驱动,一般由厂家直接提供,也可以使用操作系统自带的CDC类串口驱动等。驱动主要分为2个功能,其一注册USB设备驱动,完成对USB设备的控制与数据通讯,其二注册串口驱动,为串口应用层提供相应的实现方法。

二、补充实验

在上一次寄存器方式点亮LED灯的基础上,改用标准库方式,完成LED的点灯或流水灯实验。

(一)几个常见的库函数、结构体

1.时钟配置函数

APB2外设时钟控制命令函数
定义
在这里插入图片描述

通俗易懂地解释一下参数的含义:
①uint32_t RCC_APB2Periph:要求参数应当是一个无符号的32位整形数,然而在实际调用过程中,它传入的参数是经过“位掩码”技术处理过的RCC_APB2Periph_GPIOA、等外设名称。说直白点就是RCC_APB2Periph_GPIOA代码的就是一个无符号的32位整形数。这样调用起来会更加方便。
②FunctionalState NewState:参数类型名为函数状态,只有两个值可供传入。ENABLE-时钟开启,DISABLE-时钟关闭。

2.GPIO口的初始化类型定义结构体

GPIO口的初始化类型定义结构体定义
定义
在这里插入图片描述
解释一下成员属性
①uint16_t GPIO_Pin:应该也是采用了位掩码的方式,赋的值可以直接传入引脚号码如:GPIO_Pin_3,而不是整形数据。
②GPIOSpeed_TypeDef GPIO_Speed:赋的值如:GPIO_Speed_50MHz,在GPIO_Speed后面加一个下划线和具体的数值。(这部分传入的参数或赋的值都是以这种形式。在实际操作时,可以先列出他的属性,GPIO_InitStructure.GPIO_Speed=,再复制粘贴GPIO_Speed,再按ctrl+alt+空格,进行操作提示。)
③GPIOMode_TypeDef GPIO_Mode:赋的值如GPIO_Mode_Out_PP。
即类型定义结构体的三板斧是引脚号码、速度和输入输出模式。

3.GPIO口的类型定义结构体

定义
在这里插入图片描述

4.GPIO口的初始化函数

①定义
在这里插入图片描述
②参数实例

在这里插入图片描述

定义太复杂了,在这里简单解释一下:就是将初始化结构体的地址和结构体地址综合起来。GPIOx参数指定了目标GPIO端口的引脚名称(是GPIOA,GPIOB还是GPIOC…),而GPIO_InitStruct则提供了是它已初始好的三个成员属性。
即第一个GPIO口的类型定义结构体参数传入的是A、B、C…的哪种类型的引脚,而第二个GPIO口的初始化类型定义结构体参数提供的是它的已配置好的成员属性。
待会GPIO_WriteBit(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin, BitAction B)等函数调用的时候两者都要用。

(二)实验

1.标准库实现LED灯闪烁

(1)代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"

int main()
	
{	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启外设GPIOA的时钟
	
	GPIO_InitTypeDef GPIO_InitStructure;   				//调用库函数的中封装好的具体引脚结构体
	
	//分别给结构体的三个成员属性赋初值
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; 		//配置端口为推挽输出模式
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;             //启用0号端口
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;     //端口输出速度为50MHz
	
	GPIO_Init(GPIOA, &GPIO_InitStructure);    //GPIOA的0号口配置完成,准备调试它的高低电平。

	
	while(1)
	{
		GPIO_ResetBits(GPIOA,GPIO_Pin_0);   //设置A0端口为低电平
		Delay_s(1);
		GPIO_SetBits(GPIOA,GPIO_Pin_0);		//设置A0端口为高电平
		Delay_s(1);
	}
}

(2)Proteus仿真

注意:
在这里插入图片描述
得将外时钟频率设置为70Mhz,时钟比例设置为8倍。

在这里插入图片描述

(3)实验现象

2024_05_09_001

2.标准库实现LED流水灯

(1)代码

在这里插入图片描述

(2)Proteus仿真

在这里插入图片描述

(3)实验现象

20240509_002

3.用STM32发送数据,并用串口助手接收

(1)代码

①main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Serial.h"

int main()
{	
			Serial_Init();	
			char String[]="hello windons!\r\n";
	
	while(1)
	{
			
			Serial_SendString(String);
			Delay_s(1);              						  						//间隔1s发送
		
	}
}

②Serial.c文件

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>


void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);	//打开USART1外设的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//因为要用到GPIOA引脚,所以打开GPIOA的时钟


	GPIO_InitTypeDef GPIO_InitStructure;  					//用自带的通用输出口结构体,实例化了一个结构体变量

	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;  			//数据发送,选择复用推挽输出模式
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

	GPIO_Init(GPIOA,&GPIO_InitStructure);					//配置好引脚输入输出模式


	USART_InitTypeDef USART_InitStructure;

	//引出该结构体的所有属性,进行赋值。
	USART_InitStructure.USART_BaudRate=9600;          									//波特率
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;         					//不需要校验选择8bit的字长,如果需要一位奇偶校验位,则选择9bit的字长
	USART_InitStructure.USART_StopBits=USART_StopBits_1;         						//选择一位停止位。
	USART_InitStructure.USART_Parity=USART_Parity_No;                      				//奇偶性:奇偶校验,选择不校验。odd是奇校验,even是偶校验。
	USART_InitStructure.USART_Mode=USART_Mode_Tx;           							//选择发送模式 
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; 		//硬件流控制:选择无流控制


	USART_Init(USART1,&USART_InitStructure);					//配置好USART1外设的模式

	USART_Cmd(USART1,ENABLE);									//使能USART1,串口开始运行。第一个参数是要操作的USART外设,第二个参数是指定是否使能该外设
}



void Serial_SendByte(uint8_t Byte)     //int8_t代表字符型
{
	USART_SendData(USART1,Byte);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET)  //USART_GetFlagStatus(USART1,USART_FLAG_TXE)这个函数的返回值先简单地理解为:数据已经发送了,返回SET;数据还没有发送,返回RESET。
	{
		;
	}
}


void Serial_SendString(char *String)				//发送一个字符数组
{

	uint8_t i;
	for(i=0;String[i]!='\0';i++)
	{
		Serial_SendByte(String[i]);
	}
}

(2)串口助手接收

在这里插入图片描述

4.串口查询方式点亮LED灯

(1)代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
#include <string.h>

uint8_t Rdata;


int main()
{
	LED_Init();			//LED初始化
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//因为要用到GPIOA引脚,所以打开GPIOA的时钟
	GPIO_InitTypeDef GPIO_InitStructure;  					//用自带的通用输出口结构体,实例化了一个结构体变量

	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;  			//数据发送,选择复用推挽输出模式
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

	GPIO_Init(GPIOA,&GPIO_InitStructure);					//配置好引脚输入输出模式

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);	//打开USART1外设的时钟
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;  			//数据接收,选择上拉输入模式
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

	GPIO_Init (GPIOA,&GPIO_InitStructure);					//配置好引脚输入输出模式
	
	

	USART_InitTypeDef USART_InitStructure;

	//引出该结构体的所有属性,进行赋值。
	USART_InitStructure.USART_BaudRate=9600;          																		//波特率
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;         									//不需要校验选择8bit的字长,如果需要一位奇偶校验位,则选择9bit的字长
	USART_InitStructure.USART_StopBits=USART_StopBits_1;         													//选择一位停止位。
	USART_InitStructure.USART_Parity=USART_Parity_No;                      											//奇偶性:奇偶校验,选择不校验。odd是奇校验,even是偶校验。
	USART_InitStructure.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;           							//选择发送和接收模式 
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; 		//硬件流控制:选择无流控制


	USART_Init(USART1,&USART_InitStructure);					//配置好USART1外设的模式
	USART_Cmd (USART1,ENABLE);									//使能USART1,串口开始运行。第一个参数是要操作的USART外设,第二个参数是指定是否使能该外设
	

	while (1)
	{
		while (	USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
		Rdata = USART_ReceiveData(USART1);
		if (Rdata == '1')
		{
			LED1_ON();
		}
		if (Rdata == '0')
		{
			LED1_OFF();
		}
		while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//USART_GetFlagStatus(USART1,USART_FLAG_TXE)这个函数的返回值先简单地理解为:数据已经发送了,返回SET;数据还没有发送,返回RESET。
		
	}
}

(2)实验现象

20240512_02

三、keil波形

1.配置合适的Xtal(External Crystal Oscillator)外部晶振

在这里插入图片描述

2.输入正确的芯片参数

在这里插入图片描述

3.进入波形仿真界面

在这里插入图片描述

这里第四步输入对应的波形源要重点强调。
在这里我配置的是PA1和PA9引脚,那么创建波形源的时候就分别输入:PORTA.1和PORTA.9即可。如果你按上述输入了,报错。

在这里插入图片描述
那么说明你步骤2的四个参数,没有配置正确,请按步骤2的要求重新配置。

4.仿真波形

在这里插入图片描述
在我实验(二)3.用STM32发送数据,并用串口助手接收代码中,PA9口的波形应该是起伏变化的,PA1时不变的。在我实验(二)4.串口查询方式点亮LED灯中,PA1应该是起伏变化的。但是我的两个实验的波形都如上图所示,我也想了许多办法去改进,但都败不旋踵。我估计应该是晶振的问题,我也改了晶振的值但仍得不到波形。这确实很奇怪,因为我的实验现象是正确的,波形仿真却有问题。还望大佬能够不吝赐教,进行指证。

四、心得体会

串口通信是单片机学习十分必不可少的一环,它关系着单片机之间的数据能否顺利的发送和接收,实现机器与机器的数据交换与数据共享。
在本节使用STM32F103C8T6的串口通信时,我主要遇到了两个问题。
问题1:我犯了一个致命的错误就是在连接USART1的引脚时,将发送数据引脚P9连接到了转串口模块的TXD上,将接收数据P10连接到了RXD上。即单片机发送端连接到了电脑发送端,单片机接收端连接到了电脑接收端。导致我浪费了一个下午的时间,去怀疑并验证STM芯片是不是坏了、转串口模块是否坏了、引脚是否坏了…这对于初学者来说确实是一个极其容易犯错的步骤。
问题2:我的实验现象是正确的,但是得不到波形,波形一直保持低电平不变。这一点希望阅读本文章的大佬能够指正一下。
总的来说,串口通信的学习,和前面点亮小灯泡的实验具有很多相似之处,都要进行时钟的配置,初始化结构体的实例化,并用初始化结构体进行输入输出模式的配置。这在代码中的体现是十分直观的。但是,串口模块的学习难度,还是要大一点,因为它涉及的是双向数据的传输,还要合理展示,并且它和很多STM32学习模块密切相关,例如:中断、OLED显示屏…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值