C語言不允許這樣,keil允許是因?yàn)樗皇菄?yán)格的C語言,IAR不允許說明他嚴(yán)格按照C語言標(biāo)準(zhǔn)。
創(chuàng)新互聯(lián)公司專注于亳州企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè),商城網(wǎng)站建設(shè)。亳州網(wǎng)站建設(shè)公司,為亳州等地區(qū)提供建站服務(wù)。全流程專業(yè)公司,專業(yè)設(shè)計,全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)
如果是我我會把函數(shù)指針定義為void (*Pq)(uint8),
然后如果有void f(void)類型的函數(shù)就把他定義為void f(uint8 temp){temp=temp; ........ }
首先系統(tǒng)復(fù)位時,Cortex-M3從代碼區(qū)偏移0x0000'0000處獲取棧頂?shù)刂?,用來初始化MSP寄存器的值。
接下來從代碼區(qū)偏移0x0000'0004獲取第一個指令的跳轉(zhuǎn)地址。這些地址,是CM3要求放置中斷向量表的地方。
這里是一個程序的啟動區(qū)的反匯編:
__vector_table:
08004000 2600
08004002 2000
08004004 7E1D
08004006 0800
這個程序是由IAP程序來啟動的,IAP程序獲取0x0800'4000處的MSP值(0x20002600),并設(shè)置為MSP的值,即主堆棧最大
范圍是0x2000'0000~0x2000'25FF。接下來IAP程序獲取0x0800'4004處的Reset_Handler的地址
(0x0800'7E1D),并跳轉(zhuǎn)到Reset_Handler()執(zhí)行。
IAP在這里完全是模仿了Cortex-M3的復(fù)位序列,也就是說,在沒有IAP的系統(tǒng)上,CM3只能從0x0800'0000獲取MSP,從
0x0800'0004獲取第一條指令所處地址。而IAP就存在在0x0800'0000這個地址上,IAP的啟動,已經(jīng)消耗掉了這個復(fù)位序列,所以
IAP要啟動UserApp程序的時候,也是完全模仿Cortex-M3的復(fù)位序列的。
接下來我們看看復(fù)位后第一句指令——Reset_Handler()函數(shù)里有什么。
若我們使用的是ST公司標(biāo)準(zhǔn)外設(shè)庫,那么已經(jīng)有了現(xiàn)成的Reset_Handler,不過他是弱定義——PUBWEAK,可以被我們重寫的同名函數(shù)覆蓋。一般來說,我們使用的都是ST提供的Reset_Handler,在V3.4版本的庫中,可以在startup_stm32f10x_xx.s中找到這個函數(shù):
PUBWEAK Reset_Handler
SECTION .text:CODE:REORDER(2)
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0
看來ST沒有做太多的事,他只調(diào)用了自家?guī)焯峁┑腟ystemInit函數(shù)進(jìn)行系統(tǒng)時鐘、Flash讀取的初始化,并把大權(quán)交給了
__iar_program_start這個IAR提供的“內(nèi)部函數(shù)”了,我們就跟緊這個__iar_program_start跳轉(zhuǎn),看看IAR做了什
么,上面一段代碼的反匯編如下:
Reset_Handler:
__iar_section$$root:
08007E1C 4801 LDR R0, [PC, #0x4]; LDR R0, =SystemInit
08007E1E 4780 BLX R0;BLX R0
08007E20 4801 LDR R0, [PC, #0x4];LDR R0, =__iar_program_start
08007E22 4700 BX R0;BX R0
08007E24 6C69
08007E26 0800
08007E28 7D8D
08007E2A 0800
細(xì)心的觀眾會發(fā)現(xiàn)地址是0x0800'7E1C,比我們查到的0x0800'7E1D差了1,這是ARM家族的遺留問題,因?yàn)锳RM處理器的指令至
少是半字對齊的(16位THUMB指令集 or
32位ARM指令集),所以PC指針的LSB是常為0的,為了充分利用寄存器,ARM公司給PC的LSB了一個重要的使命,那就是在執(zhí)行分支跳轉(zhuǎn)時,PC
的LSB=1,表示使用THUMB模式,LSB=0,表示使用ARM模式,但在最新的Cortex-M3內(nèi)核上,只使用了THUMB-2指令集挑大梁,所
以這一位要常保持1,所以我們查到的地址是0x0800'7E1D(C=1100,D=1101),放心,我們的CM3內(nèi)核會忽略掉LSB(除非為0,那
么會引起一個fault),從而正確跳轉(zhuǎn)到0x0800'7E1C。
從0x0800'7E20處的加載指令,我們可以算出__iar_program_start所處的位置,就是當(dāng)前PC指針
(0x0800'7E24),再加上4,即0x0800'7E28處的所指向的地址——0x0800'7D8D(0x0800'7D8C),我們跟緊著跳
轉(zhuǎn),__iar_program_start果然在這里:
__iar_program_start:
08007D8C F000F88C BL __low_level_init
08007D90 2800 CMP R0, #0x0
08007D92 D001 BEQ __iar_init$$done
08007D94 F7FFFFDE BL __iar_data_init2
08007D98 2000 MOVS R0, #0x0
08007D9A F7FDFC49 BL main
我們看到IAR提供了__low_level_init這個函數(shù)進(jìn)行了“底層”的初始化,進(jìn)一步跟蹤,我們可以查到__low_level_init這個函數(shù)做了些什么,不是不是我們想象中的不可告人。
__low_level_init:
08007EA8 2001 MOVS R0, #0x1
08007EAA 4770 BX LR
__low_level_init出乎想象的簡單,只是往R0寄存器寫入了1,就立即執(zhí)行"BX
LR"回到調(diào)用處了,接下來,__iar_program_start檢查了R0是否為0,為0,則執(zhí)行__iar_init$$done,若不是0,就
執(zhí)行__iar_data_init2。__iar_init$$done這個函數(shù)很簡單,只有2句話,第一句是把R0清零,第二句就直接"BL
main",跳轉(zhuǎn)到main()函數(shù)了。不過既然__low_level_init已經(jīng)往R0寫入了1,那么我們還是得走下遠(yuǎn)路——看看
__iar_data_init2做了些什么,雖然距離main只有一步之遙,不過這中間隱藏了編譯器的思想,我們得耐心看下去。
__iar_data_init2:
08007D54 B510 PUSH {R4,LR}
08007D56 4804 LDR R0, [PC, #0x10]
08007D58 4C04 LDR R4, [PC, #0x10]
08007D5A E002 B 0x8007D62
08007D5C F8501B04 LDR R1, [R0], #0x4
08007D60 4788 BLX R1
08007D62 42A0 CMP R0, R4
08007D64 D1FA BNE 0x8007D5C
08007D66 BD10 POP {R4,PC}
08007D68 7C78
08007D6A 0800
08007D6C 7C9C
08007D6E 0800
看來IAR遲遲不執(zhí)行main()函數(shù),就是為了執(zhí)行__iar_data_init2,我們來分析分析IAR都干了些什么壞事~
首先壓R4,LR入棧,然后加載0x0800'7C78至R0,0x0800'7C9C至
R4,馬上跳轉(zhuǎn)到0x0800'7D62執(zhí)行R0,R4的比較,結(jié)果若是相等,則彈出R4,PC,然后立即進(jìn)入main()。不過IAR請君入甕是自不會
那么快放我們出來的——結(jié)果不相等,跳轉(zhuǎn)到0x0800'7D5C執(zhí)行,在這里,把R0指向的地址——0x0800'7C78中的值——
0x0800'7D71加載到R1,并且R0中的值自加4,更新為0x0800'7C7C,并跳轉(zhuǎn)到R1指向的地址處執(zhí)行,這里是另一個IAR函
數(shù):__iar_zero_init2:
__iar_zero_init2:
08007D70 2300 MOVS R3, #0x0
08007D72 E005 B 0x8007D80
08007D74 F8501B04 LDR R1, [R0], #0x4
08007D78 F8413B04 STR R3, [R1], #0x4
08007D7C 1F12 SUBS R2, R2, #0x4
08007D7E D1FB BNE 0x8007D78
08007D80 F8502B04 LDR R2, [R0], #0x4
08007D84 2A00 CMP R2, #0x0
08007D86 D1F5 BNE 0x8007D74
08007D88 4770 BX LR
08007D8A 0000 MOVS R0, R0
__iar_data_init2還沒執(zhí)行完畢,就跳轉(zhuǎn)到了這個__iar_zero_inti2,且看我們慢慢分析這個幫兇——__iar_zero_inti2做了什么。
__iar_zero_inti2將R3寄存器清零,立即跳轉(zhuǎn)到0x0800'7D80執(zhí)行'LDR R2, [R0],
#0x4',這句指令與剛才在__iar_data_init2見到的'LDR R1, [R0],
#0x4'很類似,都為“后索引”。這回,將R0指向的地址——0x0800'7C7C中的值——0x0000'02F4加載到R2寄存器,然后R0中的
值自加4,更新為0x0800'7C80。接下來的指令檢查了R2是否為0,顯然這個函數(shù)沒那么簡單想放我我們,R2的值為2F4,我們又被帶到了
0x0800'7D74處,隨后4條指令做了如下的事情:
1、將R0指向的地址——0x0800'7C80中的值——0x2000'27D4加載到R1寄存器,然后R0中的值自加4,更新為0x0800'7C84。
2、將R1指向的地址——0x2000'27D4中的值——改寫為R3寄存器的值——0,然后R1中的值自加4,更新為0x2000'27D8。
3、R2自減4
4、檢查R2是否為0,不為0,跳轉(zhuǎn)到第二條執(zhí)行。不為,則執(zhí)行下一條。
這簡直就是一個循環(huán)!——C語言的循環(huán)for(r2=0x2F4;r2-=4;r!=0){...},我們看看循環(huán)中做了什么。
第一條指令把一個地址加載到了R1——0x2000'27D4
是一個RAM地址,以這個為起點(diǎn),在循環(huán)中,對長度為2F4的RAM空間進(jìn)行了清零的操作。那為什么IAR要做這個事情呢?消除什么記錄么?用Jlink
查看這片內(nèi)存區(qū)域,可以發(fā)現(xiàn)這片區(qū)域是我們定義的全局變量的所在地。也就是說,IAR在每次系統(tǒng)復(fù)位后,都會自動將我們定義的全局變量清零0。
清零完畢后,接下來的指令"LDR R2, [R0],
#0x4"將R0指向的地址——0x0800'7C84中的值——0加載到R2寄存器,然后R0中的值自加4,更新為0x0800'7C88。隨后檢查
R2是否為0,這里R2為0,執(zhí)行'BX
LR'返回到__iar_data_init2函數(shù),若是不為0,我們可以發(fā)現(xiàn)又會跳轉(zhuǎn)至“4指令”處進(jìn)行一個循環(huán)清零的操作。
4-ADC12應(yīng)有例程
//******************************************************************************
//MSP430F149 ADC12模塊+串行通訊的實(shí)驗(yàn)程序
//使用ADC12采集實(shí)驗(yàn),將采集到數(shù)據(jù)送向PC.(單路單次采集)
//P3.4為發(fā)送,P3.5為接收 晶體使32768HZ/8MHZ. 串行波特率B/S
//使用SMCLK作為波特率發(fā)器時,不能使用LPM2,LPM3!
//以下程序已驗(yàn)證通過,初學(xué)者可直接使用.由時間倉促和水平有限,請讀者批評指正.
//編寫:
//******************************************************************************
#include msp430x14x.h
//********************************************
//表區(qū)
unsigned char number_table[]={'0','1','2','3','4','5','6','7','8','9'};
unsigned char display_buffer[]={0x00,0x00,0x00,0x00,0xff};
//*******************波特率***********300 600 1200 2400 4800 9600 19200 38400 76800 115200const
//************************************[0]**[1]**[2]*[3]**[4]**[5]***[6]***[7]****[8]***[9]*
unsigned char BaudrateUBR0[] ={0x6D,0x36,0x1B,0x0D,0x06,0x03, 0xA0, 0xD0, 0x68, 0x45};
unsigned const char BaudrateUBR1[] ={0x00,0x00,0x00,0x00,0x00,0x00, 0x01, 0x00, 0x00, 0x00};
unsigned const char BaudrateUMCTL[]={0x22,0xD5,0x03,0x6B,0x6F,0x4A, 0xC0, 0x40, 0x40, 0x4A};
unsigned char timp;
//變量區(qū)
unsigned int ADC0 ;
//子程序聲明
void init (void); //初始化
void ADC12setup(void); //ADC12初始化
void BaudrateSetup(unsigned char U0); //UART0初始化
void data_converter(unsigned char *p,unsigned int vaule); //數(shù)據(jù)變換
void send_data(unsigned char *p); //串行口發(fā)送數(shù)組
//********************************************
void main(void)
{
init();
//主循環(huán)
for ()
{
LPM0;
ADC12CTL0 |= ADC12SC; //sampling open,AD轉(zhuǎn)換完成后(ADC12BUSY=0),ADC12SC自動復(fù)位;
while((ADC12IFG BIT0) == 0); //等轉(zhuǎn)換結(jié)束
ADC0 = ADC12MEM0; //讀轉(zhuǎn)換數(shù)據(jù)值,同時清ADC12IFG0標(biāo)志
data_converter(display_buffer,ADC0); //數(shù)據(jù)變換
send_data(display_buffer); //發(fā)送數(shù)據(jù)
}
}
//********************************************************************************
void init(void)
{
WDTCTL = WDTPW + WDTHOLD; // 停止WDT
P1DIR=0x01;P1OUT=0x0f; //LED設(shè)置
BaudrateSetup(6);
ADC12setup();
_EINT(); // 全局中斷使能
}
//**********************************************************************************
//串口接收中斷,退出LPM0模式.
#pragma vector=USART0RX_VECTOR
__interrupt void usart0_rx (void)
{
LPM0_EXIT;
}
//**********************************************************************************
//ADC12初始化
void ADC12setup(void)
{
//ADC12設(shè)置**************************
P6SEL |= 0x01; //使用A/D通道 A0
ADC12CTL0 = ADC12ON ; //開ADC12內(nèi)核,設(shè)SHT0=2 (N=4)
ADC12CTL1 = SHP ; //SAMPCON信號選為采樣定時器輸出
//ADC12內(nèi)部參考電壓設(shè)置
ADC12CTL0 |= REF2_5V; //選用內(nèi)部參考電壓為2.5V
ADC12CTL0 |= REFON; //內(nèi)部參考電壓打開
ADC12MCTL0 |= SREF_1; //R+=2.5V R-=VSS
//轉(zhuǎn)換允許
ADC12CTL0 |= ENC ; //轉(zhuǎn)換允許(上升沿)
ADC0=0x00;
}
//**********************************************************************************
//UART0初始化
void BaudrateSetup(unsigned char U0)
{
unsigned int i;
if(U05) //當(dāng)U05時,啟用XT2
{
BCSCTL1 = ~XT2OFF; //啟動XT2,
do
{ IFG1 = ~OFIFG; //清OSCFault標(biāo)志
for(i=0xFF;i0;i--); //延時等待
}
while((IFG1 OFIFG) != 0); //查OSCFault,為0時轉(zhuǎn)換完成
BCSCTL2 |= SELS; //SMCLK為XT2
}
//UART0
P1OUT=0x00;
if(U05){UTCTL0=SSEL1;} // 時鐘源:SMCLK
else{UTCTL0=SSEL0;} // 時鐘源:ACLK
UCTL0 = ~SWRST; // SWRST復(fù)位, USART允許
UCTL0=CHAR; // 8bit
ME1|=UTXE0 + URXE0; // Enable Tx0,Rx0
IE1|=URXIE0; // RX使能
UBR00=BaudrateUBR0[U0]; // 低位分頻器因子
UBR10=BaudrateUBR1[U0]; // 高位分頻器因子
UMCTL0=BaudrateUMCTL[U0]; // 波特率調(diào)整因子
P3SEL |= 0x30; // 將P3.4,5使用外圍模塊 = USART0 TXD/RXD
P3DIR |= 0x10; // 將P3.4設(shè)為輸出(發(fā)),P3.5默認(rèn)為輸入(收)
}
//**********************************************************************************
//數(shù)據(jù)變換
void data_converter(unsigned char *p,unsigned int value)
{
unsigned int m,n,j=0;
p[0]=number_table[value/1000];
m=value%1000;
p[1]=number_table[m/100];
n=m%100;
p[2]=number_table[n/10];
j=n%10;
p[3]=number_table[j/1];
}
//**********************************************************************************
//串行口發(fā)送數(shù)組
void send_data(unsigned char *p)
{unsigned int n;
timp=RXBUF0;
for(n=0;p[n]!=0xff;n++)
{
while ((IFG1 UTXIFG0) == 0); // USART0發(fā)送UTXIFG0=1,表示UTXBUF準(zhǔn)備好發(fā)送一下字符
TXBUF0 = p[n];
}
}
//**********************************************************************************
//ADC12模塊例程(1)結(jié)速
添加相應(yīng)的頭文件即可。
IAR中使用printf()函數(shù)和允許位定義的方法
⑴使用printf()函數(shù):
①包含stdio.h頭文件
②編寫putchar函數(shù)
示例:
int putchar(int c)
{
while (!(UCSRA 0x20));
return (UDR=c);
}
③設(shè)置
Option
名稱欄目:iarc語言怎么做函數(shù) c語言 argc
文章地址:http://bm7419.com/article6/ddephog.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、微信公眾號、小程序開發(fā)、網(wǎng)站制作、標(biāo)簽優(yōu)化、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)