STM32第十四天串口

发布于:2025-07-07 ⋅ 阅读:(24) ⋅ 点赞:(0)

一:串口发送字符和字符串和printf重定向

usart.c

#include "stm32f10x.h"
#include "usart.h"
#include "stdio.h"



void my_usart_Init()//千万不要和32库里面串口定于的名字一样,不然会报错
{
	GPIO_InitTypeDef my_usart_Initstruct;
	USART_InitTypeDef USART_Initstruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);
	
	
	my_usart_Initstruct.GPIO_Pin=GPIO_Pin_9 ;
	my_usart_Initstruct.GPIO_Mode=GPIO_Mode_AF_PP ;
	my_usart_Initstruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &my_usart_Initstruct);
	
	my_usart_Initstruct.GPIO_Pin=GPIO_Pin_10 ;
	my_usart_Initstruct.GPIO_Mode=GPIO_Mode_IN_FLOATING ;
	GPIO_Init(GPIOA, &my_usart_Initstruct);
	
	USART_Initstruct.USART_BaudRate=115200;
	USART_Initstruct.USART_HardwareFlowControl= USART_HardwareFlowControl_None  ;
	USART_Initstruct.USART_Mode= USART_Mode_Rx | USART_Mode_Tx;
	USART_Initstruct.USART_Parity= USART_Parity_No;
	USART_Initstruct.USART_StopBits=USART_StopBits_1  ;
	USART_Initstruct.USART_WordLength=USART_WordLength_8b;
	USART_Init(USART1,&USART_Initstruct );
	USART_Cmd(USART1, ENABLE);
	
	
	

}
void My_Usart_Send_Byte(USART_TypeDef* USARTx, uint16_t Data)
{
	USART_SendData( USARTx, Data);
	while(USART_GetFlagStatus( USARTx, USART_FLAG_TXE) != SET);


}

void My_Usart_Send_Sting(USART_TypeDef* USARTx,char * str)
{
	uint16_t i=0;
	do
	{
		My_Usart_Send_Byte(USARTx,*(str+i));
		i++;
		
		
	}while(*(str+i) != '\0');
	
	while(USART_GetFlagStatus( USARTx, USART_FLAG_TC) != SET);
	

}

int fputc(int ch, FILE * p)
{
	USART_SendData( USART1, (u8)ch);
	while(USART_GetFlagStatus( USART1, USART_FLAG_TXE) != SET);
	return ch;

}



usart.h

#ifndef USART_H_
#define USART_H_

void my_usart_Init(void);
void My_Usart_Send_Byte(USART_TypeDef* USARTx, uint16_t Data);
void My_Usart_Send_Sting(USART_TypeDef* USARTx, char * str);

#endif

main.c

#include "stm32f10x.h"
#include "main.h"
#include "led.h"
#include "Bear.h"
#include "key.h"
#include "relay.h"
#include "shake.h"
#include "wireless.h"
#include "exti_key.h"
#include "usart.h"
#include "stdio.h"
 
void delay(uint16_t time)//延时1ms  软件延时粗延时
{
	uint16_t i=0;
	while(time --)
	{
		i=12000;
		while(i --);
	}
	
}
 
 
int  main()
{
	my_usart_Init();
	LED_Init();

//	My_Usart_Send_Byte( USART1, 'A');
//	My_Usart_Send_Byte( USART1, 'B');
//	My_Usart_Send_Byte( USART1, 'C');

//	My_Usart_Send_Sting( USART1, "kobe \r\n");
	printf("kobe is king \r\n");
   while(1)
	 {
//		USART_SendData( USART1,  'A');
		
		 
	 }
		 
		
}
	

代码心得

1:void My_Usart_Send_Sting(USART_TypeDef* USARTx,char * str)复习一下char*指针的用法和知识点

知识点 说明
char* 本质 指向字符内存地址的指针,用于表示字符串。
字符串终止符 必须以 '\0' 结尾,否则函数会越界访问。
指针运算 str++ 移动至下一字符;*str 访问当前字符。
函数参数传递 传递字符串首地址,高效且节省内存。
const 安全性 不修改字符串时使用 const char*,避免误操作只读数据。
常见错误 修改字符串常量、未添加终止符会导致未定义行为。

 2:do-while循环详解

