目录
1. 串行并行
串口,全称为串行通信接口(Serial Communication Interface),是一种计算机硬件设备用于与其他设备进行数据传输的接口。与串行通信对应的是并行通信,在这里也一并说一说。
串行通信:
- 定义:串行通信是指数据以一位一位的顺序进行传输的方式。
- 特点:
- 使用较少的线路资源,通常只需要一条数据线加上必要的控制和时钟信号线。
- 适合长距离传输,因为减少了线路间的干扰。
- 传输速度相对较慢,因为一次只能传输一位数据。
- 实现起来比较简单,成本较低。
- 应用:广泛应用于计算机与外部设备之间的通信,如 RS-232、USB、SPI、I²C 等。
并行通信:
- 定义:并行通信是指数据以多位同时进行传输的方式。
- 特点:
- 使用较多的线路资源,通常需要多条数据线同时传输多位数据。
- 适合短距离传输,因为多条线路容易受到干扰。
- 传输速度较快,因为一次可以传输多位数据。
- 实现起来相对复杂,成本较高。
- 应用:主要用于计算机内部组件之间的通信,如早期的打印机端口(LPT)、PCI 总线等。
小结:
- 串行通信:适合长距离、低成本的应用场景,如远程通信和嵌入式系统。
- 并行通信:适合短距离、高速度的应用场景,如计算机内部组件之间的通信。
2. UART
在STM32单片机中,集成了一种片上外设,USART(Universal Synchronous/Asynchronous Receiver/Transmitter",通用同步/异步接收/发送器),是一种全双工通用同步/异步串行收发模块,它是串口通信的一种具体实现。支持同步和异步模式下的数据传输,可以灵活地与外部设备进行全双工数据交换。
而UART(Universal Asynchronous Receiver/Transmitter,通用异步接收/发送器)是USART去掉同步功能(时钟)后的一种通信方式。因为去掉了时钟,使得其通信更为简单方便(不需要考虑同步时钟),得到了更广泛的应用。
- 定义:UART 是一种用于实现异步串行通信的硬件接口,它支持全双工通信,即可以同时发送和接收数据。
- 特点:
- 支持异步通信,不需要外部时钟信号同步。
- 通常使用两条线路:TX(发送)和 RX(接收)。
- 支持多种波特率和数据格式。
- 包含数据位、起始位、停止位以及可选的奇偶校验位。
- 应用:广泛应用于微控制器、计算机与外设之间的通信,如 RS-232、USB 等接口。
UART/USART通信接线示意图:
3. 串口重定向
串口重定向是一种技术手段,用于改变串口数据的流向,使得原本应该通过串口输出的数据被导向到其他地方,比如文件、网络或其他设备。串口重定向可以应用于不同的场景,包括但不限于终端服务器、嵌入式系统开发和调试等。
在C语言中有一个输出函数printf
(),通常通过标准输出 stdout
输出数据到控制台,我们通过将 stdout
重定向到串口,可以实现 printf
的串口输出。
这样做的目的是为了能够在没有显示器的情况下也能查看调试信息,这对于嵌入式开发特别有用。
实现方法 1:
(1)重写fputc函数:
printf
函数底层调用fputc
函数来输出字符。- 通过重新定义
fputc
函数,使其将字符通过串口发送出去,就可以实现printf
的串口重定向。(2)配置 Micro-LIB:
- 在魔术棒选项卡中,勾选
use Micro-LIB
选项。- 这样可以启用 Micro-LIB,它是一个小型的 C 标准库,用于嵌入式系统。
(3)添加标准输入输出:
- 在 main.c 文件中添加标准输入输出相关的头文件。
- #include <stdio.h> #include <stdlib.h>
实现方法 2:
加入以下代码,不需要勾选use MicroLIB。
#pragma import(__use_no_semihosting)
/*********************************************************************
@Function : 标准库需要的支持函数
@Parameter : N/A
@Return : N/A
**********************************************************************/
struct __FILE
{
int handle;
};
FILE __stdout;
/*********************************************************************
@Function : 定义_sys_exit()以避免使用半主机模式
@Parameter : X :
@Return : N/A
**********************************************************************/
void _sys_exit(int x)
{
x = x;
}
/*********************************************************************
@Function : 重定义fputc函数
@Parameter : ch :入口数据
f :入口指针
@Return : ch :入口数据
**********************************************************************/
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
4. UART通信实现
初始化配置没有太多好说的,就是片上外设的配置,需要什么中断就开哪个中断,并在串口中断处理函数里做相应处理就行了。
要说的几点:
(1)输出引脚配置为复用,因为是把原本的通用IO功能,用作片上外设UART的Tx功能,所以是复用。一般来讲,只要是把原本的通用IO接到片上外设做输出,就是复用。
(2)输入引脚配置为浮空,因为要接收的数据有高又有低。不能上下拉,那样会使它保持到高或低,无法正确接收数据。
串口初始化配置:
void USART1_Init(uint32_t Baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/*时钟使能*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | \
RCC_APB2Periph_GPIOA, ENABLE);
/*引脚复用*/ //PA9->TXD
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//PA10->RXD
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*NVIC 配置*/
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2 ;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/*USART设置*/
USART_InitStructure.USART_BaudRate = Baud; //波特率,根据需求自己选
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //发8位数据
USART_InitStructure.USART_StopBits = USART_StopBits_1; //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);
/*中断配置*/
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开接收中断
// USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //开空闲中断
// USART_ITConfig(USART1,USART_IT_TXE,ENABLE); //开发送中断
USART_Cmd(USART1, ENABLE);
}
串口发送数据与字符串:
*Data++就是一个后++生效时间的问题,当前语句时++没加上(后++
:首先返回 Data
的当前值,然后将 Data
的值增加,增加的时候已经执行到第二句了),因此这个语句是先访问Data指向的首地址的内容,再访问Data指向的下一地址的内容,直到结束。
//发送数据
void USART_SendByte(USART_TypeDef* USARTx, uint8_t* Data,uint8_t len)
{
uint8_t i = 0;
for(i=0;i<len;i++)
{
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) != SET);
USART_SendData(USARTx,*Data++);
}
}
//发送字符串
void USART_Send_String(USART_TypeDef* USARTx, const uint8_t* str)
{
while(*str != '\0') //字符串自带一个结束标志'\0',等于这个代表字符发完了
{
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) != SET);
USART_SendData(USARTx,*str++);
}
}
串口中断处理函数 :
//串口1中断服务程序
void USART1_IRQHandler(void)
{
u8 r = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
r = USART_ReceiveData(USART1); //读取接收到的数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) != SET);
USART_SendData(USART1,r); //原样发回
}
USART_ClearFlag(USART1,USART_FLAG_TXE); //清除标志位,标志位有的是硬件自动清除的,记不住就手动再清一遍
}
到此,串口的数据收发就实现了。
在嵌入式系统中,串口除了进行基本通信外,还可用于调试,是一个十分重要的必备外设。