嵌入式软件架构--多窗口表明1(后台软件实现)

开始

嵌入式系统常见显示器

分类1:
1,动态数码管
8个动态数码管分辨率有164
2,点阵屏
3,LCD
单色屏:1个像素点占1bit 分辨率128
64
彩色屏:分辨率有480*320
8位:332格式 RGB 分别占3,3,2bit
26位:565格式 RGB 分别占5,6,5bit
32位:888格式RGBA 分别占8,8,8,8bit
针对这些不同类型的显示器,要显示图像本质都是将字模写入到控制器,软件架构是一样的。

分类2:
是否带驱动器:
是:OLED,LCD 驱动器自动不断刷新 有显存 有时要防止闪烁还要双缓冲。
否:动态数码管,点阵屏 需要软件不断刷新 没有显存需要自己开辟内存空间当显存

显示架构

整个的显示架构分为三层:
1,前台软件:用户设计的界面(多窗口多界面的方式显示),人机交互信息(消息与事件机制)
2,后台软件:进行编码格式的转换,功能性的转换 (命令式)
3,底层硬件驱动:gpio配置 IO状态变化,updateLed是驱动的入口函数。
一屏内容先分成多个窗口,窗口再分解成控件

用户界面是能看到的属于前台软件,其他用户看不到的属于后台软件,这里要分成两个任务。驱动每3ms调用一次,后台任务每100ms调用一次刷新显示。

底层驱动

一共8位共阴数码管,段选接在PB8-PB15,位选通过38译码器控制接PB5,PB6,PB7

//一些端口宏定义
#define LED_PORT 			 GPIOB   
#define LED_PIN 			(GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15)
#define LED_PORT_RCC		RCC_APB2Periph_GPIOB
#define LED_CHIP_PORT      GPIOB
#define LED_CHIP_PIN        (GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7)
#define LED_CHIP_PORT_RCC		RCC_APB2Periph_GPIOB
extern void ledDrvInit(void);
extern void ledDrv(UINT8 dat,UINT8 chip);
extern void led_write(UINT8 dat);

驱动的实现

void ledDrvInit(void)
{
GPIO_InitTypeDef led;  //声明一个结构体变量,用来初始化GPIO
/* 开启GPIO时钟 */
RCC_APB2PeriphClockCmd(LED_PORT_RCC,ENABLE);
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
/*  配置GPIO的模式和IO口 */
led.GPIO_Pin=LED_PIN;	  //选择你要设置的IO口
led.GPIO_Mode=GPIO_Mode_Out_OD;
led.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(LED_PORT,&led);		/* 初始化GPIO */
LED_PORT->ODR = 0x00;
RCC_APB2PeriphClockCmd(LED_CHIP_PORT_RCC,ENABLE);
/*  配置GPIO的模式和IO口 */
led.GPIO_Pin=LED_CHIP_PIN;	  //选择你要设置的IO口
led.GPIO_Mode=GPIO_Mode_Out_OD;
led.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(LED_CHIP_PORT,&led);		/* 初始化GPIO */
}
void ledDrv(UINT8 dat,UINT8 chip)
{
UINT16 temp;
GPIOB->BRR = 0xff00;//所有段选拉低,因为是共阴数码管,消影
temp = GPIOB->ODR;//读取输出寄存器值,防止修改低八位
temp &= 0x001f;//PB5-PB15清零
temp |= chip << 5;//写入位选值
temp |= (~dat) << 8;//字模是共阳的所以需要取反
GPIOB->ODR = temp;	 //写入输出寄存器。
}
//这个函数给1位的共阳静态数码管,所以不用管位选
void led_write(UINT8 dat)
{
LED_PORT->ODR = (dat<<8);
}

显示的抽象

typedef struct{
UINT16 cnt;		//显示时间计数器
UINT16 reload;//显示时间重载值
}LED_TIMER_S;
//窗口结构体
typedef struct{
UINT8 dat[LED_NUM];//缓冲区
UINT8 start;//此窗口显示内容在显存中的起始位置
UINT8 len;//窗口长度
LED_TIMER_S timer;
UINT8 cmd;//转换命令
UINT8 attr;//参数,不同场景意义不同
void * p;//兼容多种类型参数
}LED_WIN_S;
//多窗口
typedef struct {
UINT8 Buf[LED_NUM];//显存
LED_WIN_S pWin[2];
UINT8 cnt;//窗口个数
}LED_S;

一些宏定义

