03-串口-铁头山羊STM32标准库新版笔记

一、通信协议
image

Tx:数据发送引脚
Rx:数据接收引脚

image

波特率:每秒钟最多传输多少位
常用的波特率:9600、115200、921600

例子1:通过串口发送十进制数字27

例子2:发送字符串“Hello”
ASCII码:H-0x48(0100 1000) e-0x65(0110 0101) l-0x6c(0110 1010) o-0x6f(0110 1111)

二、USART模块的使用方法
image

image

三、初始化IO引脚
image

image

image

image

点击查看代码
#include "stm32f10x.h"

int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);  //开启时钟
	
	USART_InitTypeDef USART_InitStruct;
	
	USART_InitStruct.USART_BaudRate = 115200;  //波特率115200
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;  //双向
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;  //8位数据位
	USART_InitStruct.USART_StopBits = USART_StopBits_1;  //无校验位
	USART_InitStruct.USART_Parity = USART_Parity_No;  //1位停止位
	
	USART_Init(USART1, &USART_InitStruct);  //初始化串口
	
	GPIO_InitTypeDef GPIO_InitStruct;
	
//	// PA9 tx 复用输出推挽
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
//	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
//	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
//	GPIO_Init(GPIOA, &GPIO_InitStruct);
//	
//	// PA10 rx 输入浮空 输入上拉
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
//	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
//	GPIO_Init(GPIOA, &GPIO_InitStruct);
  
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  //使能AFIO模块的时钟
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);  //USART1_REMAP = 1
	
	// PB6 tx 输出推挽
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	// PB7 rx 输入浮空 输入上拉
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	while(1)
	{
	}
}

四、发送数据
TxE:-发送数据寄存器空-当TDR空时,TxE=1;否则TxE=0
TC:-发送完成-当TDR空且移位寄存器空时,TC=1;否则TC=0
image

效果:向串口调试窗口发送1,2,3,4,5

点击查看代码
#include "stm32f10x.h"

void My_USART_SendBytes(USART_TypeDef *USARTx, uint8_t *pData, uint16_t Size);

int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	
	// PB6
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	// PB7
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	USART_InitTypeDef USART_InitStruct;
	
	USART_InitStruct.USART_BaudRate = 115200;
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	
	USART_Init(USART1, &USART_InitStruct);
	
	USART_Cmd(USART1, ENABLE); // 闭合总开关
	
	uint8_t bytesToSend[] = {1,2,3,4,5};
	
	My_USART_SendBytes(USART1, bytesToSend, 5);
	
	while(1)
	{
	}
}

// @作用:使用串口一次性发送多个字节
// @参数:pData-要发送的数据  Size-字节的数量
void My_USART_SendBytes(USART_TypeDef *USARTx, uint8_t *pData, uint16_t Size)
{
	for(uint32_t i = 0; i < Size; i++)
	{
		// #1. 等待发送数据寄存器为空
		while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
		
		// #2. 将要发送的数据写入发数据寄存器当中
		USART_SendData(USART1, pData[i]);
	}
	
	// #3等待数据发送完成
	while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}

五、格式化打印字符串

点击查看代码
#include "stm32f10x.h"
#include <stdio.h>
#include "delay.h"

void My_USART_SendBytes(USART_TypeDef *USARTx, uint8_t *pData, uint16_t Size);
void My_USART1_Init(void);

int main(void)
{	
	Delay_Init();
	My_USART1_Init();
	
//	printf("Hello world. \r\n");
	
	while(1)
	{
		uint32_t currentTick = GetTick();
		
		uint32_t miliSeconds = currentTick % 1000;
		currentTick = currentTick / 1000;
		
		uint32_t seconds = currentTick % 60;
		currentTick = currentTick / 60;
		
		uint32_t minutes = currentTick % 60;
		currentTick = currentTick / 60;
		
		uint32_t hour = currentTick;
		
		printf("%02u:%02u:%02u.%03u\r\n", hour, minutes, seconds, miliSeconds);
		
		Delay(100);
	}
}

//
// @简介:通过串口发送多个字节
// @参数 USARTx:填写串口的名称
// @参数 pData:要发送的数据
// @参数 Size:要发送数据的数量,单位是字节
//
void My_USART_SendBytes(USART_TypeDef *USARTx, uint8_t *pData, uint16_t Size)
{
	for(uint32_t i = 0; i < Size; i++)
	{
		// #1. 等待发送数据寄存器为空
		while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
		
		// #2. 写入发数据寄存器当中
		USART_SendData(USART1, pData[i]);
	}
	
	// 等待发送完成
	while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}

