久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 10595|回復: 1
打印 上一主題 下一主題
收起左側

stm32實現printf重定向到LCD顯示屏

[復制鏈接]
跳轉到指定樓層
樓主
ID:82083 發表于 2015-6-9 02:30 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
嘿嘿,學習stm32已經有一段時間了。以前糾結過一個問題,(USART)串口的可變參數問題,查找C語言的書終于還是解決了,自己編寫了一個USART_printf()函數,功能模仿C語言的printf,實現可變參數處理。有點小成就感。
  我也因此發表了一下C語言可變參數的博文, 同學們有興趣可以參考一下:http://www.zg4o1577.cn/bbs/dpj-35995-1.html
最近幾天在玩LCD顯示屏,基本驅動寫好了,并寫了一個函數支持中文英文混合打印,但是函數功能還是不夠強大啊!串口的時候可以使用printf重定向,這么說開printf也可以重定向到LCD?
基于這個問題,本人昨天著手編寫fputc()函數,目的是實現printf重定向到LCD。經過漫長的幾個小時,調試成功了!!!調用printf("hello,%s\n",str)其中str內容是“world“;實現了在LCD上打印hello,world了。嘿嘿,后續的學習就方便很多了。
首先還是同樣先復習一下printf重定向到USART,原理是修改fputc()函數,將傳遞進來的參數轉發到USART(串口),具體fputc()函數內容編寫可以參考以下例子:
int fputc(int ch, FILE* f)
{
  if(ch == '\n')
  {
    USART_SendData(USART1,'\r');
    USART_SendData(USART1,'\n');
  }
  USART_SendData(USART1, (uint8_t)ch);
  while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
  return ch;
}
看起來很熟悉吧。但是我們現在是要把它重定向到LCD,怎么實現呢?

  同樣的方法,把傳遞進來的參數轉發到LCD就行了!嘿嘿,首先要有LCD驅動,也就是有LCD打印一個字符的函數(中文是兩個字節),舉一個例子。假設已經有一個打印一個字符的函數
LCD_Ascii_one();
函數參數先不理,來到這一步就已經成功了一半了。像串口一樣,只不過把
USART_SendData(USART1, (uint8_t)ch);
修改成
LCD_Ascii_One(...);//其中參數...中包含ch信息
這樣, 整個框架就形成了, 下面是具體實現過程

先說明一下我的LCD_Ascii_One函數:
LAC_Ascii_One(uint8_t X, uint16_t Y, uint8_t *Chinese,uint16_t Color);
四個參數的含義分別為:
uint8_t X       打印字符的橫坐標
uint16_t Y      打印字符的縱坐標
uint8_t *Chinese 打印字符的字模首地址  //字模提取軟件得到的,字模放在sdcard, 使用時需要打開文件
uint16_t Color   打印字符的顏色
獲取字符的字模不是這里的重點, 同學們另找資料.
string = Get_Ascii('a'); //獲取字符a的字模
調用LCD_Ascii_One(0, 10, string, WHITE);//打印
就實現了在LCD上的位置(0, 10)打印字符'a', 顏色為白色

所以fputc函數里面會有這么一句
LCD_Ascii_One(x, y, ch, color);
不過還是不夠, 我們需要打印一個字符后跳到下一個位置, 不然下次打印就覆蓋了本次字符了
所以還必須封裝x, y, 讓他們帶有記憶功能, 呵呵,關鍵字static派上用場了

fputc函數需要聲明
static uint16_t x = 0, y = 0;
為什么是uint16_t 應為我的LCD顯示屏是240 * 320的
uint8_t 無法表示320, 所以是uint16_t, x也就順便uint16_t了哈
調用玩LCD_Ascii_one之后
x += 8;  //因為我提取字模是8 * 16的Ascii字模
還有一部是fputc需要的
return ch;

這樣功能就完善一點了,加把勁, 繼續換行功能
在調用LCD_Ascii_One()之前, 先判斷x是否越界, 也就是當前這一行已經無法打印一個字符了
if(x > X_MAX - 8)  //X_MAX是宏定義,該值為240
{
  x = 0;         //x置零
  y += 16;       //y移至下一行
}
但是y也可能越界, 也就是說已經到了LCD的底部了, 我這里處理是越界直接退出, 不打印字符了
if(y > Y_MAX - 16)  //Y_MAX是宏定義,該值是320
{
  return ch;      //直接退出
}
換行功能還有調用printf()時候的'\n'
所以還是老樣子, 跟串口一樣:
if(ch == '\n')
{
  x = 0;
  y += 16;
}
是否越界是上面的函數在判斷.

來到這里, 我們重新再這里一下fputc的內容
需要打印一個字符自動跳到下一個位置, x越界換行, y越界退出
當然這里只是一個框架, 還有其他需要封裝的, 例如中文, Ascii區分, 接下來會討論
只是fputc至少必須包含語句:
int fputc(int ch, FILE* f)
{
  static uint16_t x = 0, y = 0;
  uint8_t string[16];

  if(ch == '\n')   //換行
  {
    x = 0;
    y += 16;
    return ch;
  }

  if(x > X_MAX - 8) //X_MAX是宏定義, 該值為240
  {
    x = 0;         //x置零
    y += 16;       //y移至下一行
  }
  if(y > Y_MAX - 16) //Y_MAX是宏定義, 該值是320
  {
    return ch;      //直接退出
  }

  string = Get_Ascii(ch);  //獲取ch的字模
  LCD_Ascii_One(x, y, string, color);//打印字符ch
  x += 8;                //跳轉到下一個位置, 是否越界有上面函數判斷

  return ch;
}

(能夠理解的同學接下來可以測試自己封裝一下中英文了, 接下來我們繼續討論實現中英文混輸.)

嘿嘿, 我的大概框架就是這樣, 當然這樣的功能還是不夠強大的, 對, 我們需要的是再強大一點的printf, 接下來是,實現中英文混輸, 可變參數C語言已經幫我們實現了, 也就是說我們調用printf("hello %s",str);已經能打印出hello world并且換行了, 但是如果printf("你好\n");會怎么樣? 亂碼? 沒錯,我們現在只是實現了Ascii打印, 并沒有中文, 這是與串口所不同的, 因為串口打印到電腦的超級終端,超級終端已經幫我們實現了這個功能了, 所以printf重定向到LCD還有再封裝, 對, 要更強大的函數,實現完美的打印功能!!

中英文混輸先要有一點預備知識.
1. 一個中文占兩個字節, 一個Ascii占一個字節.
2. Ascii最高字節為0, 中文的兩個字節最高字節都是1
上面的第二點再說明一下
如果將Ascii看成是無符號數字, 它的數值小于128, 也就是0~127
如果將中文的兩個字節分別看成無符號數字, 第一個字節范圍為128~255, 第二個字節也是128~255
嘿嘿, 判斷中英文就有辦法了~~~~
if(ch & 0x80)  //判斷最高位是否為1, 最高位為1表達式ch& 0x80為真
{
  中文字節;
}
else
{
  Ascii字節;
}

中文有兩個字節, 所以要進入fputc兩個才能打印出一個中文, 所以還必須把先進來的ch保存起來, 等下一個字節進來在打印,當然Ascii如果進來就直接打印. 這樣我們又要有記憶功能的tmp[2]分別保存中文的兩個字節了.并且需要記錄是計入的是第一個中文字節還是第二個中文字節
static uint8_t flag;
static uint8_t tmp[2];
當然, 要打印中文, 還必須有一個打印中文的函數, 我這里的函數是
LCD_Chinese_One(uint8_t X, uint16_t Y, uint8_t *Chinese,uint16_t Color);
uint8_t X        打印中文的橫坐標
uint16_t Y       打印中文的縱坐標
uint8_t *Chinese  打印中文的字模
uint16_t Color   打印中文的顏色
string = Get_Chinese("好");                //獲取'好'的字模
例如調用LCD_Chinese_One(5, 20, string, WHITE); //打印
就在LCD的位置(5, 20)打印"好", 顏色為白色

來到這里, 相信同學們都懂了吧, 啰嗦一下, 我們再來看看fputc應該怎么寫
int fputc(int ch, FILE* f)
{
  static uint16_t x = 0, y = 0; //坐標
  uint8_t string[32];          //讀取字模數組
  static uint8_t flag = 0;      //漢字第一第二字節標志
  static uint8_t tmp[2];       //保存漢字的兩個字節

  if(ch == '\n')   //換行
  {
    x = 0;
    y += 16;
    return ch;
  }

  if(ch & 0x80)   //先判斷是否為中文
  {
    if(flag == 0) //中文的第一個字節還是第二個字節
    {
      flag= 1;    //接下來是第二個字節
     tmp[0] = ch; //記錄該字節
     return ch;   //按照C語言的fputc需要返回ch
    }
    else
    {
      flag= 0;
     tmp[1] = ch;
      if(x> X_MAX - 16)  //判斷是否越界
     {
       x = 0;          //換行處理
       y += 16;        //字模大小為16*16
     }
      if(y> Y_MAX - 16)  //行越界
     {
       return ch;       //直接退出處理
     }
     string = Get_Chinese(tmp);
     LCD_Chinese_One(x, y, string, color);
      x +=16;
     return ch;
    }
  }
else
  {
    if(x > X_MAX - 8) //X_MAX是宏定義, 該值為240
    {
      x =0;          //x置零
      y +=16;        //y移至下一行
    }
    if(y > Y_MAX - 16) //Y_MAX是宏定義, 該值是320
    {
     return ch;       //直接退出
    }

    string = Get_Ascii(ch);  //獲取ch的字模
    LCD_Ascii_One(x, y,string, color); //打印字符ch
    x += 8;               //跳轉到下一個位置, 是否越界有上面函數判斷

    return ch;
  }
}
至此, 函數封裝完成了.
測試調用一下
uint8_t str = "hello world";
printf("你好, 這是一個測試程序\n%s\n", str);
在LCD上打印的是
你好, 這是一個測試程序
hello world
嘿嘿, 重定向成功了!!!

相信已經能滿足廣大開發者的需求了, 有待完善的地方是控制符'\t''\r'等, 不過都是相當簡單的.
下面給出我的封裝, 不過我是把字模放到sdcard的, 所以需要打開文件, 讀取文件等操作, 但是原理還是一樣的,嘿嘿~~~~~~

#ifdef STDOUT_LCD
int fputc(int ch, FILE* f)
{
static uint32_t Offset, Read_Count;
static uint8_t Read_Font[32];
static FRESULT File_Status;
static FIL File;

static uint16_t x = 0, y = 0;
static uint8_t flag = 0;
static uint8_t tmp[2];
File_Status = f_open(&File, CHINESE_FONT, FA_OPEN_EXISTING| FA_READ); //中文字庫

if(ch == '\n') //換行處理
{
x = 0; //...
y += 18; //...
return ch;
}

if(ch & 0x80) //中文處理
{
if(flag == 0)     //中文的兩個字節判斷, flag == 0 為第一個字節
{
tmp[0] = (uint8_t)(ch); //把第一個字節保存進來
flag = 1;   //接下來是第二個字節
return ch; //按照C語言官方庫, 返回ch
}
else  //是中文的第二個字節
{
tmp[1] = (uint8_t)(ch); //保存該字節
flag = 0; //下一次是第一個字節
if(x > X_MAX - 16) //判斷是否需要換行
{
x = 0; //換行操作...
y += 18; //...
}
if(y > Y_MAX - 16) //判斷是否越界
{
return ch; //越界退出
}

File_Status = f_open(&File, CHINESE_FONT, FA_OPEN_EXISTING| FA_READ);

if(Check_FileStatus(File_Status)) //判斷文件操作狀態
{
Offset = Chinese_Offset(tmp); //根據中文字庫字模提取軟件計算出漢字偏移位置
f_lseek(&File, Offset); //文件指針移至偏移位置
}
if(Check_FileStatus(File_Status)) //判斷文件操作狀態
{
File_Status = f_read(&File, Read_Font, 32,&Read_Count);
}
if(Check_FileStatus(File_Status)) //判斷文件操作狀態
{
LCD_Chinese_One(x, y, Read_Font, WHITE);//調用中文打印函數打印漢字
x += 16; //液晶橫坐標+16, 中文為16*16的
}
File_Status = f_close(&File); //關閉文件
}
}
else
{
if(x > X_MAX - 8) //判斷是否需要換行
{
x = 0; //換行操作...
y += 18; //...
}
if(y > Y_MAX - 16) //判斷是否越界
{
return ch; //越界退出
}

File_Status = f_open(&File, ASCII_FONT, FA_OPEN_EXISTING |FA_READ);

if(Check_FileStatus(File_Status)) //判斷文件操作狀態
{
Offset = ((uint8_t)(ch) - 0x20) << 4; //計算出Ascii偏移地址,要先跳過Ascii控制字符
f_lseek(&File, Offset); //文件指針移至偏移位置
}
if(Check_FileStatus(File_Status))//判斷文件操作狀態
{
f_read(&File, Read_Font, 16,&Read_Count);//讀取字模信息
}
if(Check_FileStatus(File_Status))//判斷文件操作狀態
{
LCD_Ascii_One(x, y, Read_Font, WHITE); //調用Ascii打印函數打印字符
x += 8;//Ascii像素為8*16, 橫坐標+8
}

File_Status = f_close(&File); //關閉文件
}
return ch;
}
#endif //#ifdef STDOUT_LCD

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:92114 發表于 2015-10-11 10:26 | 只看該作者
謝謝樓主,我現在正在做并口的LCD數據顯示呢,學習了!
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: www.久久.com | 一区二区三区欧美 | 国产成人在线视频播放 | 国产精品入口麻豆www | 精品一区二区三区视频在线观看 | 黑人精品| 一级黄色片一级黄色片 | 一区二区高清不卡 | 欧美999| 99精品一区二区三区 | 91天堂| 欧美视频在线看 | 一区二区三区在线 | 欧美日韩手机在线观看 | 久久se精品一区精品二区 | 国产大学生情侣呻吟视频 | 久草中文网| 免费小视频在线观看 | 精品亚洲一区二区三区 | 亚洲精品观看 | 亚洲精品黄色 | 国产精品99久久久久久久久 | 欧美综合色 | 免费成人在线网 | 中文字幕第一页在线 | 欧美日韩三区 | 久久伊人影院 | 久久久男人的天堂 | 手机在线不卡av | 超碰在线97国产 | 欧美中文字幕一区二区 | 亚洲午夜av | 色视频网站在线观看 | 日韩三级在线 | 日本在线免费视频 | 成人免费福利 | 国产一区二区三区视频 | 亚洲一区二区三区四区五区中文 | 久久久久久久一区 | 国产99久久精品一区二区永久免费 | 精品久久久久久国产 |