文章目录
- 一、串口协议和RS-232标准,以及RS232电平与TTL电平的区别
- 1.串口通信协议
- 2.RS-232标准
- 3.RS232电平与TTL电平的区别
- 4.USB/TTL转232“模块(CH340芯片为例)
- 二、补充实验
- (一)几个常见的库函数、结构体
- 1.时钟配置函数
- 2.GPIO口的初始化类型定义结构体
- 3.GPIO口的类型定义结构体
- 4.GPIO口的初始化函数
- (二)实验
- 1.标准库实现LED灯闪烁
- (1)代码
- (2)Proteus仿真
- (3)实验现象
- 2.标准库实现LED流水灯
- (1)代码
- (2)Proteus仿真
- (3)实验现象
- 3.用STM32发送数据,并用串口助手接收
- (1)代码
- (2)串口助手接收
- 4.串口查询方式点亮LED灯
- (1)代码
- (2)实验现象
- 三、keil波形
- 1.配置合适的Xtal(External Crystal Oscillator)外部晶振
- 2.输入正确的芯片参数
- 3.进入波形仿真界面
- 4.仿真波形
- 四、心得体会
一、串口协议和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显示屏…