【stm32】USART串口的发送



前言

使用b站江科大课程进行学习


一、定义概念 + 缩写

1. USART

Universal Synchronous/Asynchronous Receiver/Transmitter
通用--------同步------------异步-----------------收发器

2. 波特率(Baud Rate)

单位时间内传送的码元符号的个数: 百度 - 波特率

3. USART的最基本结构

在这里插入图片描述
数据流:接收端 -> 寄存器

二、性质

1. 串口的时序

数电内容

2. stm32f103c8t6的USART资源

在这里插入图片描述
USART 1 在 APB2 总线上
USART 2、USART 3 在 APB1 总线上

3. 引脚的复用

在这里插入图片描述

三、使用步骤(函数代码)

1. 观察H文件内的函数,寻找自己要用的函数

像用一个软件一样,刚开始用的时候大部分功能(函数)我们都用不上,就需要到H文件(用于声明函数名和参数,如果参数是结构体(像Python里用字典传递参数)意味着参数很多)去寻找自己需要的功能

// stm32f10x_usart.h
// init
void USART_DeInit(USART_TypeDef* USARTx);
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);

// 同步时钟输出
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

// DMA
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);

// 设置地址,唤醒,LIN
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address);
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp);
void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength);
void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState);

// 重要:sendData 发送数据,receiveData 接收数据
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

// 智能卡,IRDA等函数
void USART_SendBreak(USART_TypeDef* USARTx);
void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime);
void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler);
void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode);
void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState);

// 标志位相关函数
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

2. 串口初始化

  • 开启时钟
void Serial_Init(void)
{
}

在函数内配置以下内容:

  • 开启时钟
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
  • 通过配置GPIO结构体来初始化GPIO
	// GPIO初始化,配置结构体
	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;
	
	// 将PA9引脚初始化为复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure); 
  • 通过配置USART1结构体来初始化USART1
	// USART初始化,配置结构体
	USART_InitTypeDef USART_InitStructure; // 定义结构体变量
	USART_InitStructure.USART_BaudRate = 9600;	 // 波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制 -> 不需要
	USART_InitStructure.USART_Mode = USART_Mode_Tx;	// 模式,选择为发送模式
	USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位
	USART_Init(USART1, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART1
  • 使能USART1
	/*USART使能*/
	USART_Cmd(USART1, ENABLE);								//使能USART1,串口开始运行

3. 发送一个字节

void Serial_SendByte(uint8_t Byte)
{
	// 将字节数据写入数据寄存器,写入后USART自动生成时序波形
	USART_SendData(USART1, Byte);
			
	// 等待标志位变为 RESET 标志发送完成
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
	
	/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}

4. 对SendByte函数进行封装(成发送数组)

  • 封装为发送数组,实现方法是调用 Length 次 Serial_SendByte()
/**
  * 函    数:串口发送一个数组
  * 参    数:Array 要发送数组的首地址
  * 参    数:Length 要发送数组的长度
  * 返 回 值:无
  */
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)		//遍历数组
	{
		Serial_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据
	}
}
  • 封装为发送字符串,原理与发送数组相似,结束标志 String[i] == ‘\0’
/**
  * 函    数:串口发送一个字符串
  * 参    数:String 要发送字符串的首地址
  * 返 回 值:无
  */
void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止
	{
		Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据
	}
}
  • 传送 int 数字,将 int 数字的每位依次SendByte
/**
  * 函    数:串口发送数字
  * 参    数:Number 要发送的数字,范围:0~4294967295
  * 参    数:Length 要发送数字的长度,范围:0~10
  * 返 回 值:无
  */
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)		//根据数字长度遍历数字的每一位
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');	//依次调用Serial_SendByte发送每位数字
	}
}
  • 给sendbyte用,函数结果是得到数的乘方,作用是提取sendbyte输入的某一位
/**
  * 函    数:次方函数(内部使用)
  * 返 回 值:返回值等于X的Y次方
  */
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;	//设置结果初值为1
	while (Y --)			//执行Y次
	{
		Result *= X;		//将X累乘到结果
	}
	return Result;
}

5. printf函数

打开Keil的MicroLIB

  • 首先创建一个重定向的函数,将printf的底层重定向到 SendByte
    在原始情况下,电脑上的 printf 底层就是通过fputc(file output char)一个字一个字打印在 cmd 上的,这里重定向了printf的底层函数
/**
  * 函    数:使用printf需要重定向的底层函数
  * 参    数:保持原始格式即可,无需变动
  * 返 回 值:保持原始格式即可,无需变动
  */
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数
	return ch;
}
  • 另一种 printf 方法
	/*方法2:使用sprintf打印到字符数组,再用串口发送字符数组,此方法打印到字符数组,之后想怎么处理都可以,可在多处使用*/
	char String[100];					// 定义字符数组
	sprintf(String, "\r\nNum3=%d", 333);// 使用sprintf,把格式化字符串打印到字符数组
	Serial_SendString(String);			// 串口发送字符数组(字符串)

之前没有学过sprintf,sprintf函数的格式:

int sprintf( char *buffer, const char *format [, argument,] );
// 原文链接:https://blog.csdn.net/yishizuofei/article/details/78195255

除了前两个参数固定外,可选参数可以是任意个。buffer是字符数组名;format是格式化字符串(像:"%3d%6.2f%#x%o",%与#合用时,自动在十六进制数前面加上0x)。只要在printf中可以使用的格式化字符串,在sprintf都可以使用。其中的格式化字符串是此函数的精华。

  • 第三种printf方法,对可变参数sprintf进行封装
void Serial_Printf(char *format, ...) // ... 代表可变参数
{
	char String[100];				// 定义字符数组
	va_list arg;					// 定义可变参数列表数据类型的变量arg
	va_start(arg, format);			// 从format开始,接收参数列表到arg变量
	vsprintf(String, format, arg);	// 使用vsprintf打印格式化字符串和参数列表到字符数组中,不能用sprintf
	va_end(arg);					// 结束变量arg,释放参数表
	Serial_SendString(String);		// 串口发送字符数组(字符串)
}

6. 汉字编码

汉字编码需要同时改变 Keil 编译器和串口助手 / TX 信号的接受方的编码,比如都是utf-8
需要给编译器输入--no-multibyte-chars
在这里插入图片描述


总结

以上就是江科大32串口的发送内容,不包括接收

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值