教程示例:嵌入式软件移植 printf

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

    在嵌入式中printf 这种功能强大的函数可谓是c语言库函数的中的一股清流也就是太好用了吧今天分享的例程有 stm32f4ZG 和 cc2530f256这个两款芯片的移植例程和移植教程相信你看完后也可以移植到别的芯片去

    • 使用的keil版本为5.21a
    • IAR for 8051 version 为 10.10.1

    可能会因为有些版本不同配置就略有所不同f4串口1

    f4串口2

    下面这个是TI 的cc2530(就先跑着寄存器版本的吧协议栈里头配置差不太多):

    为啥要移植呢相信学过实时操作系统的都有所了解有个叫互斥信号量的东西就是为了防止同一时间内有两个寄存器(那十来个寄存器不是指外设)在访问同一块内存这个很严重的可能会导致程序死机或者卡在了某个死循环里面

    我们所使用的 printf 就是首先通过 vsprinf 对我们传进来的参数进行格式化我们传进去多少数据他按照我们传进来的格式格式化后统统存放在一个buf里如果我们定义了重定向它就会把这个buf发送至你要发送的地方

    想象一下如果在某个系统中有一个线程在vsprintf里面运行突然有一个任务级别比他高的任务把它运行的时间抢了过去而任务优先级高的任务也在printf里面使用了那个buf那就会产生上述的问题

    那就意味着我们所使用的 printf 存在不安全问题当然我们在系统中可以通过临界保护区来处理也可以通过信号量等等处理

    但是目前有一个线程安全的函数摆在你面前就问你用不用

    那我们的上边 printf_u1 它是怎么处理的呢毫无疑问看代码就能知道它是直接发送的也就是说你传进来的每一个参数它都会顺手就发送出去当然要看你配置它的数据将不会存放在一个 buf 里边在发送。这就是他给力的地方了

    keil移植printf

    首先我们到文章的末尾获取到源代码后打开 keil 软件, 点击 project > Options for… 就能打开一下页面

    点击 C/C++,选择c99标准。到此为止编译器配置完毕

    下面来配置代码部分打开头文件为printf.h

    1添加这个两个文件的路径还有头文件如果不懂可以搜搜2点击图片上面的printf_ 去它定义的地方我们要做出一定的修改。

    来到这里之后我们可以看到_vsprintf 这个代码里面最复杂的就是这一串函数了

    //我们只需要关心第一个参数即可它是一个函数指针
    static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
    //指向了一个参数为下图的函数返回值为void
    typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
    

    我们只要修改这_putchar 里面的内容即可。参数二是我加上去的为了区分不同的串口类型有多少个串口就的写多少个_out_char 这样的函数(这个是我的方法)

    static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
    {
      (void)buffer; (void)idx; (void)maxlen;
      if (character) 
      {
        _putchar(character,1);
      }
    }
    

    上图是串口1的。再来看看串口2的

    int printf_u2(const char* format, ...)
    {
      va_list va;
      va_start(va, format);
      char buffer[1];
      const int ret = _vsnprintf(u2_out_char, buffer, (size_t)-1, format, va);
      va_end(va);
      return ret;
    }
    //注意vsprintf第一参数
    static inline void u2_out_char(char character, void* buffer, size_t idx, size_t maxlen)
    {
      (void)buffer; (void)idx; (void)maxlen;
      if (character) {
        _putchar(character,2);
      }
    }
    

    而_putchar 就是我们数据最终流向的地方了我是这样写的。

    void _putchar(char character,char sw)
    {
      // send char to console etc.
      if(sw == 1)
      {
        while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
        USART1->DR = (u8) character;
      }
      if(sw == 2)
      {
      while((USART2->SR&0X40)==0);//循环发送,直到发送完毕
         USART2->DR = (u8) character;
      }
      if(sw == 3)
      {
      while((USART3->SR&0X40)==0);//循环发送,直到发送完毕
         USART3->DR = (u8) character;
      }
    }
    

    这个 USART3->DR 为stm32串口的寄存器读者可参照自己的芯片来配置

    简单就完了

     资料直通车Linux内核源码技术学习路线+视频教程内核源码

    学习直通车Linux内核源码内存调优文件系统进程管理设备驱动/网络协议栈

    IAR 配置方法

    首先添加文件

    我在这里起名字为 C_Library。然后在添加文件

    选择 printf.c (反正就是添加这个文件放到哪里看你自己)

    接着就是添加编译器头文件路径

    在选择第一个大框框右边的那个

    点击Click to add。选择头文件路径所在的文件夹后 select接着就会回到这里

    上面是配置头文件路径的第一个是绝对路径也就是说你工程文件移动了就回到不到了第二个是相对路径选第二个吧接着编译就会发现报错了不要慌我们首先选择Options > General Options

    此时在编译一次就会发现也还是错了说什么没定义。经研究哦原来是stdint.h这个头文件在for 8051 这款工具上没有定义64为的无符号整形那个怎么办啊经发现哦 原来注释这个宏就行

    程序编译通过printf_u0 和stm32 配置类型无非就是修改一下数据的流向读者自己研究一下吧

     

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6