解决STM8类型单片机空间太小,使用不了printf串口打印问题

2019-12-04来源: eefocus关键字:STM8  单片机空间太小  printf  串口打印

概述:


在使用STM8L101F3这款单片机时,由于它只有8K的flash,空间非常小,只要调用C库函数printf编译后整个文件很大,直接程序溢出。这也就意味着我们实现printf串口打印调试就没办法进行。既然使用不了库函数,那么我们就可以自己动手封装类似printf的函数,这样我们就可以实现数据串口打印啦。这里就直接放上我的STM8L101F3的部分源码了,希望可以给你一些参考。


源码:


#include "stdarg.h"

#include "stm8l10x.h"

 

void USART_Config(void)

{

    /*Set the USART RX and USART TX at high level*/

    GPIO_ExternalPullUpConfig(GPIOC,GPIO_Pin_3|GPIO_Pin_4, ENABLE);

    /* Enable USART clock */

    CLK_PeripheralClockConfig(CLK_Peripheral_USART, ENABLE);

    USART_DeInit();

    USART_Init((uint32_t)9600, USART_WordLength_8D, USART_StopBits_1,

                USART_Parity_No, (USART_Mode_TypeDef)(USART_Mode_Rx | USART_Mode_Tx));

    USART_Cmd(DISABLE);

    enableInterrupts();

    USART_Cmd(ENABLE);    

}

 

/*发送串口数据*/

void send_uart_data(uint8_t data)

{

  while (USART_GetFlagStatus(USART_FLAG_TXE) == RESET);

  USART_SendData8(data);

}

 

/*

  功能:将int型数据转为2,8,10,16进制字符串

  参数:value --- 输入的int整型数

        str --- 存储转换的字符串

        radix --- 进制类型选择

  注意:8位单片机int字节只占2个字节

*/

char *sky_itoa(int value, char *str, unsigned int radix)

{

  char list[] = "0123456789ABCDEF";

  unsigned int tmp_value;

  int i = 0, j, k = 0;

  if (NULL == str) {

    return NULL;

  }

  if (2 != radix && 8 != radix && 10 != radix && 16 != radix) {

    return NULL;

  }

  if (radix == 10 && value < 0) {

    //十进制且为负数

    tmp_value = (unsigned int)(0 - value);

    str[i++] = '-';

    k = 1;

  } else {

    tmp_value = (unsigned int)value;

  }

  //数据转换为字符串,逆序存储

  do {

    str[i ++] = list[tmp_value%radix];

    tmp_value /= radix;

  } while(tmp_value);

  str[i] = '';

  //将逆序字符串转换为正序

  char tmp;

  for (j = k; j < (i+k)/2; j++) {

    tmp = str[j];

    str[j] = str[i-j-1+k];

    str[i-j-1+k] = tmp;

  }

  return str;

}

 

/*

  功能:将double型数据转为字符串

  参数:value --- 输入的double浮点数

        str --- 存储转换的字符串

        eps --- 保留小数位选择,至少保留一个小数位,至多保留4个小数位

  注意:8位单片机int字节只占2个字节

*/

void sky_ftoa(double value, char *str, unsigned int eps)

{

  unsigned int integer;

  double decimal;

  char list[] = "0123456789";

  int i = 0, j, k = 0;

  //将整数及小数部分提取出来

  if (value < 0) {

    decimal = (double)(((int)value) - value);

    integer = (unsigned int)(0 - value);

    str[i ++] = '-';

    k = 1;

  } else {

    integer = (unsigned int)(value);

    decimal = (double)(value - integer);

  }

  //整数部分数据转换为字符串,逆序存储

  do {

    str[i ++] = list[integer%10];

    integer /= 10;

  } while(integer);

  str[i] = '';

  //将逆序字符串转换为正序

  char tmp;

  for (j = k; j < (i+k)/2; j++) {

    tmp = str[j];

    str[j] = str[i-j-1+k];

    str[i-j-1+k] = tmp;

  }

  //处理小数部分

  if (eps < 1 || eps > 4) {

    eps = 4;

  }

  

  //精度问题,防止输入1.2输出1.19等情况

  double pp = 0.1;

  for (j = 0; j <= eps; j++) {

    pp *= 0.1;

  }

  decimal += pp;

  while (eps) {

    decimal *= 10;

    eps --;

  }

  int tmp_decimal = (int)decimal;

  str[i ++] = '.';

  k = i;

  //整数部分数据转换为字符串,逆序存储

  do {

    str[i ++] = list[tmp_decimal%10];

    tmp_decimal /= 10;

  } while(tmp_decimal);

  str[i] = '';

  //将逆序字符串转换为正序

  for (j = k; j < (i+k)/2; j++) {

    tmp = str[j];

    str[j] = str[i-j-1+k];

    str[i-j-1+k] = tmp;

  }

  str[i] = '';

}

 

 

void mprintf(char * Data, ...)

