一个完整的硬件产品是由多种模块组合实现产品功能的,微控制器 MCU 充当大脑,外围的存储单元、显示单元、发声单元、传感器单元、运动单元等等是其躯干和四肢,而不同类型的硬件单元需要有机的结合起来,就离不开相互之间的数据通信,电子工业经过了百余年的发展,衍生出了繁多的协议,其中既有行业公认的标准协议,也有企业自研的内部标准,这些协议通常可以分为并行通信协议和串行通信协议。本文就介绍了基于STM32串口操作的基础内容。
串口是串行接口(serial port)的简称,也称为串行通信接口或COM接口。串口通信是指采用串行通信协议(serial communication)在一条信号线上将数据一个比特一个比特地逐位进行传输的通信模式。
STM32F1 芯片内含有非常多的通信接口,学习这些通信接口前,我 们很有必要了解下通信的基本概念。通信的方式可以分为多种,按照数据传送方 式可分为串行通信和并行通信。按照通信的数据同步方式,可分为异同通信和同 步通信。按照数据的传输方向又可分为单工、半双工和全双工通信。下面我们就 来简单介绍这几种通信方式。

串行通讯与并行通讯的特性对比:

并行可以同时发送多位数据所以速度比串行的速度要快很多,但并行要的数据线也更多相对成本会更高,而且并行传输对同步要求较高,且随着通讯速率的提高,信号干扰的问题会显著影响通讯性能。
同步通信: 发送端在发送串行数据的同时,提供一个时钟信号,并按照一定的约定(例如:在时钟信号的上升沿的时候,将数据发送出去)发送数据,接收端根据发送端提供的时钟信号,以及大家的约定,接收数据。如:I2C、SPI等有时钟信号的协议,都属于这种通信方式。

异步通信: 接收方并不知道数据什么时候会到达,收发双方可以有各自自己的时钟。不需要时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧(串口:起始位 数据 校验位(可以没有) 停止位)的格式传输数据,某些通讯中还需要双方约定数据的传输速率(波特率),以便更好地同步。

在异步通信方式中,发送方只发送数据帧,不传输时钟,发送和接收双方必须约定相同的传输率。当然双方实际工作速率不可能绝对相等,但是只要误差不超过一定的限度,就不会造成传输出错。
举个例子
同步通信:就是送快递的面对面给交你,交互完成即完在,但双方都需要在同一时间内反应,否则会造成另一方阻塞等待。
异步通信:就是送快递的放在门卫或快递箱,你自己去取,中间不是同步完成的。
单工通信:单工是指数据传输仅能沿一个方向,不能实现反向传输。
半双工通信:半双工是指数据传输可以沿两个方向,但需要分时进行。
全双工通信:全双工是指数据可以同时进行双向传输。

串口通信(Serial Communication),是指外设和计算机间通过数据信号线、 地线等按位进行传输数据的一种通信方式,属于串行通信方式。串口是一种接口 标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。
很多单片机内部例如我们所用的STM32,以及一些传感器一般都是TTL电平。
RS232是一种串行数据传输形式,称其为串行连接,最经典的标志就是 9 针孔的 DB9 电缆RS232电压表示逻辑 1 ,0的范围大极大的增强了容错率,主要用于工业设备直接通信。

由上图可知,TLL与RS-232标准逻辑相反,而且电平也大不相同,若单片机与单片机或其他设备TLL设备通信采用RS-232通信(DB9),肯定先要进行电平的转化TLL->RS232 RS232->TTL

两个通讯设备的“DB9 接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232 标准”传输数据信号。由于 RS-232 电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL 标准”的电平信号,才能实现通讯。




USB转串口:主要用于设备(STM32)与电脑通信

电平转换芯片一般有CH340、PL2303、CP2102、FT232
使用的时候电脑要按照电平转换芯片的驱动(虚拟出一个串口)我这里装的是CH340


RS232 的通信协议比较简单,通常遵循 96-N-8-1 格式。

案例:实现使用串口调试助手完成数据原样的发送和接收。
编程要点:
#ifndef __usart_H
#define __usart_H
#include "system.h"
#include "stdio.h"
void USART1_Init(u32 bound);
#endif
#include "usart.h"
int fputc(int ch,FILE *p) //函数默认的,在使用printf函数时自动调用
{
USART_SendData(USART1,(u8)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
return ch;
}
/*******************************************************************************
* 函 数 名 : USART1_Init
* 函数功能 : USART1初始化函数
* 输 入 : bound:波特率
* 输 出 : 无
*******************************************************************************/
void USART1_Init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //打开时钟
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX //串口输出PA9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入IO */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX //串口输入PA10
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
//USART1 初始化设置
USART_InitStructure.USART_BaudRate = bound;//波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_Cmd(USART1, ENABLE); //使能串口1
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}
/*******************************************************************************
* 函 数 名 : USART1_IRQHandler
* 函数功能 : USART1中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 r;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
r =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
USART_SendData(USART1,r);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
}
USART_ClearFlag(USART1,USART_FLAG_TC);
}
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
int main()
{
u8 i=0;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
LED_Init();
USART1_Init(9600);
while(1)
{
i++;
if(i%20==0)
{
led1=!led1;
}
delay_ms(10);
}
}
实验效果

以上就是今天要讲的内容,本文仅仅简单介绍了STM32的USART的使用。关于字符串存储,以及指令解析欢迎大家评论区留言加持。