printf重定向
- fputc函数实现
- 使用 Use MicroLIB

usart.h
#ifndef __USART_H
#define __USART_H
#include "system.h"
#include "stdio.h" //要实现int fputc(int ch,FILE *p) 原型,需要包含该头文件
void USART1_Init(u32 bound);
#endif
usart.c
#include "usart.h"
int fputc(int ch,FILE *p) //函数默认的,函数原型不能改变,在使用printf函数时自动调用
{
USART_SendData(USART1,(u8)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); //USART_FLAG_TXE状态标志表示可以发送下一个数据了,发送数据寄存器已经空了(发送移位寄存器数据可能没有发完)
return ch;
}
//bound:参数为输入波特率
void USART1_Init(u32 bound)
{
GPIO_InitTypeDef MY_GPIO_Init; //定义结构体变量
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//默认引脚 PA9/PA10,没有重映射 → 不需要使能 AFIO 时钟!
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能串口1时钟
MY_GPIO_Init.GPIO_Pin = GPIO_Pin_9; //TX -- PA9
MY_GPIO_Init.GPIO_Mode = GPIO_Mode_AF_PP; //设置复用推挽输出模式
MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOA, &MY_GPIO_Init);
MY_GPIO_Init.GPIO_Pin = GPIO_Pin_10; //RX -- PA10
MY_GPIO_Init.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置浮空输入
MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOA, &MY_GPIO_Init);
USART_InitStructure.USART_BaudRate = bound; //设置波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置数据位为8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //设置停止位为1位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送使能和接收使能
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE); //使能 USART1串口
//清除USART_FLAG_TC发送完成标志位,这个是状态标志位,并非中断标志位
USART_ClearFlag(USART1, USART_FLAG_TC); //清除USART_FLAG_TC发送完成标志位
//设置串口中断类型并使能
//USART_IT_RXNE ------ 接收数据寄存器非空中断,"我收到了一个字节数据,已经放进接收寄存器了,快来读!"
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //我们选择的是接收中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //在"stm32f10x.h"查看
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler(void)
{
u8 data = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //串口接收中断标志位,检查指定的 USART 中断发生与否,当调用接收函数时会被自动清除。
{
//当你调用 USART_ReceiveData() 读取数据时,硬件会自动清除 USART_IT_RXNE!
data = USART_ReceiveData(USART1);
USART_SendData(USART1, data); //将接收的数据发送出去
//USART_FLAG_TC发送完成标志位,表示当移位寄存器的最后一位数据被发送到 TX 引脚,且总线空闲时,才置 1。表示数据已经完全发完,总线已经空闲。
//USART_FLAG_TXE发送数据寄存器空,当 TDR 寄存器的数据被转移到移位寄存器后,立刻置 1,表示 "我可以写下一个数据了"。
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET); //USART_FLAG_TC发送完成标志位
//while(USART_GetITStatus(USART1, USART_FLAG_TC) != SET); //开始错用了这个函数,导致中断程序在这里卡死!!!
}
USART_ClearFlag(USART1, USART_FLAG_TC); //清除USART_FLAG_TC发送完成标志位,这个是状态标志位,并非中断标志位
}
main.c
#include "system.h" //已经包含了"stm32f10x.h" 以及定义了位带操作的宏,后续只包含该头文件就可以了
#include "SysTick.h" //使用delay_ms和delay_us
#include "usart.h"
#include "led.h" //需要调用Led_Init()对8个LED GPIO初始化
#include "stdio.h" //使用printf函数时需要包含
//这里不用定义SystemInit函数,是因为在system_stm32f10x.c中已有实现
int main()
{
u8 i = 0;
u16 j = 0;
u16 data = 1234;
float fdata=12.34;
char str[]="Hello World!";
SysTick_Init(72); //72为SYSCLK delay_ms延时函数需要使用
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断分组2:2 在使用中断回调函数时都需要调用
Led_Init();
USART1_Init(9600); //在之后才能使用printf函数
while(1)
{
i++;
if(i%20 == 0)
{
PCout(4) = !PCout(4); //LED5状态取反 20*10ms = 200ms
j++;
if(j%25 == 0) //5S打印一次
{
printf("打印整数类型 data = %d\r\n", data);
printf("打印浮点数 fdata = %f\r\n", fdata);
printf("打印字符串 str = %s\r\n", str);
}
}
delay_ms(10);
}
}
现象,每间隔5S打印一次,如图:
