一、串口协议
串口协议是一种计算机通信协议,用于在串行通信过程中规定数据的传输方式和格式。与并行通信不同,串行通信是按位序列进行数据传输的,意味着数据是通过单个线路,按顺序一个接一个地传输的。串口协议在计算机与外部设备或计算机之间的通信中起着至关重要的作用。
1.1、常用串口协议
在串口通信中,常用的协议包括:
1)RS-232:最传统的串行通信协议之一,用于计算机和外部设备之间的通信。它定义了信号的电气特性和信号线的功能。
2)RS-485:支持多点通信,即多个设备可以连接到同一条通信线上。它适用于较长距离的数据传输,并且比RS-232更加鲁棒。
3)RS-422:与RS-485类似,但提供更高的数据传输速率,通常用于较高速的串行通信。
4)USB(通用串行总线):虽然USB是一种并行接口,但它内部实际上使用串行通信来传输数据。USB协议定义了主机和设备之间的数据传输方式,是现代计算机中最常见的接口之一。
5)I2C(集成电路总线):用于连接低速外围设备的串行通信协议,通常用于在 PCB 上连接多个芯片。
6)SPI(串行外设接口):高速的、全双工、同步的通信协议,常用于短距离通信,如微控制器和外围设备之间的通信。
7)1-Wire:由Dallas Semiconductor公司开发,允许通过单个线路进行数据传输和供电。
1.2、串口协议的几个关键参数
1)波特率 : 波特率是串口通信中的一个重要参数,它表示每秒钟传输的符号数。波特率决定了数据传输的速度,通信双方必须使用相同的波特率才能正确地传输数据。
2)数据位: 数据位是指每个数据包中包含的有效数据位数。最常见的数据位设置是7位或8位。数据位的长度决定了每次传输可以发送的信息量。
3)停止位: 停止位是数据包的结束标志,用于指示一个数据包的传输结束。停止位的长度可以是1位、1.5位或2位。
4)校验位: 校验位是一种错误检测机制,用于检测数据在传输过程中是否发生错误。校验位可以是偶校验、奇校验或者无校验。在偶校验和奇校验的情况下,数据包中1的数量会被计算,以确保总共有偶数个或奇数个1。
5)起始位: 起始位是数据包的开始标志,用于指示一个新数据包的开始。起始位总是设置为低电平(0),当接收设备检测到电平从高到低的跳变时,它知道一个新的数据包即将开始传输。
这些参数需要通信双方事先约定好,并且确保在数据传输过程中保持一致,以保证数据的正确传输。串口协议的具体内容可能会根据所使用的协议标准(RS-232、RS-485等)有所不同。
1.3、RS-232标准(数据终端设备和数据通讯设备之间串行二进制数据交换接口技术标准)
RS-232 是常用的串行通信接口标准之一。规定采用一个25个脚的DB-25连接器,对连接器的每个引脚的信号内容加以规定,还对各种信号的电平加以规定。后来IBM的PC机将RS232简化成了DB-9连接器,工业控制的RS-232口一般只使用RXD、TXD、GND三条线。
RS232-标准的主要特点:
1)信号线少
RS-232总线规定了25条线,包含了两个信号通道,第一通道(称为主通道)和第二通道(称为副通道)。利用RS- 232总线可以实现全双工通信,通常使用的是主通道,而副通道使用较少。在一般应用中,使用3条~9条信号线就可以实现全双工通信,采用三条信号线(接收线、发送线和信号线)能实现简单的全双工通信过程。
2)波特率选择灵活
RS-232规定的标准传送速率有50b/s、75b/s、110b/s、150b/s、300b/s、600b/s、1200b/s、2400b/s、4800b/s、9600b/s、19200b/s,可以灵活地适应不同速率的设备。对于慢速外设,可以选择较低的传送速率:反之,可以选择较高的传送速率。
3)采用负逻辑传送
规定逻辑“1”的电平为-3V~-15 V,逻辑“0”的电平为+3 V~+15 V。选用该电气标准的目的在于提高抗干扰能力,增大通信距离。RS -232的噪声容限为2V,接收器将能识别高至+3V的信号作为逻辑“0”,将低到-3 V的信号作为逻辑“1”。
但是,RS-232接口的信号电平值较高,易损坏接口电路的芯片,又因为与TTL电平不兼容,所以需要使用电平转换电路才能与TTL电路连接。
在上面的通讯方式中,两个通讯设备的"DB9接口"之间通过串口信号线建立起连接,串口信号线中使用"RS232标准"传输数据信号。由于RS232电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个"电平转换芯片"转换成控制器能识别的"TTL校准"的电平信号,才能实现通讯。
1.4、RS-232电平与TTL电平的区别
(一)、TTL电平标准
逻辑1的电平为2.4v-5V,逻辑0的电平为0-0.5V。
(二)、RS232标准
逻辑1的电平为-3~-15V,逻辑0的电平为+3~+15V,电平的定义反相了一次。
1.5、USB/TTL转232"模块(以CH340芯片模块为例)的工作原理
- 功能:实现TTL或RS-232与USB之间的转换。
- 转换方式:内部电路将USB数据转换为串口数据或反之。
- 控制器:内部包含芯片控制器,负责数据转换的协调工作。
1.5.1 模块特点
CH340C USB转TTL模块以CH340C芯片为核心,内部自带晶振,最高波特率可达2Mbps,软件兼容CH341驱动,过流保护,引出相应的通讯接口与电源接口,通讯接口带有指示灯指示工作状态,通讯稳定,体积小。
模块接口引脚
Symbol(符号) | Type(类型) | Deion(描述) |
TXD | 输出 | 串行数据输出口 |
RXD | 输入串行数据 | 输入口 |
GND | 电源 | 接地引脚 |
3V3 | 电源 | 3.3V电源输出引脚(最高250mA) |
5V | 电源 | 5V电源输出引脚(最高500mA) |
DTS | 输出 | MODEM联络输出信号,请求发送 |
DTR | 输出 | MODEM联络输出信号,数据终端就绪 |
输入输出接口引脚均带有LED指示灯
电源接口引脚中,5V的接口引脚带有LED指示灯
1.5.2 硬件设计
硬件电路设计主要介绍以CH340C芯片为核心,设计出一个USB转TTL的模块(也就是该模块)。主要包括电源的设计、功能的设计等,其中选用的器件规格型号可以参考产品手册的BOM表。
1.5.3 模块的USB转TTL电路设计
在CH340C的引脚功能表中蓝色部分是信号相关的引脚,黑色部分的与设计无关的引脚,全部悬空。CH340C芯片的D-,D+与USB的D-,D+连接到一起作为USB电平的信号连接,同时引出TTL电平信号的接口TXD与RXD,还有两个MODEM输出信号接口RTS与DTR。
二、STM32基于标准库点亮LED灯
2.1、安装STM32CubeMX
1、下载地址:https://www.st.com/en/development-tools/stm32cubemx.html
2.2、标准库点亮LED灯
2.2.1 CubeMX创建项目
1、点击File->project
2、选择芯片STM32F103C8
3、SYS选择
4、RCC选择
4、端口输出设置
创建完成
2.2.2 main.c代码实现
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
//使用各个外设前必须开启时钟,否则对外设的操作无效
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO引脚,赋值为第0号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数
//函数内部会自动根据结构体的参数配置相应寄存器
//实现GPIOA的初始化
/*主循环,循环体内的代码会一直循环执行*/
while (1)
{
/*设置PA0引脚的高低电平,实现LED闪烁*/
/GPIO_ResetBits设置低电平,GPIO_SetBits设置高电平*/
GPIO_ResetBits(GPIOA, GPIO_Pin_0); //将PA0引脚设置为低电平
Delay_ms(500); //延时500ms
GPIO_SetBits(GPIOA, GPIO_Pin_0); //将PA0引脚设置为高电平
Delay_ms(500); //延时500ms
}
}
2.2.3 烧录运行
2.3、STM32系统给上位机(win10)连续发送“hello windows!”
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Serial_Init(); //串口初始化
while (1)
{
Serial_SendString("hello world!"); //串口发送字符串
Delay_ms(500);
}
}
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的时钟
/*GPIO初始化*/
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); //将PA9引脚初始化为复用推挽输出
/*USART初始化*/
USART_InitTypeDef USART_InitStructure; //定义结构体变量
USART_InitStructure.USART_BaudRate = 9600; //波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制,不需要
USART_InitStructure.USART_Mode = USART_Mode_Tx; //模式,选择为发送模式
USART_InitStructure.USART_Parity = USART_Parity_No; //奇偶校验,不需要
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位,选择1位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长,选择8位
USART_Init(USART1, &USART_InitStructure); //将结构体变量交给USART_Init,配置USART1
/*USART使能*/
USART_Cmd(USART1, ENABLE); //使能USART1,串口开始运行
}
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte); //将字节数据写入数据寄存器,写入后USART自动生成时序波形
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送完成
/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++) //遍历数组
{
Serial_SendByte(Array[i]); //依次调用Serial_SendByte发送每个字节数据
}
}
void Serial_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止
{
Serial_SendByte(String[i]); //依次调用Serial_SendByte发送每个字节数据
}
}
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1; //设置结果初值为1
while (Y --) //执行Y次
{
Result *= X; //将X累乘到结果
}
return Result;
}
2_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i ++) //根据数字长度遍历数字的每一位
{
Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0'); //依次调用Serial_SendByte发送每位数字
}
}
/**
* 函 数:使用printf需要重定向的底层函数
* 参 数:保持原始格式即可,无需变动
* 返 回 值:保持原始格式即可,无需变动
*/
int fputc(int ch, FILE *f)
{
Serial_SendByte(ch); //将printf的底层重定向到自己的发送字节函数
return ch;
}
void Serial_Printf(char *format, ...)
{
char String[100]; //定义字符数组
va_list arg; //定义可变参数列表数据类型的变量arg
va_start(arg, format); //从format开始,接收参数列表到arg变量
vsprintf(String, format, arg); //使用vsprintf打印格式化字符串和参数列表到字符数组中
va_end(arg); //结束变量arg
Serial_SendString(String); //串口发送字符数组(字符串)
}
运行结果
三、总结
本次实验我们了解了串口协议相关知识,学习了STM32标准库开发的基本方法,并利用标准库方式实现了LED灯的点亮,以及STM32系统给上位机(win10)连续发送“hello windows!”。初学嵌入式开发,文章多有不足欢迎大家批评指正。
参考文献:
STM32基于HAL库流水灯实验_hel库安装教程中文版-CSDN博客
https://blog.csdn.net/qq_58804208/article/details/127457311?spm=1001.2014.3001.5502一. 了解串口协议和RS-232标准,以及RS232电平与TTL电平的区别;了解USBTTL转232模块(以CH340芯片模块为例)的工作原理。_usb的串口协议规范-CSDN博客