在 LCD 上显示数码管字符
在 LCD 上显示数码管字符

在 LCD 上显示数码管字符

使用填充函数,实现在 LCD 上显示数码管字符

驱动源码

驱动代码的 API 接口如下所示。GUIDrawNixieTubeLine2 函数用于绘制单一的字符笔画,GUIDrawNixieTube 函数则用于绘制数码管字符。用户使用的时候,只关心 GUIDrawNixieTube 函数即可。GUIDrawNixieTube 函数调用 GUIDrawNixieTubeLine2 函数实现字符绘制。

GUIDrawNixieTube 函数各个参数的意义在源码中有注释,使用的时候需要仔细核对。

注意:GUIDrawNixieTube 函数中,space0 和 space1 用来控制不同笔画之间的间隙,间隙过小有可能会出现笔画重合的情况。

void GUIDrawNixieTubeLine2(unsigned int x, unsigned int y, unsigned int width, unsigned int height,
  unsigned int type, unsigned int color);
void GUIDrawNixieTube(unsigned int x, unsigned int y, unsigned int width, unsigned int height,
  unsigned int lineSize, unsigned int space0, unsigned int space1, char code, 
  unsigned int color, unsigned int bcolor);

驱动代码源码如下所示。

/*********************************************************************************************************
* 函数名称: GUIDrawNixieTubeLine2
* 函数功能: 绘制数码管直线
* 输入参数: x,y  :整型变量。起点坐标
*            width :整型变量。宽度。
*            height:整型变量。高度。
*            type  :整型变量。线条类型取值范围 0-6,具体如下所示。
*                        _________
*                       |    0    |
*                     5 |         | 1
*                       |_________|
*                       |    6    |
*                     4 |         | 2
*                       |_________|
*                            3
*            color :颜色值。
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年10月24日
* 注    意:
*********************************************************************************************************/
void GUIDrawNixieTubeLine2(unsigned int x, unsigned int y, unsigned int width, unsigned int height,
  unsigned int type, unsigned int color)
{
  unsigned int tx0, ty0, tx1, ty1, x0, y0, x1, y1, inverseFlag;

  //参数校验
  if ((0 == width) || (0 == height) || (type > 6)) { return; }

  //求解目标区域
  tx0 = x;
  ty0 = y;
  tx1 = tx0 + width - 1;
  ty1 = ty0 + height - 1;
  
  //绘制
  if (0 == type)
  {
    x0 = tx0;
    x1 = tx1;
    y0 = ty0;
    y1 = ty1;
    while ((y0 <= y1) && (x0 <= x1) && (x0 >= tx0) && (x1 <= tx1) && (y0 >= ty0) && (y1 <= ty1))
    {
      GUIFillColor(x0, y0, x1, y0, color);
      y0++; x0++; x1--;
    }
  }
  else if (1 == type)
  {
    x0 = x1 = tx1;
    y0 = ty0;
    y1 = ty1 - (1 * width / 3);
    inverseFlag = 0;
    while ((x0 >= tx0) && (x1 <= tx1) && (y0 >= ty0) && (y1 <= ty1))
    {
      GUIFillColor(x0, y0, x1, y1, color);
      if (0 == inverseFlag)
      {
        x0--; x1--; y0++; y1++;
        if (y1 > ty1)
        {
          y1 = ty1 - 1;
          inverseFlag = 1;
        }
      }
      else
      {
        x0--; x1--; y0++; y1--;
      }
    }
  }
  else if (2 == type)
  {
    x0 = x1 = tx1;
    y0 = ty0 + (1 * width / 3);
    y1 = ty1;
    inverseFlag = 0;
    while ((x0 >= tx0) && (x1 <= tx1) && (y0 >= ty0) && (y1 <= ty1))
    {
      GUIFillColor(x0, y0, x1, y1, color);
      if (0 == inverseFlag)
      {
        x0--; x1--; y0--; y1--;
        if (y0 < ty0)
        {
          y0 = ty0 + 1;
          inverseFlag = 1;
        }
      }
      else
      {
        x0--; x1--; y0++; y1--;
      }
    }
  }
  else if (3 == type)
  {
    x0 = tx0;
    x1 = tx1;
    y0 = ty0;
    y1 = ty1;
    while ((y0 <= y1) && (x0 <= x1) && (x0 >= tx0) && (x1 <= tx1) && (y0 >= ty0) && (y1 <= ty1))
    {
      GUIFillColor(x0, y1, x1, y1, color);
      y1--; x0++; x1--;
    }
  }
  else if (4 == type)
  {
    x0 = x1 = tx0;
    y0 = ty0 + (1 * width / 3);
    y1 = ty1;
    inverseFlag = 0;
    while ((x0 >= tx0) && (x1 <= tx1) && (y0 >= ty0) && (y1 <= ty1))
    {
      GUIFillColor(x0, y0, x1, y1, color);
      if (0 == inverseFlag)
      {
        x0++; x1++; y0--; y1--;
        if (y0 < ty0)
        {
          y0 = ty0 + 1;
          inverseFlag = 1;
        }
      }
      else
      {
        x0++; x1++; y0++; y1--;
      }
    }
  }
  else if (5 == type)
  {
    x0 = x1 = tx0;
    y0 = ty0;
    y1 = ty1 - (1 * width / 3);
    inverseFlag = 0;
    while ((x0 >= tx0) && (x1 <= tx1) && (y0 >= ty0) && (y1 <= ty1))
    {
      GUIFillColor(x0, y0, x1, y1, color);
      if (0 == inverseFlag)
      {
        x0++; x1++; y0++; y1++;
        if (y1 > ty1)
        {
          y1 = ty1 - 1;
          inverseFlag = 1;
        }
      }
      else
      {
        x0++; x1++; y0++; y1--;
      }
    }
  }
  else if (6 == type)
  {
    x0 = tx0;
    x1 = tx1;
    y0 = ty0;
    y1 = (ty0 + ty1) / 2;
    while ((y0 <= y1) && (x0 <= x1) && (x0 >= tx0) && (x1 <= tx1) && (y0 >= ty0) && (y1 <= ty1))
    {
      GUIFillColor(x0, y1, x1, y1, color);
      y1--; x0++; x1--;
    }
    x0 = tx0;
    x1 = tx1;
    if (0 == (height % 2)) { y0 = (ty0 + ty1 + 1) / 2; }
    else { y0 = (ty0 + ty1) / 2; }
    y1 = ty1;
    while ((y0 <= y1) && (x0 <= x1) && (x0 >= tx0) && (x1 <= tx1) && (y0 >= ty0) && (y1 <= ty1))
    {
      GUIFillColor(x0, y0, x1, y0, color);
      y0++; x0++; x1--;
    }
  }
}

/*********************************************************************************************************
* 函数名称: GUIDrawNixieTube
* 函数功能: 绘制数码管
* 输入参数: x,y    :整型变量。起点坐标
*            width   :整型变量。宽度。
*            height  :整型变量。高度。
*            lineSize:整型变量。线条宽度。
*            space0  :整型变量。用于控制 0-5 号线之间的缝隙大小。
*            space1  :整型变量。用于控制 6 号线的缝隙大小。
*            code    :整型变量。字符编码,取值 '0'-'9'、'A'、'b'、'C'、'd'、'E'、'F'、'-'
*            color   :整型变量。颜色值
*            bcolor  :整型变量。背景颜色值。
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年10月24日
* 注    意:
*********************************************************************************************************/
void GUIDrawNixieTube(unsigned int x, unsigned int y, unsigned int width, unsigned int height,
  unsigned int lineSize, unsigned int space0, unsigned int space1, char code,
  unsigned int color, unsigned int bcolor)
{
  //共阳极七段数码管译码表
  //对应 '0'-'9'、'A'、'b'、'C'、'd'、'E'、'F'、'-'
  static const unsigned char s_arrNixieTubeTbl[] = {
  0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E, 0xBF };
  unsigned int tx0, ty0, tx1, ty1, x0, y0, x1, y1, tWidth, tHeight, tCode;

  //参数校验
  if((0 == width) || (0 == height) || (0 == lineSize)) { return; }

  //取出译码表中的数据
  if ((code >= '0') && (code <= '9'))
  {
    tCode = s_arrNixieTubeTbl[code - '0'];
  }
  else
  {
    //'A'、'b'、'C'、'd'、'E'、'F'、'-'
    switch (code)
    {
      case 'A': tCode = s_arrNixieTubeTbl[10]; break;
      case 'b': tCode = s_arrNixieTubeTbl[11]; break;
      case 'C': tCode = s_arrNixieTubeTbl[12]; break;
      case 'd': tCode = s_arrNixieTubeTbl[13]; break;
      case 'E': tCode = s_arrNixieTubeTbl[14]; break;
      case 'F': tCode = s_arrNixieTubeTbl[15]; break;
      case '-': tCode = s_arrNixieTubeTbl[16]; break;
      default: return;
    }
  }

  //确定显示区域大小
  tx0 = x;
  ty0 = y;
  tx1 = tx0 + width - 1;
  ty1 = ty0 + height - 1;

  //绘制 0 号线
  x0 = tx0 + space0;
  x1 = tx1 - space0;
  y0 = ty0;
  y1 = ty0 + lineSize - 1;
  tWidth = x1 - x0 + 1;
  tHeight = y1 - y0 + 1;
  if(0 == (tCode & 0x01)) { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 0, color); }
  else { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 0, bcolor); }
  tCode = tCode >> 1;

  //绘制 1 号线
  x0 = tx1 - lineSize + 1;
  x1 = tx1;
  y0 = ty0 + space0;
  y1 = ((ty0 + ty1) / 2) - space0;
  tWidth = x1 - x0 + 1;
  tHeight = y1 - y0 + 1;
  if (0 == (tCode & 0x01)) { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 1, color); }
  else { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 1, bcolor); }
  tCode = tCode >> 1;

  //绘制 2 号线
  x0 = tx1 - lineSize + 1;
  x1 = tx1;
  y0 = ((ty0 + ty1) / 2) + space0;
  y1 = ty1 - space0;
  tWidth = x1 - x0 + 1;
  tHeight = y1 - y0 + 1;
  if (0 == (tCode & 0x01)) { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 2, color); }
  else { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 2, bcolor); }
  tCode = tCode >> 1;

  //绘制 3 号线
  x0 = tx0 + space0;
  x1 = tx1 - space0;
  y0 = ty1 - lineSize + 1;
  y1 = ty1;
  tWidth = x1 - x0 + 1;
  tHeight = y1 - y0 + 1;
  if (0 == (tCode & 0x01)) { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 3, color); }
  else { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 3, bcolor); }
  tCode = tCode >> 1;

  //绘制 4 号线
  x0 = tx0;
  y0 = ((ty0 + ty1) / 2) + space0;
  x1 = tx0 + lineSize - 1;
  y1 = ty1 - space0;
  tWidth = x1 - x0 + 1;
  tHeight = y1 - y0 + 1;
  if (0 == (tCode & 0x01)) { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 4, color); }
  else { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 4, bcolor); }
  tCode = tCode >> 1;

  //绘制 5 号线
  x0 = tx0;
  y0 = ty0 + space0;
  x1 = tx0 + lineSize - 1;
  y1 = ((ty0 + ty1) / 2) - space0;
  tWidth = x1 - x0 + 1;
  tHeight = y1 - y0 + 1;
  if (0 == (tCode & 0x01)) { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 5, color); }
  else { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 5, bcolor); }
  tCode = tCode >> 1;

  //绘制 6 号线
  x0 = tx0 + space1;
  x1 = tx1 - space1;
  y0 = ((ty0 + ty1) / 2) - (lineSize / 2);
  y1 = ((ty0 + ty1) / 2) + (lineSize / 2);
  tWidth = x1 - x0 + 1;
  tHeight = y1 - y0 + 1;
  if (0 == (tCode & 0x01)) { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 6, color); }
  else { GUIDrawNixieTubeLine2(x0, y0, tWidth, tHeight, 6, bcolor); }
  tCode = tCode >> 1;
}

