《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 模擬設(shè)計(jì) > 設(shè)計(jì)應(yīng)用 > 基于LCD12864顯示器的數(shù)字示波器設(shè)計(jì)
基于LCD12864顯示器的數(shù)字示波器設(shè)計(jì)
摘要: 本文針對LCD12864特性,完成了數(shù)字示波器顯示必須的繪圖驅(qū)動程序設(shè)計(jì),這個教程定位給初學(xué)者使用.
Abstract:
Key words :

關(guān)鍵字:LCD12864 數(shù)字示波器

  本文針對LCD12864 特性,完成了數(shù)字示波器顯示必須的繪圖驅(qū)動程序設(shè)計(jì),這個教程定位給初學(xué)者使用,我立足從簡單到復(fù)雜一步一步介紹設(shè)計(jì)過程,甚至是調(diào)試的過程,還包括一些經(jīng)驗(yàn)總結(jié),特別是提供了完整的keil 工程附件。希望讀者立足示波器項(xiàng)目,學(xué)到更多軟硬件設(shè)計(jì)經(jīng)驗(yàn)技巧。

  一、簡易數(shù)字示波器原理

  數(shù)字示波器基本原理可以簡單理解為:數(shù)據(jù)采集+ 圖形顯示,該過程循環(huán)進(jìn)行,如圖1 所示。

圖1 簡易數(shù)字示波器流程圖

圖1 簡易數(shù)字示波器流程圖

  LCD 圖形顯示需要根據(jù)LCD 特性設(shè)計(jì),不同LCD驅(qū)動程序不同,本篇將結(jié)合不帶字庫的LCD12864 設(shè)計(jì)顯示程序。

  二、圖形液晶LCD12864繪圖驅(qū)動設(shè)計(jì)基礎(chǔ)

  關(guān)于LCD 的硬件接口電路,在其他教程中有詳細(xì)介紹,涉及單片機(jī)總線知識和CPLD 內(nèi)部電路,需要認(rèn)真學(xué)習(xí),這里借助現(xiàn)成的驅(qū)動函數(shù),重點(diǎn)講解LCD繪圖程序設(shè)計(jì)。

  LCD12864 的電路接口在頭文件中定義:

  #define LCD_LCW XBYTE[0xf4ea]

  // 左屏命令寫入

  #define LCD_LDW XBYTE[0xf5ea]

  // 左屏數(shù)據(jù)寫入

  #define LCD_LCR XBYTE[0xf6ea]

  // 左屏命令讀出

  #define LCD_LDR XBYTE[0xf7ea]

  // 左屏數(shù)據(jù)讀出

  #define LCD_RCW XBYTE[0xf8ea]

  // 右屏命令寫入

  #define LCD_RDW XBYTE[0xf9ea]

  // 右屏數(shù)據(jù)寫入

  #define LCD_RCR XBYTE[0xfaea]

  // 右屏命令讀出

  #define LCD_RDR XBYTE[0xfbea]

  // 右屏數(shù)據(jù)讀出

  后面所有對LCD 的編程操作都是基于以上接口定義進(jìn)行的各種讀寫操作。

  首先來看LCD12864 的點(diǎn)陣結(jié)構(gòu)圖,如圖2 所示。

圖2 LCD點(diǎn)陣分布結(jié)構(gòu)圖

圖2 LCD點(diǎn)陣分布結(jié)構(gòu)圖

  此LCD 屏由水平128 列,垂直64 行組成。水平128 列分左右各64 列兩個半屏構(gòu)成。垂直64 行又分8 頁,每頁8 行(1 列8 點(diǎn)剛好1 字節(jié))。程序每次對LCD 的繪圖操作就是以最小單位1 字節(jié)進(jìn)行操作的。

  理解這點(diǎn)至關(guān)重要。也就是每次只能針對8 點(diǎn)進(jìn)行操作,而不是1 點(diǎn)進(jìn)行操作。左右屏由單獨(dú)地址線控制(前面的接口定義就是分左右屏定義的)。實(shí)際打點(diǎn)只需往指定“位置”寫入數(shù)據(jù),“1”亮,“0”暗。

LCD 驅(qū)動忙檢測函數(shù)void loop_lcd12864_is_busy(unsigned char right)。

  void loop_lcd12864_is_busy(unsigned char right)

  {

  unsigned char tmp,counter=0;

  do {

  if(right) tmp = LCD_RCR;

  else tmp = LCD_LCR;

  if(counter++>50) break; // 超時跳出

  }

  while ((tmp|0x7f)==0xff); //bit7 為1 則表示LCD 內(nèi)部執(zhí)行命令,處于“忙”狀態(tài)

  }

  對LCD 進(jìn)行讀寫操作時,需要進(jìn)行“忙”檢測,LCD 內(nèi)部也是由控制器來完成一系列刷屏操作的,執(zhí)行各種操作都是需要一定的時間,也就是說不是任何時候外部控制器都可以對LCD 發(fā)操作指令的,只有LCD為空閑狀態(tài)時才可以操作,忙檢測就是循環(huán)讀取LCD狀態(tài)標(biāo)志位,判斷是否空閑,關(guān)于命令的細(xì)節(jié)請參考數(shù)據(jù)手冊。

  命令寫入函數(shù)void lcd_cmd_wr(unsigned char cmd,right)。

  void lcd_cmd_wr(unsigned char cmd, right)

  {

  loop_lcd12864_is_busy(right); // 忙檢測

  if(right) LCD_RCW = cmd; // 右屏命令寫入

  else LCD_LCW = cmd; // 左屏命令寫入

  }

  數(shù)據(jù)寫入函數(shù)void lcd_dat_wr(unsigned char data,right)。

  void lcd_dat_wr(unsigned char data,right)

  {

  loop_lcd12864_is_busy(right);

  if(right) LCD_RDW = data;

  else LCD_LDW = data;

  }

  lcd_cmd_wr() 和lcd_dat_wr() 兩個函數(shù)分別是給LCD 寫命令和寫數(shù)據(jù)函數(shù),通過寫命令函數(shù)設(shè)定地址。每個函數(shù)都分左右屏,“right”參數(shù)選擇,“0”選左屏,“非0”選右屏。

  讀數(shù)據(jù)函數(shù)unsigned char lcd_dat_rd(unsigned char right)。

  unsigned char lcd_dat_rd(unsigned char right)

  {

  loop_lcd12864_is_busy(right);

  if(right) return(LCD_RDR);

  else retuen(LCD_LDR);

  }

  該函數(shù)可以讀出LCD 當(dāng)前顯示的數(shù)據(jù),首次操作需要讀2次才有效。

  LCD 清屏函數(shù)void lcd12864_clr(void)。

  void lcd12864_clr(void)

  {

  unsigned char i,j;

  for(i=0;i<8;i++) { // 從0 到7 共8 頁

  lcd_cmd_wr(ORGX,0); // 分頁設(shè)定左屏0 點(diǎn)地址

  lcd_cmd_wr(ORGY+i,0);

  lcd_cmd_wr(ORGX,1); // 分頁設(shè)定右屏0 點(diǎn)地址

  lcd_cmd_wr(ORGY+i,1);

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

  lcd_data_wr(0,0);

  lcd_data_wr(0,1);

  }

  }

  }

  該函數(shù)對LCD 所有點(diǎn)陣寫0,完成一次清屏操作。這里的ORGY,PRGX 是設(shè)定光標(biāo)的命令,光標(biāo)指向(0,0)字節(jié),是一個固定值。實(shí)際在執(zhí)行數(shù)據(jù)寫入的時,x 坐標(biāo)范圍從0 到63,在連續(xù)寫入過程中能夠?qū)崿F(xiàn)自動加1,y 軸頁地址范圍從0 到7,需要逐頁設(shè)定。

  LCD 初始化函數(shù)void lcd12864_init(void)。

  void lcd12864_init(void)

  {

  lcd_cmd_wr(DISPON,0); // 顯示開啟

  lcd_cmd_wr(DISPFIRST,0); // 設(shè)定顯示首行地址,修改首行地址可以實(shí)現(xiàn)屏幕滾動顯示效果

  lcd_cmd_wr(ORGY,0); // 設(shè)定初始光標(biāo)

  lcd_cmd_wr(ORGX,0);

  lcd_cmd_wr(DISPON,1); // 初始另外一半

  lcd_cmd_wr(DISPFIRST,1);

  lcd_cmd_wr(ORGY,1);

  lcd_cmd_wr(ORG,1);

  lcd12864_clr(); // 執(zhí)行清屏,非必須操作

  }

  該函數(shù)用來初始化LCD,設(shè)置顯示模式,光標(biāo)位置等,在對LCD 繪圖時,最多的命令就是設(shè)定當(dāng)前光標(biāo)位置,通過光標(biāo)位置來指定將要操作的LCD 顯示點(diǎn)。

  在對LCD 編程操作以前,一定要執(zhí)行此函數(shù)對LCD 進(jìn)行初始化操作。

  從驅(qū)動函數(shù)可見,一次對LCD 寫入數(shù)據(jù)是以字節(jié)為單位,通過寫命令設(shè)定坐標(biāo),y 坐標(biāo)從0 頁到7 頁,x 坐標(biāo)從0 列到63 列,分左右屏,左上角為坐標(biāo)(0,0)點(diǎn),這和我們習(xí)慣的左下角為(0,0)坐標(biāo)軸是不一樣的。

  因?yàn)槊看尾僮鱈CD 是一個字節(jié)為單位,對應(yīng)8 點(diǎn),如果我們希望以任意點(diǎn)為坐標(biāo)顯示,還得另外尋找別的辦法編程實(shí)現(xiàn)真正“點(diǎn)”顯示。

  如圖3 所示,在屏幕上指定位置畫點(diǎn),水平軸就是x,與LCD 坐標(biāo)一致,垂直軸需要將點(diǎn)坐標(biāo)變成字節(jié)為單位的坐標(biāo),我們先按習(xí)慣將y 軸64 點(diǎn)從下至上編號0 到63,其中0 到7 點(diǎn)為字節(jié)0,8 到15 點(diǎn)為字節(jié)1,依此類推對應(yīng)8 字節(jié)。

圖3 LCD“點(diǎn)”顯示示例

圖3 LCD“點(diǎn)”顯示示例

  第一點(diǎn)y 軸為30,應(yīng)該對應(yīng)垂直哪個字節(jié)的哪個比特呢?

  實(shí)際30 點(diǎn)應(yīng)該在第4 字節(jié)(24 到31)的Bit 6 上,拿30/8 取整為3,剛好是應(yīng)該跳過的前3 字節(jié)(對應(yīng)0 到23),那么30%8(30 除8 取余數(shù))呢,余數(shù)是6,不是剛好是Bit 位嗎?所以可以這樣將y 值映射到某字節(jié)的某點(diǎn)上,如果y 軸64 點(diǎn)對應(yīng)8 字節(jié)變量Da[n],n從0 到7,則:

  da[y/8] = 1 《 (y%8);或da[y》3]=0x01《(y&0x07);后一種算法更優(yōu)。

關(guān)鍵字:LCD12864 數(shù)字示波器

 

通過總結(jié)規(guī)律,用以上算法可以將任意0 到63 之間的數(shù)據(jù)作為坐標(biāo)描點(diǎn)到對應(yīng)的8 個字節(jié)中,然后將8個字節(jié)全部寫入LCD,則通過剛才算法就會有一點(diǎn)與所給坐標(biāo)一致。

  第一點(diǎn):da[30/8] = 1 《 30%8; 即da[3] = 0x40;

  第二點(diǎn):da[10/8] = 1 《10%8; 即da[1] = 0x04;

  首先給出列顯示子函數(shù),在任意列顯示y 值對應(yīng)點(diǎn)。

  {

  unsigned char j;

  if(x<64) { // 根據(jù)列坐標(biāo)選擇左右半屏

  for(j=0;j<8;j++) { // 寫左半屏

  lcd_cmd_wr(ORGY+j,0);

  lcd_cmd_wr(ORGX+x,0);

  lcd_data_wr(da[j],0);

  }

  }

  else {

  x-=64;// 坐標(biāo)調(diào)整

  for(j=0;j<8;j++) { // 寫右半屏

  lcd_cmd_wr(ORGY+j,1);

  lcd_cmd_wr(ORGX+x,1);

  lcd_data_wr(da[j],1);

  }

  }

  }

  有了列顯示函數(shù),LCD 任何坐標(biāo)位置上描點(diǎn)繪圖函數(shù)為:

  void lcd_disp(unsigned char x,y) //x 水平坐標(biāo),y 垂直坐標(biāo)

  {

  unsigned char dat[8];

  unsigned char j;

  y=63-y;// 使xy 坐標(biāo)符合習(xí)慣

  for(j=0;j<8;j++) dat[j] = 0x0;

  dat[y/8] |= 0x01《(y&0x07);

  lcd_row_wr(x,dat);

  }

  以上函數(shù)能夠在指定坐標(biāo)(x,y)上描點(diǎn),下面嘗試將ADC 的值采集后送LCD 顯示。再按時間軸x 軸順序?qū)⒉煌瑫r刻采集到的y 值順序?qū)懭隠CD,這是我們就可以在LCD 上看到隨時間變化的電壓曲線了。主程序?yàn)椋?/p>

  void main()

  {

  unsigned char i;

  lcd12864_init();

  for(i=0;i<128;i++) {

  lcd_disp(i,read_adc(0)/16);// 從10bit 映射到6bit,要除16

  }

  while(1);

  }

  調(diào)整輸入給ADC 的信號頻率,可以得到滿意的波形圖了,效果如圖4 所示。

圖4 LCD實(shí)時顯示ADC圖

圖4 LCD實(shí)時顯示ADC圖

  如果你成功做到了這一步,可喜可賀,已經(jīng)掌握了繪圖基礎(chǔ)了,不過程序還要繼續(xù)完善。

  三、圖形液晶LCD12864繪圖驅(qū)動設(shè)計(jì)提高

  如何在現(xiàn)實(shí)波形顯示的基礎(chǔ)上,同時將定標(biāo)網(wǎng)格也顯示出來呢?

  首先我們看一種C 語法“A = 0x05; A |= 0x50;”運(yùn)行以上指令后,A = 0x55 ;也可以說第二個數(shù)據(jù)0x50 是疊加到第一個數(shù)據(jù)上的,我們可以用這種算法把需要顯示的亮點(diǎn)(也就是“1”)按一定的算法疊加在一起,送LCD 顯示,就出現(xiàn)了我們希望的在波形上增加背景網(wǎng)格的效果。

  因?yàn)榫W(wǎng)格與水平x 軸是嚴(yán)格關(guān)聯(lián)的,所以我們可以對x 軸數(shù)據(jù)進(jìn)行判斷,有規(guī)律的將邊框和背景格點(diǎn)加入。

  改進(jìn)帶背景格的lcd_disp() 函數(shù)。

  void lcd_disp(unsigned char x,unsigned char y)

  {

  unsigned char da[8];

  unsigned char j;

  y = 63-y;

  for(j=1;j<7;j++) da[j] = 0x0;

  da[0]=0x01;

  da[7]=0x80;

  if(x%5==0) {

  da[21/8] |= 0x01《(21%8);

  da[42/8] |= 0x01《(42%8);

  }

  if((x==0)||(x==127)) {// 加兩端邊框

  for(j=0;j<8;j++) da[j] = 0xff;

  }

  da[y/8] |= 0x01《(y%8);

  lcd_row_wr(x,da);

  }

  效果如圖5 所示。

圖5 帶網(wǎng)格的LCD顯示圖

圖5 帶網(wǎng)格的LCD顯示圖

四、圖形液晶LCD12864移動游標(biāo)線繪圖驅(qū)動設(shè)計(jì)

  聲明控制水平線的變量“unsigned charpointY=0;” 范圍0 到63, 聲明控制垂直線的變量“unsigned char pointX=0;”范圍0 到127。

  void lcd_disp(unsigned char x,unsigned char y)

  {

  unsigned char da[8];

  unsigned char j;

  y = 63-y;

  for(j=1;j<7;j++) da[j] = 0x0;

  {// 繪制邊框

  da[0]=0x01;

  da[7]=0x80;

  if((x==0)||(x==127)) {

  for(j=0;j<8;j++) da[j] = 0xff;

  }

  }

  if(x%5==0) da[pointY>>3] |= 0x01 《 (pointY&0x07);

  // 繪制由變量pointY 控制的水平游標(biāo)線

  if(x==pointX) // 繪制由變量pointX 控制的垂直游標(biāo)線

  for(j=0;j<64;j++)

  if(j%5==0) da[j>>3] |= 0x01 《(j&0x07);

  da[y/8] |= 0x01《(y%8); // 繪制信號波形

  lcd_row_wr(x,da);

  }

  運(yùn)行效果如圖6 所示。

圖6 水平垂直移動游標(biāo)線示例

圖6 水平垂直移動游標(biāo)線示例

  五、圖形液晶LCD12864數(shù)字符號顯示

  圖形點(diǎn)陣LCD 顯示數(shù)字,原理是把數(shù)字以點(diǎn)陣的形式取模,再把點(diǎn)陣模寫入特定的LCD 空間即可,首先來看數(shù)字取模,如圖7 所示,對數(shù)字“0”按8×5點(diǎn)取模。

圖7 數(shù)字取模示例圖

圖7 數(shù)字取模示例圖

  縱向看,8 點(diǎn)一列,從上至下對應(yīng)bit0 到bit7,我們用1 表示“亮”,0 表示“暗”,從左至右,依次確定為0111 1100,即0x7c ;1000 0010,即0x82 ;10000010, 即0x82 ;1000 0010, 即0x82 ;0111 1100,即0x7c ;如果我們依次將這5 個字節(jié)寫入LCD 某頁連續(xù)5個地址空間,LCD 上就會顯示“0”。

  下面我們把數(shù)字變量在LCD 上動態(tài)顯示,就是數(shù)值變了,顯示跟著變。

  字符顯示LCD 驅(qū)動函數(shù),實(shí)現(xiàn)8×n 點(diǎn)陣字符寫入函數(shù)。

  void lcd_put_xyns(unsigned char x,y,n,unsigned char *s)

  {

  unsigned char i;

  for(i=0;i

  if((x+i)>63) {

  lcd_cmd_wr(ORGY+y,1);

  lcd_cmd_wr(ORGX+x+i-64,1);

  lcd_dat_wr(s[i],1);

  }

  else {

  lcd_cmd_wr(ORGY+y,0);

  lcd_cmd_wr(ORGX+x+i,0);

  lcd_dat_wr(s[i],0);

  }

  }

  }

  參數(shù):“x, y”是坐標(biāo),這里y 是頁坐標(biāo),取值從0 到7,“n”是點(diǎn)陣模字節(jié)數(shù),“*s”是點(diǎn)陣模起始地址。

  將字模生成字模表:

  unsigned char code number[]={

  0x7C,0x82,0x82,0x7C,0x84,0xFE,0x80,0x00,0xCC,0xA2,0x

  92,0x8C,0x44,0x92,0x92,0x6C,

  0x38,0x24,0xFE,0x20,0x9E,0x92,0x92,0x62,0x7C,0x92,0x9

  2,0x64,0x06,0xF2,0x0E,0x02,

  0x6C,0x92,0x92,0x6C,0x4C,0x92,0x92,0x7C,0x80,

  };

  lcd_put_xyns(0,0,4,number+0*4); // 直接顯示字符0

  lcd_put_xyns(4,0,4,number+1*4); // 直接顯示字符1

  ……

  tmp=3421; // 以下代碼顯示變量tmp

  lcd_put_xyns(80,2,4,number+(tmp/1000)*4);

  lcd_put_xyns(84,2,4,number+(tmp/100%10)*4);

  lcd_put_xyns(88,2,4,number+(tmp/10%10)*4);

  lcd_put_xyns(92,2,4,number+(tmp%10)*4);

關(guān)鍵字:LCD12864 數(shù)字示波器

 

運(yùn)行效果如圖8 所示。

圖8 LCD顯示數(shù)字圖

