http://home.eeworld.com.cn/my/space-uid-338727-blogid-47176.htmlphp
// 发送数据 int fputc(int ch, FILE *f) { USART_SendData(USART1, (unsigned char) ch);// USART1 能够换成 USART2 等 while (!(USART1->SR & USART_FLAG_TXE)); return (ch); } // 接收数据 int GetKey (void)
{ while (!(USART1->SR & USART_FLAG_RXNE)); return ((int)(USART1->DR & 0x1FF)); }
#include <stdio.h> #include <rt_misc.h> #pragma import(__use_no_semihosting_swi) extern int SendChar(int ch); // 声明外部函数,在main文件中定义 extern int GetKey(void); struct __FILE { int handle; // Add whatever you need here }; FILE __stdout; FILE __stdin; int fputc(int ch, FILE *f) { return (SendChar(ch)); } int fgetc(FILE *f) { return (SendChar(GetKey())); } void _ttywrch(int ch) { SendChar (ch); } int ferror(FILE *f) { // Your implementation of ferror return EOF; } void _sys_exit(int return_code) { label: goto label; // endless loop }
三、在main文件中添加定义如下两个函数html
int SendChar (int ch) { while (!(USART1->SR & USART_FLAG_TXE)); // USART1 可换成你程序中通讯的串口 USART1->DR = (ch & 0x1FF); return (ch); } int GetKey (void) { while (!(USART1->SR & USART_FLAG_RXNE)); return ((int)(USART1->DR & 0x1FF)); }
至此完成配置,能够在main文件中随意使用 printf 。编程
http://wojiushiwolxw.spaces.eepw.com.cn/articles/article/item/92847less
标准库函数的默认输出设备是显示器,要实如今串口或LCD输出,必须重定义标准库函数里调用的与输出设备相关的函数.异步
例如:printf输出到串口,须要将fputc里面的输出指向串口(重定向),方法以下:ide
只要本身添加一个int fputc(int ch, FILE *f)函数,可以输出字符就能够了#ifdef __GNUC__ /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf set to 'Yes') calls __io_putchar() */ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif /* __GNUC__ */
PUTCHAR_PROTOTYPE { /* Place your implementation of fputc here */ /* e.g. write a character to the USART */ USART_SendData(USART1, (uint8_t) ch); /* Loop until the end of transmission */ while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); return ch; }
因printf()之类的函数,使用了半主机模式。使用标准库会致使程序没法运行,如下是解决方法:函数
方法1.使用微库,由于使用微库的话,不会使用半主机模式.oop
方法2.仍然使用标准库,在主程序添加下面代码:
#pragma import(__use_no_semihosting) _sys_exit(int x) { x = x; } struct __FILE { int handle; }; FILE __stdout;
General Options -- Library Configuration -- Library : Full < file descriptor support >post
#include <stdio.h>
#ifdef __GNUC__ /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf set to 'Yes') calls __io_putchar() */ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif /* __GNUC__ */ PUTCHAR_PROTOTYPE { /* Place your implementation of fputc here */ /* e.g. write a character to the USART */ USART_SendData(USART1, (uint8_t) ch); /* Loop until the end of transmission */ while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); return ch; }
General Options -- Library Configuration -- Library : Normal < NO file descriptor support >ui
#include <stdio.h> size_t __write(int handle, const unsigned char * buffer, size_t size) { // byte by byte write } size_t __dwrite(int handle, const unsigned char * buffer, size_t size) { // buffer[ 0x50 ] }
Buffered Terminal Output : Enabled
xxwritebuffered.c
#define STORE_SIZE 80 static size_t storeLen = 0; static unsigned char store[STORE_SIZE];
uint8_t store[ 0x50 ];
uint32_t storelen;
printf() --> __dwrite() : buffer[0x50]
Buffered Terminal Output : Disabled
printf() --> __write(), byte by byte
自定义输出缓冲区
#define LOG_MAX_STR_LEN 512 void log_printf( const char * fmt, ... ) { char log_buf[ LOG_MAX_STR_LEN ]; va_list args; va_start( args, fmt ); int count = vsnprintf( log_buf, LOG_MAX_STR_LEN, fmt, args ); va_end( args ); // If an output error is encountered, a negative value is returned. if ( count < 0 ) return; // "123456" [123456][0X] : count = 6, n = 8 // "1234567" [1234567][0] : count = 7, n = 8 // "12345678" [1234567][0] : count = 8, n = 8 // "123456789" [1234567][0] : count = 9, n = 8 if ( count >= LOG_MAX_STR_LEN ) count = LOG_MAX_STR_LEN - 1; // now log_buf is C string with the terminating null character __write(0, log_buf, count ); }
log_printf --> __write(), bufferred
http://leon0820.blog.51cto.com/5893766/1440146
在程序的调试过程当中,除了那些高大上的调试手段外,printf无疑是咱们最熟悉最顺手的调试方法。
经过使用printf,咱们能够很方便很直观的获取当前程序的运行状态。
printf()函数是格式化输出函数, 通常用于向标准输出设备按规定格式输出信息。
可是在单片机开发中,通常状况下并不存在标准输出设备,所以咱们须要将printf的输出信息重定向,也就是输出到其余输出设备中去。
在stm32平台上实现重定向的方式有两种,重定向至UART,或者经过JTAG的SW模式将printf重定向至SWO引脚输出。
代码以下:在对UART进行初始化后,经过以下代码对printf进行重定向
int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t) ch); /* Loop until the end of transmission */ while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {} return ch; }
1.在源码中添加对ITM端口寄存器的定义
#define ITM_Port8(n) (*((volatile unsigned char *)(0xE0000000+4*n))) #define ITM_Port16(n) (*((volatile unsigned short*)(0xE0000000+4*n))) #define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000+4*n))) #define DEMCR (*((volatile unsigned long *)(0xE000EDFC))) #define TRCENA 0x01000000
2.经过以下代码将printf的输出重定向至ITM的Port 0
int fputc(int ch, FILE *f) { if (DEMCR & TRCENA) { while (ITM_Port32(0) == 0); ITM_Port8(0) = ch; } return(ch); }
3.经过printf输出调试信息
printf("AD value = 0x%04X\r\n", AD_value);
4.将Jtag设置为SW模式,并设置ITM的Port 0 获取信息。
http://www.dashashi.com/index.php/2014/03/1488
printf在命令行编程的时候是很是经常使用的,虽然是个老函数,可是功能强大,经久不衰
51等8位单片机因为RAM比较小,栈就比较小,跑printf比较吃力,
可是STM32这种32位单片机跑printf就很容易了,而做为一种调试手段,printf十分方便、直观。
比较常见的方法是把printf重定向到串口,不过这须要外接一个串口线,比较麻烦。
其实STM32自带的SWO口是可以异步输出数据的,并且不须要外接什么设备,
ST-LINK/J-Link等带SWO口的调试器都支持。
下面以STM32F4Discovery开发板+GCC为例说明。
根据这里的方法,也能够把printf定位到其余外设。
PS:IAR在编译选项里自带了printf via SWO的功能,就不须要外加设置了。
http://community.silabs.com/t5/Microcontroller-How-to-Guides/SWO-printf-in-IAR/td-p/98257
首先来讲说怎么把信息输出到SWO口,一句话搞定。
ITM_SendChar(ch);
这是在core_cm4.h(若是是F1系列的那就是core_cm3.h)中定义的内联函数。
不过不须要特地去include这个头文件,经过#include "stm32f4xx.h"就间接地将core_cm4.h包含进来。
不过提及来,ITM这个东西其实严格来讲是Cortex-M提供的一个特性,而不是STM32。
利用这个函数把信息输出到SWO口以后再打开St-Link Utility,
在菜单里找到ST-LINK→Printf via SWO Viewer就会弹出一个窗口,
设置System Clock为单片机内核频率,点Start就能看到输出的信息了。
接下来就是把printf函数输出的字符串重定向过去了。
因为单片机的外设功能是根据需求变化的,编译器不可能肯定printf须要用到的外设资源,
因而乎它就干脆留了个接口,也就是_write函数,
固然除了_write函数以外还有_read等其余函数,不过这里咱们用不到。
其声明为 int _write(int fd, char* ptr, int len);
关于_write函数,说简单点,就是全部涉及到输出字符串的函数,
好比printf和putchar(),最终都会跑到_write函数,这里fd是文件标识符,说开来就比较复杂了,
这里咱们用获得的就只有STDOUT_FILENO跟STDERR_FILENO,
其中前一个是标准输出的文件标识符的预约义变量,后一个是错误输出的文件标识符预约义变量。
第二个变量ptr是须要输出的字符串首地址,len就是输出长度。
当咱们调用printf函数后,C运行库会把输入变量转换为最终须要输出的字符串,
而后调用_write函数,将结果输出。咱们的工做就是实现一个_write函数。
新建一个_write.c文件,内容以下:
#include <stdio.h> #include <unistd.h> #include "stm32f10x.h" #ifdef _DEBUG int _write(int fd, char* ptr, int len) { if (fd == STDOUT_FILENO || fd == STDERR_FILENO) { int i = 0; while(i<len) ITM_SendChar(ptr[i++]); } return len; } #endif
加了个#ifdef _DEBUG 的效果是未加 _DEBUG 定义的时候就忽略下面的东西,
由于这东西主要是用在调试阶段,RELEASE版本里面都用不到了,并且多少仍是会影响速度。
其余东西就很简单了- -不须要多说明了吧。
直接编译能经过,可是连接会报错,提示没法找到_read之类的一堆函数。
在连接脚本的下面libgcc.a ( * )后面加上libnosys.a ( * ),就不会报错了。
具体缘由涉及到Cortex-M3使用的newlib库的实现,就不具体展开了。
好吧好吧,其实我也不知道。
若是想把信息定位到串口,能够直接把ITM_SendChar改为相应的串口函数,
也能够利用DMA,先把数据拷贝到DMA缓冲区,让DMA自动传数据,提升响应速度。
http://blog.sina.com.cn/s/blog_79b01f6601018ymr.html
1) 加入stdio.h,这样你就能够调用printf函数了
2) 使能SWO输出
使能SWO输出。最简单的办法就是将以下的函数拷贝到你的工程里面,而且在mian函数初始化以后调用该函数。
void setupSWO(void) { uint32_t *dwt_ctrl = (uint32_t *) 0xE0001000; uint32_t *tpiu_prescaler = (uint32_t *) 0xE0040010; uint32_t *tpiu_protocol = (uint32_t *) 0xE00400F0; CMU->HFPERCLKEN0 |= CMU_HFPERCLKEN0_GPIO; GPIO->ROUTE |= GPIO_ROUTE_SWOPEN; #if defined(_EFM32_GIANT_FAMILY) GPIO->ROUTE = (GPIO->ROUTE & ~(_GPIO_ROUTE_SWLOCATION_MASK)) | GPIO_ROUTE_SWLOCATION_LOC0; GPIO->P[5].MODEL &= ~(_GPIO_P_MODEL_MODE2_MASK); GPIO->P[5].MODEL |= GPIO_P_MODEL_MODE2_PUSHPULL; #else GPIO->ROUTE = (GPIO->ROUTE & ~(_GPIO_ROUTE_SWLOCATION_MASK)) | GPIO_ROUTE_SWLOCATION_LOC1; GPIO->P[2].MODEH &= ~(_GPIO_P_MODEH_MODE15_MASK); GPIO->P[2].MODEH |= GPIO_P_MODEH_MODE15_PUSHPULL; #endif CMU->OSCENCMD = CMU_OSCENCMD_AUXHFRCOEN; while(!(CMU->STATUS & CMU_STATUS_AUXHFRCORDY)); CoreDebug->DHCSR |= 1; CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; *dwt_ctrl = 0x400113FF; *tpiu_prescaler = 0xf; *tpiu_protocol = 2; ITM->LAR = 0xC5ACCE55; ITM->TCR = 0x10009; }
3) 配置Keil的工程选项
打开Keil的工程配置,选择Debug页面,选择仿真器为Cortex-M/R J-Link/J-Trace, 并点击仿真器选项边上的setting选项,打开具体的设置窗口。
在打开的窗口中,切换到Trace页面,选中Enable,而且设置Core Clock为14MHz,分频选项为Core Clock/16。详情以下:
4) 在初始化SWO函数以后的地方,使用printf函数进行输出。例如printf("Hello world")。
5) 在你的工程里面,须要添加以下的函数:
struct __FILE
{
int handle;
};
FILE __stdout; FILE __stdin;
int fputc(int ch, FILE *f)
{ ITM_SendChar(ch); return(ch); }
6) 编译你的代码,而且进入Debug状态
7) 打开Keil的printf-view窗口, 经过 View -> Serial Windows -> Debug(printf) View
8) 点击运行以后,在Debug (printf) View里便可查看
http://www.keil.com/support/man/docs/jlink/jlink_trace_itm_viewer.htm