指針是C語言最核心的部分,而UART串口通信是單片機最常用的一種通信方式。因此這兩部分內(nèi)容,除了在第10章進行簡單的介紹外,本章還需要進一步加深學習,用實用的例子來不斷增強對于這兩部分內(nèi)容的理解和應(yīng)用能力。11.1指向數(shù)組元素的指針
11.1.1指向數(shù)組元素的指針和運算法則所謂指向數(shù)組元素的指針,其本質(zhì)還是變量的指針。因為數(shù)組中的每個元素,其實都可以直接看成是一個變量,所以指向數(shù)組元素的指針,也就是變量的指針。 指向數(shù)組元素的指針不難,但很常用。 unsigned char number[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; unsigned char *p; 如果寫p = &number[0];那么指針p就指向了number的第0號元素,也就是把number[0]的地址賦值給了p,同理,如果寫p = &number[1];p就指向了數(shù)組number的第1號元素。p = &number[x];其中x的取值范圍是0~9,就表示p指向了數(shù)組number的第x號元素。 指針本身,也可以進行幾種簡單的運算,這幾種運算對于數(shù)組元素的指針來說應(yīng)用最多。 1、比較運算。比較的前提是兩個指針指向同種類型的對象,比如兩個指針變量p和q它們指向了具有同種數(shù)據(jù)類型的數(shù)組,那它們可以進行<,>,>=,<=,==等關(guān)系運算。如果p==q為真的話,表示這兩個指針指向的是同一個元素。 2、指針和整數(shù)可以直接進行加減運算。比如還是前邊講的指針p和數(shù)組number,如果p = &number[0],那么p+1就指向了number[1],p+9就指向了number[9]。當然了,如果p = &number[9],p-9也就指向了number[0]。 3、兩個指針變量在一定條件下可以進行減法運算。如p = &number[0]; q = &number[9];那么q-p的結(jié)果就是9。這個地方要特別注意,這個9代表的是元素的個數(shù),而不是真正的地址差值。如果number的變量類型是unsigned int型,占2個字節(jié),q-p的結(jié)果依然是9,因為它代表的是數(shù)組元素的個數(shù)。 在數(shù)組元素指針這里還有一種情況,數(shù)組名字就代表了數(shù)組元素的首地址,也就是說: p = &number[0]; p = number; 這兩種表達方式是等價的,因此以下幾種表達形式和內(nèi)容需要格外注意一下。 根據(jù)指針的運算規(guī)則,p+x代表的是number[x]的地址,那么number+x代表的也是number[x]的地址。或者說,它們指向的都是number數(shù)組的第x號元素。 *(p+x)和*(number+x)都表示number[x]。 指向數(shù)組元素的指針也可以表示成數(shù)組的形式,也就是說,允許指針變量帶下標,即p[ i]和*(p+i)是等價的。為了避免混淆,這里建議不要寫成前者,而一律采用后者的寫法。 [i ]二維數(shù)組元素的指針和一維數(shù)組類似,需要介紹的內(nèi)容不多。假如現(xiàn)在一個指針變量p和一個二維數(shù)組number[3][4],它的地址的表達方式也就是p=&number[0][0],有一個地方要注意,既然數(shù)組名代表了數(shù)組元素的首地址,那么也就是說p和number都是指數(shù)組的首地址。對二維數(shù)組來說,number[0],number[1],number[2]都可以看成是一維數(shù)組的數(shù)組名字,所以number[0]等價于&number[0][0],number[1]等價于&number[1][0],number[2]等價于&number[2][0]。加減運算和一維數(shù)組是類似的,不再詳述。 [i ]11.1.2 數(shù)組元素指針的實例在C語言里邊,sizeof()可用來獲取括號內(nèi)對象所占用的字節(jié)數(shù),雖然寫成函數(shù)形式,但它不是一個函數(shù),而是C語言的一個關(guān)鍵字,sizeof()在程序中相當于一個常量,也就是說這個獲取操作是在程序編譯的時候進行的,而不是在程序運行的時候進行。這是一個實際編程中很有用的關(guān)鍵字,靈活運用它可以為程序帶來更好的可讀性、易維護性和可移植性。 sizeof()括號中可以是變量名,也可以是變量類型名。而其更大的用處是與數(shù)組名搭配使用,可以獲取整個數(shù)組占用的字節(jié)數(shù),不用自己動手計算了,可以避免錯誤,而如果日后改變了數(shù)組的維數(shù)時,也不需要執(zhí)行代碼中逐個修改,便于程序的維護和移植。 下面提供一個簡單的串口演示例程,可以體驗一下指針和sizeof()的用法。例程首先接收上位機下發(fā)的命令,根據(jù)命令值分別把不同數(shù)組的數(shù)據(jù)回發(fā)給上位機,程序還用到了指針的自增運算,也就是+1運算,體會一下指針ptrTxd在串口發(fā)送的過程中的指向是如何變化的。在上位機串口調(diào)試助手中分別下發(fā)1、2、3、4,就會得到不同的數(shù)組回發(fā),注意這里都用十六進制發(fā)送和十六進制顯示。 此外,前邊講了串口發(fā)送中斷標志位TI是硬件置位,軟件清零的。通常來講,如果想一次發(fā)送多個數(shù)據(jù)的時候,就需要把第一個字節(jié)寫入SBUF,然后再等待發(fā)送中斷,在后續(xù)中斷中再發(fā)送剩余的數(shù)據(jù),這樣數(shù)據(jù)發(fā)送過程就被拆分到了兩個地方——主循環(huán)內(nèi)和中斷服務(wù)函數(shù)內(nèi),無疑就使得程序結(jié)構(gòu)變得零散了。這個時候,為了使程序結(jié)構(gòu)盡量緊湊,在啟動發(fā)送的時候,不是向SBUF中寫入第一個待發(fā)的字節(jié),而是直接讓TI=1,注意,這時候會馬上進入串口中斷,因為中斷標志位置1了,但是串口線上并沒有發(fā)送任何數(shù)據(jù)。于是,所有的數(shù)據(jù)發(fā)送都可以在中斷中進行,而不用再分為兩部分了。 #include <reg52.h> bit cmdArrived = 0; //命令到達標志,即接收到上位機下發(fā)的命令 unsigned char cmdIndex = 0; //命令索引,即與上位機約定好的數(shù)組編號 unsigned char cntTxd = 0; //串口發(fā)送計數(shù)器 unsigned char *ptrTxd; //串口發(fā)送指針 unsigned char array1[1] = {1}; unsigned char array2[2] = {1,2}; unsigned char array3[4] = {1,2,3,4}; unsigned char array4[8] = {1,2,3,4,5,6,7,8}; void ConfigUART(unsigned int baud); void main() { EA = 1; //開總中斷 ConfigUART(9600); //配置波特率為9600 while (1) { if (cmdArrived) { cmdArrived = 0; switch (cmdIndex) { case 1: ptrTxd = array1; //數(shù)組1的首地址賦值給發(fā)送指針 cntTxd = sizeof(array1); //數(shù)組1的長度賦值給發(fā)送計數(shù)器 TI = 1; //手動方式啟動發(fā)送中斷,處理數(shù)據(jù)發(fā)送 break; case 2: ptrTxd = array2; cntTxd = sizeof(array2); TI = 1; break; case 3: ptrTxd = array3; cntTxd = sizeof(array3); TI = 1; break; case 4: ptrTxd = array4; cntTxd = sizeof(array4); TI = 1; break; default: break; } } } } /* 串口配置函數(shù),baud-通信波特率 */ void ConfigUART(unsigned int baud) { SCON = 0x50; //配置串口為模式1 TMOD &= 0x0F; //清零T1的控制位 TMOD |= 0x20; //配置T1為模式2 TH1 = 256 - (11059200/12/32)/baud; //計算T1重載值 TL1 = TH1; //初值等于重載值 ET1 = 0; //禁止T1中斷 ES = 1; //使能串口中斷 TR1 = 1; //啟動T1 } /* UART中斷服務(wù)函數(shù) */ void InterruptUART() interrupt 4 { if (RI) //接收到字節(jié) { RI = 0; //清零接收中斷標志位 cmdIndex = SBUF; //接收到的數(shù)據(jù)保存到命令索引中 cmdArrived = 1; //設(shè)置命令到達標志 } if (TI) //字節(jié)發(fā)送完畢 { TI = 0; //清零發(fā)送中斷標志位 if (cntTxd > 0) //有待發(fā)送數(shù)據(jù)時,繼續(xù)發(fā)送后續(xù)字節(jié) { SBUF = *ptrTxd; //發(fā)出指針指向的數(shù)據(jù) cntTxd--; //發(fā)送計數(shù)器遞減 ptrTxd++; //發(fā)送指針遞增 } } } 采用邏輯分析儀將4次收發(fā)數(shù)據(jù)全部抓出來,直觀的做一下對比,如圖11-1所示。
11-1.png (62.23 KB, 下載次數(shù): 0)
下載附件
2026-4-23 10:09 上傳
圖11-1 邏輯分析儀抓取串口數(shù)據(jù)數(shù)據(jù) |