当前位置: 首页 > news >正文

佛山建设外贸网站公司可信网站图标

佛山建设外贸网站公司,可信网站图标,wordpress做的外贸网站,怎么样推广一个网站[导读] 大家好#xff0c;我是逸珺。前面说会写一下Modbus-RTU的实现#xff0c;写了1000多字了#xff0c;有兴趣的稍等一下哈。前面在一个群里看到一个朋友在一个串口接收中断里打印遇到了问题#xff0c;今天聊下这个话题。扒一扒printf 对于单片机中printf到底向哪里打… [导读] 大家好我是逸珺。前面说会写一下Modbus-RTU的实现写了1000多字了有兴趣的稍等一下哈。前面在一个群里看到一个朋友在一个串口接收中断里打印遇到了问题今天聊下这个话题。扒一扒printf 对于单片机中printf到底向哪里打印这个不同的编译器会有不同的处理方式。比如IAR的printf如果是在线调试有可能通过c-spy打印到IAR的调试终端如果已经将printf重映射到串口那么会从指定的串口打印出去。以IAR ARM开发环境为例来撸一下printf背后究竟是怎么实现的首先写一个简单的hello world开始#include stdio.h int main() {printf(Hello world);return 0; } 接着来查找一下printf的出处在stdio.h中找到了其声明__EFF_NW1  __ATTRIBUTES   void perror(const char *); __EFF_NW1  __DEPREC_PRINTF int  printf(const char *_Restrict, ...); __EFF_NW1  __ATTRIBUTES   int  puts(const char *); __EFF_NW1  __DEPREC_SCANF  int  scanf(const char *_Restrict, ...); __EFF_NR1NW2 __DEPREC_PRINTF int  sprintf(char *_Restrict,                                              const char *_Restrict, ...); __EFF_NW1NW2 __DEPREC_SCANF int  sscanf(const char *_Restrict,  到这里好像无法再进行下去了先看看map文件这里只放了map的一部分dl7M_tln.a: [3]XShttio.o     60  3  9abort.o       6exit.o        4low_level_init.o   4printf.o     40putchar.o    32xfail_s.o      64        1        4xprintffull_nomb.o   3 618xprout.o   22-------------------------------------------------Total:       3 850        4       13...... printf  0x00001be9   0x28  Code  Gb  printf.o [3] putchar  0x00001c6d   0x20  Code  Gb  putchar.o [3] 看到了有一个printf.o模块被编译了有这个文件那么应该有源文件试着在IAR的安装目录下找找果然有.\IAR Systems\Embedded Workbench 8.0\arm\src\lib\dlib\file\printf.cint printf(const char * _Restrict fmt, ...) { /* print formatted to stdout */int ans;va_list ap;  va_start(ap, fmt);ans  _Printf(_Prout, (void *)1, fmt, ap, 0);va_end(ap);return ans; } printf通过使用va_list/va_start/va_end在这里进行可变参数的解析而真正实现最终打印的函数是哪一个呢是下面这句话在起作用_Printf(_Prout, (void *)1, fmt, ap, 0); _Printf的原型是怎样的呢在.\IAR Systems\Embedded Workbench 8.0\arm\src\lib\dlib\DLib.h中发现__ATTRIBUTES int _Printf(_PrintfPfnType *, void *, const char *, __Va_list *,int); _PrintfPfnType这个是啥玩意继续撸下去#if _DLIB_PRINTF_CHAR_BY_CHARtypedef void *(_PrintfPfnType)(void *, char); #elsetypedef void *(_PrintfPfnType)(void *, const char *, _Sizet); #endif 明白了这个是一个函数指针根据打印方式是否是逐字符打印函数指针分了两种模式逐字符模式或者缓冲区模式。在回到printf的定义处发现这个指针传的是_Prout。好接着扒下去在.\arm\src\lib\dlib\formatters\xprout.c发现了其具体的实现#if _DLIB_PRINTF_CHAR_BY_CHAR void *_Prout(void *str, char c) {return (putchar(c)  c ? str : 0); } #else#if _DLIB_FILE_DESCRIPTORvoid *_Prout(void *str, const char *buf, size_t n){return fwrite(buf, 1, n, stdout)  n ? str : 0;}#elsevoid *_Prout(void *str, const char *buf, size_t n){return __write(_LLIO_STDOUT, (unsigned char const *)buf, n)  n ? str : 0;}#endif #endif _DLIB_PRINTF_CHAR_BY_CHAR 宏是根据IAR的DLIB配置做定义。所以IAR编译的时候会包含DLib_Defaults.h这里就定义了逐字符模式宏如果要采用文件方式则需要修改配置。但是一般单片机里不会这么干。所以真正的 _Prout的实现就是这样的了void *_Prout(void *str, char c) {return (putchar(c)  c ? str : 0); } 这样就定位到最终实现字符打印的函数是putchar了而putchar是在哪里声明的呢在stdio.h中发现了它的踪迹__ATTRIBUTES int  putchar(int); 来了一个好像没见过的函数前缀再继续找一下在.\arm\inc\c\yvals.h中找到了#define __ATTRIBUTES  __intrinsic __nounwind 这两个关键字是编译内部使用的文档里没有说明这个是怎么使用的但是我猜想编译器在编译时可能会检测这个函数是否用户定义了同名函数如定义了就使用用户定义的没定义就使用系统库。放一个空的putchar来验证一下#include stdio.h int putchar(int c) {return(c); }int main() {printf(Hello world);return 0; } 然后再看看map文件dl7M_tln.a: [3]abort.o         6exit.o          4low_level_init.o    4printf.o         40xfail_s.o       64    4xprintffull_nomb.o   3 618xprout.o        22------------------------------------------------Total:       3 758   4.......putchar   0x00001bbd    0x2  Code  Gb  main.o [1]    putchar使用了main.o的实现。而如果使用库实现的从前面的map文件看到putchar.o一找发现了putchar.c文件int putchar(int c) { /* put character to stdout */unsigned char uc  c;if (__write(_LLIO_STDOUT, uc, 1)  1){return uc;}return EOF; } 系统原来是调用了__write函数在.\IAR Systems\Embedded Workbench 8.0\arm\inc\c\LowLevelIOInterface.h中找到了 __ATTRIBUTES size_t __write(int, const unsigned char *, size_t); 到这里不继续了你如果再找就发现.\8.0\arm\RTOS\SEGGER\NXP\LPC4357\Start_LPC4357_CMSIS\Setup\SEGGER_RTT_Syscalls_IAR.c有它的实现size_t __write(int handle, const unsigned char * buffer, size_t size) {(void) handle;  /* Not used, avoid warning */SEGGER_RTT_Write(0, (const char*)buffer, size);return size; } 其实就是各种底层具体输出的实现了比如打印到c-spy或者打印到串口。比如在.\8.0\arm\src\flashloader\ST\FlashSTM32F10x\Flash_stm32f10xx.cint putchar(int c) {USART1-DR  c;while(0  (USART1-SR  (1UL  7)));return(c); } 这就是printf重映射到串口的实现这个是一个同步查询单字节串口输出函数。大致就上面的分析总结成一个图就是这样当然这里仅仅分析了逐字符打印的串口的情况。下面回到问题本身为什么中断里不能调用printf为啥ISR不能printf 慢首先中断里肯定不适合调用printf那么为什么呢就比如上面的串口实现方式就以96001个起始位1个停止位8个数据位的常见方式为例你看传输一个字节要1个毫秒如果打印好几个字节就是好几个毫秒了所以答案几乎就已经很清楚了在中断函数里打印会增加中断函数执行的时间。中断需要快进快出比如是一个串口逐字节接收中断函数外部的报文逐字节输入而中断函数先打印一点日志好几个毫秒就过去了。如果UART外设是一个单字节的接收寄存器那完了报文指定被冲掉了。有的UART可能有多字节FIFO但是即便是这样也有很大的概率会被冲掉。这是一个中断里不能调用printf的主要原因执行费时在IAR的文档里也阔以看到如果要实现printf的重定向需要用户实现底层的__write函数那为啥前面又是实现的putchar呢其实putchar最终是调用的__write函数所以直接覆盖putchar肯定也是可以的。大另外如果编译环境配置printf不一样这个内部实现也可能需要很多的存储空间。这对单片机而言也是不合算的。来比较一下把printf去掉int main() {return 0; } 编译出来的结果是    152 bytes of readonly  code memory1024 bytes of readwrite data memory 加上后编译出来是这样  7470 bytes of readonly  code memory34 bytes of readonly  data memory1037 bytes of readwrite data memory 看就这么一句printfcode区增加了近7K字节当然如果你选择其他的printf配置可能会小一些比如不同的单片机编译器对printf的处理会不相同具体可以查查相关文档。不安全这个printf内部再很多编译环境下有可能是线程安全的。如果函数实现内部有加锁在应用程序中调用了printf但还没有执行完。但此时中断来了转而执行中断中断时是无法获取这个锁的此时程序就挂了。解决办法 可以自己实现一个print系统开辟一个环形缓冲区。如果想在中断里打印一点数据不要同步打印先将数据打印到内存再设置一个标志然后再中断外面实现真正的串口输出。如果是裸机程序只需要在主循环里检测缓冲区是否有数据有就输出到真正的串口。如果是RTOS应用可以开辟一个任务将优先级设的低一点在任务内管理这个缓冲区如果有数据就输出到串口。需要注意的是就如前面所说调用接口是不能加锁的否则就不能在中断里使用。有了这个思路要实现就不难了。—END—推荐阅读专辑|Linux文章汇总专辑|程序人生专辑|C语言我的知识小密圈关注公众号后台回复「1024」获取学习资料网盘链接。欢迎点赞关注转发在看您的每一次鼓励我都将铭记于心~嵌入式Linux微信扫描二维码关注我的公众号
http://www.zqtcl.cn/news/518441/

