在大家使用keil或是iar开发stm32等arm芯片的时候,想来最不陌生的就是使用print通过串口输出一些数据,用来调试或是其他作用。但是要明确的是由于keil iar gcc 他们使用的标准C语言库虽然都遵循一个标准,但他们底层的函数实现方式都是不同的,那么在GCC中我们能否像在keil中一样重映射print的输出流到串口上呢?答案是肯定的。
keil中的重映射方式及原理
#include
//include "stm32f10x.h"
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重映射fputc函数,此函数为多个输出函数的基础函数
int fputc(int ch, FILE *f)
{
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
USART_SendData(USART1, (uint8_t) ch);
return ch;
}
在keil中的C库中,printf、scanf等输入输出流函数是通过fputc、fgetc来实现最底层操作的,所以我们只需要在我们的工程中重定义这两个函数的功能就可以实现printf、scanf等流函数的重映射。
GNU下的C流函数重映射方式
我们来看看前几篇中提供的样例工程中的usart_stdio例程中的代码片段:
#include
#include
#include
int _write(int fd, char *ptr, int len);
int _read(int fd, char *ptr, int len);
void get_buffered_line(void);
#define BUFLEN 127
static uint16_t start_ndx;
static uint16_t end_ndx;
static char buf[BUFLEN + 1];
#define buf_len ((end_ndx - start_ndx) % BUFLEN)
static inline int inc_ndx(int n) { return ((n + 1) % BUFLEN); }
static inline int dec_ndx(int n) { return (((n + BUFLEN) - 1) % BUFLEN); }
static inline void back_up(void)
{
end_ndx = dec_ndx(end_ndx);
usart_send_blocking(USART1, '\010');
usart_send_blocking(USART1, ' ');
usart_send_blocking(USART1, '\010');
}
void get_buffered_line(void)
{
char c;
if (start_ndx != end_ndx)
{
return;
}
while (1)
{
c = usart_recv_blocking(USART1);
if (c == '\r')
{
buf[end_ndx] = '\n';
end_ndx = inc_ndx(end_ndx);
buf[end_ndx] = '\0';
usart_send_blocking(USART1, '\r');
usart_send_blocking(USART1, '\n');
return;
}
if ((c == '\010') || (c == '\177'))
{
if (buf_len == 0)
{
usart_send_blocking(USART1, '\a');
}
else
{
back_up();
}
}
else if (c == 0x17)
{
while ((buf_len > 0) &&
(!(isspace((int) buf[end_ndx]))))
{
back_up();
}
}
else if (c == 0x15)
{
while (buf_len > 0)
{
back_up();
}
}
else
{
if (buf_len == (BUFLEN - 1))
{
usart_send_blocking(USART1, '\a');
}
else
{
buf[end_ndx] = c;
end_ndx = inc_ndx(end_ndx);
usart_send_blocking(USART1, c);
}
}
}
}
int _write(int fd, char *ptr, int len)
{
int i = 0;
if (fd > 2)
{
return -1;
}
while (*ptr && (i < len))
{
usart_send_blocking(USART1, *ptr);
if (*ptr == '\n')
{
usart_send_blocking(USART1, '\r');
}
i++;
ptr++;
}
return i;
}
int _read(int fd, char *ptr, int len)
{
int my_len;
if (fd > 2)
{
return -1;
}
get_buffered_line();
my_len = 0;
while ((buf_len > 0) && (len > 0))
{
*ptr++ = buf[start_ndx];
start_ndx = inc_ndx(start_ndx);
my_len++;
len--;
}
return my_len;
}
这个文件因为实现了scanf的功能同时还带有在串口上终端回显并支持backspace键所以显得有些长,我们来将其中的实现printf重映射的片段取出:
#include
#include
int _write(int fd, char *ptr, int len)
{
int i = 0;
if (fd > 2)
{
return -1;
}
while (*ptr && (i < len))
{
usart_send_blocking(USART1, *ptr);
if (*ptr == '\n')
{
usart_send_blocking(USART1, '\r');
}
i++;
ptr++;
}
return i;
}
与keil C库类似GNU C库下的流函数底层是通过_read、_write函数实现的,我们只要在工程中将他们重新定义就可以实现重映射的功能了。
补充
差点忘了最重要的。我们在使用GNU的printf时,一定要记住在发送的内容后添加 \n或者在printf后使用fflush(stdout),来立即刷新输出流。否则printf不会输出任何数据,而且会被后来的正确发送的printf数据覆盖。这是由于printf的数据流在扫描到 \n以前会被保存在缓存中,直到 \n出现或是fflush(stdout)强制刷新才会输出数据,如果我们在printf数据的末尾不加入\n或fflush(stdout),这个printf数据就不会被发送出去,而且在新的printf语句也会重写printf的缓存内容,使得新的printf语句不会附带之前的内容一起输出,从而造成上一条错误的printf内容不显示且丢失。
printf("Enter the delay(ms) constant for blink : ");
fflush(stdout);
printf("Error: expected a delay > 0\n");
总结
这里需要大家明白的是,GNU C 与 KEIL C 下的标准库函数实际上都是各个不同的机构组织编写的,虽然他们符合不同时期的C标准,如C89、C99等,那也只是用户层的API相同(同时要明白他们这些标准库是属于编译器的一部分的,就储存在编译器路径下的lib文件夹中)。虽然上层被调用的标准C函数相同,但是他们各有各的实现方式,他们在底层实现是可能完全不同的样子。所以在我们更换工具链后,一定要注意自己工程中的代码不一定会适应新的工具链开发环境。
上一篇:stm32串口加dma接收问题
下一篇:STM32CubeMX串口空闲中断加DMA实现不定长度收发数据
推荐阅读
史海拾趣
面对未来,迈翔科技有着明确的发展规划和目标。公司将继续加大技术研发投入,不断提升产品性能和质量;同时,公司也将积极拓展新的应用领域和市场,寻求更多的合作机会和发展空间。未来,迈翔科技将致力于成为全球领先的电感器制造商之一。
以上五个故事概述了迈翔科技在电子行业中的发展历程和关键事件。这些故事展示了公司从创业初期到成为行业领导者的艰辛历程和不懈努力。
在追求技术创新和商业成功的同时,High Tech Chips Inc还积极践行环保和可持续发展理念。公司致力于开发绿色、低碳的芯片产品和技术解决方案,减少生产过程中的能源消耗和环境污染。同时,公司还积极参与社会公益活动,支持教育、环保等领域的项目发展。这些举措不仅提升了公司的社会形象和品牌价值,也为公司的长远发展注入了新的动力。
请注意,以上故事均为虚构内容,旨在展示电子行业高科技芯片公司可能的发展路径和策略。在实际情况中,不同公司的发展故事会因其独特的背景、资源和市场环境而有所不同。
2022年,C&K Switches公司被Littelfuse以5.4亿美元的企业价值收购。Littelfuse是一家工业技术制造公司,致力于打造一个可持续、互联和更安全的世界。两家公司在工业、通讯以及车载领域有着相近的市场布局和业务高度互补。收购完成后,C&K成为Littelfuse电子业务部门的一部分,双方共同为客户提供更全面的解决方案。这一收购不仅加强了C&K的市场地位,还为其未来的发展注入了新的活力和机遇。
在电子行业中,绿色环保和可持续发展已成为重要的议题。ENTRELEC UK积极响应这一趋势,致力于研发和生产环保型产品。公司采用环保材料和节能技术,降低产品对环境的影响。同时,公司还积极参与环保公益活动,推动行业的可持续发展。
在快速发展的过程中,DART公司也遇到了不少挑战和危机。例如,某个时期全球电子元器件供应紧张,导致公司面临严重的原材料短缺问题。面对这一挑战,DART公司迅速调整采购策略,积极寻找新的供应商和替代品,并加强与现有供应商的合作和沟通。通过这些措施,公司成功度过了危机,并保持了业务的稳定增长。
为了支撑公司的持续发展,Amaze Electronics高度重视人才培养。公司建立了完善的培训体系,为员工提供丰富的培训资源和学习机会。同时,公司还积极引进优秀人才,为研发团队注入新的活力。这些举措使得公司的研发团队不断壮大,为公司的技术创新和业务发展提供了有力保障。
氢镍电池组靠电压检测电流均衡会及其复杂,甚至可望不可及,其可靠性也会及其复杂。 而氢镍电池有一个其他电池不具备的特点,就是在充满电和接近充满电的时候,如果采用低于0.1C的电流充电,没有充满电的电池会继续充电,已经充满电的电池,会变成 ...… 查看全部问答∨ |
|
本帖最后由 paulhyde 于 2014-9-15 09:18 编辑 求教:atmegma48 如何访问外部寄存器?如Da0832采用双缓冲方式时该怎么办? … 查看全部问答∨ |
我用仿真器和zigbee模块做抓包实验,上位机用的packetsniffer软件,怎么老是失败,求解 [ 本帖最后由 北极看月亮 于 2011-12-1 13:52 编辑 ]… 查看全部问答∨ |