测试代码

测试代码如下所示,这里做了一个简单的计时器,假定屏幕尺寸 800×480。

int main(void)
{
  int min, sec;

  //设置初始状态
  min = 0;
  sec = 0;

  //初始化
  InitLCD();

  //刷纯黑背景
  GUIFillColor(0, 0, 799, 479, GUI_COLOR_BLACK);

  //绘制冒号
  GUIFillColor(365, 200, 405, 240, GUI_COLOR_WHITE);
  GUIFillColor(365, 300, 405, 340, GUI_COLOR_WHITE);

  //主循环
  while (1)
  {
    //显示当前时间
    GUIDrawNixieTube(75 , 150, 100, 200, 25, 6, 13, (min / 10) + '0', GUI_COLOR_WHITE, GUI_COLOR_BLACK);
    GUIDrawNixieTube(200, 150, 100, 200, 25, 6, 13, (min % 10) + '0', GUI_COLOR_WHITE, GUI_COLOR_BLACK);
    GUIDrawNixieTube(475, 150, 100, 200, 25, 6, 13, (sec / 10) + '0', GUI_COLOR_WHITE, GUI_COLOR_BLACK);
    GUIDrawNixieTube(600, 150, 100, 200, 25, 6, 13, (sec % 10) + '0', GUI_COLOR_WHITE, GUI_COLOR_BLACK);

    //计数加一
    sec = (sec + 1) % 60;
    if (0 == sec)
    {
      min = (min + 1) % 60;
    }

    //延时 1s 钟
    Sleep(1000);
  }

  return 0;
}

最终效果如下所示。这个数码管显示驱动可以轻松的移植到单片机上,在 LCD 或 OLED 显示数码管字符。