在单片机程序中使用printf 2015-05
【准备目标平台的串口驱动】 【在KEIL中配置使用stdio】 【测试程序】
在PC机上编写C语言程序的时候,最常用的一个函数就是printf了,printf可以进行格式化输出,控制输出格式,使用起来非常方便,那么在单片机程序开发的时候,能不能也使用这个printf函数呢? Of course,当然可以,但如果要自己从零开始实现,那必定是一个超级麻烦的事情。靠,你不会真的熬夜给写出来了吧,好吧,其实我想问问,如果用这些时间写点有意思的小程序是不是更好。对于大多数喜欢偷懒的童鞋们,比如说我,会用更简单的方式实现在单片机中使用printf的功能。 在常用的集成开发环境中,一般都自带了针对交叉编译环境下目标平台的标准输入输出的算法库,这些库帮忙实现的与硬件无关的部分,我们只要在具体的平台上调用底层驱动同库进行对接,就能通过库函数控制底层设备了。幸运的是,stdio就包含在这些预先准备好的库中。想想stdio在PC机编程中提供的服务我就激动不已,一旦对接了stdio并且在应用程序中引用了stdio.h文件,那就意味着我们可以在程序中调用N多方便的库函数,它们是:printf, scanf, putchar, getchar。你没有看错,比printf更麻烦的scanf在这里也能搞定。那么我们赶快看看如何快速地对接stdio吧。 这里我使用FRDM-KL25作为具体的目标硬件平台,在常用的KEIL环境下进行说明,IAR也可类似配置,此处暂略。 【准备目标平台的串口驱动】无论stdio能够玩出多NB的算法,最终都要“接地气”,我们需要将stdio的输入输出通道映射到串口上。在FRDM-KL25板子上,OpenSDA对接的串口是UART0,那么我就用这个UART0作为stdio的信道。为此,我准备了最最简单的UART0轮询驱动程序(用中断驱动或是DMA驱动也可以,不过实现起来会麻烦得多),配置好波特率及传输格式后,只要实现轮询发送一个字节和接收一个字节的驱动函数即可。 我为UART0实现的基本驱动函数如下: typedef struct { uint32_t BusClkHz; uint32_t Baudrate; } UART_Config_T; /* UART0. */ bool UART0_ConfigTransfer(const UART_Config_T *configPtr) { uint16_t sbr_val; /* Disable the Rx and Tx. */ UART0->C2 &= ~(UART0_C2_TE_MASK | UART0_C2_RE_MASK); /* configure uart1 for 8-bit mode , no parity */ UART0->C1 = 0U; /* calculate the sbr value. */ sbr_val = (configPtr->BusClkHz >> 4)/configPtr->Baudrate; UART0->BDH = (uint8_t)(((0x1F00 & sbr_val) >> 8)&UART0_BDH_SBR_MASK); UART0->BDL = (uint8_t)(sbr_val & UART0_BDL_SBR_MASK); UART0->C3 = 0U; UART0->S1 = 0x1FU; UART0->S2 = 0U; /* enable the tx and rx */ UART0->C2 |= (UART0_C2_TE_MASK | UART0_C2_RE_MASK); return true; } void UART0_PutTxDataBlocking(uint8_t txData) { while (0U == (UART0->S1 & UART0_S1_TDRE_MASK) ) {} UART0->D = txData; } uint8_t UART0_GetRxDataBlocking(void) { while ( 0U != (UART0->S1 & UART0_S1_RDRF_MASK) ) {} return UART0->D; } |
然后,我在stdio_adapter.c文件中实现了fputc和fgetc,完成了stdio同底层驱动的对接。 #include <app_inc.h> #include <stdio.h> int fputc(int c, FILE *f) { UART0_PutTxDataBlocking((uint8_t)c); return c; } int fgetc(FILE *f) { return (UART0_GetRxDataBlocking()); } |
在程序中使用stdio的服务前一定要初始化好UART0,对UART0模块严格遵循先初始化再使用的原则,否则有你好看的。 到此为止,代码部分的准备工作就已经结束了。后面还需要在集成开发环境的工程中进行配置,在工程中包含库。 【在KEIL中配置使用stdio】打开KEIL工程后,戳开“Option for Target <工程名>”对话框,在“Target”标签页下勾选“Use MicroLIB”。
【测试程序】编写测试工程的main.c代码如下: #include "app_inc.h" #include <stdint.h> #include <stdbool.h> #include <stdio.h> int main(void) { char ch; init_board(); printf("\r\nHello, Board.\r\n"); printf("Compiled at %s on %s\r\n", __TIME__, __DATE__); printf("Press any key to display charactor ...\r\n"); while (1) { ch = getchar(); putchar(ch); } } /* EOF. */ |
编译工程将程序下载到FRDM-KL25板子中,打开串口终端,运行,Bingo!
在测试程序中没有验证scanf函数,小伙伴们赶快自己试试吧。 - End 附上PDF版本的文档及工程代码:
|