前一段时间工作中进行软件调试的时候,将写的程序烧到芯片中,最终总是达不到预期的效果,甚至是就不工作,也不知道具体死在了哪里,这是硬件调试根本查不出来的地方(单步调试又太慢),想起在LINUX开发的时候用到的printf()函数,在合适的地方添加打印信息,并将其打印到终端上,能够很方便的定位到出错的地方,在网上一查果然在STM32中也可以通过某种啥藕断==
对于printf()函数,大家都不陌生,printf()函数是格式化输出函数,一般用于向标准设备按规定格式输出信息,于是终端(显示器、控制台等)输出字符,格式控制由要输出的字符和数据格式组成。要输出的字符除了可以使字母、数字、空格和一些字符号以外,还可以使用转义字符表示特殊的含义。
在STM32中不能够直接使用printf()函数,所以在做嵌入式的开发的时候,虽然可以进行JTAGE调试与软件的仿真,但是有时候还是比较的麻烦,所以这里要对printf()函数进行重定位,以便开发调试人员在STM32的嵌入式的开发的时候调试更加的方便 。
首先初始化一个串口,这里我使用的库函数初始化的USART1
/************************************************
函数名称 : USART_GPIO_Configuration
功 能 : USART所使用管脚输出输入定义
参 数 : 无
返 回 值 : 无
*************************************************/
void USART_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 定义 USART-TX 引脚为复用输出 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //IO口的第9脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //IO口复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //USART输出IO口
/* 定义 USART-Rx 引脚为悬空输入 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //IO口的第10脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //IO口悬空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //USART输入IO口
}
/************************************************
函数名称 : USART_Configuration
功 能 : 配置USART
参 数 : 无
返 回 值 : 无
*************************************************/
void USART_Configuration(void)
{
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE); //开启时钟
/******************************************************************
USART参数初始化: 波特率 传输位数 停止位数 校验位数
115200 8 1 0(NO)
*******************************************************************/
USART_InitStructure.USART_BaudRate = 115200; //设定传输速率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设定传输数据位数
USART_InitStructure.USART_StopBits = USART_StopBits_1; //设定停止位个数
USART_InitStructure.USART_Parity = USART_Parity_No ; //不用校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不用流量控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //使用接收和发送功能
USART_Init(USART1, &USART_InitStructure); //初始化USART1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能USART1接收中断
USART_Cmd(USART1, ENABLE); //使能USART1
}
然后将标准库函数中的fputc(),与fgetc()函数重定位到USART1上去,方法如下:
/************************************************
函数名称 : fputc
功 能 : 重定义putc函数
参 数 : ch --- 字符
*f --- 文件指针
返 回 值 : 字符
*************************************************/
int fputc(int ch, FILE *f)
{
while((USART1->SR & USART_FLAG_TXE) == RESET);
USART1->DR = (ch & (uint16_t)0x01FF);
return ch;
}
/************************************************
函数名称 : fgetc
功 能 : 重定义getc函数
参 数 : *f --- 文件指针
返 回 值 : 字符
*************************************************/
int fgetc(FILE *f)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
当然做完这两步还是不够的,需要添加如下代码,以支持printf()函数,(这里具体为什么要添加这些代码,我也不甚清楚,望各位博友拍砖)
//加入以下代码,支持printf函数,而不需要选择use MicroLIB(在魔术棒,target)
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
/* FILE is typedef’ d in stdio.h. */
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
上面三步完成之后,就可通过串口助手将printf()函数中的信息答应在屏幕上啦。
注:以上是参看博友的博客,再根据自己的理解总结的。