#define LED_NUM   8  // 数码管8个
//================转换命令===========================
#define LED_TEXT	0X00 //普通文本显示
#define LED_FLASH	0x01 //闪烁显示
#define LED_FLASH_N 0x02	//多次闪烁
#define LED_DEC		0x03	//倒计时
#define LED_MOVE	0x04	//滚动
#define LED_PIC 	0x05	
#define LED_PASSWORD 0x06	//显示**
#define LED_FLASH_STATUS 0x80
#define FLASH_N 	0x3f	//attr低6位记录显示次数
#define LED_FLUSH	0x80	//是否闪烁

显示过程梳理

时间片轮询中每100ms调用一次ledMain函数,对每个窗口的显示命令进行解析显示

ledMain–》ledCmd–》
disLedFlash(pLed,winNum);
disLedFlashN(pLed,winNum);
ledDec(pLed,winNum);
ledMove(pLed,winNum);
disLedGif(pLed,winNum);
disLedPassword(pLed,winNum);
disLedText(pLed,winNum);

void ledMain(void *pMsg)
{
UINT8 i;
LED_S *pLed = (LED_S *)pMsg;
for(i=0;i<pLed->cnt;i++)
  {
  ledCmd(pLed,i);
  }
  }
后台软件具体代码
#include "led.h"
UINT8  LedChars[]={
//  0	   1    2    3    4    5    6    7    8    9
0x03,0x9f,0x25,0x0d,0x99,0x49,0x41,0x1f,0x01,0x09,
//  a    b    c    d    e    f    g	   h	  i	   j
0x11,0xc1,0x63,0x85,0x61,0x71,0x09,0xd1,0xdf,0x8f,
//	k	   L    m	   n	  o	   p	  q	   r	  s	   t
0xe1,0xe3,0x55,0x13,0x03,0x31,0x19,0x73,0x49,0xf1,
//	u	   v	  w	   x	  y	   z   ' '	 .	  -    #
0x83,0xc7,0x46,0xd9,0x89,0x25,0xff,0xfe,0xfd,0x31
};
const UINT8 MyGif[] =
{
9,                 // 0 :总共有9帧
8,                 // 1 :一帧占8个字节
10,10,10,10,10,5,5,5,5, // 2:  图片显示时长
0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, //11   9帧图像数据。
0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25,
0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,
0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99,
0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09
};
/*
函数功能:多窗口初始化
参数说明:
参数1:
返回值:void
*/
void Ledinit(LED_S *p){
UINT8 i;
p->cnt=0x00;
for(i=0;i<LED_NUM;i++){
p->Buf[i]=0xff;
}
}
/*
函数功能:添加窗口个数
参数说明:
参数1:窗口数组指针
参数2:数量
返回值:void
*/
void setWinNum(LED_S *pLed,UINT8 num){
pLed->cnt=num;
}
/*
函数功能:显存地址映射
参数说明:
参数1:窗口数组指针
餐数2:第几个窗口
参数3:此窗口在显存的起始地址
参数4:窗口长度
返回值:void
*/
void setWinSize(LED_S *pLed,UINT8 winNum,UINT8 start,UINT8 len){
pLed->pWin[winNum].start=start;
pLed->pWin[winNum].len=len;
}
/*
函数功能:设置缓冲区到显存的转换命令
参数说明:
参数1:窗口数组指针
餐数2:第几个窗口
参数3:命令
参数4:窗口显示时间计数重载值
参数5:命令参数1   attr分位表示
参数6:命令参数2
返回值:void
*/
void setWinCmd(LED_S *pLed,UINT8 winNum,UINT8 cmd,UINT16 cnt,UINT16 reload,UINT8 attr,void *p){
pLed->pWin[winNum].cmd=cmd;
pLed->pWin[winNum].attr=attr;
pLed->pWin[winNum].timer.cnt=cnt;
pLed->pWin[winNum].timer.reload=reload;
pLed->pWin[winNum].p=p;
}
/*
函数功能:窗口显示倒计时
参数说明:
参数1:窗口数组指针
餐数2:第几个窗口
返回值:
TRUE:计时到
FALSE:计时未到
*/
BOOL ledDelay(LED_S *pLed,UINT8 winNum){
if(!pLed->pWin[winNum].timer.cnt){//即时到
pLed->pWin[winNum].timer.cnt=pLed->pWin[winNum].timer.reload;
return TRUE;
}
pLed->pWin[winNum].timer.cnt--;
return FALSE;
}
/*
函数功能:窗口命令解析
参数说明:
参数1:窗口数组指针
餐数2:第几个窗口
返回值:void
*/
void ledCmd(LED_S *pLed,UINT8 winNum)
{
switch(pLed->pWin[winNum].cmd)
{
case LED_FLASH:
{
disLedFlash(pLed,winNum);
break;
}
case LED_FLASH_N:
{
disLedFlashN(pLed,winNum);
break;
}
case LED_DEC:
{
ledDec(pLed,winNum);
break;
}
case LED_MOVE:
{
ledMove(pLed,winNum);
break;
}
case LED_PIC:
{
disLedGif(pLed,winNum);
break;
}
case LED_PASSWORD:
{
disLedPassword(pLed,winNum);
break;
}
default:
{
disLedText(pLed,winNum);
break;
}
}
}
/*
函数功能:解析所有窗口的命令
参数说明:
参数1:窗口数组的指针
返回值:void
*/
void ledMain(void *pMsg){
UINT8 i;
LED_S *pLed=(LED_S *)pMsg;
for(i=0;i<pLed->cnt;i++){
  ledCmd(pLed,i);
  }
  }
  /*
  函数功能:查找小数点的位置,并将小数点后的显示内容前移一位
  参数说明:
  参数1:显示内容
  参数2:显示内容长度
  返回值:小数点位置
  */
  UINT8 findDot(UINT8 *pDat,UINT8 len){
  UINT8 i,dot=0x00;
  for(i=0;i<len;i++){
  if(pDat[i]=='.'){
  dot=i;
  break;
  }
  }
  if(dot){
  for(;i<len;i++){
  pDat[i]=pDat[i+1];
  }
  }
  return dot;
  }
  /*
  函数功能:将整个的显示内容加工成数码管能显示的字模
  参数说明:
  参数1:窗口数组指针
  餐数2:第几个窗口
  参数3:格式化字符串
  参数4:参数列表
  返回值:void
  */
  void writeLed(LED_S *pLed,UINT8 winNum,const char *fmt,...)
  {
  int len,dot;
  UINT8 *pDat = pLed->pWin[winNum].dat;//拿到缓冲区的指针
  __va_list  arg;//定义vi_list 变量
  va_start(arg,fmt);//初始化vi_list 变量arg ,使其指向可变参数列表(即...)的的第一参数
  len = vsprintf((INT8 *)pDat,fmt,arg);//将格式化字符串写入pDat中并拿到写入个数
  dot = findDot(pDat,len);//找小数点位置
  toChar(pDat,len);//转字模
  if(dot)
  {
  printDot(pDat,dot-1);//字模层面添加小数点
  }
  }
  /*
  函数功能:计算字符在字模LedChars中的位置
  参数说明:
  参数1:字符
  返回值:字模位置
  */
  UINT8 toIdx(UINT8 dat){
  if(dat<=0x0f){
  return dat;//数字0-15对应LedChar的前15个
  }else if(dat>='0'&& dat<='9'){//字符0-9 也是要显示0-9 也就是相对于字符0的偏移位置
  return dat-'0';
  }else if(dat>='a'&&dat<='z'){
  return  dat-'a'+10;//LedChar从低11个开始位a
  }else if(dat >='A'&& dat<= 'Z'){
  return dat-'A'+10;
  }else if(dat=='.'){
  return 37;
  }else if(dat == '-'){
  return 38;
  }else if(dat == '#'){
  return 39;
  }else{
  return 36;//空格
  }
  }
  /*
  函数功能:显示内容转字模
  参数说明:
  参数1:显示缓冲指针
  参数2:长度
  返回值:void
  */
  void toChar(UINT8 *pDat,UINT8 len){
  UINT8 i,index;
  for(i=0;i<len;i++){
  index=toIdx(pDat[i]);
  pDat[i]=LedChars[index];
  }
  }
  /*
  函数功能:字模层面合并小数点
  参数说明:
  参数1:显示缓冲指针
  参数2:小数点位置
  返回值:void
  */
  void printDot(UINT8 *pDat,UINT8 dot){
  if(dot){
  pDat[dot]^=0x01;//pDat[dot-1]|=0x01;为什么要使用异或,直接或不就行了?
  }
  }
  /*
  函数功能:普通显示,缓冲区数据写入显存。
  参数说明:
  参数1:窗口数组指针
  参数2:第几个窗口
  返回值:void
  */
  void disLedText(LED_S *pLed,UINT8 winNum){
  UINT8 i;
  for(i=0;i<pLed->pWin[winNum].len;i++){
    //因为有多个窗口只有一个显存,这里显存的下标使用start+i,通过每个窗口的start来分配显存而不是使用i
    //所以start是次此窗口在显存中的起始位置		
    pLed->Buf[pLed->pWin[winNum].start+i]=pLed->pWin[winNum].dat[i];//缓冲区数据写到显存
    }
    }
    /*
    函数功能:闪烁多次
    参数说明:
    参数1:窗口数组指针
    参数2:第几个窗口
    返回值:void
    */
    void disLedFlashN(LED_S *pLed,UINT8 winNum){
    UINT8 i;
    if(pLed->pWin[winNum].attr & FLASH_N){
    if(ledDelay(pLed,winNum)){
    pLed->pWin[winNum].attr^=LED_FLASH_STATUS;//每次进来翻转一下显示状态
    if(pLed->pWin[winNum].attr&LED_FLASH_STATUS){//正常显示
    disLedText(pLed,winNum);
    }else{//显示为空
    pLed->pWin[winNum].attr--;//闪烁次数递减
    for(i=0;i<pLed->pWin[winNum].len;i++){
      pLed->Buf[pLed->pWin[winNum].start+i]=LedChars[toIdx(' ')];
      }
      }
      }
      }
      }
      /*
      函数功能:闪烁显示
      参数说明:
      参数1:窗口数组指针
      参数2:第几个窗口
      假如一秒闪烁,ledDelay一秒时间到,attr原来是0x80亮 异或0x80就是0x00灭,
      下一次计数时间再到,0x00异或0x80为0x80又亮。
      返回值:void
      */
      void disLedFlash(LED_S *pLed,UINT8 winNum){
      UINT8 i;
      if(ledDelay(pLed,winNum)==TRUE){
      pLed->pWin[winNum].attr^=LED_FLASH_STATUS;//attr第8位记录亮灭状态
      if(pLed->pWin[winNum].attr&LED_FLASH_STATUS){
      disLedText(pLed,winNum);
      }else{
      for(i=0;i<pLed->pWin[winNum].len;i++){
        pLed->Buf[pLed->pWin[winNum].start+i]=LedChars[toIdx(' ')];
        }
        }
        }
        }
        /*
        函数功能:显示倒计时
        参数说明:
        参数1:窗口数组指针
        参数2:第几个窗口
        attr代表的是倒计时时间
        解释1:这里为什么不是检测attr!=0?
        初始化时cnt=0,ledDelay返回TRUE,会立即调用disLedText显示用户给定值
        初始化是cnt!=0,ledDelay返回FALSE,将延迟一会再显示用户给定值
        要求用户设定完立即显示所以cnt=0,
        实际显示   :		3	2	1	0	0->3	2	1	0	...
        attr-- 	   :	2	1	0	ff	fe
        假如从3开始倒计时,3显示attr--=2,2显示attr--=1,1显示attr--=0,
        0开始显示,attr--=0xff,要求0要显示1秒所以下一次还是显示0,attr--=0xfe
        所以显示0的判断条件是attr==0xff,attr==0xfe,当attr=0xfe显示一轮结束。如果需要循环显示则
        在attr=0xfe时修改attr=3,进行下一轮显示。
        attr是UINT8类型,表示0-255,显示长度可能为1,2,3,这里使用len填充格式化字符串,自动调整
        显示的长度。
        返回值:void
        */
        void ledDec(LED_S *pLed,UINT8 winNum){
        if(pLed->pWin[winNum].attr !=0xfe){//解释1
        if(ledDelay(pLed,winNum)==TRUE){//延迟时间到
        UINT8 fmt[4]="% d";
        fmt[1]=pLed->pWin[winNum].len +'0';
        if(pLed->pWin[winNum].attr ==0xff || pLed->pWin[winNum].attr ==0xfe){
        writeLed(pLed,winNum,(const char *)fmt,0);
        } else{
        writeLed(pLed,winNum,(const char *)fmt,pLed->pWin[winNum].attr);
        }
        disLedText(pLed,winNum);
        pLed->pWin[winNum].attr--;
        }
        }
        }
        /*
        函数功能:滚动显示
        参数说明:
        参数1:窗口数组指针
        参数2:第几个窗口
        注释1:
        p[0]:用来记录移动了多少个字符
        p[1]:用来记录移动完成
        返回值:void
        */
        void ledMove(LED_S *pLed,UINT8 winNum){
        UINT8 i;
        UINT8 temp;
        UINT8 * p=(UINT8 *)pLed->pWin[winNum].p;//注释1
        UINT8 winLen=pLed->pWin[winNum].len;//窗口长度
        if(ledDelay(pLed,winNum)==TRUE){
        temp=pLed->Buf[pLed->pWin[winNum].start];//拿到显存指针
        for(i=0;i<winLen;i++){
        //显存里的内容向前移动一位(实现滚动效果)
        pLed->Buf[pLed->pWin[winNum].start+i]=pLed->Buf[pLed->pWin[winNum].start+i+1];
        }
        //窗口缓冲区中的第一个字符拷贝到本窗口显存的最后一个位置
        //等于说将缓冲区的头与显存的尾进行连接
        //类似于spi通信,接收缓冲出去一位,发送缓冲移动一位到接收缓冲的最后一位
        pLed->Buf[pLed->pWin[winNum].len-1]=pLed->pWin[winNum].dat[0];
        for(i=0;i<pLed->pWin[winNum].attr-1;i++){//这里attr代表的是滚动个数?
          pLed->pWin[winNum].dat[i]=pLed->pWin[winNum].dat[i+1];//窗口缓冲区中的内容也全部向前移动一位
          }
          //窗口缓冲区最后一位用显存中的第一位递补,这样就将显存的头和缓冲区的尾相连接了
          pLed->pWin[winNum].dat[pLed->pWin[winNum].attr-1]=temp;
          p[0]++;//移动次数加一
          if(p[0]==pLed->pWin[winNum].attr+winLen){
          p[0]=0x00;
          p[1]=0x01;//移动一屏结束
          }
          }
          }
          /*
          函数功能:判断倒计时显示是否结束
          参数说明:
          参数1:窗口数组指针
          参数2:第几个窗口
          返回值:
          TRUE:倒计时显示结束
          FASLE:倒计时显示未结束
          */
          BOOL ledDecOver(LED_S *pLed,UINT8 winNum){
          return (pLed->pWin[winNum].attr ==0xfe)?TRUE :FALSE;
          }
          /*
          函数功能:显示图片
          参数说明:
          参数1:窗口数组指针
          参数2:第几个窗口
          图片数据结构:
          用一个数组将图片的信息头和图片的内容数据放在一起。
          图片信息头包含:
          1,每张图片的大小,1个字节存储
          2,图片的帧数,1个字节存储
          3,图片播放时长,单位是ms
          attr存放当前显示的帧数,void * p存放图像数组的内容
          返回值:void
          */
          void disLedGif(LED_S *pLed,UINT8 winNum){
          UINT8 i;
          UINT8 * p=(UINT8 *)pLed->pWin[winNum].p;//记录一些参数
          UINT8 frame=p[0];//图像总共几帧
          UINT8 size =p[1];//一帧占几个字节
          UINT8 *pDelay=&p[2];//每帧的显示时长,
          UINT8 *pGif=&p[2+frame];//图片实际内容
          UINT8 *pBuf=&pLed->Buf[pLed->pWin[winNum].start];//拿到本窗口显存位置
          UINT8 current = pLed->pWin[winNum].attr;//当前显示帧
          if(ledDelay(pLed,winNum)==TRUE){
          pLed->pWin[winNum].timer.cnt=pDelay[current];//显示时长计数器保存当前显示帧的显示时长
          for(i=0;i<size;i++){
          //修改显存,实际上就是循环给8个数码管赋值
          pBuf[i]=pGif[i+current*size];
          }
          pLed->pWin[winNum].attr=(current+1)%frame;//跳到下一帧 0-8
          }
          }
          /*
          函数功能:数码管驱动入口
          参数说明:
          参数1:显示对象数组指针
          3ms调用一次
          返回值:void
          */
          void updataLed(void *pMsg){
          static UINT8 idx=0x00;
          LED_S *p =(LED_S *)pMsg;
          ledDrv(p->Buf[idx],idx);//数码管驱动
          idx=(idx+1)%8;
          }
          /*
          函数功能:闪烁结束判断
          参数说明:
          参数1:窗口数组指针
          参数2:第几个窗口
          返回值:void
          */
          BOOL FlashNOver(LED_S *pLed,UINT8 winNum){
          if(pLed->pWin[winNum].attr){
          return FALSE;
          }else{
          return TRUE;
          }
          }
          void flushLed(LED_S *pLed,UINT8 winNum){
          pLed->pWin[winNum].attr|=LED_FLASH;//开启闪烁	
          }
          //void disLedPassword(LED_S *pLed,UINT8 winNum)
          //{
          //	  UINT8 len,i;
          //    ABUF_S  *pBuf;
          //	  if(pLed->pWin[winNum].attr & LED_FLUSH)
          //		{
          //				pBuf = (ABUF_S *)pLed->pWin[winNum].p;
          //				len = (pBuf->idx <= pLed->pWin[winNum].len)?  pBuf->idx:pLed->pWin[winNum].len;
            //				if(!len)
            //				{
            //						for(i=0;i<pLed->pWin[winNum].len;i++)
              //						{
              //								pLed->Buf[pLed->pWin[winNum].start+i] = LedChars[toIdx(' ')];
              //						}
              //				}
              //				else
              //				{
              //						for(i=0;i<len-1;i++)
              //						{
              //								pLed->Buf[pLed->pWin[winNum].start+i] = LedChars[toIdx('-')];
              //						}	
              //						pLed->Buf[pLed->pWin[winNum].start+i] = LedChars[toIdx(pBuf->dat[len-1])];	
              //						i++;
              //						for(;i<pLed->pWin[winNum].len;i++)
                //						{
                //								pLed->Buf[pLed->pWin[winNum].start+i] = LedChars[toIdx(' ')];
                //						}				
                //				}
                //				if(ledDelay(pLed,winNum)== TRUE)
                //				{
                //						 pLed->Buf[pLed->pWin[winNum].start+len-1] = LedChars[toIdx('-')];
                //					   pLed->pWin[winNum].attr &= ~LED_FLUSH;
                //				}
                //		}
                //}
                //=========================以下为单个共阳数码管测试函数==============================
                void test(void *pMsg);
                void combinationKey(void *pMsg);
