1. 通信概念
在计算机设备与设备之间或集成电路之间进行数据传输。
2. 通信协议的分类
2.1. 按照方向分类
- 单工:指数据传输仅能沿一个方向,不能实现反方向传输,如校园广播。
- 半双工:指数据传输可以沿着两个方向,但是需要分时进行,如对讲机。
- 全双工:指数据可以同时进行双向传输,日常的打电话属于这种情形。
2.2. 按照数据传输的方式
- 串行通信:是指设备之间通过一根数据信号线,地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式,同一时刻只能传输一位(bit)数据。
- 并行通信:是指使用 8、16、32 及 64 根或更多的数据线(有多少信号为就需要多少信号位)进行传输的通讯方式,可以同一时刻传输多个数据位的数据。
2.2.1. 串行与并行传输方式对比
特性 | 串行通讯 | 并行通讯 |
通信距离 | 较远 | 较近 |
抗干扰能力 | 较强 | 较弱 |
传输速率 | 较慢 | 较高 |
成本 | 较低 | 较高 |
2.3. 按照有无时钟信号分类:
- 同步通信:要求通信双方共用同一时钟信号,在总线上保持统一的时序和周期完成信息传输。
- 异步通信:不需要时钟信号,而是在数据信号中加入开始位和停止位等一些同步信号,以便使接收端能够正确地将每一个字符接收下来,某些通信中还需要双方约定传输速率
3. USART简述
USART是一种通信协议,它是通用同步/异步收发传输器(Universal Synchronous/Asynchronous Receiver/Transmitter),USART是一种串行通信协议,可以在同步和异步模式下进行数据传输,用于将数据从一个设备传输到另一个设备。
3.1. USART与UART的区别
USART:通用同步和异步收发器
UART:通用异步收发器
当进行异步通信时,这两者是没有区别的。区别在于USART比UART多了同步通信功能。
3.2. UART接线
3.3. 数据传输流程(18n19600115200)
起始位(Start Bit):UART通信的起始位始终为逻辑低电平(0),用于标识数据传输的开始。
数据位(Data Bits):UART通信可以使用5、6、7或8个数据位来传输数据。数据位按照位顺序从最低位到最高位传输。
奇偶校验位(Parity Bit):可选的奇偶校验位用于检测和纠正数据传输中的错误。校验位可以是奇校验(使得数据位的总数包含奇数个1)或偶校验(使得数据位的总数包含偶数个1)。
停止位(Stop Bit):停止位是UART通信的结束位,始终为逻辑高电平(1)。它标识数据传输的结束,并允许接收器进行下一次数据接收的准备。
波特率(Bps): 每秒钟传送的码元数(一个数字脉冲);
4. UART、RS232、TTL关系阐述
4.1. RS232标准
TLL与RS-232标准逻辑相反,而且电平也大不相同,若单片机与单片机或其他设备TLL设备通信采用RS-232通信(DB9串口线),并且进行电平的转化:TTL-->RS232 RS232 --> TTL;
4.2. USB转串口
USB转串口:主要用于设备(STM32)与电脑通信
USB转串口的电平转换芯片一般有CH340、PL2303、CP2102、FT232
使用的时候电脑要按照电平转换芯片的驱动(虚拟出一个串口)我这里装的是CH340
4.3. 串口到串口
串口通信主要是控制器跟串口的设备或者传感器通信他们但是TLL电平,不需要经过电平转换芯片来转换电平,直接就用TTL电平通信,GPS模块、GSM模块、串口转WIFI模块、HC04蓝牙模块
5. USART的功能框图
![](https://img-blog.csdnimg.cn/img_convert/657345b2d21da7fbd2db056ebf3d9128.png)
6. 串口编程(基于寄存器)
6.1. 串口轮询(轮询发送)
#include "Driver_USART.h"
#include "Delay.h"
int main()
{
Driver_USART1_Init();
while (1)
{
printf("hello atguigu\r\n");
Delay_ms(500);
}
}
#include "Delay.h" // Device header
void Delay_us(uint16_t us)
{
/* 定时器重装值 */
SysTick->LOAD = 72 * us;
/* 清除当前计数值 */
SysTick->VAL = 0;
/*设置内部时钟源(2位->1),不需要中断(1位->0),并启动定时器(0位->1)*/
SysTick->CTRL = 0x5;
/*等待计数到0, 如果计数到0则16位会置为1*/
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG));
/* 关闭定时器 */
SysTick->CTRL &= ~SysTick_CTRL_ENABLE;
}
void Delay_ms(uint16_t ms)
{
while (ms--)
{
Delay_us(1000);
}
}
void Delay_s(uint16_t s)
{
while (s--)
{
Delay_ms(1000);
}
}
#include "Driver_USART.h"
/**
* @description: 初始化串口1
*/
void Driver_USART1_Init(void)
{
/* 1. 开启时钟 */
/* 1.1 串口1外设的时钟 */
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
/* 1.2 GPIO时钟 */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
/* 2. 配置GPIO引脚的工作模式 PA9=Tx(复用推挽 CNF=10 MODE=11) PA10=Rx(浮空输入 CNF=01 MODE=00)*/
GPIOA->CRH |= GPIO_CRH_CNF9_1;
GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
GPIOA->CRH |= GPIO_CRH_MODE9;
GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
GPIOA->CRH |= GPIO_CRH_CNF10_0;
GPIOA->CRH &= ~GPIO_CRH_MODE10;
/* 3. 串口的参数配置 */
/* 3.1 配置波特率 115200 */
USART1->BRR = 0x271;
/* 3.2 配置一个字的长度 8位 */
USART1->CR1 &= ~USART_CR1_M;
/* 3.3 配置不需要校验位 */
USART1->CR1 &= ~USART_CR1_PCE;
/* 3.4 配置停止位的长度 */
USART1->CR2 &= ~USART_CR2_STOP;
/* 3.5 使能接收和发送 */
USART1->CR1 |= USART_CR1_TE;
USART1->CR1 |= USART_CR1_RE;
/* 3.6 使能串口的各种中断 */
USART1->CR1 |= USART_CR1_RXNEIE; /* 接收非空中断 */
USART1->CR1 |= USART_CR1_IDLEIE; /* 空闲中断 */
/* 4. 配置NVIC */
/* 4.1 配置优先级组 */
NVIC_SetPriorityGrouping(3);
/* 4.2 设置优先级 */
NVIC_SetPriority(USART1_IRQn, 2);
/* 4.3 使能串口1的中断 */
NVIC_EnableIRQ(USART1_IRQn);
/* 4. 使能串口 */
USART1->CR1 |= USART_CR1_UE;
}
/**
* @description: 发送一个字节
* @param {uint8_t} byte 要发送的字节
*/
void Driver_USART1_SendChar(uint8_t byte)
{
/* 1. 等待发送寄存器为空 */
while ((USART1->SR & USART_SR_TXE) == 0)
;
/* 2. 数据写出到数据寄存器 */
USART1->DR = byte;
}
/**
* @description: 发送一个字符串
* @param {uint8_t} *str 要发送的字符串
* @param {uint16_t} len 字符串中字节的长度
* @return {*}
*/
void Driver_USART1_SendString(uint8_t *str, uint16_t len)
{
for (uint16_t i = 0; i < len; i++)
{
Driver_USART1_SendChar(str[i]);
}
}
/**
* @description: 接收一个字节的数据
* @return {*} 接收到的字节
*/
uint8_t Driver_USART1_ReceiveChar(void)
{
/* 等待数据寄存器非空 */
while ((USART1->SR & USART_SR_RXNE) == 0)
;
return USART1->DR;
}
/**
* @description: 接收变长数据.接收到的数据存入到buff中
* @param {uint8_t} buff 存放接收到的数据
* @param {uint8_t} *len 存放收到的数据的字节的长度
*/
void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len)
{
uint8_t i = 0;
while (1)
{
// 等待接收非空
while ((USART1->SR & USART_SR_RXNE) == 0)
{
// 在等待期间, 判断是否收到空闲帧
if (USART1->SR & USART_SR_IDLE)
{
*len = i;
return;
}
}
buff[i] = USART1->DR;
i++;
}
}
/* 缓冲接收到的数据 */
uint8_t buff[100] = {0};
/* 存储接收到的字节的长度 */
uint8_t len = 0;
uint8_t isToSend = 0;
void USART1_IRQHandler(void)
{
/* 数据接收寄存器非空 */
if (USART1->SR & USART_SR_RXNE)
{
// 对USART_DR的读操作可以将接收非空的中断位清零。 所以不用单独清除了.
//USART1->SR &= ~USART_SR_RXNE;
buff[len] = USART1->DR;
len++;
}
else if (USART1->SR & USART_SR_IDLE)
{
/* 清除空闲中断标志位: 先读sr,再读dr.就可以实现清除了 */
USART1->SR;
USART1->DR;
/* 变长数据接收完毕 */
//Driver_USART1_SendString(buff, len);
isToSend = 1;
/* 把接收字节的长度清0 */
// len = 0;
}
}
// 当调用printf的时候,会自动调用这个方法来执行,只需要调用一个通过串口发送字符的函数
int fputc(int c, FILE *file)
{
Driver_USART1_SendChar(c);
return c;
}
6.2. 串口中断(发送一次回复一次)
#include "Driver_USART.h"
/**
* @description: 初始化串口1
*/
void Driver_USART1_Init(void)
{
/* 1. 开启时钟 */
/* 1.1 串口1外设的时钟 */
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
/* 1.2 GPIO时钟 */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
/* 2. 配置GPIO引脚的工作模式 PA9=Tx(复用推挽 CNF=10 MODE=11) PA10=Rx(浮空输入 CNF=01 MODE=00)*/
GPIOA->CRH |= GPIO_CRH_CNF9_1;
GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
GPIOA->CRH |= GPIO_CRH_MODE9;
GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
GPIOA->CRH |= GPIO_CRH_CNF10_0;
GPIOA->CRH &= ~GPIO_CRH_MODE10;
/* 3. 串口的参数配置 */
/* 3.1 配置波特率 115200 */
USART1->BRR = 0x271;
/* 3.2 配置一个字的长度 8位 */
USART1->CR1 &= ~USART_CR1_M;
/* 3.3 配置不需要校验位 */
USART1->CR1 &= ~USART_CR1_PCE;
/* 3.4 配置停止位的长度 */
USART1->CR2 &= ~USART_CR2_STOP;
/* 3.5 使能接收和发送 */
USART1->CR1 |= USART_CR1_TE;
USART1->CR1 |= USART_CR1_RE;
/* 3.6 使能串口的各种中断 */
USART1->CR1 |= USART_CR1_RXNEIE; /* 接收非空中断 */
USART1->CR1 |= USART_CR1_IDLEIE; /* 空闲中断 */
/* 4. 配置NVIC */
/* 4.1 配置优先级组 */
NVIC_SetPriorityGrouping(3);
/* 4.2 设置优先级 */
NVIC_SetPriority(USART1_IRQn, 2);
/* 4.3 使能串口1的中断 */
NVIC_EnableIRQ(USART1_IRQn);
/* 4. 使能串口 */
USART1->CR1 |= USART_CR1_UE;
}
/**
* @description: 发送一个字节
* @param {uint8_t} byte 要发送的字节
*/
void Driver_USART1_SendChar(uint8_t byte)
{
/* 1. 等待发送寄存器为空 */
while ((USART1->SR & USART_SR_TXE) == 0)
;
/* 2. 数据写出到数据寄存器 */
USART1->DR = byte;
}
/**
* @description: 发送一个字符串
* @param {uint8_t} *str 要发送的字符串
* @param {uint16_t} len 字符串中字节的长度
* @return {*}
*/
void Driver_USART1_SendString(uint8_t *str, uint16_t len)
{
for (uint16_t i = 0; i < len; i++)
{
Driver_USART1_SendChar(str[i]);
}
}
/**
* @description: 接收一个字节的数据
* @return {*} 接收到的字节
*/
uint8_t Driver_USART1_ReceiveChar(void)
{
/* 等待数据寄存器非空 */
while ((USART1->SR & USART_SR_RXNE) == 0)
;
return USART1->DR;
}
/**
* @description: 接收变长数据.接收到的数据存入到buff中
* @param {uint8_t} buff 存放接收到的数据
* @param {uint8_t} *len 存放收到的数据的字节的长度
*/
void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len)
{
uint8_t i = 0;
while (1)
{
// 等待接收非空
while ((USART1->SR & USART_SR_RXNE) == 0)
{
// 在等待期间, 判断是否收到空闲帧
if (USART1->SR & USART_SR_IDLE)
{
*len = i;
return;
}
}
buff[i] = USART1->DR;
i++;
}
}
/* 缓冲接收到的数据 */
uint8_t buff[100] = {0};
/* 存储接收到的字节的长度 */
uint8_t len = 0;
uint8_t isToSend = 0;
void USART1_IRQHandler(void)
{
/* 数据接收寄存器非空 */
if (USART1->SR & USART_SR_RXNE)
{
// 对USART_DR的读操作可以将接收非空的中断位清零。 所以不用单独清除了.
//USART1->SR &= ~USART_SR_RXNE;
buff[len] = USART1->DR;
len++;
}
else if (USART1->SR & USART_SR_IDLE)
{
/* 清除空闲中断标志位: 先读sr,再读dr.就可以实现清除了 */
USART1->SR;
USART1->DR;
/* 变长数据接收完毕 */
//Driver_USART1_SendString(buff, len);
isToSend = 1;
/* 把接收字节的长度清0 */
// len = 0;
}
}
#ifndef __DRVIER_USART_H
#define __DRVIER_USART_H
#include "stm32f10x.h"
void Driver_USART1_Init(void);
void Driver_USART1_SendChar(uint8_t byte);
void Driver_USART1_SendString(uint8_t *str, uint16_t len);
uint8_t Driver_USART1_ReceiveChar(void);
void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len);
#endif
#include "Driver_USART.h"
/* 缓冲接收到的数据 */
extern uint8_t buff[100];
/* 存储接收到的字节的长度 */
extern uint8_t len;
extern uint8_t isToSend;
int main()
{
Driver_USART1_Init();
Driver_USART1_SendString("abc", 3);
while (1)
{
if(isToSend){
Driver_USART1_SendString(buff, len);
isToSend = 0;
len = 0;
}
}
}