相关文章:

  • 沈阳.....网站设计连云港优化网站团队
  • 网站添加白名单想学ui设计从哪里入手
  • 做期货与做网站的关系淮安市城市建设档案馆网站
  • 网站建设的技术亮点单位宣传册设计样本
  • 网站建设与维护服务敦化市建设局网站
  • 官方网站建设思路wordpress改成织梦
  • 网站建设推广方法网站调优
  • 苏州做企业网站wordpress前台发布文章
  • 怎么建个私人网站网络营销就业前景和薪水
  • 专业的网站开发团队京东电商平台
  • 做网站手机微信小程序怎么加入我的小程序
  • 做网站困难吗公司如何注册网站
  • 可信网站认证收费吗建设化工网站的目的
  • 查网站死链必用工具微信 wordpress
  • 做网站凡科新手如何开微商城店
  • 网站空间维护个人怎么注册一个品牌
  • 连云港网站设计城乡建设网站 资料员
  • 网络优化工程师有多累seo前线
  • 囊谦县公司网站建设新沂网页定制
  • 公众平台网页版wordpress换主题影响seo吗
  • 网站建设什么是静态网页设置wordpress文章标题高亮的代码
  • 男女做那事是什 网站wordpress怎么上传ppt
  • 电商网站图片处理东莞网络营销策划
  • 做知识产权相关的网站网站怎么做登录界面
  • 网站空间备份东莞企业网站教程
  • 新桥企业网站建设有关网站建设的毕业设计
  • 中山网站建设工作修改wordpress后台地址
  • 西安app网站开发如何制作一个自己的网页
  • 陇西学做网站鄂州网约车
  • 做类似58类型网站免费源码分享