{

  const char *s;

  int d;   

  char buf[16];

  uint8_t txdata;

  va_list ap;

  va_start(ap, Data);

  while ( * Data != 0 ) {                          

    if ( * Data == 0x5c )  {  

      switch ( *++Data ) {

        case 'r':          

          txdata = 0x0d;

          send_uart_data(txdata);

          Data ++;

          break;

        case 'n':          

          txdata = 0x0a;

          send_uart_data(txdata);

          Data ++;

          break;

        default:

          Data ++;

          break;

      }  

    } else if ( * Data == '%') {  

      switch ( *++Data ) {

      case 's':  

        s = va_arg(ap, const char *);

        for ( ; *s; s++) {

          send_uart_data(*((uint8_t *)s));

        }

        Data++;

        break;

      case 'd':

        d = va_arg(ap, int);

        sky_itoa(d, buf, 10);

        for (s = buf; *s; s++) {

          send_uart_data(*((uint8_t *)s));

        }

        Data++;

        break;

      case 'x': {

        d = va_arg(ap, int);

        sky_itoa(d, buf, 16);

        for (s = buf; *s; s++) {

          send_uart_data(*((uint8_t *)s));

        }

        Data++;

        break;

      }

      case 'f': {

        double num = va_arg(ap, double);

        sky_ftoa(num, buf, 4);

        for (s = buf; *s; s++) {

          send_uart_data(*((uint8_t *)s));

        }

        Data++;

        break;

      }

      default:

        Data++;

        break;

      }  

    } else {

        send_uart_data(*((uint8_t *)Data));

        Data++;

    }

  }

}

 

void main(void)

{

    USART_Config();

    mprintf("STM8L start...rn");

    mprintf("%f %f %frn", 1.2, 12.36, -1.2364568);

    mprintf("%x %x %xrn", 0x1035, 0x0830, 0x2018);

    mprintf("%d %d %drn", 12, -12345, 2);

    mprintf("STM8L end...rn");

    while (1) {

        

    }

}

执行串口打印结果:

注意事项:


如果你不需要打印浮点数,那么就把浮点数打印部分代码注释掉。由于像这种8位单片机处理浮点数,是通过编译器来软实现,故对浮点数处理可能每增加一次浮点数处理,代码就会增加几十上百个字节,我这边的整型数和浮点数处理后的编译代码大小可以看看,浮点数处理和整型数处理代码空间的差异。

关键字:STM8  单片机空间太小  printf  串口打印 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic481988.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:基于STM8的IIC协议--协议篇
下一篇:基美电子新型表面贴装MLCC满足小型化的广泛需求

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

74LS164 for stm32 源码
:74LS164.c---------------------------------------------------------------------------------------------------------------------------------------------#include "stm32f10x.h"#include "stm32f10x_rcc.h"#include "stm32f10x_gpio.h"#include "74LS164.h"/* 延时模块82615468 sp-320-12 * */static void delay(u32 t){ u32 i; while(t--) for (i = 0; i < 1; i++);}void Ls164Init(void){ GPIO_InitTypeDef
发表于 2019-12-11
74LS164 for stm32 源码
STM32 SysTick定时器应用【worldsing笔记】
SysTick是CM内核独立的定时器,时钟可以用内核内部的,也可以用芯片厂家(ST)的时钟,参考《Cortex-M3权威指南》的第13章: 另外也可以考《STM32F10xxx Cortex-M3 programming manual.pdf》第4章CM3系统外设,从手册来看,ST应该只提供了内核外部时钟,而外部时钟又分成两种:HCLK/8 和 HCLK,  ST官方提供了库直接可以操作SYSTICK,但不同的版本使用起来有些区别: 在V2.0的版本中对SysTick的操作是使用的stm32f10x_systick.c和stm32f10x_systick.h void
发表于 2019-12-11
STM32 SysTick定时器应用【worldsing笔记】
Keil uCos 2.52 stm32 【worldsing笔记】
;    RevBit(LED_GPIO->ODR, 12)           /*LED 状态反转                              */ 2、stm32 Lib V3.5 
发表于 2019-12-11
stm32_CAN总线知识
一、CAN总线的特点:bxCAN主要特点● 支持CAN协议2.0A和2.0B主动模式● 波特率最高可达1兆位/秒● 支持时间触发通信功能发送● 3个发送邮箱● 发送报文的优先级特性可软件配置● 记录发送SOF时刻的时间戳接收● 3级深度的2个接收FIFO● 可变的过滤器组:─ 在互联型产品中,CAN1和CAN2分享28个过滤器组─ 其它STM32F103xx系列产品中有14个过滤器组● 标识符列表● FIFO溢出处理方式可配置● 记录接收SOF时刻的时间戳时间触发通信模式● 禁止自动重传模式● 16位自由运行定时器● 可在最后2个数据字节发送时间戳管理● 中断可屏蔽● 邮箱占用单独1块地址空间,便于提高软件效率双CAN● CAN1
发表于 2019-12-11
stm32_CAN总线知识
STM8L051F3_03_CLK应用
本文介绍STM8L的CLK相关知识。内容分为以下几部分:CLK简介系统时钟频率切换1、CLK简介STM8L051F3的时钟控制系统设计时非常稳定的,同时也很容易使用,它可以使得MUC在低消耗下获仍然保持优性能。用户可以通过管理分配到CPU&外设的时钟来达到降低功耗。STM8L051F3有一个安全无干扰的时钟切换机制允许用户切换系统时钟源,同时可以通过预分频器来控制系统时钟频率。时钟结构图如下:注:STM8L051F3的Peripheral Clock enable是13 bit,没有LCD外设STM8L051F3的系统时钟源有以下4种:16MHz内部高速(出厂已校准)RC时钟HSI1~16MHz外部高速振荡器时钟
发表于 2019-12-11
STM8L051F3_03_CLK应用
低功耗版STM8L051的一个诡异配置
为了做个低功耗项目,从最便宜的STM8S003切换到低功耗系列里较便宜的STM8L051;在STM8S003上调通了HALT休眠处理后,最低待机电流是67uA;用了STM8L051后,实测能到2uA;但是后来遇到个诡异的问题,TIMER4莫名其妙不动作,好像不计时了。开关中断,改初始化配置,都没用,很奇怪。最后发现,是init'初始化的时候,没有给TIMER4配置CLK时钟源,导致的,原来的代码并没有写TIM4,        CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_4);       
发表于 2019-12-11
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved