一:串口發送字符和字符串和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
?標準輸出)。
?