使用vsprintf生成显示字模

先看包装过程:

void writeLed(LED_S *pLed,UINT8 winNum,const char *fmt,...)
{
int len,dot;
UINT8 *pDat = pLed->pWin[winNum].dat;//拿到缓冲区的指针
__va_list  arg;//定义vi_list 变量
va_start(arg,fmt);//初始化vi_list 变量arg ,使其指向可变参数列表(即...)的的第一参数
len = vsprintf((INT8 *)pDat,fmt,arg);//将格式化字符串写入pDat中并拿到写入个数
dot = findDot(pDat,len);//找小数点位置
toChar(pDat,len);//转字模
if(dot)
{
printDot(pDat,dot-1);//字模层面添加小数点
}
}
vsprintf函数简介

用于将格式化的数据写入一个字符串缓冲区。它的功能与 printf 类似,但是输出不是到标准输出,而是到一个字符数组中。

//函数原型:
int vsprintf(char *str, const char *format, va_list ap);

参数说明:
str:指向一个字符数组的指针,该数组将存储生成的格式化字符串。必须确保该缓冲区足够大以容纳生成的字符串。

format:格式化字符串,指定如何格式化后续参数。格式字符串与 printf 函数的格式字符串相同。
ap:一个 va_list 类型的变量,该变量已经由 va_start 初始化,用于访问可变参数列表。

返回值:
如果成功,返回写入的字符数(不包括字符串结尾的空字符);如果发生错误,则返回一个负数。

/*
使用步骤:
定义一个足够大的缓冲区来存储结果字符串。使用 va_list 类型的变量来管理可变参数列表。使用 va_start 初始化 va_list 变量。调用 vsprintf 进行格式化。使用 va_end 清理 va_list 变量。
*/
#include <stdio.h>
  #include <stdarg.h>
    void my_printf(char *format, ...) {
    char buffer[100];
    va_list args;
    va_start(args, format);
    vsprintf(buffer, format, args);
    va_end(args);
    printf("%s", buffer);
    }
    int main() {
    my_printf("This is a number: %d, and a string: %s\n", 123, "hello");
    return 0;
    }
va_list 是什么
//可以理解为一个指针
typedef struct __va_list {
void *__ap;
} va_list;

va_start 是一个宏

#define va_start(ap, parmN) __va_start(ap, parmN)

参数:
ap:va_list 类型的变量
parmN:最后一个已知的固定参数(…之前的参数)
功能:初始化 va_list 对象,使其指向可变参数列表的第一个参数。

posted @ 2025-12-07 19:01  gccbuaa  阅读(2)  评论(0)    收藏  举报