//
// @简介:对USART1进行初始化
//        PB6 - Tx, PB7 - Rx
//        115200, 8, 1, None, 双向
//
void My_USART1_Init(void)
{
	// #1. 初始化PB6和PB7
	GPIO_InitTypeDef GPIO_InitStruct;
	
//	// PA9 tx
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
//	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
//	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
//	GPIO_Init(GPIOA, &GPIO_InitStruct);
//	
//	// PA10 rx
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
//	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
//	GPIO_Init(GPIOA, &GPIO_InitStruct);
  
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	
	// PB6
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	// PB7
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	
	// #2. 初始化USART1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	USART_InitTypeDef USART_InitStruct;
	
	USART_InitStruct.USART_BaudRate = 115200;
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	
	USART_Init(USART1, &USART_InitStruct);
	
	USART_Cmd(USART1, ENABLE); // 闭合总开关
}

//重写fputc函数:通过串口发送出去
int fputc(int ch, FILE *f)
{
	// #1. 等待发送寄存器为空
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
	
	// #2. 写入发送寄存器当中
	USART_SendData(USART1, (uint8_t)ch);
	
	return ch;
}

六、接收数据
RxNE:-接收数据寄存器非空-当RDR非空时,RxNE=1;否则RxNE=0
PE:-奇偶校验错-如果接收到的数据有校验错误,则PE=1;否则PE=0
FE:-帧格式错误-接收到了无效的数据帧,则FE=1;否则FE=0
NE:-噪声错-接收的数据中检测到了噪声,则NE=1;否则NE=0
ORE:-过载错-由于过载造成了数据丢失,则ORE=1;否则ORE=0

效果:串口调试窗口发送1,板载LED亮,发送0,板载LED灭
main.c

点击查看代码
#include "stm32f10x.h"

void My_USART1_Init(void);
void My_OnBoardLED_Init(void);

int main(void)
{	
	My_USART1_Init();
	My_OnBoardLED_Init();
	
	while(1)
	{
		// #1. 等待接收数据寄存器非空
		while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
		
		// #2. 把数据从接收数据寄存器里读出来
		uint8_t byteRcvd = USART_ReceiveData(USART1);
		
		// #3. 对数据进行处理
		if(byteRcvd == '0')
		{
			GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); // 亮灯
		}
		else if(byteRcvd == '1')
		{
			GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); // 灭灯
		}
	}
}

//
// @简介:对USART1进行初始化
//        PB6 - Tx, PB7 - Rx
//        115200, 8, 1, None, 双向
//
void My_USART1_Init(void)
{
	// #1. 初始化PB6和PB7
	GPIO_InitTypeDef GPIO_InitStruct;
  
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	
	// PB6
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	// PB7
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	
	// #2. 初始化USART1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	USART_InitTypeDef USART_InitStruct;
	
	USART_InitStruct.USART_BaudRate = 115200;
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	
	USART_Init(USART1, &USART_InitStruct);
	
	USART_Cmd(USART1, ENABLE); // 闭合总开关
}

void My_OnBoardLED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOC, &GPIO_InitStruct);
	
	GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
}

七、封装常用功能
usart.h

点击查看代码
#ifndef _USART_H_
#define _USART_H_

#include "stm32f10x.h"

#define LINE_SEPERATOR_CR   0x00 // 回车 \r
#define LINE_SEPERATOR_LF   0x01 // 换行 \n
#define LINE_SEPERATOR_CRLF 0x02 // 回车+换行 \r\n

void My_USART_SendByte(USART_TypeDef *USARTx, const uint8_t Data);
void My_USART_SendBytes(USART_TypeDef *USARTx, const uint8_t *pData, uint16_t Size);
void My_USART_SendChar(USART_TypeDef *USARTx, const char C);
void My_USART_SendString(USART_TypeDef *USARTx, const char *Str);
void My_USART_Printf(USART_TypeDef *USARTx, const char *Format, ...);

 uint8_t My_USART_ReceiveByte(USART_TypeDef *USARTx);
uint16_t My_USART_ReceiveBytes(USART_TypeDef *USARTx, uint8_t *pDataOut, uint16_t Size, int Timeout);
     int My_USART_ReceiveLine(USART_TypeDef *USARTx, char *pStrOut, uint16_t MaxLength, uint16_t LineSeperator, int Timeout);

#endif

usart.c

点击查看代码
#include "usart.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "delay.h"

static USART_TypeDef *usart_for_printf = 0; // 用于printf的USART名称

//
// @简介:使用串口发送一个字节的数据
// 
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 Data  : 要发送的数据
//
void My_USART_SendByte(USART_TypeDef *USARTx, const uint8_t Data)
{
	My_USART_SendBytes(USARTx, &Data, 1);
}

//
// @简介:使用串口发送多个字节的数据
// 
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 pData : 要发送的数据(数组)
// @参数 Size  :要发送数据的数量,单位是字节
//
void My_USART_SendBytes(USART_TypeDef *USARTx, const uint8_t *pData, uint16_t Size)
{
	if(Size == 0) return;
	
	for(uint16_t i=0; i < Size; i++)
	{
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
		
		USART_SendData(USARTx, pData[i]);
	}
	
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}

//
// @简介:通过串口发送一个字符
// 
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 C     :要发送的字符
//
void My_USART_SendChar(USART_TypeDef *USARTx, const char C)
{
	My_USART_SendBytes(USARTx, (const uint8_t *)&C, 1);
}

//
// @简介:通过串口发送字符串
// 
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 Str   :要发送的字符串
//
void My_USART_SendString(USART_TypeDef *USARTx, const char *Str)
{
	My_USART_SendBytes(USARTx, (const uint8_t *)Str, strlen(Str));
}

//
// @简介:通过串口格式化打印字符串
// 
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 Format:字符串的格式
// @参数 ...   :可变参数
//
void My_USART_Printf(USART_TypeDef *USARTx, const char *Format, ...)
{
	usart_for_printf = USARTx;
	
	va_list args;
	va_start(args, Format);
	vprintf(Format, args);
	va_end(args);
}


//
// @简介:通过串口读取一字节的数据
// 
// @参数 USARTx  :串口名称,如USART1, USART2, USART3 ...
// 
// @返回值:读取到的字节
//
uint8_t My_USART_ReceiveByte(USART_TypeDef *USARTx)
{
	while(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET);
	
	return USART_ReceiveData(USARTx);
}

//
// @简介:通过串口读取多个字节的数据
// 
// @参数 USARTx  :串口名称,如USART1, USART2, USART3 ...
// @参数 pDataOut:输出参数,读取到的数据将输出到此数组当中
// @参数 Size    :需要读取的字节数量
// @参数 Timeout :超时时间,单位是毫秒,负数表示无限长。如果超时时间内没有读取完成则返回。
// 
// @返回值:实际读取到的数据数量
//
uint16_t My_USART_ReceiveBytes(USART_TypeDef *USARTx, uint8_t *pDataOut, uint16_t Size, int Timeout)
{
	uint32_t expireTime;
	
	Delay_Init();
	
	if(Timeout >= 0)
	{
		expireTime = GetTick() + Timeout; // 计算过期时间,过期时间 = 当前时间+Timeout
	}
	
	uint16_t i = 0;
	
	do
	{
		if(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == SET)
		{
			pDataOut[i++] = USART_ReceiveData(USARTx);
			
			if(i==Size) break;
		}
	}
	while(Timeout < 0 || GetTick() < expireTime); // 判断是否超时
	
	return i;
}


//
// @简介:通过串口读取一行字符串
// 
// @参数 USARTx       :串口名称,如USART1, USART2, USART3 ...
// @参数 pStrOut      :输出参数,读取到的数据将输出到此数组当中
// @参数 MaxLength    :字符串的最大长度
// @参数 LineSeperator:行分隔符 LINE_SEPERATOR_CR   - 回车 \r
//                               LINE_SEPERATOR_LF   - 换行 \n
//                               LINE_SEPERATOR_CRLF - 回车+换行 \r\n
// @参数 Timeout      :超时时间,单位是毫秒,负数表示无限长。如果超时时间内没有读取完成则返回
// 
// @返回值:0 - 成功读到一行字符串
//         -1 - 超时(Timeout内未读到一行完整的字符串)
//         -2 - 超过字符串的最大长度(字符串的最大长度用MaxLength参数设置)
//
int My_USART_ReceiveLine(USART_TypeDef *USARTx, char *pStrOut, uint16_t MaxLength, uint16_t LineSeperator, int Timeout)
{
	int ret = -1;
	uint32_t expireTime;
	
	Delay_Init();
	
	if(Timeout >= 0)
	{
		expireTime = GetTick() + Timeout; // 计算过期时间,过期时间 = 当前时间+Timeout
	}
	
	uint16_t i = 0;
	
	do
	{
		if(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == SET)
		{
			char c = (char)USART_ReceiveData(USARTx);
			pStrOut[i++] = c;
			
			if(LineSeperator == LINE_SEPERATOR_CR && c == '\r') // \r
			{
				pStrOut[i] = 0;
				ret = 0;
				break;
			}
			else if(LineSeperator == LINE_SEPERATOR_LF && c == '\n') // \n
			{
				pStrOut[i] = 0;
				ret = 0;
				break;
			}
			else if(pStrOut[i-2] == '\r' && c == '\n') // \r\n
			{
				pStrOut[i] = 0;
				ret = 0;
				break;
			}
			
			if(i == MaxLength) // 超过最大长度
			{
				ret = -2;
				break;
			}
		}
	}
	while(Timeout < 0 || GetTick() < expireTime); // 判断是否超时
	
	return ret;
}

//
// @简介:此函数为对fputc的重写,以实现串口格式化打印功能
//
int fputc(int ch, FILE *f)
{
	// #1. 等待TXE
	while(USART_GetFlagStatus(usart_for_printf, USART_FLAG_TXE) == RESET);
	// #2. 将数据写入TDR
	USART_SendData(usart_for_printf, (uint8_t)ch);
	
	return ch;
}
posted @ 2025-10-22 15:08  有点闲  阅读(38)  评论(0)    收藏  举报