<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > stm32實(shí)現(xiàn)printf重定向到LCD顯示屏

          stm32實(shí)現(xiàn)printf重定向到LCD顯示屏

          作者: 時(shí)間:2016-11-11 來(lái)源:網(wǎng)絡(luò) 收藏
          嘿嘿,學(xué)習(xí)stm32已經(jīng)有一段時(shí)間了。以前糾結(jié)過(guò)一個(gè)問(wèn)題,(USART)串口的可變參數(shù)問(wèn)題,查找C語(yǔ)言的書(shū)終于還是解決了,自己編寫(xiě)了一個(gè)USART_printf()函數(shù),功能模仿C語(yǔ)言的printf,實(shí)現(xiàn)可變參數(shù)處理。有點(diǎn)小成就感。
          我也因此發(fā)表了一下C語(yǔ)言可變參數(shù)的博文, 同學(xué)們有興趣可以參考一下:
          http://blog.sina.com.cn/s/blog_6e22f4ce01010uud.html
          最近幾天在玩LCD顯示屏,基本驅(qū)動(dòng)寫(xiě)好了,并寫(xiě)了一個(gè)函數(shù)支持中文英文混合打印,但是函數(shù)功能還是不夠強(qiáng)大??!串口的時(shí)候可以使用printf重定向,這么說(shuō)開(kāi)printf也可以重定向到LCD?
          基于這個(gè)問(wèn)題,本人昨天著手編寫(xiě)fputc()函數(shù),目的是實(shí)現(xiàn)printf重定向到LCD。經(jīng)過(guò)漫長(zhǎng)的幾個(gè)小時(shí),調(diào)試成功了?。?!調(diào)用printf("hello,%sn", str)其中str內(nèi)容是“world“;實(shí)現(xiàn)了在LCD上打印hello,world了。嘿嘿,后續(xù)的學(xué)習(xí)就方便很多了。
          首先還是同樣先復(fù)習(xí)一下printf重定向到USART,原理是修改fputc()函數(shù),將傳遞進(jìn)來(lái)的參數(shù)轉(zhuǎn)發(fā)到USART(串口),具體fputc()函數(shù)內(nèi)容編寫(xiě)可以參考以下例子:
          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;
          }
          看起來(lái)很熟悉吧。但是我們現(xiàn)在是要把它重定向到LCD,怎么實(shí)現(xiàn)呢?
            同樣的方法,把傳遞進(jìn)來(lái)的參數(shù)轉(zhuǎn)發(fā)到LCD就行了!嘿嘿,首先要有LCD驅(qū)動(dòng),也就是有LCD打印一個(gè)字符的函數(shù)(中文是兩個(gè)字節(jié)),舉一個(gè)例子。假設(shè)已經(jīng)有一個(gè)打印一個(gè)字符的函數(shù)
          LCD_Ascii_one();
          函數(shù)參數(shù)先不理,來(lái)到這一步就已經(jīng)成功了一半了。像串口一樣,只不過(guò)把
          USART_SendData(USART1, (uint8_t)ch);
          修改成
          LCD_Ascii_One(...);//其中參數(shù)...中包含ch信息
          這樣, 整個(gè)框架就形成了, 下面是具體實(shí)現(xiàn)過(guò)程
          先說(shuō)明一下我的LCD_Ascii_One函數(shù):
          LAC_Ascii_One(uint8_t X, uint16_t Y, uint8_t *Chinese, uint16_t Color);
          四個(gè)參數(shù)的含義分別為:
          uint8_t X 打印字符的橫坐標(biāo)
          uint16_t Y 打印字符的縱坐標(biāo)
          uint8_t *Chinese 打印字符的字模首地址 //字模提取軟件得到的, 字模放在sdcard, 使用時(shí)需要打開(kāi)文件
          uint16_t Color 打印字符的顏色
          獲取字符的字模不是這里的重點(diǎn), 同學(xué)們另找資料.
          string = Get_Ascii(a); //獲取字符a的字模
          調(diào)用LCD_Ascii_One(0, 10, string, WHITE);//打印
          就實(shí)現(xiàn)了在LCD上的位置(0, 10)打印字符a, 顏色為白色
          所以fputc函數(shù)里面會(huì)有這么一句
          LCD_Ascii_One(x, y, ch, color);
          不過(guò)還是不夠, 我們需要打印一個(gè)字符后跳到下一個(gè)位置, 不然下次打印就覆蓋了本次字符了
          所以還必須封裝x, y, 讓他們帶有記憶功能, 呵呵,關(guān)鍵字static派上用場(chǎng)了
          fputc函數(shù)需要聲明
          static uint16_t x = 0, y = 0;
          為什么是uint16_t 應(yīng)為我的LCD顯示屏是240 * 320的
          uint8_t 無(wú)法表示320, 所以是uint16_t, x也就順便uint16_t了哈
          調(diào)用玩LCD_Ascii_one之后
          x += 8; //因?yàn)槲姨崛∽帜J? * 16的Ascii字模
          還有一部是fputc需要的
          return ch;
          這樣功能就完善一點(diǎn)了,加把勁, 繼續(xù)換行功能
          在調(diào)用LCD_Ascii_One()之前, 先判斷x是否越界, 也就是當(dāng)前這一行已經(jīng)無(wú)法打印一個(gè)字符了
          if(x > X_MAX - 8) //X_MAX是宏定義, 該值為240
          {
          x = 0; //x置零
          y += 16; //y移至下一行
          }
          但是y也可能越界, 也就是說(shuō)已經(jīng)到了LCD的底部了, 我這里處理是越界直接退出, 不打印字符了
          if(y > Y_MAX - 16) //Y_MAX是宏定義, 該值是320
          {
          return ch; //直接退出
          }
          換行功能還有調(diào)用printf()時(shí)候的n
          所以還是老樣子, 跟串口一樣:
          if(ch == n)
          {
          x = 0;
          y += 16;
          }
          是否越界是上面的函數(shù)在判斷.
          來(lái)到這里, 我們重新再這里一下fputc的內(nèi)容
          需要打印一個(gè)字符自動(dòng)跳到下一個(gè)位置, x越界換行, y越界退出
          當(dāng)然這里只是一個(gè)框架, 還有其他需要封裝的, 例如中文, Ascii區(qū)分, 接下來(lái)會(huì)討論
          只是fputc至少必須包含語(yǔ)句:
          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; //跳轉(zhuǎn)到下一個(gè)位置, 是否越界有上面函數(shù)判斷
          return ch;
          }
          (能夠理解的同學(xué)接下來(lái)可以測(cè)試自己封裝一下中英文了, 接下來(lái)我們繼續(xù)討論實(shí)現(xiàn)中英文混輸.)
          嘿嘿, 我的大概框架就是這樣, 當(dāng)然這樣的功能還是不夠強(qiáng)大的, 對(duì), 我們需要的是再?gòu)?qiáng)大一點(diǎn)的printf, 接下來(lái)是, 實(shí)現(xiàn)中英文混輸, 可變參數(shù)C語(yǔ)言已經(jīng)幫我們實(shí)現(xiàn)了, 也就是說(shuō)我們調(diào)用printf("hello %s", str);已經(jīng)能打印出hello world并且換行了, 但是如果printf("你好n");會(huì)怎么樣? 亂碼? 沒(méi)錯(cuò), 我們現(xiàn)在只是實(shí)現(xiàn)了Ascii打印, 并沒(méi)有中文, 這是與串口所不同的, 因?yàn)榇诖蛴〉诫娔X的超級(jí)終端, 超級(jí)終端已經(jīng)幫我們實(shí)現(xiàn)了這個(gè)功能了, 所以printf重定向到LCD還有再封裝, 對(duì), 要更強(qiáng)大的函數(shù), 實(shí)現(xiàn)完美的打印功能!!
          中英文混輸先要有一點(diǎn)預(yù)備知識(shí).
          1. 一個(gè)中文占兩個(gè)字節(jié), 一個(gè)Ascii占一個(gè)字節(jié).
          2. Ascii最高字節(jié)為0, 中文的兩個(gè)字節(jié)最高字節(jié)都是1
          上面的第二點(diǎn)再說(shuō)明一下
          如果將Ascii看成是無(wú)符號(hào)數(shù)字, 它的數(shù)值小于128, 也就是0~127
          如果將中文的兩個(gè)字節(jié)分別看成無(wú)符號(hào)數(shù)字, 第一個(gè)字節(jié)范圍為128~255, 第二個(gè)字節(jié)也是128~255
          嘿嘿, 判斷中英文就有辦法了~~~~
          if(ch & 0x80) //判斷最高位是否為1, 最高位為1表達(dá)式ch & 0x80為真
          {
          中文字節(jié);
          }
          else
          {
          Ascii字節(jié);
          }
          中文有兩個(gè)字節(jié), 所以要進(jìn)入fputc兩個(gè)才能打印出一個(gè)中文, 所以還必須把先進(jìn)來(lái)的ch保存起來(lái), 等下一個(gè)字節(jié)進(jìn)來(lái)在打印,當(dāng)然Ascii如果進(jìn)來(lái)就直接打印. 這樣我們又要有記憶功能的tmp[2]分別保存中文的兩個(gè)字節(jié)了. 并且需要記錄是計(jì)入的是第一個(gè)中文字節(jié)還是第二個(gè)中文字節(jié)
          static uint8_t flag;
          static uint8_t tmp[2];
          當(dāng)然, 要打印中文, 還必須有一個(gè)打印中文的函數(shù), 我這里的函數(shù)是
          LCD_Chinese_One(uint8_t X, uint16_t Y, uint8_t *Chinese, uint16_t Color);
          uint8_t X 打印中文的橫坐標(biāo)
          uint16_t Y 打印中文的縱坐標(biāo)
          uint8_t *Chinese 打印中文的字模
          uint16_t Color 打印中文的顏色
          string = Get_Chinese("好"); //獲取好的字模
          例如調(diào)用LCD_Chinese_One(5, 20, string, WHITE); //打印
          就在LCD的位置(5, 20)打?。⒑茫?, 顏色為白色
          來(lái)到這里, 相信同學(xué)們都懂了吧, 啰嗦一下, 我們?cè)賮?lái)看看fputc應(yīng)該怎么寫(xiě)
          int fputc(int ch, FILE* f)
          {
          static uint16_t x = 0, y = 0; //坐標(biāo)
          uint8_t string[32]; //讀取字模數(shù)組
          static uint8_t flag = 0; //漢字第一第二字節(jié)標(biāo)志
          static uint8_t tmp[2]; //保存漢字的兩個(gè)字節(jié)
          if(ch == n) //換行
          {
          x = 0;
          y += 16;
          return ch;
          }
          if(ch & 0x80) //先判斷是否為中文
          {
          if(flag == 0) //中文的第一個(gè)字節(jié)還是第二個(gè)字節(jié)
          {
          flag = 1; //接下來(lái)是第二個(gè)字節(jié)
          tmp[0] = ch; //記錄該字節(jié)
          return ch; //按照C語(yǔ)言的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; //跳轉(zhuǎn)到下一個(gè)位置, 是否越界有上面函數(shù)判斷
          return ch;
          }
          }
          至此, 函數(shù)封裝完成了.
          測(cè)試調(diào)用一下
          uint8_t str = "hello world";
          printf("你好, 這是一個(gè)測(cè)試程序n%sn", str);
          在LCD上打印的是
          你好, 這是一個(gè)測(cè)試程序
          hello world
          嘿嘿, 重定向成功了!!!
          相信已經(jīng)能滿足廣大開(kāi)發(fā)者的需求了, 有待完善的地方是控制符tr等, 不過(guò)都是相當(dāng)簡(jiǎn)單的.
          下面給出我的封裝, 不過(guò)我是把字模放到sdcard的, 所以需要打開(kāi)文件, 讀取文件等操作, 但是原理還是一樣的, 嘿嘿~~~~~~
          #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); //中文字庫(kù)
          if(ch == n) //換行處理
          {
          x = 0; //...
          y += 18; //...
          return ch;
          }
          if(ch & 0x80) //中文處理
          {
          if(flag == 0) //中文的兩個(gè)字節(jié)判斷, flag == 0 為第一個(gè)字節(jié)
          {
          tmp[0] = (uint8_t)(ch); //把第一個(gè)字節(jié)保存進(jìn)來(lái)
          flag = 1; //接下來(lái)是第二個(gè)字節(jié)
          return ch; //按照C語(yǔ)言官方庫(kù), 返回ch
          }
          else//是中文的第二個(gè)字節(jié)
          {
          tmp[1] = (uint8_t)(ch); //保存該字節(jié)
          flag = 0; //下一次是第一個(gè)字節(jié)
          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)) //判斷文件操作狀態(tài)
          {
          Offset = Chinese_Offset(tmp);//根據(jù)中文字庫(kù)字模提取軟件計(jì)算出漢字偏移位置
          f_lseek(&File, Offset); //文件指針移至偏移位置
          }
          if(Check_FileStatus(File_Status)) //判斷文件操作狀態(tài)
          {
          File_Status = f_read(&File, Read_Font, 32, &Read_Count);
          }
          if(Check_FileStatus(File_Status)) //判斷文件操作狀態(tài)
          {
          LCD_Chinese_One(x, y, Read_Font, WHITE);//調(diào)用中文打印函數(shù)打印漢字
          x += 16; //液晶橫坐標(biāo)+16, 中文為16*16的
          }
          File_Status = f_close(&File); //關(guān)閉文件
          }
          }
          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)) //判斷文件操作狀態(tài)
          {
          Offset = ((uint8_t)(ch) - 0x20) << 4; //計(jì)算出Ascii偏移地址, 要先跳過(guò)Ascii控制字符
          f_lseek(&File, Offset); //文件指針移至偏移位置
          }
          if(Check_FileStatus(File_Status))//判斷文件操作狀態(tài)
          {
          f_read(&File, Read_Font, 16, &Read_Count);//讀取字模信息
          }
          if(Check_FileStatus(File_Status))//判斷文件操作狀態(tài)
          {
          LCD_Ascii_One(x, y, Read_Font, WHITE); //調(diào)用Ascii打印函數(shù)打印字符
          x += 8;//Ascii像素為8*16, 橫坐標(biāo)+8
          }
          File_Status = f_close(&File); //關(guān)閉文件
          }
          return ch;
          }
          #endif //#ifdef STDOUT_LCD



          關(guān)鍵詞: stm32printf重定向LCD顯示

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();