特点 | 传输速率 | 抗干扰能力 | 通信距离 | IO资源占用 | 成本 |
串行通信 | 较低 | 较强 | 较长 | 较少 | 较低 |
并行通信 | 较高 | 较弱 | 较短 | 较多 | 较高 |
每秒钟传送的比特数,单位bit/s.
每秒钟传送的码元数,单位Baud.
码元:信号经过调制之后并且进行了编码。
比特率=波特率*log2M,M表示每个码元承载的信息量;
二进系统中,波特率数值上等于比特率。
通信接口 | 接口引脚 |
数据同步方式 | 数据传输方向 |
UART (通用异步收发器) |
TXD:发送端 RXD:接收端 |
异步通信 | 全双工 |
1-wire | DQ:发送/接收端 | 异步通信 | 半双工 |
IIC | SCL:同步时钟 SDA:数据输入/输出端 |
同步信号 | 半双工 |
SPI | SCK:同步时钟 MISO:主机输入,从机输出 MOSI:主机输出,从机输入 CS:片选信号 |
同步通信 | 全双工 |
串行通信接口:指按位发送和接收的接口。如:RS-232/422/485等。
数据线:
握手(同步信号):
地线:
GND(pin5):信号地,与两根数据线可组成异步通信。
其他:
RI(pin9):振铃指示。
逻辑1:-15V ~ -3V
逻辑0:+3V ~ +15V
逻辑1:3.3V
逻辑0:0V
逻辑1:5V
逻辑0:0V
注意:CMOS/TTL电平不能与RS-232电平直接交换信息。
电平转换芯片:MAX3232、SP3232等
注意:两个设备之间的TXD和RXD,必须交叉连接,方可正常通信。
PC还需要安装CH340 USB虚拟串口驱动。
注意:两个设备之间的TXD和RXD,必须交叉连接,方可正常通信。
由于是异步通信,所以SCLK线并不使用
Universal synchronous asynchronous receiver transmitter,通用同步异步收发器;
Universal asynchronous receiver transmitter,通用异步收发器。
USART/UART都可以与外部设备进行全双工异步通信;
USART,我们常用的也是异步通信。
1.全双工异步通信;
2.单线半双工通信;
3.单独的发送器和接收器使能位;
4.可配置使用DMA的多缓冲器通信;
5.多个带标志的中断源。
波特率计算公式:baud = f(ck) / 16 * USARTDIV;
其中f(ck)是串口的时钟,如:USART1的时钟是PCLK2,其他串口都是PCLK1;
USARTDIV = DIV_Mantissa + (DIV_Fraction / 16)。
把USARTDIV的整数部分写入[15:4],USARTDIV的小数部分写入[3:0]。
设置好控制和波特率寄存器后,往该寄存器(DR)写入数据即可发送,接收数据则读该寄存器。
HAL库相关函数:
为了解决F1系列存在的IO复用功能冲突问题,F4往后的系列都加入了复用器。
复用器特点:
注意:复位完成后,所有IO都会连接到系统的复用功能0(AF0)。
uart.c
#include "uart.h"
/******************************************************************************************/
/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */
#if 1
#if (__ARMCC_VERSION >= 6010050) /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t"); /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t"); /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */
#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
#endif
/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
ch = ch;
return ch;
}
/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
x = x;
}
char *_sys_command_string(char *cmd, int len)
{
return NULL;
}
/* FILE 在 stdio.h里面定义. */
FILE __stdout;
/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
while ((USART_UX->SR & 0X40) == 0); /* 等待上一个字符发送完成 */
USART_UX->DR = (uint8_t)ch; /* 将要发送的字符 ch 写入到DR寄存器 */
return ch;
}
#endif
/******************************************************************************************/
uint8_t g_rx_buffer[1];/*HAL库使用到的串口接受数据缓冲区*/
uint8_t g_usart1_rx_flag = 0;/*串口接收到数据标志*/
UART_HandleTypeDef g_uart1_hadle;/*UART句柄*/
/*uart初始化*/
void usart_init(uint32_t baudrate)
{
g_uart1_hadle.Instance = USART1;
g_uart1_hadle.Init.BaudRate = baudrate;
g_uart1_hadle.Init.WordLength = UART_WORDLENGTH_8B;
g_uart1_hadle.Init.StopBits = UART_STOPBITS_1;
g_uart1_hadle.Init.Parity = UART_PARITY_NONE;
g_uart1_hadle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
g_uart1_hadle.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&g_uart1_hadle);
HAL_UART_Receive_IT(&g_uart1_hadle,(uint8_t *)g_rx_buffer,1);
}
/*串口MSP回调函数*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef gpio_init_struct;
if(huart->Instance == USART1) /*如果是串口1,进行串口1 MSP初始化*/
{
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_9;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&gpio_init_struct);
gpio_init_struct.Pin = GPIO_PIN_10;
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA,&gpio_init_struct);
HAL_NVIC_SetPriority(USART1_IRQn,3,3);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/*(1)使能USART1和对应IO时钟,(2)初始化IO,(3)使能USART1中断,设置优先级*/
}
}
/*串口1中断服务函数*/
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&g_uart1_hadle);
HAL_UART_Receive_IT(&g_uart1_hadle,(uint8_t *)g_rx_buffer,1);
}
/*串口数据接收完成回调函数*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
g_usart1_rx_flag = 1;/*如果用到多个串口,要先判断串口寄存器基地址*/
}
uart.h
#ifndef _UART_H
#define _UART_H
#include "./SYSTEM/sys/sys.h"
#include "stdio.h"
/******************************************************************************************/
/* 引脚 和 串口 定义
* 默认是针对USART1的.
* 注意: 通过修改这几个宏定义,可以支持USART1~UART5任意一个串口.
*/
#define USART_TX_GPIO_PORT GPIOA
#define USART_TX_GPIO_PIN GPIO_PIN_9
#define USART_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define USART_RX_GPIO_PORT GPIOA
#define USART_RX_GPIO_PIN GPIO_PIN_10
#define USART_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define USART_UX USART1
#define USART_UX_IRQn USART1_IRQn
#define USART_UX_IRQHandler USART1_IRQHandler
#define USART_UX_CLK_ENABLE() do{ __HAL_RCC_USART1_CLK_ENABLE(); }while(0) /* USART1 时钟使能 */
/******************************************************************************************/
extern uint8_t g_rx_buffer[1];/*HAL库使用到的串口接受数据缓冲区*/
extern uint8_t g_usart1_rx_flag;/*串口接收到数据标志*/
extern UART_HandleTypeDef g_uart1_hadle;/*UART句柄*/
void usart_init(uint32_t bound); /* 串口初始化函数 */
#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "uart.h"
#include "./SYSTEM/delay/delay.h"
void led_init(void); /* LED初始化函数声明 */
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 波特率设置为115200 */
printf("请输入一个字符:\r\n\r\n");
while(1)
{
if(g_usart1_rx_flag == 1)
{
printf("您输入的字符为:\r\n");
HAL_UART_Transmit(&g_uart1_hadle,(uint8_t *)g_rx_buffer,1,1000);
while(__HAL_UART_GET_FLAG(&g_uart1_hadle,UART_FLAG_TC) != 1)
printf("\r\n");
g_usart1_rx_flag = 0;
}else
{
delay_ms(10);
}
}
}
uart.c
#include "uart.h"
/******************************************************************************************/
/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */
#if 1
#if (__ARMCC_VERSION >= 6010050) /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t"); /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t"); /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */
#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
#endif
/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
ch = ch;
return ch;
}
/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
x = x;
}
char *_sys_command_string(char *cmd, int len)
{
return NULL;
}
/* FILE 在 stdio.h里面定义. */
FILE __stdout;
/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
while ((USART_UX->SR & 0X40) == 0); /* 等待上一个字符发送完成 */
USART_UX->DR = (uint8_t)ch; /* 将要发送的字符 ch 写入到DR寄存器 */
return ch;
}
#endif
/******************************************************************************************/
/* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t g_usart_rx_buf[USART_REC_LEN];
/* 接收状态
* bit15, 接收完成标志
* bit14, 接收到0x0d
* bit13~0, 接收到的有效字节数目
*/
uint16_t g_usart_rx_sta = 0;
uint8_t g_rx_buffer[RXBUFFERSIZE]; /* HAL库使用的串口接收缓冲 */
UART_HandleTypeDef g_uart1_hadle;/*UART句柄*/
/*uart初始化*/
void usart_init(uint32_t baudrate)
{
g_uart1_hadle.Instance = USART1;
g_uart1_hadle.Init.BaudRate = baudrate;
g_uart1_hadle.Init.WordLength = UART_WORDLENGTH_8B;
g_uart1_hadle.Init.StopBits = UART_STOPBITS_1;
g_uart1_hadle.Init.Parity = UART_PARITY_NONE;
g_uart1_hadle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
g_uart1_hadle.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&g_uart1_hadle);
HAL_UART_Receive_IT(&g_uart1_hadle,(uint8_t *)g_rx_buffer,1);
}
/*串口MSP回调函数*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef gpio_init_struct;
if(huart->Instance == USART1) /*如果是串口1,进行串口1 MSP初始化*/
{
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_9;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&gpio_init_struct);
gpio_init_struct.Pin = GPIO_PIN_10;
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA,&gpio_init_struct);
HAL_NVIC_SetPriority(USART1_IRQn,3,3);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/*(1)使能USART1和对应IO时钟,(2)初始化IO,(3)使能USART1中断,设置优先级*/
}
}
/**
* @brief 串口1中断服务函数
* @param 无
* @retval 无
*/
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&g_uart1_hadle); /* 调用HAL库中断处理公用函数 */
}
/**
* @brief 串口数据接收回调函数
数据处理在这里进行
* @param huart:串口句柄
* @retval 无
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART_UX) /* 如果是串口1 */
{
if ((g_usart_rx_sta & 0x8000) == 0) /* 接收未完成 */
{
if (g_usart_rx_sta & 0x4000) /* 接收到了0x0d(即回车键) */
{
if (g_rx_buffer[0] != 0x0a) /* 接收到的不是0x0a(即不是换行键) */
{
g_usart_rx_sta = 0; /* 接收错误,重新开始 */
}
else /* 接收到的是0x0a(即换行键) */
{
g_usart_rx_sta |= 0x8000; /* 接收完成了 */
}
}
else /* 还没收到0X0d(即回车键) */
{
if (g_rx_buffer[0] == 0x0d)
g_usart_rx_sta |= 0x4000;
else
{
g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0];
g_usart_rx_sta++;
if (g_usart_rx_sta > (USART_REC_LEN - 1))
{
g_usart_rx_sta = 0; /* 接收数据错误,重新开始接收 */
}
}
}
}
HAL_UART_Receive_IT(&g_uart1_hadle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
}
}
uart.h
#ifndef _UART_H
#define _UART_H
#include "./SYSTEM/sys/sys.h"
#include "stdio.h"
/******************************************************************************************/
/* 引脚 和 串口 定义
* 默认是针对USART1的.
* 注意: 通过修改这几个宏定义,可以支持USART1~UART5任意一个串口.
*/
#define USART_TX_GPIO_PORT GPIOA
#define USART_TX_GPIO_PIN GPIO_PIN_9
#define USART_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define USART_RX_GPIO_PORT GPIOA
#define USART_RX_GPIO_PIN GPIO_PIN_10
#define USART_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define USART_UX USART1
#define USART_UX_IRQn USART1_IRQn
#define USART_UX_IRQHandler USART1_IRQHandler
#define USART_UX_CLK_ENABLE() do{ __HAL_RCC_USART1_CLK_ENABLE(); }while(0) /* USART1 时钟使能 */
/******************************************************************************************/
#define USART_REC_LEN 200 /* 定义最大接收字节数 200 */
#define USART_EN_RX 1 /* 使能(1)/禁止(0)串口1接收 */
#define RXBUFFERSIZE 1 /* 缓存大小 */
extern UART_HandleTypeDef g_uart1_hadle; /* HAL UART句柄 */
extern uint8_t g_usart_rx_buf[USART_REC_LEN]; /* 接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 */
extern uint16_t g_usart_rx_sta; /* 接收状态标记 */
extern uint8_t g_rx_buffer[RXBUFFERSIZE]; /* HAL库USART接收Buffer */
void usart_init(uint32_t bound); /* 串口初始化函数 */
#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "uart.h"
#include "./SYSTEM/delay/delay.h"
void led_init(void); /* LED初始化函数声明 */
int main(void)
{
uint8_t len;
uint16_t times = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 波特率设置为115200 */
printf("请输入一个字符:\r\n\r\n");
while(1)
{
if (g_usart_rx_sta & 0x8000) /* 接收到了数据? */
{
len = g_usart_rx_sta & 0x3fff; /* 得到此次接收到的数据长度 */
printf("\r\n您发送的消息为:\r\n");
HAL_UART_Transmit(&g_uart1_hadle,(uint8_t*)g_usart_rx_buf, len, 1000); /* 发送接收到的数据 */
while(__HAL_UART_GET_FLAG(&g_uart1_hadle,UART_FLAG_TC) != SET); /* 等待发送结束 */
printf("\r\n\r\n"); /* 插入换行 */
g_usart_rx_sta = 0;
}
else
{
times++;
if (times % 5000 == 0)
{
printf("\r\n 串口实验 r\n");
}
if (times % 200 == 0) printf("请输入数据,以回车键结束\r\n");
delay_ms(10);
}
}
}