圖8 LCD顯示數(shù)字圖

  基于以上原理,我們還可以將其他字符取模顯示。

  最終完成的效果如圖9 所示。

圖9 完整示波器效果圖

圖9 完整示波器效果圖

  六、數(shù)字示波器幾個參數(shù)

  1. 數(shù)據(jù)采樣率與時間軸定標(biāo)

  數(shù)據(jù)采樣率也就是每秒鐘連續(xù)采集到的數(shù)據(jù)個數(shù),或者說兩個有效數(shù)據(jù)之間的時間間隔。為了在LCD 上還原波形圖,時間軸與采樣率之間要有嚴(yán)格的換算關(guān)系。

  為了還原波形圖,采樣率與被測量信號頻率之間有一定的關(guān)聯(lián),不同的采樣率,對應(yīng)不同頻段的信號,高采樣率適合高頻信號,低采樣率適合變化緩慢的低頻信號。

  從顯示效果來看,為了在屏幕上再現(xiàn)信號的頻率特征,根據(jù)Nyquist 頻率特征定理,一般要保證信號的每個周期內(nèi)至少有2 點(diǎn),用LCD 再現(xiàn),2 點(diǎn)效果已經(jīng)很差了。最高采樣率一般由ADC 器件決定,這里用的TLC1549 最快只能做到88μs 采集一點(diǎn),按2 點(diǎn)每周期計(jì)算的話,可以做到對5.6K(1000000/88/2) 信號采樣,實(shí)際效果已經(jīng)很差了,1K 以內(nèi)效果最好。

  實(shí)際在設(shè)計(jì)采樣率檔位時,最快檔按100μs 每點(diǎn),屏幕每5 點(diǎn)(500μs)一格,第一檔就是0.5ms/div,這一檔位需要連續(xù)采樣,用少量延時控制采樣率為10k,第二檔就是1ms/div,第三檔2ms/div,第四檔5ms/div,第五檔10ms/div,后面四檔分別用定時器控制完成。

  2. 電壓軸分檔定標(biāo)

  由于這版沒做模擬電路,沒有信號電調(diào)理電路,所謂電壓檔定標(biāo)是純軟件算法實(shí)現(xiàn),屏幕同樣取5 點(diǎn)一格,共10 格,當(dāng)輸入信號從0 到5V 變化時,LCD 剛好滿屏顯示,每格0.5V,另外還設(shè)計(jì)了0.1v/div,0.2v/div,1v/div,2v/div,5v/div。共6 檔。

  3. 交流直流切換

  功能完善的示波器應(yīng)該從硬件上實(shí)現(xiàn)交直流切換,本版先從軟件上實(shí)現(xiàn)交流直流顯示,設(shè)計(jì)思想是根據(jù)采樣得到的數(shù)據(jù)的最大值和最小值確定交流信號幅度和中值,把直流部分減掉,顯示只顯示交流部分,這就是交流檔,直流檔就是不做去直流處理。

  4. 運(yùn)行停止切換

  該功能實(shí)現(xiàn)起來很容易,所謂停止,就是停止新的數(shù)據(jù)采集,重復(fù)顯示同一幀數(shù)據(jù),顯示的效果就是波形穩(wěn)定無抖動,便于對信號電壓和頻率進(jìn)行測量。

  5. 電壓測量

  根據(jù)當(dāng)前電壓檔位(每格表示多少伏),計(jì)算出兩測量線之間的電壓值,在LCD 上顯示。

  6. 頻率測量

  根據(jù)當(dāng)前時間軸檔位(每格表示多長時間),計(jì)算出兩測量線之間的時間,假定兩測量線之間剛好是一個周期,轉(zhuǎn)換成頻率值在LCD 上顯示。

  7. 波形平移

  用變量控制波形在LCD 上顯示的相對位置,實(shí)現(xiàn)波形上下左右平移。

  8. 幀同步

  我們使用的數(shù)字示波器,對有規(guī)律的周期信號能夠穩(wěn)定顯示,要想實(shí)現(xiàn)穩(wěn)定顯示,需要在起始點(diǎn)顯示信號的不同周期的同一點(diǎn),如何做到這點(diǎn)呢?

  如圖10 所示,我們對比兩幀數(shù)據(jù),波形起點(diǎn)不在同一點(diǎn),我們沒辦法保證每次數(shù)據(jù)采樣剛好在同一點(diǎn)(硬件觸發(fā)的除外),我需要按一定的規(guī)律,在LCD 上繪圖時,總是從同一點(diǎn)開始,例如過零點(diǎn),這里我們首先用統(tǒng)計(jì)的辦法找出信號的最大值和最小值,計(jì)算出信號中交流部分的中點(diǎn),也就是過零點(diǎn),然后逐點(diǎn)比較,搜尋過零點(diǎn)。

