1、串口是什么
串口呢,作为硬件调试的时候,是很有用的,我觉得搞嵌入式是经常和串口打交道的
串口(Serial Port)是一种用于计算机和外部设备之间进行数据通信的接口。它通过串行通信方式传输数据,即一次只传输一个比特(位)。串口通信在早期的计算机系统中非常常见,用于连接键盘、鼠标、调制解调器、打印机等外部设备。
串口的主要特点包括:
- 简单性:串口通信协议相对简单,易于实现。
- 低成本:串口通信的硬件成本较低,适合一些低成本的应用。
- 广泛应用:虽然现代计算机更多地使用USB等高速接口,但串口在工业控制、嵌入式系统和调试设备中仍然广泛应用。
常见的串口类型包括 RS-232、RS-485 和 RS-422。RS-232 是最常见的串口标准,广泛用于个人计算机和各种嵌入式系统中。
总之,串口是一种重要的通信接口,尽管在现代计算机中不再那么常见,但在许多专业和工业应用中仍然非常有用。
2、串口的时序
在很多时候,我们经常忘记串口作为一种通信协议,是有自己的时序的,不过在使用串口的过程中,时序的特点以及需求,已经被波特率替代了,所以我们了解串口,不需要去了解他的时序,了解波特率就可以了
串口通信确实有时序(timing),这是因为串口通信是基于时间的。在串口通信中,数据以一定的速率(波特率,单位为比特每秒,bps)发送和接收。波特率决定了每秒钟传输的比特数。
以下是串口通信中的一些关键时序参数:
- 波特率(Baud Rate):决定了数据传输的速度。常见的波特率包括 9600 bps、19200 bps、115200 bps 等。
- 数据位(Data Bits):通常是 8 位,表示每个字符的长度。
- 停止位(Stop Bits):通常是 1 位或 2 位,用于标识一个字符的结束。
- 奇偶校验位(Parity Bit):用于检测传输错误,可以是无校验(None)、奇校验(Odd)或偶校验(Even)。
在串口通信中,这些参数需要在发送方和接收方之间一致,才能正确地进行数据传输。如果这些参数不匹配,可能会导致数据传输错误或通信失败。
总之,串口通信有明确的时序要求,这些时序参数确保数据在发送和接收端之间能够正确同步和解析。
3、自己生成串口时序
波特率: 9600 bps
数据位: 8 位
停止位: 1 位
奇偶校验位: Odd
时序示意图:
- Start Bit(开始位):高电平,表示新的数据传输开始(1 bit)
- Data Bits(数据位):8 个比特,表示要传输的数据(8 bits)
- 高电平表示 1,低电平表示 0
- Parity Bit(奇偶校验位):1 个比特,用于检测传输错误(1 bit)
- 高电平表示奇校验,低电平表示偶校验
- Stop Bit(停止位):1 个比特,表示数据传输结束(1 bit)
- 高电平表示停止位
时序示例:
假设要传输的数据为 0x12(00010010),波特率为 9600 bps。
- Start Bit:高电平(1 bit)
- Data Bits:
- 00010010(8 bits)
- 高电平(1):0
- 低电平(0):0
- 高电平(1):0
- 低电平(0):1
- 高电平(1):0
- 低电平(0):0
- 高电平(1):1
- 低电平(0):0
- 00010010(8 bits)
- Parity Bit:高电平(1 bit)
- 奇校验,因为数据中有奇数个 1
- Stop Bit:高电平(1 bit)
总共传输 11 个比特(1 + 8 + 1 + 1),每秒钟传输 9600 个比特。
所以呢,数据位很简单啦~
奇偶校验位虽然比较复杂,但是呢,一般不用,所以
奇偶校验位(Parity Bit)是串口传输中的一种错误检测机制,它用于检测传输数据中的错误。奇偶校验位是一种简单的错误检测方法,它通过在数据中添加一个额外的比特来检测数据是否包含错误。
奇偶校验位的计算过程如下:
- 计算数据中 1 的个数(Odd Count):将要传输的数据中的 1 个数计数。
- 如果 Odd Count 是奇数,那么添加一个 1 作为奇校验位(Odd Parity Bit)。
- 如果 Odd Count 是偶数,那么添加一个 0 作为偶校验位(Even Parity Bit)。
奇偶校验位的作用是:
- 检测数据中的错误:如果数据中包含错误,奇偶校验位将不同于预期的值。
- 检测数据的完整性:如果数据中缺少或添加了比特,奇偶校验位将不同于预期的值。
奇偶校验位的类型有两种:
- 奇校验(Odd Parity):如果数据中 1 的个数是奇数,那么添加一个 1 作为奇校验位。
- 偶校验(Even Parity):如果数据中 1 的个数是偶数,那么添加一个 0 作为偶校验位。
奇偶校验位的优缺点:
优点:
- 简单易行,计算成本低
- 可以检测简单的错误,例如数据中缺少或添加了比特
缺点:
- 不可靠,无法检测复杂的错误,例如数据中的某些比特被损坏
- 可能会产生误报,例如数据中包含多个错误时,奇偶校验位可能不同于预期的值
总的来说,奇偶校验位是一种简单的错误检测方法,但它并不是 foolproof 的解决方案。在实际应用中,通常会使用其他错误检测和纠正方法来确保数据的可靠传输。
4、STM32——配置
各位,请把目光集中,重点来了
其实呢串口到现在已经发展出了很多不同的通信协议,咱们主要还是以stm32的为主
首先,串口本身可以是全双工、半双工或单工,取决于具体的硬件实现和通信协议。
串口的数据线一般为两条一条TX输出一条RX接受
如图~串口的特点呢就是一方的RX————TX另一方的
电源呢随意,能通电就行,注意共地就ok
4.1、通信协议——TTL
串口作为一种通信协议,又有着不同的标准,stm32一般使用TTL协议
5VTTL电平标准
2.4V~5V:逻辑1。
0~0.4V: 逻辑0。
TTL信号的抗干扰能力较差,所以其通信距离很短,一般只能用于一个电路板上的两个不同芯片之间的通信。
4.2、stm32的寄存器
重点呢还是这张图;
其实呢仔细一看,发送和接受的都有着同样的配置,唯一的区别呢就是接收多了一个唤醒单元
4.3、配置流程(寄存器)
- 通过在USART_CR1寄存器上置位UE位来激活USART
- 编程USART_CR1的M位来定义字长
- 在USART_CR2中编程停止位的位长
- 如果采用多缓冲器通信,配置USART_CR3中的DMA使能位(DMAT)。按多缓冲器通信中的描述配置DMA寄存器,关于DMA下期再详细讲解。
- 利用USART_BRR寄存器选择要求的波特率。
- 设置USART_CR1中的TE位,
发送一个空闲帧帧(一个数据帧长度的高电平)
作为第一次数据发送 - 把要发送的数据写进USART_DR寄存器(此动作清除TXE位)。在只有一个缓冲器的情况下,对每个待发送的数据重复步骤7。
在USART_DR寄存器中写入最后一个数据字后,要等待TC=1,它表示最后一个数据帧的传输结束(移位寄存器中的数据全部发送完毕)
。当需要关闭USART或需要进入停机模式之前,需要确认传输结束,避免破坏最后一次传输。
4.4、标准库源码
usart.h
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h" // Device header
void Usart1_Init(int baud);
void Usart1_SendByte(u8 ch);
void Usart1_SendStr(u8* str, u32 len);
#endif
usart.c
#include "stm32f10x.h" // Device header
#include "usart.h"
#include "stdio.h"
#define USART1_IDLE_ON
//启动文件 startup_stm32f10x_md.s 中
//中断处理函数
//RXNE标志,每次收到一个字节时触发中断
//接收数据
//解析数据
//执行指令
u8 g_U1_RecvBuf[32] = "";
u8 g_U1_RecvFlag = 0;//接收完成标志 告诉main
#ifndef USART1_IDLE_ON
//定长数据 4个字节
void USART1_IRQHandler(void)
{
static int index = 0;
if(SET == USART_GetITStatus(USART1, USART_IT_RXNE))//检测中断源
{
if(0 == g_U1_RecvFlag){
g_U1_RecvBuf[index] = USART_ReceiveData(USART1);
index++;
if(index >= 4){
g_U1_RecvFlag = 1;
index = 0;
}
}else{
USART_ReceiveData(USART1);
}
}
}
#else
//不定长
//1.规定接收字符 \r\n AT指令
//2.使用空闲中断 IDLE -- 上一帧数据后,一帧时间内没有收到新的一帧,则视作空闲事件
//3.超时检测 收到一帧后,定时器定时10ms,接收接收完成,触发定时器中断视作接收完成,收到新数据,清空计数器
void USART1_IRQHandler(void)
{
static int index = 0;
if(SET == USART_GetITStatus(USART1, USART_IT_RXNE))//检测中断源
{
if(0 == g_U1_RecvFlag){
g_U1_RecvBuf[index] = USART_ReceiveData(USART1);
index++;
}else{
USART_ReceiveData(USART1);
}
}
if(SET == USART_GetITStatus(USART1, USART_IT_IDLE))//空闲中断
{
g_U1_RecvFlag = 1;
index = 0;
//清除中断标志位
USART1->SR;
USART1->DR;
}
}
#endif
//给printf进行重定向,fputc
int fputc(int ch, FILE* file)
{
Usart1_SendByte(ch);
return ch;
}
//USART1
//PA9 -- TX
//PA10 -- RX
void Usart1_Init(int baud)
{
//打开时钟 GPIOA,USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
//PA9 -- 复用推挽输出
GPIO_InitTypeDef gpio_struct;
gpio_struct.GPIO_Mode = GPIO_Mode_AF_PP;
gpio_struct.GPIO_Pin = GPIO_Pin_9;
gpio_struct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_struct);
//PA10 -- 浮空输入
gpio_struct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpio_struct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &gpio_struct);
//USART1 的初始化
USART_InitTypeDef usart_struct;
usart_struct.USART_BaudRate = baud; //波特率
usart_struct.USART_WordLength = USART_WordLength_8b; //8位数据位长度
usart_struct.USART_Parity = USART_Parity_No; //无校验
usart_struct.USART_StopBits = USART_StopBits_1; //1位停止位
usart_struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
usart_struct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收,发送使能
USART_Init(USART1, &usart_struct);
//USART使能中断
//接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
#ifdef USART1_IDLE_ON
//空闲中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
#endif
//NVIC设置USART对应的中断通道
NVIC_InitTypeDef nvic_struct;
nvic_struct.NVIC_IRQChannel = USART1_IRQn; //中断通道 再头文件stm32f10x.h 200多行
nvic_struct.NVIC_IRQChannelCmd = ENABLE; //通道使能
nvic_struct.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
nvic_struct.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&nvic_struct);
//USART 使能
USART_Cmd(USART1, ENABLE);
}
void Usart1_SendByte(u8 ch)
{
USART_SendData(USART1, ch);//发送
//等待发送
//TXE -- 发送数据寄存器空标志位 -- 数据被发送移位寄存器取走就会 置一
while(SET != USART_GetFlagStatus(USART1, USART_FLAG_TXE));
}
void Usart1_SendStr(u8* str, u32 len)
{
while(len--)
{
Usart1_SendByte(*str);
str++;
}
}