1.基本语法:do { // 循环体} while (condition);

执行顺序:先执行循环体内的语句,然后判断条件(condition)是否成立。若条件成立,则继续执行循环体;否则退出循环。

特点:循环体至少执行一次,即使条件一开始就不成立。

2. 与while循环的区别

while循环:先判断条件,再决定是否执行循环体(可能一次都不执行)。

do-while循环:先执行循环体一次,再判断条件(至少执行一次)。

3:理解USART_FLAG_TXE: Transmit data register empty flag  和USART_FLAG_TC: Transmission Complete flag

这两个标志位都是USART/UART通信中用于发送状态的重要标志,但它们指示的发送阶段不同,非常容易混淆。理解它们的区别对于正确使用串口发送数据至关重要:

一:USART_FLAG_TXE (Transmit Data Register Empty)

  1. USART_FLAG_TXE (Transmit Data Register Empty) - 发送数据寄存器空标志

    • 含义: 当USART_FLAG_TXE被置位(=1)时,表示发送数据寄存器 (TDR - Transmit Data Register) 已经空了

    • 触发时机: 当前正在发送的数据字节已经从TDR寄存器移入到发送移位寄存器 (Transmit Shift Register) 中。此时TDR寄存器是空的,可以安全地写入新的待发送数据。

    • 主要用途:

      • 判断是否可以写入新数据: 这是它的核心用途。在查询方式(非中断/DMA)发送时,你需要检查USART_FLAG_TXE是否为1。如果是1,说明TDR空了,你可以立即调用USART_SendData()写入下一个字节。在中断方式下,当TXEIE中断使能时,TXE事件会触发中断,在中断服务程序里你就可以写入下一个字节。

      • 避免覆盖未发送的数据:确保你在写入新数据时,硬件已经准备好接收它。

    • 关键点: TXE=1 并不表示 数据已经物理发送到线路上,只表示数据离开了TDR进入了下一个发送阶段(移位寄存器)。最后一个字节的数据可能还在移位寄存器中,正在一位一位地通过TX引脚发出。

二:USART_FLAG_TC (Transmission Complete)

  1. USART_FLAG_TC (Transmission Complete) - 传输完成标志

    • 含义: 当USART_FLAG_TC被置位(=1)时,表示一次完整的发送操作已经完成

    • 触发时机: 需要同时满足以下两个条件:

      1. 发送数据寄存器 (TDR) 为空 (USART_FLAG_TXE = 1)。

      2. 发送移位寄存器 (Transmit Shift Register) 也为空(即最后一位数据,包括停止位,已经完全从TX引脚发送出去)。

    • 主要用途:

      • 判断整个数据包是否发送完毕: 这是它的核心用途。当你发送一串数据(比如一个字符串)后,你需要确认所有字节(包括最后一个字节的所有位)都已经物理发送到线路上。此时应检查USART_FLAG_TC

      • 安全操作: 在确认TC=1之后,你可以安全地执行一些需要等待发送完全结束的操作,例如:

        • 关闭USART模块。

        • 改变通信参数(波特率、数据位等)。

        • 切换收发方向(在单线半双工通信中)。

        • 断言DE信号(在RS-485通信中释放总线)。

      • 在中断方式下,当TCIE中断使能时,TC事件会触发中断,通知你发送彻底完成。

    • 关键点: TC=1 明确表示数据已经物理离开了芯片的TX引脚,发送线路已经完全空闲。

 

形象比喻:

想象一个邮局(USART)有两个工作台:

  • TDR (工作台1 - 打包台): 工作人员在这里把信件(数据字节)装进信封(准备发送帧)。

  • 移位寄存器 (工作台2 - 寄送台): 工作人员从这里把信封投入邮筒(TX引脚),一位一位地塞进去(串行发送)。

  • TXE=1: 表示“打包台空了”。打包台的工作人员把信件装好信封后,递给了寄送台的工作人员。这时你可以把下一封信交给打包台的工作人员了。但之前装好的那封信,寄送台的工作人员可能还在往邮筒里塞呢(数据正在发送中)。

  • TC=1: 表示“打包台空了 并且 寄送台也空了”。不仅打包台没信了(TXE=1),寄送台的工作人员也已经把上一封信完全塞进了邮筒(所有位发送完毕)。整个发送流程彻底结束,线路空闲。

总结区别:

特性 USART_FLAG_TXE (TXE) USART_FLAG_TC (TC)
全称 Transmit Data Register Empty Transmission Complete
含义 发送数据寄存器 (TDR) 空 传输完成 (TDR空  移位寄存器空)
指示阶段 可以安全写入下一个字节 上一个字节(及之前所有字节)已完全物理发出
硬件条件 TDR寄存器空 (数据已移入移位寄存器) TDR寄存器空 并且 移位寄存器空 (线路空闲)
主要用途 检查是否可以写入新数据 (查询/中断填充) 检查发送是否彻底完成 (安全关闭/切换/后续操作)
发送状态 数据可能还在移位寄存器中发送 数据已完全离开TX引脚,发送线路空闲
中断关联 TXEIE (发送数据寄存器空中断使能) TCIE (发送完成中断使能)
清除方式 USART_SR寄存器 + 写USART_DR寄存器 USART_SR寄存器 + 写USART_DR寄存器  软件序列(读SR后写DR)

 

4:串口需要换行要用\r\n

5:写串口函数一定要给串口外设使能

6:while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); 它的逻辑含义是:循环等待,直到USART1的发送数据寄存器空标志(TXE)被置位(即变为SET状态)才退出循环。

7:int fputc(int /*c*/, FILE * /*stream*/)

  • 返回值 int
    成功时返回写入的字符,失败时返回 EOF(通常是 -1)。

  • 参数1 int c
    要输出的字符(以 int 形式传递,支持 EOF 特殊值)。

  • 参数2 FILE *stream
    指向文件流的指针(如 stdout 标准输出)。