电子说
MCU:MSPM0G3507
前段时间在课内做实验的时候碰到了比较丰富的交互需求,遂打开UART,#include "stdio.h",然后开始重定向。虽然网上有广为流传的重定向方案,但是常年玩STM32的我有点迷惑:为什么TI Driverlib的重定向需要定义三个函数呢?
按照 STM32 重定向的方法,先对fputc进行重定向:
int fputc(int _c, FILE *_fp) {
while((UART0 - > STAT & UART_STAT_TXFF_MASK));
UART0 - > TXDATA = _c;
return _c;
}
观察到,在这种重定向的方案下,printf函数可以输出常字符串,但是无法进行变量的格式化输出。
根据网络上的方案,补充fputs和puts函数,稍作修改:
int fputs(const char *restrict s, FILE *restrict stream) {
uint16_t i, len;
len = strlen(s);
for (i = 0; i < len; i++) {
fputc(s[i], stream);
}
return len;
}
int puts(const char *s) {
int count = fputs(s, stdout);
count += fputs("n", stdout);
return count;
}
在这种重定向方法下,printf成功实现了完整的重定向,可以进行变量的格式化输出——但是,sprintf依然无法工作,为什么呢?
观察三个函数的输入参数,其中两个都包含了一个FILE*输入变量,但是我们在使用的时候却完全没用到。找到FILE的定义:
struct __sFILE {
int fd; /* File descriptor */
unsigned char* buf; /* Pointer to start of buffer */
unsigned char* pos; /* Position in buffer */
unsigned char* bufend; /* Pointer to end of buffer */
unsigned char* buff_stop; /* Pointer to last read char in buffer */
unsigned int flags; /* File status flags (see below) */
};
typedef struct __sFILE FILE;
可见,在TI的库中, FILE类型并没有被简单地改为简单的存储指针,而是依然保留了“数据流”的形式 。再结合debug中端点的触发情况,以及函数之间的调用关系,尝试对FILE*指针进行写入。若调用了puts,认为上层的标准输出走的是printf(),就向下传递空指针,将输出导向 UART。如果stream不是自己设定的空指针,就去编辑stream指向的缓冲区。
对重定向的三个函数进行如下修改:
int fputc(int _c, FILE *_fp) {
if(!(_fp)) {
while((UART0 - > STAT & UART_STAT_TXFF_MASK));
UART0 - > TXDATA = _c;
}
else
*(_fp- >pos) = _c;
return _c;
}
int fputs(const char* restrict s, FILE* restrict stream) {
uint16_t i, len;
len = strlen(s);
for(unsigned int i=0; i < len; i++) {
fputc(s[i], stream);
if(stream) stream- >pos++;
}
return len;
}
int puts(const char *_ptr) {
int count = fputs(_ptr,NULL);
count += fputs("n",NULL);
return count;
}
重定向成功,sprintf和printf均可以正常工作!
那个结构体我还没用完,估计在重定向输入流的时候会用到更多的元素。但是知道这些已经足够了,可以搞点花招,比如把UARTx → TXDATA直接丢到stream里面去,当然FIFO只有一个入口,不需要地址偏移,这么看也是有点麻烦;或者直接把自己的指定buffer设为默认输出区域,等等,虽然更复杂了,但是相对于仅仅把FILE作为一个独立指针,还是更加灵活有趣的!
实力尚浅,还请多多指教!
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !