|
實驗目的
①了解PXA270處理器結構
②了解ARM指令集
③了解嵌入式系統的引導過程
④了解八段數碼管的知識
⑤了解系統的硬件尋址方式
⑥掌握ADS編程和調試方法
⑦掌握JTAG調試技巧
實驗內容
①分析PXA270基本結構
②分析Eeliod實驗平臺實現的存儲系統架構
③分析PXA270的引導過程
④分析Eeliod實驗平臺LED發光管和7段數碼管的設計原理圖
⑤參考系統引導示例程序完成數碼管的控制代碼
⑥編譯程序,下載執行,讓數碼管顯示一組特定的數組
實驗原理
①LED實驗原理
實驗板上的八個LED的陰極直接與鎖存器74574的輸出端相連,陽極通過限流電阻上拉到+5V,所以鎖存器的輸出直接控制了LED的發光與否;鎖存器的輸入連接到PXA270的數據總線的低八位上,鎖存器的鎖存信號來自一塊3-8譯碼器的輸出Y5,所以Y5從低到高的跳變將PXA270數據總線第八位數據送到鎖存器鎖存住,而3-8譯碼器的輸入的譯碼信號ABC連接到PXA270地址線的A20、A21和A22上,所以Y5選中需要A22~A20為101b;另外3-8譯碼器的使能控制信號G2B與PXA270的內存空間片選信號CS4相連,CS4片選的內存地址空間為0x1000_0000~0x13FF_FFFF,為簡單起見,可將設為0x1000_0000,再加上A22~A20的編碼可得到LED的片選地址為0x1000_0000+0x0050_0000=0x1050_0000,后面只需向該地址寫一個字節的數據就可以控制LED。
②數碼管實驗原理
實驗板上有四個一位共陽極八段數碼管,采用分立的鎖存器單獨控制。數碼管的陰極通過限流電阻直接與鎖存器74574的輸出相連。數碼管的第八段小數點沒有使用,相應的第八段控制信號被用來控制數碼管的通電與否,通過一個PNP的三極管來控制,鎖存器輸出的Q8腳連接到該三極管的基極,Q8低電平時三極管導通,數碼管供電,所以數碼管正常顯示時Q8必須為低電平。以上對于四個數碼管通用。對于第一二個數碼管,它們相連的鎖存器的輸入數據信號分別為PXA270數據信號的D0~D7和D8~D15,兩個鎖存器的片選信號都接到LED實驗原理中提到的3-8譯碼器的Y3上,同LED實驗原理中的地址計算方法,可得數碼管1和2的地址為0x1000_0000+0x0030_0000=0x1030_0000,后面控制數碼管1和2時只需要向這個地址寫半個字(16位)的數據即可,其中數據的低八位對應第一個數碼管,高八位對應第二個數碼管(注意需要將每個八位的最高位置0來打開數碼管的供電)。數碼管3和4的控制跟1和2的控制類似,只不過將地址改為0x1000_0000+0x0040_0000=0x1040_0000即可。
實驗步驟
第一步 分析代碼
結合以上說明,對本實驗提供的示例代碼分析,深入理解針對具體的硬件實現,軟件是如何配合工作的。
第二步 程序的編譯和下載
利用ADS打開示例工程文件,執行Project→Make,編譯、鏈接生成可執行映像文件
第三步 觀察系統運行情況,對系統進行源碼調試
程序說明
①LED控制
通過對LED的地址直接寫入數據即可完成對LED的控制,在高級語言中一般無法直接完成對內存指定地址的操作,但在C語言中可以利用指針來完成該操作。同時由于PXA270內部帶有高速緩存Cache,所以需要用關鍵字volatile來限定該指針使得每次對指針的操作都直接操作到內存,而不通過Cache。
②數碼管控制
數碼管基本控制原理與LED控制相同,只是地址換成數碼管的地址。實驗板上共有4個數碼管,4個數碼管分成兩組,每組用一個地址;在一組內,用16位二進制(半字)來控制兩個數碼管;注意要使數碼管正常工作,每個該半字的第8位和第16位必須為0來控制三極管打開使得數碼管通電。
程序源代碼、注釋
①LED代碼
- //LED地址
- #define LED_VALUE (*((volatile unsigned char *)(0x10500000)))
- //位定義
- #define BITNULL (0x00<<0)
- #define BIT0 (0x01<<0)
- #define BIT1 (0x01<<1)
- #define BIT2 (0x01<<2)
- #define BIT3 (0x01<<3)
- #define BIT4 (0x01<<4)
- #define BIT5 (0x01<<5)
- #define BIT6 (0x01<<6)
- #define BIT7 (0x01<<7)
- //粗略延時函數
- void Delay(unsigned int x)
- {
- unsigned int n, j, k;
- for (n =0; n <=x; n++)
- for (j = 0; j <0xff; j++)
- for (k = 0; k <0xff; k++);
- }
- int main(void)
- {
- int i;
- //流水花樣查找表,這里是邏輯層0表示不顯示,1表示顯示,與硬件無關
- unsigned char LUT[] =
- {
- BIT0 + BIT7,
- BIT1 + BIT6,
- BIT2 + BIT5,
- BIT3 + BIT4,
- BIT2 + BIT5,
- BIT1 + BIT6,
- BIT0 + BIT7,
- BITNULL,
- };
- while (1)
- {
- LED_VALUE = 0xff;
-
- for (i = 0; i < 8; i++)
- {
- //這里是電氣層,由于LED是共陽極,所以這里需要取反
- LED_VALUE = ~LUT[i];
- Delay(200);
- }
- }
-
- return 0;
- }
- ②數碼管代碼
- 1)SegLed.h文件
- #ifndef __SEGLED_H__
- #define __SEGLED_H__
- #define SEG_NULL ('9' + 1)
- //顯示數字函數的控制參數
- enum
- {
- DISP_NORMAL = 0,
- DISP_BLANKING
- };
- extern signed char SegLedDispAt(unsigned char, signed char);
- extern signed char SegLedDispNum(short, unsigned char);
- #endif
- 2)SegLed.c文件
- #include "SegLed.h"
- //第一組和第二組數碼管的地址
- #define SEGLED0 (*((volatile unsigned short int *)(0x10300000)))
- #define SEGLED1 (*((volatile unsigned short int *)(0x10400000)))
- //數碼管上各個段對應一個字節中的某個位的宏定義
- #define SEGA (0x01<<0u)
- #define SEGB (0x01<<1u)
- #define SEGC (0x01<<2u)
- #define SEGD (0x01<<3u)
- #define SEGE (0x01<<4u)
- #define SEGF (0x01<<5u)
- #define SEGG (0x01<<6u)
- #define SEGH (0x01<<7u)
- //數碼管顯示不顯示以及顯示0~9宏定義,這里只是邏輯層的定義,具體還需根據數碼管是共的什么極來確定需不需要取反
- #define DIGNULL (SEGH)
- #define DIG0 (SEGA + SEGB + SEGC + SEGD + SEGE + SEGF + SEGH)
- #define DIG1 (SEGB + SEGC + SEGH)
- #define DIG2 (SEGA + SEGB + SEGD + SEGE + SEGG + SEGH)
- #define DIG3 (SEGA + SEGB + SEGC + SEGD + SEGG + SEGH)
- #define DIG4 (SEGB + SEGC + SEGF + SEGG + SEGH)
- #define DIG5 (SEGA + SEGC + SEGD + SEGF + SEGG + SEGH)
- #define DIG6 (SEGA + SEGC + SEGD + SEGE + SEGF + SEGG + SEGH)
- #define DIG7 (SEGA + SEGB + SEGC + SEGH)
- #define DIG8 (SEGA + SEGB + SEGC + SEGD + SEGE + SEGF + SEGG + SEGH)
- #define DIG9 (SEGA + SEGB + SEGC + SEGD + SEGF + SEGG + SEGH)
- unsigned short segBuff0, segBuff1; //定義數碼管的"顯示緩沖"
- /*
- 名 稱:SegLedDispAt()
- 功 能:在四個數碼管的指定位置顯示指定的字符
- 入口參數:ch:需要顯示的字符的ASCII值,目前支持的字符僅限于數字0~9(即'0' <= ch <= '9')和空字符SEGNULL
- pos:顯示的字符位于哪個數碼管,0 <= pos <= 3 分別表示第一到第四個數碼管
- 出口參數:正確執行返回0,否則返回-1
- 說 明:本函數是對于底層數碼管的硬件抽象,所有上層驅動都調用此函數完成各種顯示功能,一般情況下用戶程序
- 不需要調用此函數。確實需要調用此函數時,注意第一個參數是ASCII值,不是數字,而是數字對應的ASCII
- 使用范例:SegLedDispAt('6', 0); 在第一個數碼管上顯示數字6
- */
- signed char SegLedDispAt(unsigned char ch, signed char pos)
- {
- unsigned char tempDisp;
- const unsigned char segLUT[] = {DIG0, DIG1, DIG2, DIG3, DIG4, DIG5, DIG6, DIG7, DIG8, DIG9, DIGNULL};
- if((pos < 0) || (pos > 3))
- return -1;
-
- tempDisp = ~segLUT[ch - '0']; //數碼管共陽極的需要在這里取反,注意共陰極的除了不需要取反還要修改空顯示的宏定義
-
- switch(pos)
- {
- case 0: segBuff0 = ((segBuff0 & 0xFF00) | tempDisp);break;
- case 1: segBuff0 = ((segBuff0 & 0x00FF) | tempDisp << 8);break;
- case 2: segBuff1 = ((segBuff1 & 0xFF00) | tempDisp);break;
- case 3: segBuff1 = ((segBuff1 & 0x00FF) | tempDisp << 8);break;
- default:break;
- }
-
- SEGLED0 = segBuff0;
- SEGLED1 = segBuff1;
-
- return 0;
- }
- /*
- 名 稱:SegLedDispNum()
- 功 能:在四個數碼管上顯示0~4位正整數
- 入口參數:num:需要顯示的正整數,范圍0~9999
- argv:顯示控制參數,目前可選賦值為DISP_NORMAL和DISP_BLANKING支持普通顯示(不夠四位時前面補零)
- 和消隱顯示(不夠四位時右對齊,前面不足處不顯示)
- 出口參數:正確執行返回0,否則返回-1
- 說 明:無
- 使用范例:SegLedDispNum(666, DISP_BLANKING); 在數碼管上顯示666,右對齊,第一個數碼管不顯示
- */
- signed char SegLedDispNum(short num, unsigned char argv)
- {
- unsigned char numLen = 0;
- signed char i;
- short tempNum;
-
-
- if(num < 0 || num > 9999)
- return -1;
-
- switch(argv)
- {
- case DISP_NORMAL:
- {
- for(i = 3; i != -1; i--)
- {
- SegLedDispAt(num % 10 + '0', i);
- num /= 10;
- }
- break;
- }
-
- case DISP_BLANKING:
- {
- numLen = 1;
- for(tempNum = num; tempNum /= 10; numLen++); //計算數字長度,供消隱使用
-
- for(i = 3; i != 3 - numLen; i--)
- {
- SegLedDispAt(num % 10 + '0', i); //從個位開始一直顯示到消隱前一位
- num /= 10;
- }
-
- for(; i != -1; i--)
- {
- SegLedDispAt(SEG_NULL, i); //對剩下的前面幾位消隱(寫空)
- }
- break;
- }
- default:
- break;
- }
-
- return 0;
- }
復制代碼
總結
ADS集成開發環境的編譯器不支持在函數外對全局變量進行初始化,在LED實驗時,定義了一個全局的查找表,通過查找表控制LED的顯示花樣,但是在運行時發現LED的顯示始終不對,后來在單步調試的時候發現全局變量那個查找表的內容根本不對,后來在函數內部定義了一個查找表,問題解決。
|
|