圖10 不同幀數(shù)據(jù)起點(diǎn)不同

圖10 不同幀數(shù)據(jù)起點(diǎn)不同

  程序設(shè)計(jì)思想是逐點(diǎn)比較過濾,我們?nèi)≈兄?,如圖7-1 中第5 點(diǎn)所示,信號從小于中值到大于中值的上升沿為起點(diǎn),不同幀信號可能從1、2、3、4 任何一點(diǎn)開始,這就要用程序判斷把設(shè)定的起點(diǎn)5 以前的數(shù)據(jù)丟掉。

  程序是這樣的:

  while(da_buffer[i] > dam) if(++i > (DATA_SIZE/2)) break;

  while(da_buffer[i] <= dam) if(++i > (DATA_SIZE/2)) break;

  while(da_buffer[i] > dam) if(++i > (DATA_SIZE/2)) break;

  這里的dam 就是信號交流部分的中值。

  程序用幾個while 語句,看上去很呆板,實(shí)際運(yùn)行效果很好喔。第一句把比設(shè)定值大的數(shù)據(jù)過濾,如圖10 中的1、2、3 點(diǎn),如果數(shù)據(jù)是從4 開始的,第一句會直接跳過;第二句把比設(shè)定值小的數(shù)據(jù)跳過,找出過零點(diǎn)的上升沿5 點(diǎn)。

  9. 矢量繪圖

  前面我們介紹的LCD 描點(diǎn)繪圖不是矢量繪圖,圖形由一系列的“點(diǎn)”組成,在波形繪圖區(qū),每列只有1個點(diǎn),拿方波繪圖來看,描點(diǎn)繪圖圖形如圖12 所示,矢量繪圖在點(diǎn)與點(diǎn)之間填充“直線”,方波繪圖效果如圖11 所示。對比兩種繪圖效果,最好選擇矢量繪圖,LCD 波形顯示效果較點(diǎn)繪圖好很多。

圖11 點(diǎn)繪圖

圖11 點(diǎn)繪圖

圖12 矢量繪圖

圖12 矢量繪圖

  矢量繪圖需要按時間軸連續(xù)兩點(diǎn)同時考慮,根據(jù)兩點(diǎn)之間的差值補(bǔ)點(diǎn),這就是繪圖程序disp(x, y, l)中參數(shù)l 的作用。

  七、數(shù)字示波器程序流程圖設(shè)計(jì)分析

  表達(dá)程序設(shè)計(jì)思想的關(guān)鍵就是程序流程圖,下面將重點(diǎn)分析本程序設(shè)計(jì)的幾個關(guān)鍵流程圖。

  主程序流程圖如圖13 所示,初始化完成系統(tǒng)初始化設(shè)置,包括全局變量初始化,紅外按鍵中斷、定時器中斷初始化,在主循環(huán)程序中,處理外部按鍵輸入,由于數(shù)據(jù)采集是在定時中斷中完成的,主循環(huán)中需要等待一幀數(shù)據(jù)采集完成后,才能對數(shù)據(jù)進(jìn)行同步處理,包括直流濾波,數(shù)據(jù)到LCD 映射,調(diào)用LCD 顯示函數(shù)完成波形繪圖,一幀數(shù)據(jù)處理完成后,重新開中斷,等待下一幀數(shù)據(jù)采集。

圖13 主程序流程圖圖

圖13 主程序流程圖圖

