串口简介

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。

简单双向串口通信有两根通信线(发送端TX和接收端RX),TX与RX要交叉连接。当只需单向的数据传输时,可以只接一根通信线。当电平标准不一致时,需要加电平转换芯片。

image.png

STM32 USB-TTL
5V
VCC(和 5V 或 3V3 短接)
3V3
RX TDX
TX RXD
GND GND
串口参数:
  • 波特率:串口通信的速率
  • 起始位:标志一个数据帧的开始,固定为低电平
  • 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
  • 校验位:用于数据验证,根据数据位计算得来
  • 停止位:用于数据帧间隔,固定为高电平

image.png

使用串口输出到电脑

使用串口时记得勾选 Use MicroLIB:

image.png

在 User 文件夹(或其他文件夹)下,新建两个文件:serial.c 和 serial.h(或其它名字,serial 取串口之意),并添加到「组」中。

image.png

基操之重写 printf 和 sprintf

HAL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**************** HAL ********************/
#include <stdio.h> // printf
int fputc(int ch,FILE *f)
{
uint8_t c[1]={ch};
HAL_UART_Transmit(&huart1,c,1,2); //发送消息到串口1
return ch;
}
// 调用方法 printf("hello, world!");


#include <stdarg.h> // sprintf
#define DATA_MAX 256
uint8_t TxBuf[DATA_MAX];
void XXX_Printf(char *format,...)
{
va_list arg;
va_start(arg,format);
vsprintf((char*)TxBuf,format,arg);
va_end(arg);
HAL_UART_Transmit(&huart2,TxBuf,strlen((const char*)TxBuf),0xffff); // 发送消息到串口2
}
// 调用方法 XXX_Printf("hello, world!");

标准库

serial.h:

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef _SERIAL_H
#define _SERIAL_H
#include "stm32f10x.h" // Device header
#include <stdio.h> // printf
#include <stdarg.h> // sprinf

void Serial_Init(uint32_t baud);
void Serial_SendByte(uint8_t Byte);
void Serial_SendString(char *str);
void Serial_Printf(char *format,...);
#endif

serial.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include "serial.h"


void Serial_Init(uint32_t baud)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

// 发送
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; // 发送
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// 接收
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; // 上拉输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; // 接收
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);


USART_InitTypeDef USART_InitStruture;
USART_InitStruture.USART_BaudRate=baud; // 波特率
USART_InitStruture.USART_HardwareFlowControl=USART_HardwareFlowControl_None;// 不使用流控制
USART_InitStruture.USART_Mode=USART_Mode_Tx | USART_Mode_Rx; // 同时开启发送和接收
USART_InitStruture.USART_Parity=USART_Parity_No; // 不需要校验位
USART_InitStruture.USART_StopBits=USART_StopBits_1; // 1 位停止位
USART_InitStruture.USART_WordLength=USART_WordLength_8b; // 字长
USART_Init(USART1,&USART_InitStruture);

// 接收-中断方式
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //USART_IT_RXNE一旦置1,将会向NVIC申请中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);

USART_Cmd(USART1,ENABLE);
}

void Serial_SendByte(uint8_t byte)
{
USART_SendData(USART1,byte); // 标志位自动清零
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
}

void Serial_SendString(char *str)
{
uint8_t i;
for(i=0;str[i]!='\0';i++)
{
Serial_SendByte(str[i]);
}
}


// 移植printf
// fputc原型,是printf的底层。将其重定向到串口
int fputc(int ch,FILE *f){
Serial_SendByte(ch);
return ch;
}

示例程序 main.c:

1
2
3
4
5
6
7
8
9
10
#include "stm32f10x.h"                  // Device header
#include "serial.h"
int main(void){
Serial_Init(115200);
printf("Hello World!");
while(1){

}
}

测试:

image.png