STM32串口通信中使用printf發送數據配置方法(開發環境 Keil RVMDK)
http://home.eeworld.com.cn/my/space-uid-338727-blogid-47176.html
// 發送數據 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 }
3、在main文件中添加定義以下兩個函數
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 。
STM32程序添加printf函數后無法運行的解決方法(串口實驗)
http://wojiushiwolxw.spaces.eepw.com.cn/articles/article/item/92847
標准庫函數的默認輸出設備是顯示器,要實現在串口或LCD輸出,必須重定義標准庫函數里調用的與輸出設備相關的函數.
例如:printf輸出到串口,需要將fputc里面的輸出指向串口(重定向),方法如下:
只要自己添加一個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.使用微庫,因為使用微庫的話,不會使用半主機模式.

方法2.仍然使用標准庫,在主程序添加下面代碼:
#pragma import(__use_no_semihosting) _sys_exit(int x) { x = x; } struct __FILE { int handle; }; FILE __stdout;
IAR EWARM
General Options -- Library Configuration -- Library : Full < file descriptor support >
#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 >
#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
stm32系列單片機之printf重定向
http://leon0820.blog.51cto.com/5893766/1440146
在程序的調試過程中,除了那些高大上的調試手段外,printf無疑是我們最熟悉最順手的調試方法。
通過使用printf,我們可以很方便很直觀的獲取當前程序的運行狀態。
printf()函數是格式化輸出函數, 一般用於向標准輸出設備按規定格式輸出信息。
但是在單片機開發中,一般情況下並不存在標准輸出設備,因此我們需要將printf的輸出信息重定向,也就是輸出到其他輸出設備中去。
在stm32平台上實現重定向的方式有兩種,重定向至UART,或者通過JTAG的SW模式將printf重定向至SWO引腳輸出。
首先介紹第一種,重定向至UART,這種方式我們比較熟悉,ST官方提供的固件庫中也是使用的這種方法。
代碼如下:在對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; }
通過JTAG的SW模式將printf重定向至SWO引腳輸出
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 獲取信息。
STM32中重定向printf到SWO口
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自動傳數據,提高響應速度。
STM32片內外設--DBG之Keil SWO輸出
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里即可查看

Debug (printf) Viewer
http://www.keil.com/support/man/docs/jlink/jlink_trace_itm_viewer.htm
Segger RTT : Real Time Terminal
http://segger.com/jlink-real-time-terminal.html