LCD 波形顯示流程圖如圖14 所示,LCD 波形顯示區(qū)需要根據(jù)新采集到的數(shù)據(jù)不斷刷新,這里以LCD列為單位,一次繪圖一個(x, y)坐標(biāo)點(diǎn),這里的編程思想是定義7 字節(jié)內(nèi)存變量,與LCD 的第x 列對應(yīng),首先對參數(shù)進(jìn)行檢查,例如y 參數(shù)就不能超過LCD 顯示去最大值,超過需要修正,確保點(diǎn)到點(diǎn)映射不出錯,需要顯示的內(nèi)容除了波形信號外,還有窗口邊框,背景格,游標(biāo)線等,都要同時考慮。這里的l 參數(shù)是兩點(diǎn)之間的間隔,采用矢量顯示,兩點(diǎn)之間用直線矢量繪圖代替描點(diǎn),顯示效果要好很多。

圖14 LCD波形顯示

圖14 LCD波形顯示

  數(shù)據(jù)采集流程圖如圖15 所示,數(shù)據(jù)采集需要嚴(yán)格等時,而且根據(jù)不同頻率的信號,從低頻到高頻需要用不同的采樣率,所以這里用定時器控制采樣時間間隔。

圖15 數(shù)據(jù)采集流程圖

圖15 數(shù)據(jù)采集流程圖

  由于定時器中斷任務(wù)調(diào)用需要額外時間,對于高速中斷是有一定時間間隔限制的,通過計(jì)算,100μs 周期采樣用定時器實(shí)現(xiàn)不了,所以這里定時數(shù)據(jù)采樣分高速和低速兩檔,高速檔一次一幀,低速檔一次一點(diǎn),一幀數(shù)據(jù)采集完成后,回到主程序?qū)?shù)據(jù)進(jìn)行處理。

  按鍵任務(wù)流程圖如圖16 所示,由于示波器控制需要很多按鍵,涉及電壓檔位,采樣時間檔位,游標(biāo)線等,這些檔位都由一個全局變量控制,在按鍵任務(wù)模塊,就是根據(jù)有效按鍵,對這些檔位全局變量進(jìn)行加減調(diào)整,變量調(diào)整后,有些效果會在波形顯示刷新中體現(xiàn)出來,還有的需要單獨(dú)刷新顯示。

圖16 按鍵任務(wù)流程圖

圖16 按鍵任務(wù)流程圖

  八、經(jīng)驗(yàn)總結(jié)與心得體會

  STC89C52RC 內(nèi)部擴(kuò)展RAM 只有256 字節(jié),不像數(shù)據(jù)手冊上介紹的512 字節(jié),外部RAM 超量使用會導(dǎo)致意想不到的后果,經(jīng)常莫名其妙的死機(jī)。

  窗口波形顯示有兩種模式,逐點(diǎn)顯示(采集一個數(shù)據(jù)馬上刷新顯示)和逐幀顯示(采集完成一幀數(shù)據(jù)后刷新顯示),經(jīng)過測試論證,低頻逐點(diǎn)視覺效果好,高頻沒區(qū)別,但是逐點(diǎn)沒辦法同步。

  由于版面限制,這里發(fā)表的只是一個壓縮版,詳細(xì)資料見“大學(xué)生電子實(shí)驗(yàn)室論壇” http://www.ceet.hbnu.edu.cn/bbs/。

  初學(xué)者在學(xué)習(xí)他人程序時,不要僅僅對程序代碼下功夫看懂,其實(shí)真正學(xué)習(xí)的捷徑是看懂程序流程圖,學(xué)習(xí)他人的程序設(shè)計(jì)思想,在吸收他人程序設(shè)計(jì)思想的前提下,具體編程實(shí)現(xiàn)特定的功能,程序可以有多種“寫法”。

  作為終端輸出顯示裝置,并不需要很高的刷新速度,因?yàn)樗墙o“人”看的,每秒超過10 次的變化,對人眼睛產(chǎn)生不了有用的效果。

  我寫這篇教程,特別注重解決問題方法的總結(jié),我認(rèn)為掌握一種解決問題的方法比掌握一種具體的知識要重要得多。

 

 

此內(nèi)容為AET網(wǎng)站原創(chuàng),未經(jīng)授權(quán)禁止轉(zhuǎn)載。