STM32第十四天串口
一:串口发送字符和字符串和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)
-
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)
-
USART_FLAG_TC
(Transmission Complete) - 传输完成标志-
含义: 当
USART_FLAG_TC
被置位(=1)时,表示一次完整的发送操作已经完成。 -
触发时机: 需要同时满足以下两个条件:
-
发送数据寄存器 (TDR) 为空 (
USART_FLAG_TXE
= 1)。 -
发送移位寄存器 (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
标准输出)。