用Vs调试STM32记事
直接导入keil的工程文件,会有各种问题,如关联不上头文件,即使加载了头文件,也不能访问里面的定义??一头雾水,由于对GCC编译器不熟悉,并且用这种方式导入的文件,.h文件的颜色也不对,按F12可以关联到定义处,但颜色 却显示的是没有被编译的………………只能是另外找方法。
一步一步建立个MS_Build文件,按照keil的目录结构重建文件夹,将头文件包含设置好如图:
很简单的一个程序,但发现还是不能关联头文件,由于这次的GDB的完全由自己定义一个一个找原因,找不到GPIOInit等的定义。
最终发现:
全局宏设置部分Preprocessor macros,各项是用逗号分隔开的(keil的方式),这样就没办法加载后面的,所以就关联不起来,原来后面的“+”是可以一项项添加的,并且是用分号来隔开的。
第2个问题:重定义了#define EnableIrq() __enable_irq()//开关中断,但就是提示没有非预期的函数声名,造成编译不通过,试过了各种办法,都不行,参考https://stackoverflow.com/questions/8024440/what-causes-the-error-undefined-reference-to-some-function,前面加上了void 通过#define EnableIrq() void __enable_irq()。
修改后,就可以正常关联头文件了,可以正常编译,但串口printf还是不能正常打印?怎么回事呢???
在GCC下printf的重映射不再是putch了,下是是从网上查的资料,改写—write后,串口可以正常打印了。
在大家使用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等流函数的重映射。
GUN下重映射:
#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 127static 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;
}
差点忘了最重要的。我们在使用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函数相同,但是他们各有各的实现方式,他们在底层实现是可能完全不同的样子。所以在我们更换工具链后,一定要注意自己工程中的代码不一定会适应新的工具链开发环境。
对于只能输出*.bin文件的问题,也是折腾了差不多一天才真正找到答案,下载最新的GUN_ARM最新版的是2019-7-10日,再编译,可以正常输出的*.hex文件了。但也发现其实GCC这种开源的编译器编出的代码体积还是挺大的,与MDK实际对比,要大出很多,这个问题,看了很多文章,都没有提到这回事,所以用VS开发像单片机这种项目还是不很合适的。除非是不在乎成本,选个FLASH超出项目预估很多的芯片,这样还可以勉强用用,有时再研究吧,基本的lVS下的程序已经搭建好了。就是项目是低成本的,不可能再换芯片了。
用Vs调试STM32记事相关推荐
- (转)jLink使用ITM机制实现调试stm32单片机
----------------------------------------------------------------------------------------------- 作者:p ...
- semihost/ITM机制浅析以及使用JLINK通过ITM调试stm32单片机
使用ITM机制实现调试stm32单片机,实现printf与scanf. ITM简介 ITM机制是一种调试机制,是新一代调试方式,在这之前,有一种比较出名的调试方式,称为半主机(semihosting) ...
- 009:semihost/ITM机制浅析以及使用JLINK通过ITM调试stm32单片机(转)
----------------------------------------------------------------------------------------------- 作者:p ...
- 单片机编程软件很简单(23),keil单片机编程软件调试stm32
单片机编程软件的使用意义较强,不论是工作还是学习,单片机编程软件都是常被使用的软件之一.本文对于单片机编程软件的介绍将基于如何采用keil单片机编程软件调试stm32,如果你对单片机编程软件抑或本文内 ...
- 基于 Ubuntu 18.04 Jlink + Vim + Cmake + Makefile 调试 STM32
Ubuntu 18.04 Jlink + Vim + Cmake + Makefile 调试 STM32 作者:解琛 时间:2020 年 8 月 3 日 Ubuntu 18.04 Jlink + Vi ...
- keil调试stm32无法退出debug
keil调试stm32,debug之后有时会遇到这种情况.导致无法退出debug,只能任务管理器强制结束任务. 原因: keil对中文的支持不够友好. 工程路径过深或路径中有中文. 调试过程中打了断电 ...
- semihost/ITM机制浅析以及使用JLINK通过ITM调试stm32单片机(转)
----------------------------------------------------------------------------------------------- 作者:p ...
- 使用ITM机制实现调试stm32单片机
使用ITM机制实现调试stm32单片机,实现printf与scanf. 1. ITM简介 ITM机制是一种调试机制,是新一代调试方式,在这之前,有一种比较出名的调试方式,称为半主机(semihosti ...
- 使用keil调试STM32,定时器的CNT寄存器数值在断点读取数值不准确问题
使用keil调试STM32,定时器的CNT寄存器数值在断点读取数值不准确问题 比如定时器更新中断,在中断服务程序打下断点后,程序没有执行.但是定时器的计数器一直在计数,并不会停止,同时调试器读取数值需 ...
最新文章
- 高并发高流量网站架构详解--转载
- centos8安装MySQL依赖_centos8安装mysql8
- ELK和EFK的区别
- 蓝桥杯 ADV-207 算法提高 最长字符序列
- FLASH AS3 TextField
- vue仿网易云音乐播放器界面
- 如何将几个pdf合并成一个pdf?
- 无线城域网-无线广域网
- 服务器135、137、138、139、445等端口解释和关闭方法
- 【网络基础】通俗易懂的搞明白什么是IP地址(小白向)
- 基于uni-app手机端后台管理系统uni-uadmin
- JDK8 双冒号用法
- 注册表usbstor删除不了_彻底清除注册表U盘与USB使用记录技巧
- 全国增值税发票查验平台 | 免验证码
- 计算机设计大赛感言,计算机编程比赛获奖感言.doc
- android crt证书,android https 抓包,root安装证书
- 一文读懂法拉第未来赴美上市:合并PSAC,能否“卷土重来”?
- Maven Plugin fork 在 pom.xml中的作用
- 数据结构(C语言第2版) 课后习题答案之 第六章 图
- 两轮差速小车循线控制原理分析